Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-28 09:20:49

0001 // Copyright (c) 2025 OPEN CASCADE SAS
0002 //
0003 // This file is part of Open CASCADE Technology software library.
0004 //
0005 // This library is free software; you can redistribute it and/or modify it under
0006 // the terms of the GNU Lesser General Public License version 2.1 as published
0007 // by the Free Software Foundation, with special exception defined in the file
0008 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
0009 // distribution for complete text of the license and disclaimer of any warranty.
0010 //
0011 // Alternatively, this file may be used under the terms of Open CASCADE
0012 // commercial license or contractual agreement.
0013 
0014 #ifndef _MathInteg_DoubleExp_HeaderFile
0015 #define _MathInteg_DoubleExp_HeaderFile
0016 
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathUtils_Core.hxx>
0020 
0021 #include <cmath>
0022 
0023 namespace MathInteg
0024 {
0025 using namespace MathUtils;
0026 
0027 //! Configuration for double exponential integration.
0028 struct DoubleExpConfig : IntegConfig
0029 {
0030   int    NbLevels   = 6;   //!< Number of refinement levels (each doubles points)
0031   double StepFactor = 0.5; //!< Initial step size h = StepFactor / NbPoints
0032 
0033   //! Default constructor.
0034   DoubleExpConfig() = default;
0035 
0036   //! Constructor with tolerance.
0037   explicit DoubleExpConfig(double theTolerance, int theMaxIter = 100)
0038       : IntegConfig(theTolerance, theMaxIter)
0039   {
0040   }
0041 };
0042 
0043 //! Tanh-Sinh (Double Exponential) quadrature for finite interval [a,b].
0044 //!
0045 //! The double exponential transformation maps [a,b] to (-inf, +inf):
0046 //!   x = (b+a)/2 + (b-a)/2 * tanh(pi/2 * sinh(t))
0047 //!
0048 //! The integrand is multiplied by the Jacobian:
0049 //!   dx/dt = (b-a)/2 * (pi/2) * cosh(t) / cosh^2(pi/2 * sinh(t))
0050 //!
0051 //! Properties:
0052 //! - Excellent for functions with endpoint singularities
0053 //! - Exponential convergence for analytic functions
0054 //! - Self-adaptive through level refinement
0055 //! - Handles algebraic and logarithmic singularities
0056 //!
0057 //! @tparam Function type with Value(double theX, double& theF) method
0058 //! @param theFunc function to integrate
0059 //! @param theLower lower integration bound
0060 //! @param theUpper upper integration bound
0061 //! @param theConfig integration configuration
0062 //! @return integration result with error estimate
0063 template <typename Function>
0064 IntegResult TanhSinh(Function&              theFunc,
0065                      double                 theLower,
0066                      double                 theUpper,
0067                      const DoubleExpConfig& theConfig = DoubleExpConfig())
0068 {
0069   IntegResult aResult;
0070 
0071   if (theLower >= theUpper)
0072   {
0073     aResult.Status = Status::InvalidInput;
0074     return aResult;
0075   }
0076 
0077   const double aHalfPi = M_PI / 2.0;
0078   const double aMid    = 0.5 * (theUpper + theLower);
0079   const double aHalf   = 0.5 * (theUpper - theLower);
0080 
0081   // Initial step size
0082   double aH = 1.0;
0083 
0084   // For convergence checking
0085   double aPrevSum     = 0.0;
0086   size_t aTotalPoints = 0;
0087 
0088   // Level-by-level refinement
0089   for (int aLevel = 0; aLevel < theConfig.NbLevels; ++aLevel)
0090   {
0091     double aSum      = 0.0;
0092     int    aNbPoints = 0;
0093 
0094     // For level 0, evaluate at t = 0, +/-h, +/-2h, ...
0095     // For level > 0, evaluate only at new points: +/-h/2, +/-3h/2, ...
0096     int aStart = (aLevel == 0) ? 0 : 1;
0097     int aStep  = (aLevel == 0) ? 1 : 2;
0098 
0099     // Positive t direction
0100     for (int k = aStart;; k += aStep)
0101     {
0102       double aT = k * aH;
0103 
0104       // Sinh and cosh of t
0105       double aSinhT = std::sinh(aT);
0106       double aCoshT = std::cosh(aT);
0107 
0108       // u = pi/2 * sinh(t)
0109       double aU = aHalfPi * aSinhT;
0110 
0111       // Check for overflow in exp
0112       if (std::abs(aU) > 700.0)
0113       {
0114         break; // tanh(u) ~= +/-1, contribution is negligible
0115       }
0116 
0117       // tanh(u) and sech^2(u) = 1/cosh^2(u)
0118       double aTanhU  = std::tanh(aU);
0119       double aCoshU  = std::cosh(aU);
0120       double aSech2U = 1.0 / (aCoshU * aCoshU);
0121 
0122       // x = mid + half * tanh(u)
0123       double aX = aMid + aHalf * aTanhU;
0124 
0125       // Check if x is within bounds (with small tolerance)
0126       if (aX <= theLower + MathUtils::THE_ZERO_TOL || aX >= theUpper - MathUtils::THE_ZERO_TOL)
0127       {
0128         break;
0129       }
0130 
0131       // Weight: h * (b-a)/2 * pi/2 * cosh(t) * sech^2(u)
0132       double aWeight = aHalf * aHalfPi * aCoshT * aSech2U;
0133 
0134       // Check if weight is negligible
0135       if (aWeight < MathUtils::THE_ZERO_TOL)
0136       {
0137         break;
0138       }
0139 
0140       // Evaluate function
0141       double aF = 0.0;
0142       if (!theFunc.Value(aX, aF))
0143       {
0144         // Function evaluation failed, skip this point
0145         continue;
0146       }
0147 
0148       // Handle NaN or Inf
0149       if (!std::isfinite(aF))
0150       {
0151         continue;
0152       }
0153 
0154       aSum += aWeight * aF;
0155       ++aNbPoints;
0156 
0157       // Limit number of points for safety
0158       if (aNbPoints > 10000)
0159       {
0160         break;
0161       }
0162     }
0163 
0164     // Negative t direction (skip t=0 which was already counted)
0165     int aNegStart = (aLevel == 0) ? 1 : aStart; // Start at 1 for level 0, aStart otherwise
0166     for (int k = aNegStart;; k += aStep)
0167     {
0168       double aT = -k * aH;
0169 
0170       double aSinhT = std::sinh(aT);
0171       double aCoshT = std::cosh(aT);
0172       double aU     = aHalfPi * aSinhT;
0173 
0174       if (std::abs(aU) > 700.0)
0175       {
0176         break;
0177       }
0178 
0179       double aTanhU  = std::tanh(aU);
0180       double aCoshU  = std::cosh(aU);
0181       double aSech2U = 1.0 / (aCoshU * aCoshU);
0182 
0183       double aX = aMid + aHalf * aTanhU;
0184 
0185       if (aX <= theLower + MathUtils::THE_ZERO_TOL || aX >= theUpper - MathUtils::THE_ZERO_TOL)
0186       {
0187         break;
0188       }
0189 
0190       double aWeight = aHalf * aHalfPi * aCoshT * aSech2U;
0191 
0192       if (aWeight < MathUtils::THE_ZERO_TOL)
0193       {
0194         break;
0195       }
0196 
0197       double aF = 0.0;
0198       if (!theFunc.Value(aX, aF))
0199       {
0200         continue;
0201       }
0202 
0203       if (!std::isfinite(aF))
0204       {
0205         continue;
0206       }
0207 
0208       aSum += aWeight * aF;
0209       ++aNbPoints;
0210 
0211       if (aNbPoints > 10000)
0212       {
0213         break;
0214       }
0215     }
0216 
0217     // Scale by step size
0218     double aLevelSum = aH * aSum;
0219     aTotalPoints += static_cast<size_t>(aNbPoints);
0220 
0221     // For level > 0, add to previous sum (trapezoidal refinement)
0222     // S_new = S_old/2 + h_new * new_points_sum
0223     double aNewSum = (aLevel == 0) ? aLevelSum : 0.5 * aPrevSum + aLevelSum;
0224 
0225     // Check for convergence
0226     if (aLevel > 0)
0227     {
0228       double aAbsError = std::abs(aNewSum - aPrevSum);
0229       double aRelError = aAbsError / std::max(std::abs(aNewSum), 1.0e-15);
0230 
0231       if (aRelError < theConfig.Tolerance)
0232       {
0233         aResult.Status        = Status::OK;
0234         aResult.Value         = aNewSum;
0235         aResult.AbsoluteError = aAbsError;
0236         aResult.RelativeError = aRelError;
0237         aResult.NbPoints      = aTotalPoints;
0238         aResult.NbIterations  = static_cast<size_t>(aLevel + 1);
0239         return aResult;
0240       }
0241     }
0242 
0243     aPrevSum = aNewSum;
0244     aH *= 0.5; // Halve step size for next level
0245   }
0246 
0247   // Did not converge, return best estimate
0248   aResult.Status       = Status::OK; // Still return a result
0249   aResult.Value        = aPrevSum;
0250   aResult.NbPoints     = aTotalPoints;
0251   aResult.NbIterations = static_cast<size_t>(theConfig.NbLevels);
0252   return aResult;
0253 }
0254 
0255 //! Exp-Sinh quadrature for semi-infinite interval [a, +infinity).
0256 //!
0257 //! The transformation maps [0, +infinity) to (-infinity, +infinity):
0258 //!   x = a + exp(pi/2 * sinh(t))
0259 //!
0260 //! @tparam Function type with Value(double theX, double& theF) method
0261 //! @param theFunc function to integrate
0262 //! @param theLower lower bound a
0263 //! @param theConfig integration configuration
0264 //! @return integration result
0265 template <typename Function>
0266 IntegResult ExpSinh(Function&              theFunc,
0267                     double                 theLower,
0268                     const DoubleExpConfig& theConfig = DoubleExpConfig())
0269 {
0270   IntegResult aResult;
0271 
0272   const double aHalfPi      = M_PI / 2.0;
0273   double       aH           = 1.0;
0274   double       aPrevSum     = 0.0;
0275   size_t       aTotalPoints = 0;
0276 
0277   for (int aLevel = 0; aLevel < theConfig.NbLevels; ++aLevel)
0278   {
0279     double aSum      = 0.0;
0280     int    aNbPoints = 0;
0281 
0282     int aStart = (aLevel == 0) ? 0 : 1;
0283     int aStep  = (aLevel == 0) ? 1 : 2;
0284 
0285     // Positive and negative t
0286     for (int aSign = -1; aSign <= 1; aSign += 2)
0287     {
0288       for (int k = (aSign < 0 && aLevel == 0) ? 1 : aStart;; k += aStep)
0289       {
0290         if (aSign < 0 && k == 0)
0291         {
0292           continue;
0293         }
0294 
0295         double aT = aSign * k * aH;
0296 
0297         double aSinhT = std::sinh(aT);
0298         double aCoshT = std::cosh(aT);
0299         double aU     = aHalfPi * aSinhT;
0300 
0301         // exp(u) for x transformation
0302         if (aU > 700.0)
0303         {
0304           break; // Overflow
0305         }
0306 
0307         double aExpU = std::exp(aU);
0308 
0309         // x = a + exp(u)
0310         double aX = theLower + aExpU;
0311 
0312         // Weight: h * pi/2 * cosh(t) * exp(u)
0313         double aWeight = aHalfPi * aCoshT * aExpU;
0314 
0315         if (aWeight < MathUtils::THE_ZERO_TOL || !std::isfinite(aWeight))
0316         {
0317           break;
0318         }
0319 
0320         // For negative t with large |u|, the contribution may be negligible
0321         if (aU < -30.0)
0322         {
0323           break;
0324         }
0325 
0326         double aF = 0.0;
0327         if (!theFunc.Value(aX, aF))
0328         {
0329           continue;
0330         }
0331 
0332         if (!std::isfinite(aF))
0333         {
0334           continue;
0335         }
0336 
0337         aSum += aWeight * aF;
0338         ++aNbPoints;
0339 
0340         if (aNbPoints > 10000)
0341         {
0342           break;
0343         }
0344       }
0345     }
0346 
0347     double aLevelSum = aH * aSum;
0348     aTotalPoints += static_cast<size_t>(aNbPoints);
0349 
0350     // Trapezoidal refinement: S_new = S_old/2 + h_new * new_points_sum
0351     double aNewSum = (aLevel == 0) ? aLevelSum : 0.5 * aPrevSum + aLevelSum;
0352 
0353     if (aLevel > 0)
0354     {
0355       double aAbsError = std::abs(aNewSum - aPrevSum);
0356       double aRelError = aAbsError / std::max(std::abs(aNewSum), 1.0e-15);
0357 
0358       if (aRelError < theConfig.Tolerance)
0359       {
0360         aResult.Status        = Status::OK;
0361         aResult.Value         = aNewSum;
0362         aResult.AbsoluteError = aAbsError;
0363         aResult.RelativeError = aRelError;
0364         aResult.NbPoints      = aTotalPoints;
0365         aResult.NbIterations  = static_cast<size_t>(aLevel + 1);
0366         return aResult;
0367       }
0368     }
0369 
0370     aPrevSum = aNewSum;
0371     aH *= 0.5;
0372   }
0373 
0374   aResult.Status       = Status::OK;
0375   aResult.Value        = aPrevSum;
0376   aResult.NbPoints     = aTotalPoints;
0377   aResult.NbIterations = static_cast<size_t>(theConfig.NbLevels);
0378   return aResult;
0379 }
0380 
0381 //! Sinh-Sinh quadrature for infinite interval (-infinity, +infinity).
0382 //!
0383 //! The transformation maps (-infinity, +infinity) to (-infinity, +infinity):
0384 //!   x = sinh(pi/2 * sinh(t))
0385 //!
0386 //! @tparam Function type with Value(double theX, double& theF) method
0387 //! @param theFunc function to integrate
0388 //! @param theConfig integration configuration
0389 //! @return integration result
0390 template <typename Function>
0391 IntegResult SinhSinh(Function& theFunc, const DoubleExpConfig& theConfig = DoubleExpConfig())
0392 {
0393   IntegResult aResult;
0394 
0395   const double aHalfPi      = M_PI / 2.0;
0396   double       aH           = 1.0;
0397   double       aPrevSum     = 0.0;
0398   size_t       aTotalPoints = 0;
0399 
0400   for (int aLevel = 0; aLevel < theConfig.NbLevels; ++aLevel)
0401   {
0402     double aSum      = 0.0;
0403     int    aNbPoints = 0;
0404 
0405     int aStart = (aLevel == 0) ? 0 : 1;
0406     int aStep  = (aLevel == 0) ? 1 : 2;
0407 
0408     // Positive and negative t
0409     for (int aSign = -1; aSign <= 1; aSign += 2)
0410     {
0411       for (int k = (aSign < 0 && aLevel == 0) ? 1 : aStart;; k += aStep)
0412       {
0413         if (aSign < 0 && k == 0)
0414         {
0415           continue;
0416         }
0417 
0418         double aT = aSign * k * aH;
0419 
0420         double aSinhT = std::sinh(aT);
0421         double aCoshT = std::cosh(aT);
0422         double aU     = aHalfPi * aSinhT;
0423 
0424         // sinh(u) and cosh(u) for transformation
0425         if (std::abs(aU) > 700.0)
0426         {
0427           break;
0428         }
0429 
0430         double aSinhU = std::sinh(aU);
0431         double aCoshU = std::cosh(aU);
0432 
0433         // x = sinh(u)
0434         double aX = aSinhU;
0435 
0436         // Weight: h * pi/2 * cosh(t) * cosh(u)
0437         double aWeight = aHalfPi * aCoshT * aCoshU;
0438 
0439         if (aWeight < MathUtils::THE_ZERO_TOL || !std::isfinite(aWeight))
0440         {
0441           break;
0442         }
0443 
0444         double aF = 0.0;
0445         if (!theFunc.Value(aX, aF))
0446         {
0447           continue;
0448         }
0449 
0450         if (!std::isfinite(aF))
0451         {
0452           continue;
0453         }
0454 
0455         aSum += aWeight * aF;
0456         ++aNbPoints;
0457 
0458         if (aNbPoints > 10000)
0459         {
0460           break;
0461         }
0462       }
0463     }
0464 
0465     double aLevelSum = aH * aSum;
0466     aTotalPoints += static_cast<size_t>(aNbPoints);
0467 
0468     // Trapezoidal refinement: S_new = S_old/2 + h_new * new_points_sum
0469     double aNewSum = (aLevel == 0) ? aLevelSum : 0.5 * aPrevSum + aLevelSum;
0470 
0471     if (aLevel > 0)
0472     {
0473       double aAbsError = std::abs(aNewSum - aPrevSum);
0474       double aRelError = aAbsError / std::max(std::abs(aNewSum), 1.0e-15);
0475 
0476       if (aRelError < theConfig.Tolerance)
0477       {
0478         aResult.Status        = Status::OK;
0479         aResult.Value         = aNewSum;
0480         aResult.AbsoluteError = aAbsError;
0481         aResult.RelativeError = aRelError;
0482         aResult.NbPoints      = aTotalPoints;
0483         aResult.NbIterations  = static_cast<size_t>(aLevel + 1);
0484         return aResult;
0485       }
0486     }
0487 
0488     aPrevSum = aNewSum;
0489     aH *= 0.5;
0490   }
0491 
0492   aResult.Status       = Status::OK;
0493   aResult.Value        = aPrevSum;
0494   aResult.NbPoints     = aTotalPoints;
0495   aResult.NbIterations = static_cast<size_t>(theConfig.NbLevels);
0496   return aResult;
0497 }
0498 
0499 //! Double exponential integration with automatic interval detection.
0500 //!
0501 //! Selects the appropriate DE quadrature based on the interval:
0502 //! - Finite [a,b]: TanhSinh
0503 //! - Semi-infinite [a, +infinity): ExpSinh
0504 //! - Infinite (-infinity, +infinity): SinhSinh
0505 //!
0506 //! @tparam Function type with Value(double theX, double& theF) method
0507 //! @param theFunc function to integrate
0508 //! @param theLower lower bound (use -HUGE_VAL for -infinity)
0509 //! @param theUpper upper bound (use HUGE_VAL for +infinity)
0510 //! @param theConfig integration configuration
0511 //! @return integration result
0512 template <typename Function>
0513 IntegResult DoubleExponential(Function&              theFunc,
0514                               double                 theLower,
0515                               double                 theUpper,
0516                               const DoubleExpConfig& theConfig = DoubleExpConfig())
0517 {
0518   const double aHuge = 1.0e300;
0519 
0520   bool aIsLowerInf = (theLower < -aHuge);
0521   bool aIsUpperInf = (theUpper > aHuge);
0522 
0523   if (aIsLowerInf && aIsUpperInf)
0524   {
0525     // (-infinity, +infinity)
0526     return SinhSinh(theFunc, theConfig);
0527   }
0528   else if (aIsUpperInf)
0529   {
0530     // [a, +infinity)
0531     return ExpSinh(theFunc, theLower, theConfig);
0532   }
0533   else if (aIsLowerInf)
0534   {
0535     // (-infinity, b] - transform to [b, +infinity) with x -> -x
0536     class NegatedFunc
0537     {
0538     public:
0539       NegatedFunc(Function& theF)
0540           : myFunc(theF)
0541       {
0542       }
0543 
0544       bool Value(double theX, double& theF) { return myFunc.Value(-theX, theF); }
0545 
0546     private:
0547       Function& myFunc;
0548     };
0549 
0550     NegatedFunc aNegated(theFunc);
0551     return ExpSinh(aNegated, -theUpper, theConfig);
0552   }
0553   else
0554   {
0555     // [a, b]
0556     return TanhSinh(theFunc, theLower, theUpper, theConfig);
0557   }
0558 }
0559 
0560 //! Tanh-Sinh quadrature optimized for endpoint singularities.
0561 //!
0562 //! This variant uses precomputed nodes and weights for better performance
0563 //! when integrating functions with algebraic singularities at the endpoints.
0564 //!
0565 //! @tparam Function type with Value(double theX, double& theF) method
0566 //! @param theFunc function to integrate
0567 //! @param theLower lower bound
0568 //! @param theUpper upper bound
0569 //! @param theTolerance relative tolerance
0570 //! @return integration result
0571 template <typename Function>
0572 IntegResult TanhSinhSingular(Function& theFunc,
0573                              double    theLower,
0574                              double    theUpper,
0575                              double    theTolerance = 1.0e-10)
0576 {
0577   DoubleExpConfig aConfig;
0578   aConfig.Tolerance = theTolerance;
0579   aConfig.NbLevels  = 8; // More levels for singular functions
0580 
0581   return TanhSinh(theFunc, theLower, theUpper, aConfig);
0582 }
0583 
0584 //! Integrates a function with a known singularity at an interior point.
0585 //!
0586 //! Splits the interval at the singularity and integrates each part.
0587 //!
0588 //! @tparam Function type with Value(double theX, double& theF) method
0589 //! @param theFunc function to integrate
0590 //! @param theLower lower bound
0591 //! @param theUpper upper bound
0592 //! @param theSingularity location of interior singularity
0593 //! @param theConfig integration configuration
0594 //! @return combined integration result
0595 template <typename Function>
0596 IntegResult TanhSinhWithSingularity(Function&              theFunc,
0597                                     double                 theLower,
0598                                     double                 theUpper,
0599                                     double                 theSingularity,
0600                                     const DoubleExpConfig& theConfig = DoubleExpConfig())
0601 {
0602   IntegResult aResult;
0603 
0604   if (theSingularity <= theLower || theSingularity >= theUpper)
0605   {
0606     // Singularity is at or outside bounds, use regular integration
0607     return TanhSinh(theFunc, theLower, theUpper, theConfig);
0608   }
0609 
0610   // Integrate left part [lower, singularity]
0611   IntegResult aLeft = TanhSinh(theFunc, theLower, theSingularity, theConfig);
0612   if (!aLeft.IsDone())
0613   {
0614     return aLeft;
0615   }
0616 
0617   // Integrate right part [singularity, upper]
0618   IntegResult aRight = TanhSinh(theFunc, theSingularity, theUpper, theConfig);
0619   if (!aRight.IsDone())
0620   {
0621     return aRight;
0622   }
0623 
0624   // Combine results
0625   aResult.Status       = Status::OK;
0626   aResult.Value        = *aLeft.Value + *aRight.Value;
0627   aResult.NbPoints     = aLeft.NbPoints + aRight.NbPoints;
0628   aResult.NbIterations = std::max(aLeft.NbIterations, aRight.NbIterations);
0629 
0630   if (aLeft.AbsoluteError && aRight.AbsoluteError)
0631   {
0632     aResult.AbsoluteError = *aLeft.AbsoluteError + *aRight.AbsoluteError;
0633     aResult.RelativeError = *aResult.AbsoluteError / std::max(std::abs(*aResult.Value), 1.0e-15);
0634   }
0635 
0636   return aResult;
0637 }
0638 
0639 } // namespace MathInteg
0640 
0641 #endif // _MathInteg_DoubleExp_HeaderFile