File indexing completed on 2026-09-28 09:20:49
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #ifndef _MathInteg_Kronrod_HeaderFile
0015 #define _MathInteg_Kronrod_HeaderFile
0016
0017
0018 #include <MathUtils_GaussKronrodWeights.hxx>
0019
0020 #include <MathUtils_Types.hxx>
0021 #include <MathUtils_Config.hxx>
0022 #include <MathUtils_Core.hxx>
0023
0024 #include <NCollection_DynamicArray.hxx>
0025
0026 #include <cmath>
0027
0028 namespace MathInteg
0029 {
0030 using namespace MathUtils;
0031
0032
0033 struct KronrodConfig : IntegConfig
0034 {
0035 int NbGaussPoints = 7;
0036 bool Adaptive = true;
0037
0038
0039 KronrodConfig() = default;
0040
0041
0042 explicit KronrodConfig(double theTolerance, int theMaxIter = 100)
0043 : IntegConfig(theTolerance, theMaxIter)
0044 {
0045 }
0046 };
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060 template <typename Function>
0061 IntegResult KronrodRule(Function& theFunc, double theLower, double theUpper, int theNbGauss = 7)
0062 {
0063 IntegResult aResult;
0064
0065
0066 const int aNbKronrod = 2 * theNbGauss + 1;
0067
0068
0069 math_Vector aGaussP(1, theNbGauss);
0070 math_Vector aGaussW(1, theNbGauss);
0071 math_Vector aKronrodP(1, aNbKronrod);
0072 math_Vector aKronrodW(1, aNbKronrod);
0073
0074 if (!GetKronrodPointsAndWeights(aNbKronrod, aKronrodP, aKronrodW)
0075 || !GetOrderedGaussPointsAndWeights(theNbGauss, aGaussP, aGaussW))
0076 {
0077 aResult.Status = Status::NumericalError;
0078 return aResult;
0079 }
0080
0081
0082 const double aHalfLen = 0.5 * (theUpper - theLower);
0083 const double aMid = 0.5 * (theUpper + theLower);
0084
0085
0086 const int aNPnt2 = (aNbKronrod + 1) / 2;
0087
0088 double aGaussVal = 0.0;
0089 double aKronrodVal = 0.0;
0090
0091
0092 math_Vector aF1(0, aNPnt2 - 1);
0093 math_Vector aF2(0, aNPnt2 - 1);
0094
0095
0096 for (int i = 2; i < aNPnt2; i += 2)
0097 {
0098 const double aDx = aHalfLen * aKronrodP(i);
0099 double aVal1 = 0.0, aVal2 = 0.0;
0100
0101 if (!theFunc.Value(aMid + aDx, aVal1) || !theFunc.Value(aMid - aDx, aVal2))
0102 {
0103 aResult.Status = Status::NumericalError;
0104 return aResult;
0105 }
0106
0107 aF1(i) = aVal1;
0108 aF2(i) = aVal2;
0109 aGaussVal += (aVal1 + aVal2) * aGaussW(i / 2);
0110 aKronrodVal += (aVal1 + aVal2) * aKronrodW(i);
0111 }
0112
0113
0114 double aFc = 0.0;
0115 if (!theFunc.Value(aMid, aFc))
0116 {
0117 aResult.Status = Status::NumericalError;
0118 return aResult;
0119 }
0120
0121 aKronrodVal += aFc * aKronrodW(aNPnt2);
0122
0123
0124 if (aNPnt2 % 2 == 0)
0125 {
0126 aGaussVal += aFc * aGaussW(aNPnt2 / 2);
0127 }
0128
0129
0130 for (int i = 1; i < aNPnt2; i += 2)
0131 {
0132 const double aDx = aHalfLen * aKronrodP(i);
0133 double aVal1 = 0.0, aVal2 = 0.0;
0134
0135 if (!theFunc.Value(aMid + aDx, aVal1) || !theFunc.Value(aMid - aDx, aVal2))
0136 {
0137 aResult.Status = Status::NumericalError;
0138 return aResult;
0139 }
0140
0141 aF1(i) = aVal1;
0142 aF2(i) = aVal2;
0143 aKronrodVal += (aVal1 + aVal2) * aKronrodW(i);
0144 }
0145
0146
0147
0148 const double aMean = 0.5 * aKronrodVal;
0149 double aAsc = std::abs(aFc - aMean) * aKronrodW(aNPnt2);
0150 for (int i = 1; i < aNPnt2; ++i)
0151 {
0152 aAsc += aKronrodW(i) * (std::abs(aF1(i) - aMean) + std::abs(aF2(i) - aMean));
0153 }
0154
0155
0156 aAsc *= aHalfLen;
0157 aKronrodVal *= aHalfLen;
0158 aGaussVal *= aHalfLen;
0159
0160
0161 double aAbsError = std::abs(aKronrodVal - aGaussVal);
0162
0163
0164
0165 if (aAsc != 0.0 && aAbsError != 0.0)
0166 {
0167 const double aScale = std::pow(200.0 * aAbsError / aAsc, 1.5);
0168 if (aScale < 1.0)
0169 {
0170 aAbsError = std::min(aAbsError, aAsc * aScale);
0171 }
0172 }
0173
0174 aResult.Status = Status::OK;
0175 aResult.Value = aKronrodVal;
0176 aResult.AbsoluteError = aAbsError;
0177 aResult.RelativeError = aAbsError / std::max(std::abs(aKronrodVal), 1.0e-15);
0178 aResult.NbPoints = static_cast<size_t>(aNbKronrod);
0179 aResult.NbIterations = 1;
0180 return aResult;
0181 }
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195 template <typename Function>
0196 IntegResult Kronrod(Function& theFunc,
0197 double theLower,
0198 double theUpper,
0199 const KronrodConfig& theConfig = KronrodConfig())
0200 {
0201 IntegResult aResult;
0202
0203 if (!theConfig.Adaptive)
0204 {
0205
0206 return KronrodRule(theFunc, theLower, theUpper, theConfig.NbGaussPoints);
0207 }
0208
0209
0210 struct Interval
0211 {
0212 double Lower;
0213 double Upper;
0214 double Value;
0215 double Error;
0216 };
0217
0218
0219 IntegResult anInitResult = KronrodRule(theFunc, theLower, theUpper, theConfig.NbGaussPoints);
0220 if (!anInitResult.IsDone())
0221 {
0222 return anInitResult;
0223 }
0224
0225 NCollection_DynamicArray<Interval> aHeap;
0226 aHeap.Append({theLower, theUpper, *anInitResult.Value, *anInitResult.AbsoluteError});
0227
0228 double aTotalValue = *anInitResult.Value;
0229 double aTotalError = *anInitResult.AbsoluteError;
0230 size_t aTotalPoints = anInitResult.NbPoints;
0231 int aIterations = 1;
0232
0233
0234 while (aIterations < theConfig.MaxIterations)
0235 {
0236
0237 if (aTotalError < theConfig.Tolerance * std::max(std::abs(aTotalValue), 1.0e-15))
0238 {
0239 break;
0240 }
0241
0242
0243 int aMaxIdx = 0;
0244 double aMaxError = 0.0;
0245 for (int i = 0; i < aHeap.Length(); ++i)
0246 {
0247 if (aHeap.Value(i).Error > aMaxError)
0248 {
0249 aMaxError = aHeap.Value(i).Error;
0250 aMaxIdx = i;
0251 }
0252 }
0253
0254 if (aMaxError < MathUtils::THE_ZERO_TOL)
0255 {
0256 break;
0257 }
0258
0259
0260 const Interval aWorst = aHeap.Value(aMaxIdx);
0261 const double aBisMid = 0.5 * (aWorst.Lower + aWorst.Upper);
0262
0263 IntegResult aLeftResult = KronrodRule(theFunc, aWorst.Lower, aBisMid, theConfig.NbGaussPoints);
0264 IntegResult aRightResult = KronrodRule(theFunc, aBisMid, aWorst.Upper, theConfig.NbGaussPoints);
0265
0266 if (!aLeftResult.IsDone() || !aRightResult.IsDone())
0267 {
0268 aResult.Status = Status::NumericalError;
0269 aResult.Value = aTotalValue;
0270 aResult.AbsoluteError = aTotalError;
0271 aResult.NbPoints = aTotalPoints;
0272 aResult.NbIterations = static_cast<size_t>(aIterations);
0273 return aResult;
0274 }
0275
0276
0277 aTotalValue -= aWorst.Value;
0278 aTotalError -= aWorst.Error;
0279 aTotalValue += *aLeftResult.Value + *aRightResult.Value;
0280 aTotalError += *aLeftResult.AbsoluteError + *aRightResult.AbsoluteError;
0281 aTotalPoints += aLeftResult.NbPoints + aRightResult.NbPoints;
0282 ++aIterations;
0283
0284
0285 aHeap.ChangeValue(
0286 aMaxIdx) = {aWorst.Lower, aBisMid, *aLeftResult.Value, *aLeftResult.AbsoluteError};
0287 aHeap.Append({aBisMid, aWorst.Upper, *aRightResult.Value, *aRightResult.AbsoluteError});
0288 }
0289
0290 aResult.Status = Status::OK;
0291 aResult.Value = aTotalValue;
0292 aResult.AbsoluteError = aTotalError;
0293 aResult.RelativeError = aTotalError / std::max(std::abs(aTotalValue), 1.0e-15);
0294 aResult.NbPoints = aTotalPoints;
0295 aResult.NbIterations = static_cast<size_t>(aIterations);
0296 return aResult;
0297 }
0298
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311 template <typename Function>
0312 IntegResult KronrodAuto(Function& theFunc,
0313 double theLower,
0314 double theUpper,
0315 double theTolerance = 1.0e-10,
0316 int theMaxOrder = 30)
0317 {
0318 IntegResult aBestResult;
0319 aBestResult.Status = Status::NotConverged;
0320
0321
0322 for (int aOrder = 7; aOrder <= theMaxOrder; aOrder += 4)
0323 {
0324 IntegResult aResult = KronrodRule(theFunc, theLower, theUpper, aOrder);
0325 if (!aResult.IsDone())
0326 {
0327 continue;
0328 }
0329
0330 aBestResult = aResult;
0331
0332
0333 if (aResult.RelativeError && *aResult.RelativeError < theTolerance)
0334 {
0335 return aResult;
0336 }
0337 }
0338
0339
0340 KronrodConfig aConfig;
0341 aConfig.Tolerance = theTolerance;
0342 aConfig.NbGaussPoints = 7;
0343 aConfig.Adaptive = true;
0344 aConfig.MaxIterations = 50;
0345
0346 return Kronrod(theFunc, theLower, theUpper, aConfig);
0347 }
0348
0349
0350
0351
0352
0353
0354
0355
0356
0357
0358 template <typename Function>
0359 IntegResult KronrodSemiInfinite(Function& theFunc,
0360 double theLower,
0361 const KronrodConfig& theConfig = KronrodConfig())
0362 {
0363 class TransformedFunc
0364 {
0365 public:
0366 TransformedFunc(Function& theF, double theA)
0367 : myFunc(theF),
0368 myA(theA)
0369 {
0370 }
0371
0372 bool Value(double theT, double& theF)
0373 {
0374 if (theT >= 1.0)
0375 {
0376 theF = 0.0;
0377 return true;
0378 }
0379
0380 const double aX = myA + theT / (1.0 - theT);
0381 const double aJacob = 1.0 / MathUtils::Sqr(1.0 - theT);
0382
0383 double aFx = 0.0;
0384 if (!myFunc.Value(aX, aFx))
0385 {
0386 return false;
0387 }
0388
0389 theF = aFx * aJacob;
0390 return true;
0391 }
0392
0393 private:
0394 Function& myFunc;
0395 double myA;
0396 };
0397
0398 TransformedFunc aTransformed(theFunc, theLower);
0399 return Kronrod(aTransformed, 0.0, 1.0, theConfig);
0400 }
0401
0402
0403
0404
0405
0406
0407
0408
0409
0410
0411 template <typename Function>
0412 IntegResult KronrodInfinite(Function& theFunc, const KronrodConfig& theConfig = KronrodConfig())
0413 {
0414 class TransformedFunc
0415 {
0416 public:
0417 TransformedFunc(Function& theF)
0418 : myFunc(theF)
0419 {
0420 }
0421
0422 bool Value(double theT, double& theF)
0423 {
0424 if (std::abs(theT) >= 1.0)
0425 {
0426 theF = 0.0;
0427 return true;
0428 }
0429
0430 const double aT2 = theT * theT;
0431 const double aX = theT / (1.0 - aT2);
0432 const double aJacob = (1.0 + aT2) / MathUtils::Sqr(1.0 - aT2);
0433
0434 double aFx = 0.0;
0435 if (!myFunc.Value(aX, aFx))
0436 {
0437 return false;
0438 }
0439
0440 theF = aFx * aJacob;
0441 return true;
0442 }
0443
0444 private:
0445 Function& myFunc;
0446 };
0447
0448 TransformedFunc aTransformed(theFunc);
0449 return Kronrod(aTransformed, -1.0, 1.0, theConfig);
0450 }
0451
0452 }
0453
0454 #endif