<Coding of sheet, sphere- and cylinder-shaped specimen in the emission mode>
|
// Total amount of diffusing gas which has entered the sheet at time t |
// Ref: Crank et.al. The Mathematics of Diffusion (1975) p. 47, Equation (4.18) [24] |
// D: Diffusion coefficient, L: Sheet thickness |
// M: Emitted total amount by diffusion at infinite time. |
public double Ftn_Sheet(double t) { |
|
double pi2 = Math.PI × Math.PI; |
|
double zSum = 0; |
|
for (int n = 0; n < 50; ++n) { |
|
|
int n21 = (2 × n + 1) × (2 × n + 1); |
|
|
double add = Math.Exp(−D × n21 × pi2 × t/(L × L))/n21; |
|
|
double zSumAdded = zSum + add; |
|
|
if (zSum >= zSumAdded) break; // When convergence achieved |
|
|
zSum = zSumAdded; |
|
} |
|
return M − 8/pi2 × (M × zSum); |
} |
|
// Total amount of diffusing gas which has entered the sphere at time t. |
// Ref: Crank et.al. The Mathematics of Diffusion (1975) p. 91, Equation (6.20) [24] |
// D: Diffusion Coefficient, a: Radius of sphere. |
// M: Emitted total amount by diffusion at infinite time. |
public double Ftn_Sphere(double t) { |
|
double pi2 = Math.PI × Math.PI; |
|
double zSum = 0; |
|
for (int n = 1; n < 100; ++n) { |
|
|
int n2 = n × n; |
|
|
double add = Math.Exp(−D × n2 × pi2 × t/(a × a))/n2; |
|
|
double zSumAdded = zSum + add; |
|
|
if (zSum >= zSumAdded) break; // When convergence achieved |
|
|
zSum = zSumAdded; |
|
} |
|
return M − 6/pi2 × (M × zSum); |
} |
|
// Total amount of diffusing gas which has entered the cylinder at time t. |
// Ref: Al Demarez, et. al., Acta Metallurgica (1954), Equation (5) [28] |
// D: Diffusion Coefficient, a: Radius of cylinder, L: Length of cylinder |
// M: Emitted total amount by diffusion at infinite time. |
public double Ftn_Cylinder(double t) { |
|
double pi2 = Math.PI × Math.PI; |
|
double zSum = 0; |
|
for (int n = 0; n < 100; ++n) { |
|
|
int n2 = (2 × n + 1) × (2 × n + 1); |
|
|
double add = Math.Exp(−n2 × pi2 × D × t/(L × L))/n2; |
|
|
double zSumAdded = zSum + add; |
|
|
if (zSum >= zSumAdded) break; // When convergence achieved |
|
|
zSum = zSumAdded; |
|
} |
|
double rSum = 0; |
|
for (int n = 1; n <= 50; ++n) { |
|
|
double b = Bessel0root[n − 1]; |
|
|
double add = Math.Exp(−D × b × b × t/(a × a))/(b × b); |
|
|
double rSumAdded = rSum + add; |
|
|
if (rSum >= rSumAdded) break; // When convergence achieved |
|
|
rSum = rSumAdded; |
|
} |
|
return M − 32/pi2 × (M × zSum × rSum); |
} |
|
// Zeros of first kind Bessel function J_0(x) |
public static double[] bessel0root = |
|
new double[] {2.40482556, 5.52007811, 8.65372791, 11.79153444, 14.93091771, |
|
|
18.07106397, 21.21163663, 24.35247153, 27.49347913, 30.63460647, |
|
|
33.77582021, 36.91709835, 40.05842576, 43.19979171, 46.34118837, |
|
|
49.4826099, 52.62405184, 55.76551076, 58.90698393, 62.04846919, |
|
|
65.1899648, 68.33146933, 71.4729816, 74.61450064, 77.75602563, |
|
|
80.89755587, 84.03909078, 87.18062984, 90.32217264, 93.46371878, |
|
|
96.605267951, 99.74681986, 102.88837425, 106.02993092, 109.17148965, |
|
|
112.3130503, 115.45461265, 118.59617663, 121.73774209, 124.87930891, |
|
|
128.020877, 131.1624463, 134.3040166, 137.445588, 140.5871604, |
|
|
143.7287336, 146.8703076, 150.0118825, 153.153458, 156.2950343}; |
|
// Nelder-Mead Simplex Optimization. |
// Return value is fit error |
// Optima(class): Manage all optimization procedure and calc. error and FOM etc. |
// Simplex(Class): Polytope of N + 1 vertices in N dim. undetermined param. space. |
// Vertex(Class): One of N + 1 vertices of Simplex. |
// Coord(Class): Having coord. of vertex and methods, reflect(), expand(), … |
// simplexProtocol: ExpFactor = 2.0, ConFactor = 0.5, PrcntInitCoef = 5.0, … |
public double Iterate(double[] unknowns) { |
|
int b, w; |
|
int loop = 0; // # of loops |
|
int dim = unknowns.Length; |
|
Simplex simplex = new Simplex(unknowns, simplexProtocol.PrcntInitCoef); |
|
// Calculate errors for each vertexes. |
|
for (int i = 0; i < dim + 1; ++i) |
|
simplex.s[i].error = optima.ErrorF(simplex.s[i].coord); |
|
do { // start iteration |
|
|
b = simplex.bestVertex(); // find best and worst vertices |
|
|
w = simplex.worstVertex(); |
|
|
// calculate the midPoint of the non-worst values for later calc’s |
|
|
double[] mp = simplex.midPoint(w); |
|
|
Vertex refl_vrtx = new Vertex(simplex.s[w]); // save the old values |
|
|
Coord.reflect(mp, refl_vrtx.coord); // first try a reflection |
|
|
refl_vrtx.error = optima.ErrorF(refl_vrtx.coord); |
|
|
|
// is the new vertex better or equal to the old best vertex |
|
|
if (refl_vrtx.error < simplex.s[b].error) { |
|
|
|
// try to expand the reflected vertex next |
|
|
|
Vertex expd_vrtx = new Vertex(refl_vrtx); |
|
|
|
Coord.expand(mp, expd_vrtx.coord, simplexProtocol.ExpFactor); |
|
|
|
expd_vrtx.error = optima.ErrorF(expd_vrtx.coord); |
|
|
|
// if the expanded vertex is not as good or better than the best |
|
|
|
// revert to the reflected vertex |
|
|
|
if (expd_vrtx.error <= refl_vrtx.error) simplex.s[w] = expd_vrtx; |
|
|
|
else simplex.s[w] = refl_vrtx; |
|
|
} else { |
|
|
|
if (refl_vrtx.error <= simplex.s[w].error) simplex.s[w] = refl_vrtx; |
|
|
|
else { |
|
|
|
|
Vertex cont_vrtx = new Vertex(simplex.s[w]); |
|
|
|
|
Coord.contract(mp, cont_vrtx.coord, simplexProtocol.ConFactor); |
|
|
|
|
cont_vrtx.error = optima.ErrorF(cont_vrtx.coord); |
|
|
|
|
if (cont_vrtx.error <= simplex.s[w].error) simplex.s[w] = cont_vrtx; |
|
|
|
|
else { |
|
|
|
|
|
simplex.contract_all(b, simplexProtocol.ConFactor); |
|
|
|
|
|
for (int i = 0; i < dim + 1; ++i) |
|
|
|
|
|
|
simplex.s[i].error = otima.ErrorF(simplex.s[i].coord); |
|
|
|
|
}; // else |
|
|
|
}; // else |
|
|
}; |
|
|
++loop; |
|
} while (!(simplex.s[b].error <= simplexProtocol.Tolerance || |
|
loop >= simplexProtocol.InternalLoop)); |
|
// return best result |
|
for (int i = 0; i < dim; ++i) unknowns[i] = simplex.s[b].coord[i]; |
|
return simplex.s[b].error; |
} |