1#[cfg(feature = "rayon")]
2use crate::adsorption::fea_potential::calculate_fea_potential;
3use crate::functional::HelmholtzEnergyFunctional;
4#[cfg(feature = "rayon")]
5use crate::geometry::Geometry;
6use libm::tgamma;
7use nalgebra::DVector;
8use ndarray::{Array1, Array2, Axis as Axis_nd};
9#[cfg(feature = "rayon")]
10use quantity::Length;
11use std::f64::consts::PI;
12use std::ops::Deref;
13
14const DELTA_STEELE: f64 = 3.35;
15
16#[derive(Clone)]
18pub enum ExternalPotential {
19 HardWall { sigma_ss: f64 },
21 LJ93 {
23 sigma_ss: f64,
24 epsilon_k_ss: f64,
25 rho_s: f64,
26 },
27 SimpleLJ93 { sigma_ss: f64, epsilon_k_ss: f64 },
29 CustomLJ93 {
31 sigma_sf: Array1<f64>,
32 epsilon_k_sf: Array1<f64>,
33 },
34 Steele {
36 sigma_ss: f64,
37 epsilon_k_ss: f64,
38 rho_s: f64,
39 xi: Option<f64>,
40 },
41 CustomSteele {
43 sigma_sf: Array1<f64>,
44 epsilon_k_sf: Array1<f64>,
45 rho_s: f64,
46 xi: Option<f64>,
47 },
48 DoubleWell {
50 sigma_ss: f64,
51 epsilon1_k_ss: f64,
52 epsilon2_k_ss: f64,
53 rho_s: f64,
54 },
55 #[cfg(feature = "rayon")]
57 FreeEnergyAveraged {
58 coordinates: Length<Array2<f64>>,
59 sigma_ss: Array1<f64>,
60 epsilon_k_ss: Array1<f64>,
61 pore_center: [f64; 3],
62 system_size: [Length; 3],
63 n_grid: [usize; 2],
64 cutoff_radius: Option<f64>,
65 },
66
67 Custom(Array2<f64>),
69}
70
71pub trait FluidParameters {
73 fn epsilon_k_ff(&self) -> DVector<f64>;
74 fn sigma_ff(&self) -> DVector<f64>;
75}
76
77impl<C: Deref<Target = T>, T: FluidParameters> FluidParameters for C {
78 fn epsilon_k_ff(&self) -> DVector<f64> {
79 T::epsilon_k_ff(self)
80 }
81 fn sigma_ff(&self) -> DVector<f64> {
82 T::sigma_ff(self)
83 }
84}
85
86impl ExternalPotential {
87 pub fn calculate_cartesian_potential<P: HelmholtzEnergyFunctional + FluidParameters>(
89 &self,
90 z_grid: &Array1<f64>,
91 fluid_parameters: &P,
92 #[cfg_attr(not(feature = "rayon"), expect(unused_variables))] temperature: f64,
93 ) -> Array2<f64> {
94 if let ExternalPotential::Custom(potential) = self {
95 return potential.clone();
96 }
97
98 let m = fluid_parameters.m();
100 let mut ext_pot = Array2::zeros((m.len(), z_grid.len()));
101
102 for (i, &mi) in m.iter().enumerate() {
103 ext_pot.index_axis_mut(Axis_nd(0), i).assign(&match self {
104 Self::HardWall { sigma_ss } => {
105 let sigma_sf = (fluid_parameters.sigma_ff()[i] + *sigma_ss) * 0.5;
106 z_grid.mapv(|z| if z < sigma_sf { f64::INFINITY } else { 0.0 })
107 }
108 Self::LJ93 {
109 sigma_ss,
110 epsilon_k_ss,
111 rho_s,
112 } => {
113 let epsilon_k_sf =
115 (fluid_parameters.epsilon_k_ff() * *epsilon_k_ss).map(|e| e.sqrt());
116 let sigma_sf = fluid_parameters.sigma_ff().add_scalar(*sigma_ss) * 0.5;
117
118 2.0 * PI * mi * epsilon_k_sf[i] * sigma_sf[i].powi(3) * rho_s / 45.0
119 * (2.0 * (sigma_sf[i] / z_grid).mapv(|x| x.powi(9))
120 - 15.0 * (sigma_sf[i] / z_grid).mapv(|x| x.powi(3)))
121 }
122 Self::SimpleLJ93 {
123 sigma_ss,
124 epsilon_k_ss,
125 } => {
126 let epsilon_k_sf =
128 (fluid_parameters.epsilon_k_ff() * *epsilon_k_ss).map(|e| e.sqrt());
129 let sigma_sf = fluid_parameters.sigma_ff().add_scalar(*sigma_ss) * 0.5;
130
131 epsilon_k_sf[i]
132 * ((sigma_sf[i] / z_grid).mapv(|x| x.powi(9))
133 - (sigma_sf[i] / z_grid).mapv(|x| x.powi(3)))
134 }
135 Self::CustomLJ93 {
136 sigma_sf,
137 epsilon_k_sf,
138 } => {
139 epsilon_k_sf[i]
140 * ((sigma_sf[i] / z_grid).mapv(|x| x.powi(9))
141 - (sigma_sf[i] / z_grid).mapv(|x| x.powi(3)))
142 }
143 Self::Steele {
144 sigma_ss,
145 epsilon_k_ss,
146 rho_s,
147 xi,
148 } => {
149 let epsilon_k_sf =
151 (fluid_parameters.epsilon_k_ff() * *epsilon_k_ss).map(|e| e.sqrt());
152 let sigma_sf = fluid_parameters.sigma_ff().add_scalar(*sigma_ss) * 0.5;
153
154 (2.0 * PI * mi * xi.unwrap_or(1.0) * epsilon_k_sf[i])
155 * (sigma_sf[i].powi(2) * DELTA_STEELE * rho_s)
156 * (0.4 * (sigma_sf[i] / z_grid).mapv(|x| x.powi(10))
157 - (sigma_sf[i] / z_grid).mapv(|x| x.powi(4))
158 - sigma_sf[i].powi(4)
159 / ((3.0 * DELTA_STEELE)
160 * (z_grid + 0.61 * DELTA_STEELE).mapv(|x| x.powi(3))))
161 }
162 Self::CustomSteele {
163 sigma_sf,
164 epsilon_k_sf,
165 rho_s,
166 xi,
167 } => {
168 (2.0 * PI * mi * xi.unwrap_or(1.0) * epsilon_k_sf[i])
169 * (sigma_sf[i].powi(2) * DELTA_STEELE * rho_s)
170 * (0.4 * (sigma_sf[i] / z_grid).mapv(|x| x.powi(10))
171 - (sigma_sf[i] / z_grid).mapv(|x| x.powi(4))
172 - sigma_sf[i].powi(4)
173 / ((3.0 * DELTA_STEELE)
174 * (z_grid + 0.61 * DELTA_STEELE).mapv(|x| x.powi(3))))
175 }
176 Self::DoubleWell {
177 sigma_ss,
178 epsilon1_k_ss,
179 epsilon2_k_ss,
180 rho_s,
181 } => {
182 let epsilon1_k_sf =
184 (fluid_parameters.epsilon_k_ff() * *epsilon1_k_ss).map(|e| e.sqrt());
185 let epsilon2_k_sf =
186 (fluid_parameters.epsilon_k_ff() * *epsilon2_k_ss).map(|e| e.sqrt());
187 let sigma_sf = fluid_parameters.sigma_ff().add_scalar(*sigma_ss) * 0.5;
188
189 let bh_tail = (2.0 * PI * mi * epsilon2_k_sf[i] * sigma_sf[i].powi(3) * rho_s
190 / 45.0
191 * (2.0 * (2.0 * sigma_sf[i] / z_grid).mapv(|x| x.powi(9))
192 - 15.0 * (2.0 * sigma_sf[i] / z_grid).mapv(|x| x.powi(3))))
193 .mapv(|x| x.min(0.0));
194
195 bh_tail
196 + &(2.0 * PI * mi * epsilon1_k_sf[i] * sigma_sf[i].powi(3) * rho_s / 45.0
197 * (2.0 * (sigma_sf[i] / z_grid).mapv(|x| x.powi(9))
198 - 15.0 * (sigma_sf[i] / z_grid).mapv(|x| x.powi(3))))
199 }
200 #[cfg(feature = "rayon")]
201 Self::FreeEnergyAveraged {
202 coordinates,
203 sigma_ss,
204 epsilon_k_ss,
205 pore_center,
206 system_size,
207 n_grid,
208 cutoff_radius,
209 } => {
210 let epsilon_k_sf =
212 (fluid_parameters.epsilon_k_ff()[i] * epsilon_k_ss).map(|e| e.sqrt());
213 let sigma_sf = (fluid_parameters.sigma_ff()[i] + sigma_ss) * 0.5;
214
215 calculate_fea_potential(
216 z_grid,
217 mi,
218 coordinates,
219 sigma_sf,
220 epsilon_k_sf,
221 pore_center,
222 system_size,
223 n_grid,
224 temperature,
225 Geometry::Cartesian,
226 *cutoff_radius,
227 )
228 }
229 _ => unreachable!(),
230 });
231 }
232 ext_pot
233 }
234
235 pub fn calculate_cylindrical_potential<P: HelmholtzEnergyFunctional + FluidParameters>(
237 &self,
238 r_grid: &Array1<f64>,
239 pore_size: f64,
240 fluid_parameters: &P,
241 #[cfg_attr(not(feature = "rayon"), expect(unused_variables))] temperature: f64,
242 ) -> Array2<f64> {
243 if let ExternalPotential::Custom(potential) = self {
244 return potential.clone();
245 }
246
247 let m = fluid_parameters.m();
249 let mut ext_pot = Array2::zeros((m.len(), r_grid.len()));
250
251 for (i, &mi) in m.iter().enumerate() {
252 ext_pot.index_axis_mut(Axis_nd(0), i).assign(&match self {
253 Self::HardWall { sigma_ss } => {
254 let sigma_sf = (fluid_parameters.sigma_ff()[i] + *sigma_ss) * 0.5;
255 r_grid.mapv(|r| {
256 if r > pore_size - sigma_sf {
257 f64::INFINITY
258 } else {
259 0.0
260 }
261 })
262 }
263 Self::LJ93 {
264 sigma_ss,
265 epsilon_k_ss,
266 rho_s,
267 } => {
268 let epsilon_k_sf =
270 (fluid_parameters.epsilon_k_ff() * *epsilon_k_ss).map(|e| e.sqrt());
271 let sigma_sf = fluid_parameters.sigma_ff().add_scalar(*sigma_ss) * 0.5;
272
273 (phi(6, &(r_grid / pore_size), sigma_sf[i] / pore_size)
274 - phi(3, &(r_grid / pore_size), sigma_sf[i] / pore_size))
275 * 2.0
276 * PI
277 * mi
278 * epsilon_k_sf[i]
279 * sigma_sf[i].powi(3)
280 * *rho_s
281 }
282 Self::SimpleLJ93 {
283 sigma_ss: _,
284 epsilon_k_ss: _,
285 } => {
286 unimplemented!()
287 }
288 Self::CustomLJ93 {
289 sigma_sf: _,
290 epsilon_k_sf: _,
291 } => {
292 unimplemented!()
293 }
294 Self::Steele {
295 sigma_ss,
296 epsilon_k_ss,
297 rho_s,
298 xi,
299 } => {
300 let epsilon_k_sf =
302 (fluid_parameters.epsilon_k_ff() * *epsilon_k_ss).map(|e| e.sqrt());
303 let sigma_sf = fluid_parameters.sigma_ff().add_scalar(*sigma_ss) * 0.5;
304
305 (2.0 * PI * mi * xi.unwrap_or(1.0) * epsilon_k_sf[i])
306 * (sigma_sf[i].powi(2) * DELTA_STEELE * rho_s)
307 * (psi(6, &(r_grid / pore_size), sigma_sf[i] / pore_size)
308 - psi(3, &(r_grid / pore_size), sigma_sf[i] / pore_size)
309 - sigma_sf[i] / DELTA_STEELE
310 * phi(
311 3,
312 &(r_grid / (pore_size + DELTA_STEELE * 0.61)),
313 sigma_sf[i] / (pore_size + DELTA_STEELE * 0.61),
314 ))
315 }
316 Self::CustomSteele {
317 sigma_sf,
318 epsilon_k_sf,
319 rho_s,
320 xi,
321 } => {
322 (2.0 * PI * mi * xi.unwrap_or(1.0) * epsilon_k_sf[i])
323 * (sigma_sf[i].powi(2) * DELTA_STEELE * rho_s)
324 * (psi(6, &(r_grid / pore_size), sigma_sf[i] / pore_size)
325 - psi(3, &(r_grid / pore_size), sigma_sf[i] / pore_size)
326 - sigma_sf[i] / DELTA_STEELE
327 * phi(
328 3,
329 &(r_grid / (pore_size + DELTA_STEELE * 0.61)),
330 sigma_sf[i] / (pore_size + DELTA_STEELE * 0.61),
331 ))
332 }
333 Self::DoubleWell {
334 sigma_ss,
335 epsilon1_k_ss,
336 epsilon2_k_ss,
337 rho_s,
338 } => {
339 let epsilon1_k_sf =
341 (fluid_parameters.epsilon_k_ff() * *epsilon1_k_ss).map(|e| e.sqrt());
342 let epsilon2_k_sf =
343 (fluid_parameters.epsilon_k_ff() * *epsilon2_k_ss).map(|e| e.sqrt());
344 let sigma_sf = fluid_parameters.sigma_ff().add_scalar(*sigma_ss) * 0.5;
345
346 let bh_tail = ((phi(6, &(r_grid / pore_size), 2.0 * sigma_sf[i] / pore_size)
347 - phi(3, &(r_grid / pore_size), 2.0 * sigma_sf[i] / pore_size))
348 * 2.0
349 * PI
350 * mi
351 * epsilon2_k_sf[i]
352 * sigma_sf[i].powi(3)
353 * *rho_s)
354 .mapv(|x| x.min(0.0));
355
356 bh_tail
357 + &((phi(6, &(r_grid / pore_size), sigma_sf[i] / pore_size)
358 - phi(3, &(r_grid / pore_size), sigma_sf[i] / pore_size))
359 * 2.0
360 * PI
361 * mi
362 * epsilon1_k_sf[i]
363 * sigma_sf[i].powi(3)
364 * *rho_s)
365 }
366 #[cfg(feature = "rayon")]
367 Self::FreeEnergyAveraged {
368 coordinates,
369 sigma_ss,
370 epsilon_k_ss,
371 pore_center,
372 system_size,
373 n_grid,
374 cutoff_radius,
375 } => {
376 let epsilon_k_sf =
378 (fluid_parameters.epsilon_k_ff()[i] * epsilon_k_ss).map(|e| e.sqrt());
379 let sigma_sf = (fluid_parameters.sigma_ff()[i] + sigma_ss) * 0.5;
380
381 calculate_fea_potential(
382 r_grid,
383 mi,
384 coordinates,
385 sigma_sf,
386 epsilon_k_sf,
387 pore_center,
388 system_size,
389 n_grid,
390 temperature,
391 Geometry::Cylindrical,
392 *cutoff_radius,
393 )
394 }
395 _ => unreachable!(),
396 });
397 }
398 ext_pot
399 }
400
401 pub fn calculate_spherical_potential<P: HelmholtzEnergyFunctional + FluidParameters>(
403 &self,
404 r_grid: &Array1<f64>,
405 pore_size: f64,
406 fluid_parameters: &P,
407 #[cfg_attr(not(feature = "rayon"), expect(unused_variables))] temperature: f64,
408 ) -> Array2<f64> {
409 if let ExternalPotential::Custom(potential) = self {
410 return potential.clone();
411 }
412
413 let m = fluid_parameters.m();
415 let mut ext_pot = Array2::zeros((m.len(), r_grid.len()));
416
417 for (i, &mi) in m.iter().enumerate() {
418 ext_pot.index_axis_mut(Axis_nd(0), i).assign(&match self {
419 Self::HardWall { sigma_ss } => {
420 let sigma_sf = (fluid_parameters.sigma_ff()[i] + *sigma_ss) * 0.5;
421 r_grid.mapv(|r| {
422 if r > pore_size - sigma_sf {
423 f64::INFINITY
424 } else {
425 0.0
426 }
427 })
428 }
429 Self::LJ93 {
430 sigma_ss,
431 epsilon_k_ss,
432 rho_s,
433 } => {
434 let epsilon_k_sf =
436 (fluid_parameters.epsilon_k_ff() * *epsilon_k_ss).map(|e| e.sqrt());
437 let sigma_sf = fluid_parameters.sigma_ff().add_scalar(*sigma_ss) * 0.5;
438
439 PI * mi
440 * epsilon_k_sf[i]
441 * rho_s
442 * (sigma_sf[i].powi(12) / 90.
443 * ((r_grid - 9.0 * pore_size)
444 / (r_grid - pore_size).mapv(|x| x.powi(9))
445 - (r_grid + 9.0 * pore_size)
446 / (r_grid + pore_size).mapv(|x| x.powi(9)))
447 - sigma_sf[i].powi(6) / 3.
448 * ((r_grid - 3.0 * pore_size)
449 / (r_grid - pore_size).mapv(|x| x.powi(3))
450 - (r_grid + 3.0 * pore_size)
451 / (r_grid + pore_size).mapv(|x| x.powi(3))))
452 / r_grid
453 }
454 Self::SimpleLJ93 {
455 sigma_ss: _,
456 epsilon_k_ss: _,
457 } => {
458 unimplemented!()
459 }
460 Self::CustomLJ93 {
461 sigma_sf: _,
462 epsilon_k_sf: _,
463 } => {
464 unimplemented!()
465 }
466 Self::Steele {
467 sigma_ss,
468 epsilon_k_ss,
469 rho_s,
470 xi,
471 } => {
472 let epsilon_k_sf =
474 (fluid_parameters.epsilon_k_ff() * *epsilon_k_ss).map(|e| e.sqrt());
475 let sigma_sf = fluid_parameters.sigma_ff().add_scalar(*sigma_ss) * 0.5;
476
477 (2.0 * PI * mi * xi.unwrap_or(1.0) * epsilon_k_sf[i])
478 * (sigma_sf[i].powi(2) * DELTA_STEELE * rho_s)
479 * (2.0 / 5.0 * sum_n(10, r_grid, sigma_sf[i], pore_size)
480 - sum_n(4, r_grid, sigma_sf[i], pore_size)
481 - sigma_sf[i] / (3.0 * DELTA_STEELE)
482 * (sigma_sf[i].powi(3)
483 / r_grid
484 .mapv(|r| (pore_size + 0.61 * DELTA_STEELE - r).powi(3))
485 + sigma_sf[i].powi(3)
486 / r_grid.mapv(|r| {
487 (pore_size + 0.61 * DELTA_STEELE + r).powi(3)
488 })
489 + 1.5
490 * sum_n(
491 3,
492 r_grid,
493 sigma_sf[i],
494 pore_size + 0.61 * DELTA_STEELE,
495 )))
496 }
497 Self::CustomSteele {
498 sigma_sf,
499 epsilon_k_sf,
500 rho_s,
501 xi,
502 } => {
503 (2.0 * PI * mi * xi.unwrap_or(1.0) * epsilon_k_sf[i])
504 * (sigma_sf[i].powi(2) * DELTA_STEELE * rho_s)
505 * (2.0 / 5.0 * sum_n(10, r_grid, sigma_sf[i], pore_size)
506 - sum_n(4, r_grid, sigma_sf[i], pore_size)
507 - sigma_sf[i] / (3.0 * DELTA_STEELE)
508 * (sigma_sf[i].powi(3)
509 / r_grid
510 .mapv(|r| (pore_size + 0.61 * DELTA_STEELE - r).powi(3))
511 + sigma_sf[i].powi(3)
512 / r_grid.mapv(|r| {
513 (pore_size + 0.61 * DELTA_STEELE + r).powi(3)
514 })
515 + 1.5
516 * sum_n(
517 3,
518 r_grid,
519 sigma_sf[i],
520 pore_size + 0.61 * DELTA_STEELE,
521 )))
522 }
523 Self::DoubleWell {
524 sigma_ss,
525 epsilon1_k_ss,
526 epsilon2_k_ss,
527 rho_s,
528 } => {
529 let epsilon1_k_sf =
531 (fluid_parameters.epsilon_k_ff() * *epsilon1_k_ss).map(|e| e.sqrt());
532 let epsilon2_k_sf =
533 (fluid_parameters.epsilon_k_ff() * *epsilon2_k_ss).map(|e| e.sqrt());
534 let sigma_sf = fluid_parameters.sigma_ff().add_scalar(*sigma_ss) * 0.5;
535
536 let bh_tail = (2.0
537 * PI
538 * mi
539 * epsilon2_k_sf[i]
540 * sigma_sf[i].powi(2)
541 * rho_s
542 * (2.0 / 5.0 * sum_n(10, r_grid, 2.0 * sigma_sf[i], pore_size)
543 - sum_n(4, r_grid, 2.0 * sigma_sf[i], pore_size)))
544 .mapv(|x| x.min(0.0));
545
546 bh_tail
547 + &(2.0
548 * PI
549 * mi
550 * epsilon1_k_sf[i]
551 * sigma_sf[i].powi(2)
552 * rho_s
553 * (2.0 / 5.0 * sum_n(10, r_grid, sigma_sf[i], pore_size)
554 - sum_n(4, r_grid, sigma_sf[i], pore_size)))
555 }
556 #[cfg(feature = "rayon")]
557 Self::FreeEnergyAveraged {
558 coordinates,
559 sigma_ss,
560 epsilon_k_ss,
561 pore_center,
562 system_size,
563 n_grid,
564 cutoff_radius,
565 } => {
566 let epsilon_k_sf =
568 (fluid_parameters.epsilon_k_ff()[i] * epsilon_k_ss).map(|e| e.sqrt());
569 let sigma_sf = (fluid_parameters.sigma_ff()[i] + sigma_ss) * 0.5;
570
571 calculate_fea_potential(
572 r_grid,
573 mi,
574 coordinates,
575 sigma_sf,
576 epsilon_k_sf,
577 pore_center,
578 system_size,
579 n_grid,
580 temperature,
581 Geometry::Spherical,
582 *cutoff_radius,
583 )
584 }
585 _ => unreachable!(),
586 });
587 }
588 ext_pot
589 }
590}
591
592fn phi(n: i32, r_r: &Array1<f64>, sigma_r: f64) -> Array1<f64> {
593 let m3n2 = 3.0 - 2.0 * n as f64;
594 let n2m3 = 2.0 * n as f64 - 3.0;
595
596 (1.0 - &r_r.mapv(|r| r.powi(2))).mapv(|r| r.powf(m3n2)) * 4.0 * PI.sqrt() / n2m3
597 * sigma_r.powf(n2m3)
598 * tgamma(n as f64 - 0.5)
599 / tgamma(n as f64)
600 * taylor_2f1_phi(r_r, n)
601}
602
603fn psi(n: i32, r_r: &Array1<f64>, sigma_r: f64) -> Array1<f64> {
604 (1.0 - &r_r.mapv(|r| r.powi(2))).mapv(|r| r.powf(2.0 - 2.0 * n as f64))
605 * 4.0
606 * PI.sqrt()
607 * tgamma(n as f64 - 0.5)
608 / tgamma(n as f64)
609 * sigma_r.powf(2.0 * n as f64 - 2.0)
610 * taylor_2f1_psi(r_r, n)
611}
612
613fn sum_n(n: i32, r_grid: &Array1<f64>, sigma: f64, pore_size: f64) -> Array1<f64> {
614 let mut result = Array1::zeros(r_grid.len());
615 for i in 0..n {
616 result = result
617 + sigma.powi(n) / (pore_size.powi(i) * r_grid.mapv(|r| (pore_size - r).powi(n - i)))
618 + sigma.powi(n) / (pore_size.powi(i) * r_grid.mapv(|r| (pore_size + r).powi(n - i)));
619 }
620 result
621}
622
623fn taylor_2f1_phi(x: &Array1<f64>, n: i32) -> Array1<f64> {
624 match n {
625 3 => {
626 1.0 + 3.0 / 4.0 * x.mapv(|x| x.powi(2)) + 3.0 / 64.0 * x.mapv(|x| x.powi(4))
627 - 1.0 / 256.0 * x.mapv(|x| x.powi(6))
628 - 15.0 / 16384.0 * x.mapv(|x| x.powi(8))
629 }
630 6 => {
631 1.0 + 63.0 / 4.0 * x.mapv(|x| x.powi(2))
632 + 2205.0 / 64.0 * x.mapv(|x| x.powi(4))
633 + 3675.0 / 256.0 * x.mapv(|x| x.powi(6))
634 + 11025.0 / 16384.0 * x.mapv(|x| x.powi(8))
635 }
636 _ => unreachable!(),
637 }
638}
639
640fn taylor_2f1_psi(x: &Array1<f64>, n: i32) -> Array1<f64> {
641 match n {
642 3 => {
643 1.0 + 9.0 / 4.0 * x.mapv(|x| x.powi(2))
644 + 9.0 / 64.0 * x.mapv(|x| x.powi(4))
645 + 1.0 / 256.0 * x.mapv(|x| x.powi(6))
646 + 9.0 / 16384.0 * x.mapv(|x| x.powi(8))
647 }
648 6 => {
649 1.0 + 81.0 / 4.0 * x.mapv(|x| x.powi(2))
650 + 3969.0 / 64.0 * x.mapv(|x| x.powi(4))
651 + 11025.0 / 256.0 * x.mapv(|x| x.powi(6))
652 + 99125.0 / 16384.0 * x.mapv(|x| x.powi(8))
653 }
654 _ => unreachable!(),
655 }
656}