Control routines¶
Load control¶
A load control object is constructed as follows:
auto lc = Ikarus::LoadControl(nonlinearSolver, numLoadSteps, {loadFactorStartValue, loadFactorEndValue});
nonlinearSolver
is a nonlinear solver, e.g., Newton-Raphson method, trust-region method, etc.numLoadSteps
is the number of load steps.loadFactorStartValue
is the value of the load factor at the beginning of the simulation.loadFactorEndValue
is the value of the load factor at the end of the simulation.
The load control is started with the run()
method, i.e., for the above-mentioned example:
Obtaining information from control routines¶
The load control is an observable object, i.e. one can subscribe to the messages of the load control method. To read further on the implementation of observer patterns in Ikarus, see here.
The following messages are available:
enum class ControlMessages {
BEGIN,
CONTROL_STARTED,
CONTROL_ENDED,
STEP_STARTED,
STEP_ENDED,
SOLUTION_CHANGED,
END
};
Path-following techniques¶
A general routine based on the standard arc-length method is included, which uses a scalar subsidiary function to impose a constraint on the non-linear system of equations. The previously mentioned LoadControl method can also be recreated using this technique. For more details on the standard arc-length method, see, among others, the works of Wempner1, Crisfield2, Ramm3 and Riks4 among others. A path-following object is constructed as follows:
where nr
is a Newton-Raphson solver which considers a scalar subsidiary function and is defined by
and pft
is the desired path-following technique. Three different path-following techniques are included, namely
- Standard arc-length method
- Load control method (as a subsidiary function under this generalized implementation)
- Displacement control method (uses a vector of indices which are all controlled by a same
stepSize
).
These can be invoked by defining
auto pft = Ikarus::StandardArcLength{};
auto pft = Ikarus::LoadControlWithSubsidiaryFunction{};
auto pft = Ikarus::DisplacementControl{controlledIndices};
Note
The default path-following type is the Ikarus::StandardArcLength{}
.
In the current implementation, it is assumed that the external forces are given by
\(F_{ext} = F_{ext}^0\lambda\) such that
$$
-\frac{\partial \mathbf{R}}{\partial \lambda} = F_{ext}^0
$$
An implementation for a general non-linear \(F_{ext} = F_{ext}^0\left(\mathbf{D},\lambda\right)\) is an open task.
In order to create an own implementation for the scalar subsidiary function, the user has to create a struct
with the following three member functions:
void evaluateSubsidiaryFunction(SubsidiaryArgs& args) const;
void initialPrediction(NonLinearOperator& nonLinearOperator, SubsidiaryArgs& args);
void intermediatePrediction(NonLinearOperator& nonLinearOperator, SubsidiaryArgs& args);
For each Newton-Raphson iteration, the function evaluateSubsidiaryFunction(SubsidiaryArgs& args)
is used to evaluate the
subsidiary function and
its derivatives with respect to the displacement \(\mathbf{D}\) and the load factor \(\lambda\). The other two functions
are used to specify a prediction for \(\mathbf{D}\) and \(\lambda\) for the initial step and for
all the other intermediate subsequent load_steps
, respectively.
SubsidiaryArgs
is a struct
which is defined as
struct SubsidiaryArgs {
double stepSize; // (1)!
Eigen::VectorX<double> DD; // (2)!
double Dlambda{}; // (3)!
double f{}; // (4)!
Eigen::VectorX<double> dfdDD; // (5)!
double dfdDlambda{}; // (6)!
int currentStep; // (7)!
};
- User-desired step size
- Vector of displacement increments
- Increment in the load factor
- Scalar value evaluated from the subsidiary function
- Derivative of the subsidiary function with respect to the displacement increment
- Derivative of the subsidiary function with respect to the load factor increment
- Current load step number
An example for the standard arc-length method is shown below:
struct StandardArcLength {
void evaluateSubsidiaryFunction(SubsidiaryArgs& args) const {
if (psi) {
const auto root = sqrt(args.DD.squaredNorm() + psi.value() * psi.value() * args.Dlambda * args.Dlambda);
args.f = root - args.stepSize;
args.dfdDD = args.DD / root;
args.dfdDlambda = (psi.value() * psi.value() * args.Dlambda) / root;
} else
DUNE_THROW(Dune::InvalidStateException,
"You have to call initialPrediction first. Otherwise psi is not defined");
}
template <typename NonLinearOperator>
void initialPrediction(NonLinearOperator& nonLinearOperator, SubsidiaryArgs& args) {
auto linearSolver
= Ikarus::LinearSolver(Ikarus::SolverTypeTag::d_LDLT); // for the linear predictor step
nonLinearOperator.lastParameter() = 1.0; // lambda =1.0
nonLinearOperator.template update<0>();
const auto& R = nonLinearOperator.value();
const auto& K = nonLinearOperator.derivative();
linearSolver.factorize(K);
linearSolver.solve(args.DD, -R);
const auto DD2 = args.DD.squaredNorm();
psi = sqrt(DD2);
auto s = sqrt(psi.value() * psi.value() + DD2);
args.DD = args.DD * args.stepSize / s;
args.Dlambda = args.stepSize / s;
nonLinearOperator.firstParameter() = args.DD;
nonLinearOperator.lastParameter() = args.Dlambda;
}
template <typename NonLinearOperator>
void intermediatePrediction(NonLinearOperator& nonLinearOperator, SubsidiaryArgs& args) {
nonLinearOperator.firstParameter() += args.DD;
nonLinearOperator.lastParameter() += args.Dlambda;
}
std::string name = "Arc length";
private:
std::optional<double> psi;
};
Adaptive step-sizing for the path-following techniques¶
Interface¶
The general interface for adaptive step-sizing is represented by the following concept.
namespace Ikarus::Concepts {
template <typename AdaptiveStepSizing, typename NonLinearSolverInformation, typename SubsidiaryArgs,
typename NonLinearOperator>
concept AdaptiveStepSizingStrategy = requires(AdaptiveStepSizing adaptiveSS, NonLinearSolverInformation info,
SubsidiaryArgs args, NonLinearOperator nop) {
{ adaptiveSS(info, args, nop) } -> std::same_as<void>; // (1)!
{ adaptiveSS.targetIterations() } -> std::same_as<int>; // (2)!
{ adaptiveSS.setTargetIterations(std::declval<int>()) } -> std::same_as<void>; // (3)!
};
}
operator()
is overloaded such that the step size is modified.- Function that returns
targetIterations
to be achieved. See Iteration-based, for example. - Function used to set
targetIterations
.
For implementation details, refer to ikarus/controlroutines/adaptivestepsizing.hh
.
Implementations¶
No Operation¶
By default, AdaptiveStepSizing::NoOp
is used with a path-following technique.
AdaptiveStepSizing::NoOp
uses the step size provided by the user and doesn't modify them while using PathFollowing
.
NoOp
here stands for No Operation.
Iteration-based¶
Instead of using a constant step size, the step size can be automatically adapted for efficient computations.
The AdaptiveStepSizing::IterationBased
is implemented according to Ramm3.
The step size can be scaled as shown below:
Here, \(\hat{s}_{k+1}\) and \(\hat{s}_{k}\) are the step sizes at \(k+1\)-th and \(k\)-th iteration. Here, \(\hat{i}\) is the desired number of iterations and \(i_k\) is the number of iterations used in the previous step.
-
Gerald A. Wempner. Discrete approximations related to nonlinear theories of solids. International Journal of Solids and Structures, 7(11):1581–1599, 1971. doi:10.1016/0020-7683(71)90038-2. ↩
-
M.A. Crisfield. A fast incremental/iterative solution procedure that handles “snap-through”. Computers & Structures, 13(1):55–62, 1981. doi:10.1016/0045-7949(81)90108-5. ↩
-
E. Ramm. Strategies for Tracing the Nonlinear Response Near Limit Points. In W. Wunderlich, E. Stein, and K.-J. Bathe, editors, Nonlinear Finite Element Analysis in Structural Mechanics, pages 63–89. Springer Berlin Heidelberg, Berlin, Heidelberg, 1981. doi:10.1007/978-3-642-81589-8_5. ↩↩
-
E. Riks. The Application of Newton’s Method to the Problem of Elastic Stability. Journal of Applied Mechanics, 39(4):1060–1065, 1972. doi:10.1115/1.3422829. ↩