cubicEqn.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2017-2023 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "linearEqn.H"
27 #include "quadraticEqn.H"
28 #include "cubicEqn.H"
29 
30 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
31 
33 {
34  /*
35 
36  This function solves a cubic equation of the following form:
37 
38  a*x^3 + b*x^2 + c*x + d = 0
39  x^3 + B*x^2 + C*x + D = 0
40 
41  The following two substitutions are used:
42 
43  x = t - B/3
44  t = w - P/3/w
45 
46  This reduces the problem to a quadratic in w^3.
47 
48  w^6 + Q*w^3 - P = 0
49 
50  Where Q and P are given in the code below.
51 
52  The properties of the cubic can be related to the properties of this
53  quadratic in w^3. If it has a repeated root a zero, the cubic has a triple
54  root. If it has a repeated root not at zero, the cubic has two real roots,
55  one repeated and one not. If it has two complex roots, the cubic has three
56  real roots. If it has two real roots, then the cubic has one real root and
57  two complex roots.
58 
59  This is solved for the most numerically accurate value of w^3. See the
60  quadratic function for details on how to pick a value. This single value of
61  w^3 can yield up to three cube roots for w, which relate to the three
62  solutions for x.
63 
64  Only a single root, or pair of conjugate roots, is directly evaluated; the
65  one, or ones with the lowest relative numerical error. Root identities are
66  then used to recover the remaining roots, possibly utilising a quadratic
67  and/or linear solution. This seems to be a good way of maintaining the
68  accuracy of roots at very different magnitudes.
69 
70  */
71 
72  const scalar a = this->a();
73  const scalar b = this->b();
74  const scalar c = this->c();
75  const scalar d = this->d();
76 
77  if (a == 0)
78  {
79  return Roots<3>(quadraticEqn(b, c, d).roots(), rootType::nan, 0);
80  }
81 
82  if (d == 0)
83  {
84  return Roots<3>(rootType::real, 0, quadraticEqn(a, b, c).roots());
85  }
86 
87  // This is assumed not to over- or under-flow. If it does, all bets are off.
88  const scalar p = c*a - b*b/3;
89  const scalar q = b*b*b*scalar(2)/27 - b*c*a/3 + d*a*a;
90  const scalar disc = p*p*p/27 + q*q/4;
91 
92  // How many roots of what types are available?
93  const bool oneReal = disc == 0 && p == 0;
94  const bool twoReal = disc == 0 && p != 0;
95  const bool threeReal = disc < 0;
96  // const bool oneRealTwoComplex = disc > 0;
97 
98  static const scalar sqrt3 = sqrt(3.0);
99 
100  scalar x;
101 
102  if (oneReal)
103  {
104  const Roots<1> r = linearEqn(a, b/3).roots();
105  return Roots<3>(r.type(0), r[0]);
106  }
107  else if (twoReal)
108  {
109  if (q*b > 0)
110  {
111  x = - 2*cbrt(q/2) - b/3;
112  }
113  else
114  {
115  x = cbrt(q/2) - b/3;
116  const Roots<1> r = linearEqn(- a, x).roots();
117  return Roots<3>(Roots<2>(r, r), linearEqn(x*x, a*d).roots());
118  }
119  }
120  else if (threeReal)
121  {
122  const scalar wCbRe = - q/2, wCbIm = sqrt(- disc);
123  const scalar wAbs = cbrt(hypot(wCbRe, wCbIm));
124  const scalar wArg = atan2(wCbIm, wCbRe)/3;
125  const scalar wRe = wAbs*cos(wArg), wIm = wAbs*sin(wArg);
126  if (b > 0)
127  {
128  x = - wRe - mag(wIm)*sqrt3 - b/3;
129  }
130  else
131  {
132  x = 2*wRe - b/3;
133  }
134  }
135  else // if (oneRealTwoComplex)
136  {
137  const scalar wCb = - q/2 - sign(q)*sqrt(disc);
138  const scalar w = cbrt(wCb);
139  const scalar t = w - p/(3*w);
140  if (p + t*b < 0)
141  {
142  x = t - b/3;
143  }
144  else
145  {
146  const scalar xRe = - t/2 - b/3, xIm = sqrt3/2*(w + p/3/w);
147  x = - a*a*d/(xRe*xRe + xIm*xIm);
148 
149  // This form of solving for the remaining roots seems more stable
150  // for this case. This has been determined by trial and error.
151  return
152  Roots<3>
153  (
154  linearEqn(- a, x).roots(),
155  quadraticEqn(a*x, x*x + b*x, - a*d).roots()
156  );
157  }
158  }
159 
160  return
161  Roots<3>
162  (
163  linearEqn(- a, x).roots(),
164  quadraticEqn(- x*x, c*x + a*d, d*x).roots()
165  );
166 }
167 
168 
169 // ************************************************************************* //
Templated storage for the roots of polynomial equations, plus flags to indicate the nature of the roo...
Definition: Roots.H:67
void type(const direction i, const rootType t)
Set the type of the i-th root.
Definition: RootsI.H:120
scalar d() const
Definition: cubicEqnI.H:73
scalar c() const
Definition: cubicEqnI.H:67
Roots< 3 > roots() const
Get the roots.
Definition: cubicEqn.C:32
scalar a() const
Definition: cubicEqnI.H:55
scalar b() const
Definition: cubicEqnI.H:61
Linear equation of the form a*x + b = 0.
Definition: linearEqn.H:51
Roots< 1 > roots() const
Get the roots.
Definition: linearEqnI.H:89
Quadratic equation of the form a*x^2 + b*x + c = 0.
Definition: quadraticEqn.H:52
dimensionedScalar sign(const dimensionedScalar &ds)
dimensionedScalar sin(const dimensionedScalar &ds)
dimensionedScalar atan2(const dimensionedScalar &x, const dimensionedScalar &y)
dimensionedScalar sqrt(const dimensionedScalar &ds)
dimensionedScalar hypot(const dimensionedScalar &x, const dimensionedScalar &y)
dimensioned< scalar > mag(const dimensioned< Type > &)
dimensionedScalar cbrt(const dimensionedScalar &ds)
dimensionedScalar cos(const dimensionedScalar &ds)
volScalarField & p