Skip to content

Commit c70462d

Browse files
authored
Merge pull request #16 from StaticBeagle/feature/elliptic-integrals
Added elliptic integrals complete and incomplete of the first and second kind
2 parents bff1c98 + 9671e88 commit c70462d

File tree

2 files changed

+203
-0
lines changed

2 files changed

+203
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package com.wildbitsfoundry.etk4j.math.specialfunctions;
2+
3+
public final class Elliptic {
4+
5+
/**
6+
* Complete Elliptic integral of the first kind ({@code K}).
7+
* @param k The parameter of the elliptic integral. The absolute value of {@code K(k<sup>2</sup>} has to be less
8+
* than 1.
9+
* @return The complete elliptic integral of the first kind {@code K(k)}.
10+
* @see <a href="https://www.codeproject.com/Articles/566614/Elliptic-integrals">Elliptic Integrals</a>
11+
*/
12+
public static double completeEllipticIntegralFirstKind(double k) {
13+
double sum;
14+
double term;
15+
double above;
16+
double below;
17+
sum = 1;
18+
term = 1;
19+
20+
above = 1;
21+
below = 2;
22+
23+
for (int i = 1; i <= 100; i++) {
24+
term *= above / below;
25+
sum += Math.pow(k, i) * Math.pow(term, 2);
26+
above += 2;
27+
below += 2;
28+
}
29+
sum *= 0.5 * Math.PI;
30+
return sum;
31+
}
32+
33+
/**
34+
* Complete Elliptic integral of the second kind ({@code E}).
35+
* @param k The parameter of the elliptic integral. The absolute value of {@code E(k<sup>2</sup>} has to be less
36+
* than 1.
37+
* @return The complete elliptic integral of the second kind {@code E(k)}.
38+
* @see <a href="https://www.codeproject.com/Articles/566614/Elliptic-integrals">Elliptic Integrals</a>
39+
*/
40+
public static double completeEllipticIntegralSecondKind(double k) {
41+
double sum;
42+
double term;
43+
double above;
44+
double below;
45+
sum = 1;
46+
term = 1;
47+
48+
above = 1;
49+
below = 2;
50+
51+
for (int i = 1; i <= 100; i++) {
52+
term *= above / below;
53+
sum -= Math.pow(k, i) * Math.pow(term, 2) / above;
54+
above += 2;
55+
below += 2;
56+
}
57+
sum *= 0.5 * Math.PI;
58+
return sum;
59+
}
60+
61+
/**
62+
* Incomplete Elliptic integral of the first kind ({@code K<sub>inc</sub>}).
63+
* @param angle The angle in rad/s. The angle should be between {@code 0} and {@code &#960;/2}
64+
* @param k The parameter is taken as {@code k<sup>2</sup>}.
65+
* @return The incomplete elliptic integral of the first kind {@code K<sub>inc</sub>(k)}.
66+
* @see <a href="https://www.codeproject.com/Articles/566614/Elliptic-integrals">Elliptic Integrals</a>
67+
*/
68+
public static double incompleteEllipticIntegralFirstKind(double angle, double k) {
69+
double result;
70+
result = Math.sin(angle) * RF(Math.pow(Math.cos(angle), 2), 1 - k * Math.pow(Math.sin(angle), 2), 1);
71+
return result;
72+
}
73+
74+
/**
75+
* Incomplete Elliptic integral of the second kind ({@code E<sub>inc</sub>}).
76+
* @param angle The angle in rad/s. The angle should be between {@code 0} and {@code &#960;/2}
77+
* @param k The parameter is taken as {@code k<sup>2</sup>}.
78+
* @return The incomplete elliptic integral of the first kind {@code E<sub>inc</sub>(k)}.
79+
* @see <a href="https://www.codeproject.com/Articles/566614/Elliptic-integrals">Elliptic Integrals</a>
80+
*/
81+
public static double incompleteEllipticIntegralSecondKind(double angle, double k) {
82+
double y = 1 - k * Math.pow(Math.sin(angle), 2);
83+
return Math.sin(angle) * RF(Math.pow(Math.cos(angle), 2), y, 1) + (-1.0 / 3.0) * k * Math.pow(Math.sin(angle),
84+
(3.0)) * RD(Math.pow(Math.cos(angle), 2), y, 1);
85+
}
86+
87+
/**
88+
* Carlson's Elliptic integral of the first kind
89+
* @see <a href="http://en.wikipedia.org/wiki/Carlson_symmetric_form#Series_Expansion">Series Expansion</a>
90+
*/
91+
private static double RF(double x, double y, double z) {
92+
double dx, dy, dz;
93+
double lambda;
94+
double n = 1.0 / 3.0;
95+
double mean;
96+
double tmp;
97+
do {
98+
lambda = Math.sqrt(x * y) + Math.sqrt(y * z) + Math.sqrt(z * x);
99+
x = 0.25 * (x + lambda);
100+
y = 0.25 * (y + lambda);
101+
z = 0.25 * (z + lambda);
102+
mean = (x + y + z) * n;
103+
tmp = 1 / mean;
104+
dx = (mean - x) * tmp;
105+
dy = (mean - y) * tmp;
106+
dz = (mean - z) * tmp;
107+
} while (Math.max(Math.max(Math.abs(dx), Math.abs(dy)), Math.abs(dz)) > 1e-7);
108+
double e2 = dx * dy - dz * dz;
109+
double e3 = dx * dy * dz;
110+
double c1 = 1.0 / 24.0;
111+
double c2 = 0.1;
112+
double c3 = 3.0 / 44.0;
113+
double c4 = 1.0 / 14.0;
114+
115+
double result = 1.0 + (c1 * e2 - c2 - c3 * e3) * e2 + c4 * e3;
116+
return result / Math.sqrt(mean);
117+
}
118+
119+
/**
120+
* Carlson's Elliptic integral of the second kind
121+
* @see <a href="http://en.wikipedia.org/wiki/Carlson_symmetric_form#Series_Expansion">Series Expansion</a>
122+
*/
123+
private static double RD(double x, double y, double z) {
124+
double dx, dy, dz;
125+
double lambda;
126+
double mu;
127+
double muInv;
128+
double sum = 0.0;
129+
double pow4 = 1.0;
130+
131+
do {
132+
lambda = Math.sqrt(x * y) + Math.sqrt(y * z) + Math.sqrt(z * x);
133+
sum += pow4 / (Math.sqrt(z) * (z + lambda));
134+
135+
pow4 *= 0.25;
136+
137+
x = 0.25 * (x + lambda);
138+
y = 0.25 * (y + lambda);
139+
z = 0.25 * (z + lambda);
140+
mu = (x + y + 3.0 * z) * 0.2;
141+
muInv = 1.0 / mu;
142+
143+
dx = 1 - x * muInv;
144+
dy = 1 - y * muInv;
145+
dz = 1 - z * muInv;
146+
} while (Math.max(Math.max(Math.abs(dx), Math.abs(dy)), Math.abs(dz)) > 1e-7);
147+
double C1 = 3.0 / 14.0;
148+
double C2 = 1.0 / 6.0;
149+
double C3 = 9.0 / 22.0;
150+
double C4 = 3.0 / 26.0;
151+
double EA = dx * dy;
152+
double EB = dz * dz;
153+
double EC = EA - EB;
154+
double ED = EA - 6.0 * EB;
155+
double EF = ED + EC + EC;
156+
double S1 = ED * (-C1 + 0.25 * C3 * ED - 1.50 * C4 * dz * EF);
157+
double S2 = dz * (C2 * EF + dz * (-C3 * EC + dz * C4 * EA));
158+
159+
return 3.0 * sum + pow4 * (1.0 + S1 + S2) / (mu * Math.sqrt(mu));
160+
}
161+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.wildbitsfoundry.etk4j.math.specialfunctions;
2+
3+
import org.junit.Test;
4+
5+
import static org.junit.Assert.assertEquals;
6+
7+
public class EllipticTest {
8+
9+
@Test
10+
public void testCompleteOfFirstKind() {
11+
double k = 0.1;
12+
double expected = 1.6124413487202198;
13+
double y = Elliptic.completeEllipticIntegralFirstKind(k);
14+
15+
assertEquals(expected, y, 1e-12);
16+
}
17+
18+
@Test
19+
public void testIncompleteOfFirstKind() {
20+
double expected = 1.2261911708835167;
21+
double y = Elliptic.incompleteEllipticIntegralFirstKind(1.0, 1.0);
22+
23+
assertEquals(expected, y, 1e-12);
24+
}
25+
26+
@Test
27+
public void testCompleteOfSecondKind() {
28+
double k = 0.1;
29+
double expected = 1.5307576368977631;
30+
double y = Elliptic.completeEllipticIntegralSecondKind(k);
31+
32+
assertEquals(expected, y, 1e-12);
33+
}
34+
35+
@Test
36+
public void testIncompleteOfSecondKind() {
37+
double expected = 0.8414709848078963;
38+
double y = Elliptic.incompleteEllipticIntegralSecondKind(1.0, 1.0);
39+
40+
assertEquals(expected, y, 1e-12);
41+
}
42+
}

0 commit comments

Comments
 (0)