Loading files
4f95ecaa3This file is pinned to the selected release. Download the original to open it in your design tool.
// Copyright (c) 2025 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef _MathRoot_All_HeaderFile
#define _MathRoot_All_HeaderFile
#include <MathUtils_Types.hxx>
#include <MathUtils_Config.hxx>
#include <MathRoot_Multiple.hxx>
#include <math_Vector.hxx>
#include <NCollection_DynamicArray.hxx>
#include <cmath>
namespace MathRoot
{
using namespace MathUtils;
//! Represents an interval where the function is null (within tolerance).
struct NullInterval
{
double A = 0.0; //!< Interval start
double B = 0.0; //!< Interval end
int State = 0; //!< State number (for parametric curves)
};
//! Result for all roots finder including null intervals.
struct AllRootsResult
{
MathUtils::Status Status = MathUtils::Status::NotConverged;
NCollection_DynamicArray<double> Roots; //!< Isolated root locations
NCollection_DynamicArray<int> RootStates; //!< State numbers for roots
NCollection_DynamicArray<NullInterval> NullIntervals; //!< Intervals where function is null
bool IsDone() const { return Status == MathUtils::Status::OK; }
explicit operator bool() const { return IsDone(); }
int NbRoots() const { return Roots.Length(); }
int NbIntervals() const { return NullIntervals.Length(); }
};
//! Helper function to find multiple roots with offset.
//! @tparam Func function type
//! @param theFunc function to analyze
//! @param theA interval start
//! @param theB interval end
//! @param theNbSamples number of sample points
//! @param theEpsX tolerance for root x-value
//! @param theEpsF tolerance for function value
//! @param theOffset offset value (find roots of f(x) - offset = 0)
//! @return MultipleResult with found roots
template <typename Func>
MultipleResult FindMultipleRoots(Func& theFunc,
double theA,
double theB,
int theNbSamples,
double theEpsX,
double theEpsF,
double theOffset = 0.0)
{
MultipleConfig aConfig;
aConfig.NbSamples = theNbSamples;
aConfig.XTolerance = theEpsX;
aConfig.FTolerance = theEpsF;
aConfig.Offset = theOffset;
return FindAllRoots(theFunc, theA, theB, aConfig);
}
//! Find all roots of a function using sampling and refinement.
//!
//! Uses a sample of the function to find:
//! 1. Null intervals: where |F(x)| <= EpsNul for consecutive sample points
//! 2. Isolated roots: single points where F(x) = 0
//!
//! The algorithm:
//! 1. Evaluates F at sample points
//! 2. Identifies null intervals where |F| <= EpsNul for 2+ consecutive points
//! 3. Refines interval boundaries using root finding
//! 4. Finds isolated roots between null intervals using MultipleRoots
//!
//! @tparam Func function type with:
//! - bool Value(double, double&)
//! - bool Values(double, double&, double&) for derivative
//! @param theFunc function with derivative to analyze
//! @param theSamples sample points array
//! @param theEpsX tolerance for root x-value
//! @param theEpsF tolerance for function value at root
//! @param theEpsNul tolerance for null interval detection
//! @return AllRootsResult with roots and null intervals
template <typename Func>
AllRootsResult FindAllRootsWithIntervals(Func& theFunc,
const math_Vector& theSamples,
double theEpsX = 1.0e-10,
double theEpsF = 1.0e-10,
double theEpsNul = 1.0e-10)
{
AllRootsResult aResult;
const int aNbp = theSamples.Length();
if (aNbp < 2)
{
aResult.Status = MathUtils::Status::InvalidInput;
return aResult;
}
const int aLower = theSamples.Lower();
// Evaluate function at first sample point
double aVal, aPrevVal;
if (!theFunc.Value(theSamples(aLower), aPrevVal))
{
aResult.Status = MathUtils::Status::NotConverged;
return aResult;
}
bool aPrevNul = std::abs(aPrevVal) <= theEpsNul;
if (!aPrevNul)
{
// Save non-null value for later use
}
bool aInInterval = false;
bool aNulStart = false;
bool aNulEnd = false;
double aDebNul = 0.0, aFinNul = 0.0;
double aValSav = aPrevVal;
NCollection_DynamicArray<double> aIntervalStarts, aIntervalEnds;
// Scan through samples to find null intervals
for (int i = 1; i < aNbp; ++i)
{
if (!theFunc.Value(theSamples(aLower + i), aVal))
{
aResult.Status = MathUtils::Status::NotConverged;
return aResult;
}
bool aCurNul = std::abs(aVal) <= theEpsNul;
if (!aCurNul)
{
aValSav = aVal;
}
if (aInInterval && !aCurNul)
{
// End of null interval
aInInterval = false;
aIntervalStarts.Append(aDebNul);
// Refine end of null interval
double aCst = (aVal > 0.0) ? theEpsNul : -theEpsNul;
// Use root finding to locate precise boundary
MultipleResult aRes = FindMultipleRoots(theFunc,
theSamples(aLower + i - 1),
theSamples(aLower + i),
10,
theEpsX,
theEpsF,
aCst);
if (aRes.IsDone() && aRes.NbRoots() > 0)
{
aFinNul = aRes.Roots[0];
}
else
{
aFinNul = theSamples(aLower + i - 1);
}
// Try opposite sign
aCst = -aCst;
auto aRes2 = FindMultipleRoots(theFunc,
theSamples(aLower + i - 1),
theSamples(aLower + i),
10,
theEpsX,
theEpsF,
aCst);
if (aRes2.IsDone() && aRes2.NbRoots() > 0)
{
if (aRes2.Roots[0] < aFinNul)
{
aFinNul = aRes2.Roots[0];
}
}
aIntervalEnds.Append(aFinNul);
}
else if (!aInInterval && aPrevNul && aCurNul)
{
// Start of null interval
aInInterval = true;
if (i == 1)
{
aDebNul = theSamples(aLower);
aNulStart = true;
}
else
{
// Refine start of null interval
double aCst = (aValSav > 0.0) ? theEpsNul : -theEpsNul;
MultipleResult aRes = FindMultipleRoots(theFunc,
theSamples(aLower + i - 2),
theSamples(aLower + i - 1),
10,
theEpsX,
theEpsF,
aCst);
if (aRes.IsDone() && aRes.NbRoots() > 0)
{
aDebNul = aRes.Roots[aRes.NbRoots() - 1];
}
else
{
aDebNul = theSamples(aLower + i - 1);
}
// Try opposite sign
aCst = -aCst;
auto aRes2 = FindMultipleRoots(theFunc,
theSamples(aLower + i - 2),
theSamples(aLower + i - 1),
10,
theEpsX,
theEpsF,
aCst);
if (aRes2.IsDone() && aRes2.NbRoots() > 0)
{
if (aRes2.Roots[aRes2.NbRoots() - 1] > aDebNul)
{
aDebNul = aRes2.Roots[aRes2.NbRoots() - 1];
}
}
}
}
aPrevNul = aCurNul;
}
// Handle interval ending at last sample
if (aInInterval)
{
aIntervalStarts.Append(aDebNul);
aFinNul = theSamples(aLower + aNbp - 1);
aIntervalEnds.Append(aFinNul);
aNulEnd = true;
}
// Store null intervals
for (int k = 0; k < aIntervalStarts.Length(); ++k)
{
NullInterval anInt;
anInt.A = aIntervalStarts.Value(k);
anInt.B = aIntervalEnds.Value(k);
aResult.NullIntervals.Append(anInt);
}
const double aSampleFirst = theSamples(aLower);
const double aSampleLast = theSamples(aLower + aNbp - 1);
// Find isolated roots between null intervals
if (aIntervalStarts.IsEmpty())
{
// No null intervals - find all roots in entire range
MultipleResult aRes =
FindMultipleRoots(theFunc, aSampleFirst, aSampleLast, aNbp, theEpsX, theEpsF);
if (aRes.IsDone())
{
for (int j = 0; j < aRes.NbRoots(); ++j)
{
aResult.Roots.Append(aRes.Roots[j]);
aResult.RootStates.Append(0);
}
}
}
else
{
// Find roots before first null interval
if (!aNulStart)
{
double aStart = aSampleFirst;
double aEnd = aIntervalStarts.Value(0);
int aNbrpt =
std::max(3,
static_cast<int>(std::abs((aEnd - aStart) / (aSampleLast - aSampleFirst)) * aNbp));
MultipleResult aRes = FindMultipleRoots(theFunc, aStart, aEnd, aNbrpt, theEpsX, theEpsF);
if (aRes.IsDone())
{
for (int j = 0; j < aRes.NbRoots(); ++j)
{
aResult.Roots.Append(aRes.Roots[j]);
aResult.RootStates.Append(0);
}
}
}
// Find roots between null intervals
for (int k = 1; k < aIntervalStarts.Length(); ++k)
{
double aStart = aIntervalEnds.Value(k - 1);
double aEnd = aIntervalStarts.Value(k);
int aNbrpt =
std::max(3,
static_cast<int>(std::abs((aEnd - aStart) / (aSampleLast - aSampleFirst)) * aNbp));
MultipleResult aRes = FindMultipleRoots(theFunc, aStart, aEnd, aNbrpt, theEpsX, theEpsF);
if (aRes.IsDone())
{
for (int j = 0; j < aRes.NbRoots(); ++j)
{
aResult.Roots.Append(aRes.Roots[j]);
aResult.RootStates.Append(0);
}
}
}
// Find roots after last null interval
if (!aNulEnd)
{
double aStart = aIntervalEnds.Value(aIntervalEnds.Length() - 1);
double aEnd = aSampleLast;
int aNbrpt =
std::max(3,
static_cast<int>(std::abs((aEnd - aStart) / (aSampleLast - aSampleFirst)) * aNbp));
MultipleResult aRes = FindMultipleRoots(theFunc, aStart, aEnd, aNbrpt, theEpsX, theEpsF);
if (aRes.IsDone())
{
for (int j = 0; j < aRes.NbRoots(); ++j)
{
aResult.Roots.Append(aRes.Roots[j]);
aResult.RootStates.Append(0);
}
}
}
}
aResult.Status = MathUtils::Status::OK;
return aResult;
}
//! Find all roots using uniform sampling.
//!
//! @tparam Func function type with Value and Values methods
//! @param theFunc function with derivative
//! @param theA interval start
//! @param theB interval end
//! @param theNbSamples number of sample points
//! @param theEpsX tolerance for root x-value
//! @param theEpsF tolerance for function value
//! @param theEpsNul tolerance for null interval detection
//! @return AllRootsResult with roots and null intervals
template <typename Func>
AllRootsResult FindAllRootsWithIntervals(Func& theFunc,
double theA,
double theB,
int theNbSamples,
double theEpsX = 1.0e-10,
double theEpsF = 1.0e-10,
double theEpsNul = 1.0e-10)
{
math_Vector aSamples(0, theNbSamples - 1);
const double aStep = (theB - theA) / (theNbSamples - 1);
for (int i = 0; i < theNbSamples; ++i)
{
aSamples(i) = theA + i * aStep;
}
// Ensure last point is exactly theB
aSamples(theNbSamples - 1) = theB;
return FindAllRootsWithIntervals(theFunc, aSamples, theEpsX, theEpsF, theEpsNul);
}
} // namespace MathRoot
#endif // _MathRoot_All_HeaderFile