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-2025 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 
32 Foam::Roots<3> Foam::cubicEqn::roots(const bool real) const
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  scalar disc = p*p*p/27 + q*q/4;
91 
92  // Ensure disc is not positive if the roots are known to be real
93  // even if round-off error might cause disc to be slightly positive
94  if (real && disc > 0)
95  {
96  disc = 0;
97  }
98 
99  // How many roots of what types are available?
100  const bool oneReal = disc == 0 && p == 0;
101  const bool twoReal = disc == 0 && p != 0;
102  const bool threeReal = disc < 0;
103  // const bool oneRealTwoComplex = disc > 0;
104 
105  static const scalar sqrt3 = sqrt(3.0);
106 
107  scalar x;
108 
109  if (oneReal)
110  {
111  const Roots<1> r = linearEqn(a, b/3).roots();
112  return Roots<3>(r.type(0), r[0]);
113  }
114  else if (twoReal)
115  {
116  if (q*b > 0)
117  {
118  x = - 2*cbrt(q/2) - b/3;
119  }
120  else
121  {
122  x = cbrt(q/2) - b/3;
123  const Roots<1> r = linearEqn(- a, x).roots();
124  return Roots<3>(Roots<2>(r, r), linearEqn(x*x, a*d).roots());
125  }
126  }
127  else if (threeReal)
128  {
129  const scalar wCbRe = - q/2, wCbIm = sqrt(- disc);
130  const scalar wAbs = cbrt(hypot(wCbRe, wCbIm));
131  const scalar wArg = atan2(wCbIm, wCbRe)/3;
132  const scalar wRe = wAbs*cos(wArg), wIm = wAbs*sin(wArg);
133  if (b > 0)
134  {
135  x = - wRe - mag(wIm)*sqrt3 - b/3;
136  }
137  else
138  {
139  x = 2*wRe - b/3;
140  }
141  }
142  else // if (oneRealTwoComplex)
143  {
144  const scalar wCb = - q/2 - sign(q)*sqrt(disc);
145  const scalar w = cbrt(wCb);
146  const scalar t = w - p/(3*w);
147  if (p + t*b < 0)
148  {
149  x = t - b/3;
150  }
151  else
152  {
153  const scalar xRe = - t/2 - b/3, xIm = sqrt3/2*(w + p/3/w);
154  x = - a*a*d/(xRe*xRe + xIm*xIm);
155 
156  // This form of solving for the remaining roots seems more stable
157  // for this case. This has been determined by trial and error.
158  return
159  Roots<3>
160  (
161  linearEqn(- a, x).roots(),
162  quadraticEqn(a*x, x*x + b*x, - a*d).roots(real)
163  );
164  }
165  }
166 
167  return
168  Roots<3>
169  (
170  linearEqn(- a, x).roots(),
171  quadraticEqn(- x*x, c*x + a*d, d*x).roots(real)
172  );
173 }
174 
175 
176 // ************************************************************************* //
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 bool real=false) 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)
void mag(LagrangianPatchField< scalar > &f, const LagrangianPatchField< Type > &f1)
dimensionedScalar atan2(const dimensionedScalar &x, const dimensionedScalar &y)
dimensionedScalar hypot(const dimensionedScalar &x, const dimensionedScalar &y)
void cbrt(LagrangianPatchField< scalar > &f, const LagrangianPatchField< scalar > &f1)
void sqrt(LagrangianPatchField< scalar > &f, const LagrangianPatchField< scalar > &f1)
dimensionedScalar cos(const dimensionedScalar &ds)
volScalarField & p