2395 lines
78 KiB
C++
2395 lines
78 KiB
C++
/**
|
|
* @file BEulerInt.cpp
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Copywrite 2004 Sandia Corporation. Under the terms of Contract
|
|
* DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
|
|
* retains certain rights in this software.
|
|
* See file License.txt for licensing information.
|
|
*/
|
|
|
|
#include "BEulerInt.h"
|
|
|
|
|
|
#include "cantera/base/mdp_allo.h"
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
using namespace mdp;
|
|
|
|
#define SAFE_DELETE(a) if (a) { delete (a); a = 0; }
|
|
|
|
|
|
/*
|
|
* Blas routines
|
|
*/
|
|
extern "C" {
|
|
extern void dcopy_(int*, double*, int*, double*, int*);
|
|
}
|
|
namespace Cantera
|
|
{
|
|
|
|
//================================================================================================
|
|
/*
|
|
* Exception thrown when a BEuler error is encountered. We just call the
|
|
* Cantera Error handler in the initialization list
|
|
*/
|
|
BEulerErr::BEulerErr(std::string msg) :
|
|
CanteraError("BEulerInt", msg)
|
|
{
|
|
}
|
|
|
|
//================================================================================================
|
|
/*
|
|
* Constructor. Default settings: dense jacobian, no user-supplied
|
|
* Jacobian function, Newton iteration.
|
|
*/
|
|
BEulerInt::BEulerInt() :
|
|
m_iter(Newton_Iter),
|
|
m_method(BEulerVarStep),
|
|
m_jacFormMethod(BEULER_JAC_NUM),
|
|
m_rowScaling(true),
|
|
m_colScaling(false),
|
|
m_matrixConditioning(false),
|
|
m_itol(0),
|
|
m_reltol(1.e-4),
|
|
m_abstols(1.e-10),
|
|
m_abstol(0),
|
|
m_ewt(0),
|
|
m_hmax(0.0),
|
|
m_maxord(0),
|
|
m_time_step_num(0),
|
|
m_time_step_attempts(0),
|
|
m_max_time_step_attempts(11000000),
|
|
m_numInitialConstantDeltaTSteps(0),
|
|
m_failure_counter(0),
|
|
m_min_newt_its(0),
|
|
m_printSolnStepInterval(1),
|
|
m_printSolnNumberToTout(1),
|
|
m_printSolnFirstSteps(0),
|
|
m_dumpJacobians(false),
|
|
m_neq(0),
|
|
m_y_n(0),
|
|
m_y_nm1(0),
|
|
m_y_pred_n(0),
|
|
m_ydot_n(0),
|
|
m_ydot_nm1(0),
|
|
m_t0(0.0),
|
|
m_time_final(0.0),
|
|
time_n(0.0),
|
|
time_nm1(0.0),
|
|
time_nm2(0.0),
|
|
delta_t_n(0.0),
|
|
delta_t_nm1(0.0),
|
|
delta_t_nm2(0.0),
|
|
delta_t_np1(1.0E-8),
|
|
delta_t_max(1.0E300),
|
|
m_resid(0),
|
|
m_residWts(0),
|
|
m_wksp(0),
|
|
m_func(0),
|
|
m_rowScales(0),
|
|
m_colScales(0),
|
|
tdjac_ptr(0),
|
|
m_print_flag(3),
|
|
m_nfe(0),
|
|
m_nJacEval(0),
|
|
m_numTotalNewtIts(0),
|
|
m_numTotalLinearSolves(0),
|
|
m_numTotalConvFails(0),
|
|
m_numTotalTruncFails(0),
|
|
num_failures(0)
|
|
{
|
|
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
* Destructor
|
|
*/
|
|
BEulerInt::~BEulerInt()
|
|
{
|
|
mdp::mdp_safe_free((void**) &m_y_n);
|
|
mdp::mdp_safe_free((void**) &m_y_nm1);
|
|
mdp::mdp_safe_free((void**) &m_y_pred_n);
|
|
mdp::mdp_safe_free((void**) &m_ydot_n);
|
|
mdp::mdp_safe_free((void**) &m_ydot_nm1);
|
|
mdp::mdp_safe_free((void**) &m_resid);
|
|
mdp::mdp_safe_free((void**) &m_residWts);
|
|
mdp::mdp_safe_free((void**) &m_wksp);
|
|
mdp::mdp_safe_free((void**) &m_ewt);
|
|
mdp::mdp_safe_free((void**) &m_abstol);
|
|
mdp::mdp_safe_free((void**) &m_rowScales);
|
|
mdp::mdp_safe_free((void**) &m_colScales);
|
|
SAFE_DELETE(tdjac_ptr);
|
|
}
|
|
//================================================================================================
|
|
void BEulerInt::setTolerances(double reltol, int n, double* abstol)
|
|
{
|
|
m_itol = 1;
|
|
if (!m_abstol) {
|
|
m_abstol = mdp_alloc_dbl_1(m_neq, MDP_DBL_NOINIT);
|
|
}
|
|
if (n != m_neq) {
|
|
printf("ERROR n is wrong\n");
|
|
exit(-1);
|
|
}
|
|
for (int i = 0; i < m_neq; i++) {
|
|
m_abstol[i] = abstol[i];
|
|
}
|
|
m_reltol = reltol;
|
|
}
|
|
//================================================================================================
|
|
void BEulerInt::setTolerances(double reltol, double abstol)
|
|
{
|
|
m_itol = 0;
|
|
m_reltol = reltol;
|
|
m_abstols = abstol;
|
|
}
|
|
//================================================================================================
|
|
void BEulerInt::setProblemType(int jacFormMethod)
|
|
{
|
|
m_jacFormMethod = jacFormMethod;
|
|
}
|
|
//================================================================================================
|
|
void BEulerInt::setMethodBEMT(BEulerMethodType t)
|
|
{
|
|
m_method = t;
|
|
}
|
|
//================================================================================================
|
|
void BEulerInt::setMaxStep(doublereal hmax)
|
|
{
|
|
m_hmax = hmax;
|
|
}
|
|
//================================================================================================
|
|
void BEulerInt::setMaxNumTimeSteps(int maxNumTimeSteps)
|
|
{
|
|
m_max_time_step_attempts = maxNumTimeSteps;
|
|
}
|
|
//================================================================================================
|
|
void BEulerInt::setNumInitialConstantDeltaTSteps(int num)
|
|
{
|
|
m_numInitialConstantDeltaTSteps = num;
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
*
|
|
* setPrintSolnOptins():
|
|
*
|
|
* This routine controls when the solution is printed
|
|
*
|
|
* @param printStepInterval If greater than 0, then the
|
|
* soln is printed every printStepInterval
|
|
* steps.
|
|
*
|
|
* @param printNumberToTout The solution is printed at
|
|
* regular invervals a total of
|
|
* "printNumberToTout" times.
|
|
*
|
|
* @param printSolnFirstSteps The solution is printed out
|
|
* the first "printSolnFirstSteps"
|
|
* steps. After these steps the other
|
|
* parameters determine the printing.
|
|
* default = 0
|
|
*
|
|
* @param dumpJacobians Dump jacobians to disk.
|
|
*
|
|
* default = false
|
|
*
|
|
*/
|
|
void BEulerInt::setPrintSolnOptions(int printSolnStepInterval,
|
|
int printSolnNumberToTout,
|
|
int printSolnFirstSteps,
|
|
bool dumpJacobians)
|
|
{
|
|
m_printSolnStepInterval = printSolnStepInterval;
|
|
m_printSolnNumberToTout = printSolnNumberToTout;
|
|
m_printSolnFirstSteps = printSolnFirstSteps;
|
|
m_dumpJacobians = dumpJacobians;
|
|
}
|
|
//================================================================================================
|
|
void BEulerInt::setIterator(IterType t)
|
|
{
|
|
m_iter = t;
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
*
|
|
* setNonLinOptions()
|
|
*
|
|
* Set the options for the nonlinear method
|
|
*
|
|
* Defaults are set in the .h file. These are the defaults:
|
|
* min_newt_its = 0
|
|
* matrixConditioning = false
|
|
* colScaling = false
|
|
* rowScaling = true
|
|
*/
|
|
void BEulerInt::setNonLinOptions(int min_newt_its, bool matrixConditioning,
|
|
bool colScaling, bool rowScaling)
|
|
{
|
|
m_min_newt_its = min_newt_its;
|
|
m_matrixConditioning = matrixConditioning;
|
|
m_colScaling = colScaling;
|
|
m_rowScaling = rowScaling;
|
|
if (m_colScaling) {
|
|
if (!m_colScales) {
|
|
m_colScales = mdp_alloc_dbl_1(m_neq, 1.0);
|
|
}
|
|
}
|
|
if (m_rowScaling) {
|
|
if (!m_rowScales) {
|
|
m_rowScales = mdp_alloc_dbl_1(m_neq, 1.0);
|
|
}
|
|
}
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
*
|
|
* setInitialTimeStep():
|
|
*
|
|
* Set the initial time step. Right now, we set the
|
|
* time step by setting delta_t_np1.
|
|
*/
|
|
void BEulerInt::setInitialTimeStep(double deltaT)
|
|
{
|
|
delta_t_np1 = deltaT;
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
* setPrintFlag():
|
|
*
|
|
*/
|
|
void BEulerInt::setPrintFlag(int print_flag)
|
|
{
|
|
m_print_flag = print_flag;
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
*
|
|
* initialize():
|
|
*
|
|
* Find the initial conditions for y and ydot.
|
|
*/
|
|
void BEulerInt::initializeRJE(double t0, ResidJacEval& func)
|
|
{
|
|
m_neq = func.nEquations();
|
|
m_t0 = t0;
|
|
internalMalloc();
|
|
|
|
/*
|
|
* Get the initial conditions.
|
|
*/
|
|
func.getInitialConditions(m_t0, m_y_n, m_ydot_n);
|
|
|
|
// Store a pointer to the residual routine in the object
|
|
m_func = &func;
|
|
|
|
/*
|
|
* Initialize the various time counters in the object
|
|
*/
|
|
time_n = t0;
|
|
time_nm1 = time_n;
|
|
time_nm2 = time_nm1;
|
|
delta_t_n = 0.0;
|
|
delta_t_nm1 = 0.0;
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
*
|
|
* reinitialize():
|
|
*
|
|
*/
|
|
void BEulerInt::reinitializeRJE(double t0, ResidJacEval& func)
|
|
{
|
|
m_neq = func.nEquations();
|
|
m_t0 = t0;
|
|
internalMalloc();
|
|
/*
|
|
* At the initial time, get the initial conditions and time and store
|
|
* them into internal storage in the object, my[].
|
|
*/
|
|
m_t0 = t0;
|
|
func.getInitialConditions(m_t0, m_y_n, m_ydot_n);
|
|
/**
|
|
* Set up the internal weights that are used for testing convergence
|
|
*/
|
|
setSolnWeights();
|
|
|
|
// Store a pointer to the function
|
|
m_func = &func;
|
|
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
*
|
|
* getPrintTime():
|
|
*
|
|
*/
|
|
double BEulerInt::getPrintTime(double time_current)
|
|
{
|
|
double tnext;
|
|
if (m_printSolnNumberToTout > 0) {
|
|
double dt = (m_time_final - m_t0) / m_printSolnNumberToTout;
|
|
for (int i = 0; i <= m_printSolnNumberToTout; i++) {
|
|
tnext = m_t0 + dt * i;
|
|
if (tnext >= time_current) {
|
|
return tnext;
|
|
}
|
|
}
|
|
}
|
|
return 1.0E300;
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
* nEvals():
|
|
*
|
|
* Return the total number of function evaluations
|
|
*/
|
|
int BEulerInt::nEvals() const
|
|
{
|
|
return m_nfe;
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
*
|
|
* internalMalloc():
|
|
*
|
|
* Internal routine that sets up the fixed length storage based on
|
|
* the size of the problem to solve.
|
|
*/
|
|
void BEulerInt::internalMalloc()
|
|
{
|
|
mdp_realloc_dbl_1(&m_ewt, m_neq, 0, 0.0);
|
|
mdp_realloc_dbl_1(&m_y_n, m_neq, 0, 0.0);
|
|
mdp_realloc_dbl_1(&m_y_nm1, m_neq, 0, 0.0);
|
|
mdp_realloc_dbl_1(&m_y_pred_n, m_neq, 0, 0.0);
|
|
mdp_realloc_dbl_1(&m_ydot_n, m_neq, 0, 0.0);
|
|
mdp_realloc_dbl_1(&m_ydot_nm1, m_neq, 0, 0.0);
|
|
mdp_realloc_dbl_1(&m_resid, m_neq, 0, 0.0);
|
|
mdp_realloc_dbl_1(&m_residWts, m_neq, 0, 0.0);
|
|
mdp_realloc_dbl_1(&m_wksp, m_neq, 0, 0.0);
|
|
if (m_rowScaling) {
|
|
mdp_realloc_dbl_1(&m_rowScales, m_neq, 0, 1.0);
|
|
}
|
|
if (m_colScaling) {
|
|
mdp_realloc_dbl_1(&m_colScales, m_neq, 0, 1.0);
|
|
}
|
|
tdjac_ptr = new SquareMatrix(m_neq);
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
* setSolnWeights():
|
|
*
|
|
* Set the solution weights
|
|
* This is a very important routine as it affects quite a few
|
|
* operations involving convergence.
|
|
*
|
|
*/
|
|
void BEulerInt::setSolnWeights()
|
|
{
|
|
int i;
|
|
if (m_itol == 1) {
|
|
/*
|
|
* Adjust the atol vector if we are using vector
|
|
* atol conditions.
|
|
*/
|
|
// m_func->adjustAtol(m_abstol);
|
|
|
|
for (i = 0; i < m_neq; i++) {
|
|
m_ewt[i] = m_abstol[i] + m_reltol * 0.5 *
|
|
(fabs(m_y_n[i]) + fabs(m_y_pred_n[i]));
|
|
}
|
|
} else {
|
|
for (i = 0; i < m_neq; i++) {
|
|
m_ewt[i] = m_abstols + m_reltol * 0.5 *
|
|
(fabs(m_y_n[i]) + fabs(m_y_pred_n[i]));
|
|
}
|
|
}
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
*
|
|
* setColumnScales():
|
|
*
|
|
* Set the column scaling vector at the current time
|
|
*/
|
|
void BEulerInt::setColumnScales()
|
|
{
|
|
m_func->calcSolnScales(time_n, m_y_n, m_y_nm1, m_colScales);
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
* computeResidWts():
|
|
*
|
|
* We compute residual weights here, which we define as the L_0 norm
|
|
* of the Jacobian Matrix, weighted by the solution weights.
|
|
* This is the proper way to guage the magnitude of residuals. However,
|
|
* it does need the evaluation of the jacobian, and the implementation
|
|
* below is slow, but doesn't take up much memory.
|
|
*
|
|
* Here a small weighting indicates that the change in solution is
|
|
* very sensitive to that equation.
|
|
*/
|
|
void BEulerInt::computeResidWts(GeneralMatrix& jac)
|
|
{
|
|
int i, j;
|
|
double* data = &(*(jac.begin()));
|
|
double value;
|
|
for (i = 0; i < m_neq; i++) {
|
|
m_residWts[i] = fabs(data[i] * m_ewt[0]);
|
|
for (j = 1; j < m_neq; j++) {
|
|
value = fabs(data[j*m_neq + i] * m_ewt[j]);
|
|
m_residWts[i] = MAX(m_residWts[i], value);
|
|
}
|
|
}
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
* filterNewStep():
|
|
*
|
|
* void BEulerInt::
|
|
*
|
|
*/
|
|
double BEulerInt::filterNewStep(double timeCurrent, double* y_current, double* ydot_current)
|
|
{
|
|
return 0.0;
|
|
}
|
|
//==================================================================================================
|
|
static void print_line(const char* str, int n)
|
|
{
|
|
for (int i = 0; i < n; i++) {
|
|
printf("%s", str);
|
|
}
|
|
printf("\n");
|
|
}
|
|
//==================================================================================================
|
|
/*
|
|
* Print out for relevant time step information
|
|
*/
|
|
static void print_time_step1(int order, int n_time_step, double time,
|
|
double delta_t_n, double delta_t_nm1,
|
|
bool step_failed, int num_failures)
|
|
{
|
|
const char* string = 0;
|
|
if (order == 0) {
|
|
string = "Backward Euler";
|
|
} else if (order == 1) {
|
|
string = "Forward/Backward Euler";
|
|
} else if (order == 2) {
|
|
string = "Adams-Bashforth/TR";
|
|
}
|
|
printf("\n");
|
|
print_line("=", 80);
|
|
printf("\nStart of Time Step: %5d Time_n = %9.5g Time_nm1 = %9.5g\n",
|
|
n_time_step, time, time - delta_t_n);
|
|
printf("\tIntegration method = %s\n", string);
|
|
if (step_failed) {
|
|
printf("\tPreviously attempted step was a failure\n");
|
|
}
|
|
if (delta_t_n > delta_t_nm1) {
|
|
string = "(Increased from previous iteration)";
|
|
} else if (delta_t_n < delta_t_nm1) {
|
|
string = "(Decreased from previous iteration)";
|
|
} else {
|
|
string = "(same as previous iteration)";
|
|
}
|
|
printf("\tdelta_t_n = %8.5e %s", delta_t_n, string);
|
|
if (num_failures > 0) {
|
|
printf("\t(Bad_History Failure Counter = %d)", num_failures);
|
|
}
|
|
printf("\n\tdelta_t_nm1 = %8.5e\n", delta_t_nm1);
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
* Print out for relevant time step information
|
|
*/
|
|
static void print_time_step2(int time_step_num, int order,
|
|
double time, double time_error_factor,
|
|
double delta_t_n, double delta_t_np1)
|
|
{
|
|
printf("\tTime Step Number %5d was a success: time = %10g\n", time_step_num,
|
|
time);
|
|
printf("\t\tEstimated Error\n");
|
|
printf("\t\t-------------------- = %8.5e\n", time_error_factor);
|
|
printf("\t\tTolerated Error\n\n");
|
|
printf("\t- Recommended next delta_t (not counting history) = %g\n",
|
|
delta_t_np1);
|
|
printf("\n");
|
|
print_line("=", 80);
|
|
printf("\n");
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
* Print Out descriptive information on why the current step failed
|
|
*/
|
|
static void print_time_fail(bool convFailure, int time_step_num,
|
|
double time, double delta_t_n,
|
|
double delta_t_np1, double time_error_factor)
|
|
{
|
|
printf("\n");
|
|
print_line("=", 80);
|
|
if (convFailure) {
|
|
printf("\tTime Step Number %5d experienced a convergence "
|
|
"failure\n", time_step_num);
|
|
printf("\tin the non-linear or linear solver\n");
|
|
printf("\t\tValue of time at failed step = %g\n", time);
|
|
printf("\t\tdelta_t of the failed step = %g\n",
|
|
delta_t_n);
|
|
printf("\t\tSuggested value of delta_t to try next = %g\n",
|
|
delta_t_np1);
|
|
} else {
|
|
printf("\tTime Step Number %5d experienced a truncation error "
|
|
"failure!\n", time_step_num);
|
|
printf("\t\tValue of time at failed step = %g\n", time);
|
|
printf("\t\tdelta_t of the failed step = %g\n",
|
|
delta_t_n);
|
|
printf("\t\tSuggested value of delta_t to try next = %g\n",
|
|
delta_t_np1);
|
|
printf("\t\tCalculated truncation error factor = %g\n",
|
|
time_error_factor);
|
|
}
|
|
printf("\n");
|
|
print_line("=", 80);
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
* Print out the final results and counters
|
|
*/
|
|
static void print_final(double time, int step_failed,
|
|
int time_step_num, int num_newt_its,
|
|
int total_linear_solves, int numConvFails,
|
|
int numTruncFails, int nfe, int nJacEval)
|
|
{
|
|
printf("\n");
|
|
print_line("=", 80);
|
|
printf("TIME INTEGRATION ROUTINE HAS FINISHED: ");
|
|
if (step_failed) {
|
|
printf(" IT WAS A FAILURE\n");
|
|
} else {
|
|
printf(" IT WAS A SUCCESS\n");
|
|
}
|
|
printf("\tEnding time = %g\n", time);
|
|
printf("\tNumber of time steps = %d\n", time_step_num);
|
|
printf("\tNumber of newt its = %d\n", num_newt_its);
|
|
printf("\tNumber of linear solves = %d\n", total_linear_solves);
|
|
printf("\tNumber of convergence failures= %d\n", numConvFails);
|
|
printf("\tNumber of TimeTruncErr fails = %d\n", numTruncFails);
|
|
printf("\tNumber of Function evals = %d\n", nfe);
|
|
printf("\tNumber of Jacobian evals/solvs= %d\n", nJacEval);
|
|
printf("\n");
|
|
print_line("=", 80);
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
* Header info for one line comment about a time step
|
|
*/
|
|
static void print_lvl1_Header(int nTimes)
|
|
{
|
|
printf("\n");
|
|
if (nTimes) {
|
|
print_line("-", 80);
|
|
}
|
|
printf("time Time Time Time ");
|
|
if (nTimes == 0) {
|
|
printf(" START");
|
|
} else {
|
|
printf(" (continued)");
|
|
}
|
|
printf("\n");
|
|
|
|
printf("step (sec) step Newt Aztc bktr trunc ");
|
|
printf("\n");
|
|
|
|
printf(" No. Rslt size Its Its stps error |");
|
|
printf(" comment");
|
|
printf("\n");
|
|
print_line("-", 80);
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
* One line entry about time step
|
|
* rslt -> 4 letter code
|
|
*/
|
|
static void print_lvl1_summary(
|
|
int time_step_num, double time, const char* rslt, double delta_t_n,
|
|
int newt_its, int aztec_its, int bktr_stps, double time_error_factor,
|
|
const char* comment)
|
|
{
|
|
printf("%6d %11.6g %4s %10.4g %4d %4d %4d %11.4g",
|
|
time_step_num, time, rslt, delta_t_n, newt_its, aztec_its,
|
|
bktr_stps, time_error_factor);
|
|
if (comment) {
|
|
printf(" | %s", comment);
|
|
}
|
|
printf("\n");
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
* subtractRD():
|
|
* This routine subtracts 2 numbers. If the difference is less
|
|
* than 1.0E-14 times the magnitude of the smallest number,
|
|
* then diff returns an exact zero.
|
|
* It also returns an exact zero if the difference is less than
|
|
* 1.0E-300.
|
|
*
|
|
* returns: a - b
|
|
*
|
|
* This routine is used in numerical differencing schemes in order
|
|
* to avoid roundoff errors resulting in creating Jacobian terms.
|
|
* Note: This is a slow routine. However, jacobian errors may cause
|
|
* loss of convergence. Therefore, in practice this routine
|
|
* has proved cost-effective.
|
|
*/
|
|
double subtractRD(double a, double b)
|
|
{
|
|
double diff = a - b;
|
|
double d = MIN(fabs(a), fabs(b));
|
|
d *= 1.0E-14;
|
|
double ad = fabs(diff);
|
|
if (ad < 1.0E-300) {
|
|
diff = 0.0;
|
|
}
|
|
if (ad < d) {
|
|
diff = 0.0;
|
|
}
|
|
return diff;
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
*
|
|
* Function called by BEuler to evaluate the Jacobian matrix and the
|
|
* current residual at the current time step.
|
|
* @param N = The size of the equation system
|
|
* @param J = Jacobian matrix to be filled in
|
|
* @param f = Right hand side. This routine returns the current
|
|
* value of the rhs (output), so that it does
|
|
* not have to be computed again.
|
|
*
|
|
*/
|
|
void BEulerInt::beuler_jac(GeneralMatrix& J, double* const f,
|
|
double time_curr, double CJ,
|
|
double* const y,
|
|
double* const ydot,
|
|
int num_newt_its)
|
|
{
|
|
int i, j;
|
|
double* col_j;
|
|
double ysave, ydotsave, dy;
|
|
/**
|
|
* Clear the factor flag
|
|
*/
|
|
J.clearFactorFlag();
|
|
|
|
|
|
if (m_jacFormMethod & BEULER_JAC_ANAL) {
|
|
/********************************************************************
|
|
* Call the function to get a jacobian.
|
|
*/
|
|
m_func->evalJacobian(time_curr, delta_t_n, CJ, y, ydot, J, f);
|
|
#ifdef DEBUG_HKM
|
|
//double dddd = J(89, 89);
|
|
//checkFinite(dddd);
|
|
#endif
|
|
m_nJacEval++;
|
|
m_nfe++;
|
|
} else {
|
|
/*******************************************************************
|
|
* Generic algorithm to calculate a numerical Jacobian
|
|
*/
|
|
/*
|
|
* Calculate the current value of the rhs given the
|
|
* current conditions.
|
|
*/
|
|
|
|
m_func->evalResidNJ(time_curr, delta_t_n, y, ydot, f, JacBase_ResidEval);
|
|
m_nfe++;
|
|
m_nJacEval++;
|
|
|
|
|
|
/*
|
|
* Malloc a vector and call the function object to return a set of
|
|
* deltaY's that are appropriate for calculating the numerical
|
|
* derivative.
|
|
*/
|
|
double* dyVector = mdp::mdp_alloc_dbl_1(m_neq, MDP_DBL_NOINIT);
|
|
m_func->calcDeltaSolnVariables(time_curr, y, m_y_nm1, dyVector,
|
|
m_ewt);
|
|
#ifdef DEBUG_HKM
|
|
bool print_NumJac = false;
|
|
if (print_NumJac) {
|
|
FILE* idy = fopen("NumJac.csv", "w");
|
|
fprintf(idy, "Unk m_ewt y "
|
|
"dyVector ResN\n");
|
|
for (int iii = 0; iii < m_neq; iii++) {
|
|
fprintf(idy, " %4d %16.8e %16.8e %16.8e %16.8e \n",
|
|
iii, m_ewt[iii], y[iii], dyVector[iii], f[iii]);
|
|
}
|
|
fclose(idy);
|
|
}
|
|
#endif
|
|
/*
|
|
* Loop over the variables, formulating a numerical derivative
|
|
* of the dense matrix.
|
|
* For the delta in the variable, we will use a variety of approaches
|
|
* The original approach was to use the error tolerance amount.
|
|
* This may not be the best approach, as it could be overly large in
|
|
* some instances and overly small in others.
|
|
* We will first protect from being overly small, by using the usual
|
|
* sqrt of machine precision approach, i.e., 1.0E-7,
|
|
* to bound the lower limit of the delta.
|
|
*/
|
|
for (j = 0; j < m_neq; j++) {
|
|
|
|
|
|
/*
|
|
* Get a pointer into the column of the matrix
|
|
*/
|
|
|
|
|
|
col_j = (double*) J.ptrColumn(j);
|
|
ysave = y[j];
|
|
dy = dyVector[j];
|
|
//dy = fmaxx(1.0E-6 * m_ewt[j], fabs(ysave)*1.0E-7);
|
|
|
|
y[j] = ysave + dy;
|
|
dy = y[j] - ysave;
|
|
ydotsave = ydot[j];
|
|
ydot[j] += dy * CJ;
|
|
/*
|
|
* Call the functon
|
|
*/
|
|
|
|
|
|
m_func->evalResidNJ(time_curr, delta_t_n, y, ydot, m_wksp,
|
|
JacDelta_ResidEval, j, dy);
|
|
m_nfe++;
|
|
double diff;
|
|
for (i = 0; i < m_neq; i++) {
|
|
diff = subtractRD(m_wksp[i], f[i]);
|
|
col_j[i] = diff / dy;
|
|
//col_j[i] = (m_wksp[i] - f[i])/dy;
|
|
}
|
|
|
|
y[j] = ysave;
|
|
ydot[j] = ydotsave;
|
|
|
|
}
|
|
/*
|
|
* Release memory
|
|
*/
|
|
mdp::mdp_safe_free((void**) &dyVector);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Function to calculate the predicted solution vector, m_y_pred_n for the
|
|
* (n+1)th time step. This routine can be used by a first order - forward
|
|
* Euler / backward Euler predictor / corrector method or for a second order
|
|
* Adams-Bashforth / Trapezoidal Rule predictor / corrector method. See Nachos
|
|
* documentation Sand86-1816 and Gresho, Lee, Sani LLNL report UCRL - 83282 for
|
|
* more information.
|
|
*
|
|
* variables:
|
|
*
|
|
* on input:
|
|
*
|
|
* N - number of unknowns
|
|
* order - indicates order of method
|
|
* = 1 -> first order forward Euler/backward Euler
|
|
* predictor/corrector
|
|
* = 2 -> second order Adams-Bashforth/Trapezoidal Rule
|
|
* predictor/corrector
|
|
*
|
|
* delta_t_n - magnitude of time step at time n (i.e., = t_n+1 - t_n)
|
|
* delta_t_nm1 - magnitude of time step at time n - 1 (i.e., = t_n - t_n-1)
|
|
* y_n[] - solution vector at time n
|
|
* y_dot_n[] - acceleration vector from the predictor at time n
|
|
* y_dot_nm1[] - acceleration vector from the predictor at time n - 1
|
|
*
|
|
* on output:
|
|
*
|
|
* m_y_pred_n[] - predicted solution vector at time n + 1
|
|
*/
|
|
void BEulerInt::calc_y_pred(int order)
|
|
{
|
|
int i;
|
|
double c1, c2;
|
|
switch (order) {
|
|
case 0:
|
|
case 1:
|
|
c1 = delta_t_n;
|
|
for (i = 0; i < m_neq; i++) {
|
|
m_y_pred_n[i] = m_y_n[i] + c1 * m_ydot_n[i];
|
|
}
|
|
break;
|
|
case 2:
|
|
c1 = delta_t_n * (2.0 + delta_t_n / delta_t_nm1) / 2.0;
|
|
c2 = (delta_t_n * delta_t_n) / (delta_t_nm1 * 2.0);
|
|
for (i = 0; i < m_neq; i++) {
|
|
m_y_pred_n[i] = m_y_n[i] + c1 * m_ydot_n[i] - c2 * m_ydot_nm1[i];
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Filter the predictions.
|
|
*/
|
|
m_func->filterSolnPrediction(time_n, m_y_pred_n);
|
|
|
|
} /* calc_y_pred */
|
|
|
|
|
|
/* Function to calculate the acceleration vector ydot for the first or
|
|
* second order predictor/corrector time integrator. This routine can be
|
|
* called by a first order - forward Euler / backward Euler predictor /
|
|
* corrector or for a second order Adams - Bashforth / Trapezoidal Rule
|
|
* predictor / corrector. See Nachos documentation Sand86-1816 and Gresho,
|
|
* Lee, Sani LLNL report UCRL - 83282 for more information.
|
|
*
|
|
* variables:
|
|
*
|
|
* on input:
|
|
*
|
|
* N - number of local unknowns on the processor
|
|
* This is equal to internal plus border unknowns.
|
|
* order - indicates order of method
|
|
* = 1 -> first order forward Euler/backward Euler
|
|
* predictor/corrector
|
|
* = 2 -> second order Adams-Bashforth/Trapezoidal Rule
|
|
* predictor/corrector
|
|
*
|
|
* delta_t_n - Magnitude of the current time step at time n
|
|
* (i.e., = t_n - t_n-1)
|
|
* y_curr[] - Current Solution vector at time n
|
|
* y_nm1[] - Solution vector at time n-1
|
|
* ydot_nm1[] - Acceleration vector at time n-1
|
|
*
|
|
* on output:
|
|
*
|
|
* ydot_curr[] - Current acceleration vector at time n
|
|
*
|
|
* Note we use the current attribute to denote the possibility that
|
|
* y_curr[] may not be equal to m_y_n[] during the nonlinear solve
|
|
* because we may be using a look-ahead scheme.
|
|
*/
|
|
void BEulerInt::
|
|
calc_ydot(int order, double* y_curr, double* ydot_curr)
|
|
{
|
|
int i;
|
|
double c1;
|
|
switch (order) {
|
|
case 0:
|
|
case 1: /* First order forward Euler/backward Euler */
|
|
c1 = 1.0 / delta_t_n;
|
|
for (i = 0; i < m_neq; i++) {
|
|
ydot_curr[i] = c1 * (y_curr[i] - m_y_nm1[i]);
|
|
}
|
|
return;
|
|
case 2: /* Second order Adams-Bashforth / Trapezoidal Rule */
|
|
c1 = 2.0 / delta_t_n;
|
|
for (i = 0; i < m_neq; i++) {
|
|
ydot_curr[i] = c1 * (y_curr[i] - m_y_nm1[i]) - m_ydot_nm1[i];
|
|
}
|
|
return;
|
|
}
|
|
} /************* END calc_ydot () ****************************************/
|
|
|
|
/* This function calculates the time step truncation error estimate
|
|
* from a very simple formula based on Gresho et al. This routine can be
|
|
* called for a
|
|
* first order - forward Euler/backward Euler predictor/ corrector and
|
|
* for a
|
|
* second order Adams- Bashforth/Trapezoidal Rule predictor/corrector. See
|
|
* Nachos documentation Sand86-1816 and Gresho, Lee, LLNL report
|
|
* UCRL - 83282
|
|
* for more information.
|
|
*
|
|
* variables:
|
|
*
|
|
* on input:
|
|
*
|
|
* abs_error - Generic absolute error tolerance
|
|
* rel_error - Generic realtive error tolerance
|
|
* x_coor[] - Solution vector from the implicit corrector
|
|
* x_pred_n[] - Solution vector from the explicit predictor
|
|
*
|
|
* on output:
|
|
*
|
|
* delta_t_n - Magnitude of next time step at time t_n+1
|
|
* delta_t_nm1 - Magnitude of previous time step at time t_n
|
|
*/
|
|
double BEulerInt::time_error_norm()
|
|
{
|
|
int i;
|
|
double rel_norm, error;
|
|
#ifdef DEBUG_HKM
|
|
#define NUM_ENTRIES 5
|
|
if (m_print_flag > 2) {
|
|
int imax[NUM_ENTRIES], j, jnum;
|
|
double dmax;
|
|
bool used;
|
|
printf("\t\ttime step truncation error contributors:\n");
|
|
printf("\t\t I entry actual predicted "
|
|
" weight ydot\n");
|
|
printf("\t\t");
|
|
print_line("-", 70);
|
|
for (j = 0; j < NUM_ENTRIES; j++) {
|
|
imax[j] = -1;
|
|
}
|
|
for (jnum = 0; jnum < NUM_ENTRIES; jnum++) {
|
|
dmax = -1.0;
|
|
for (i = 0; i < m_neq; i++) {
|
|
used = false;
|
|
for (j = 0; j < jnum; j++) {
|
|
if (imax[j] == i) {
|
|
used = true;
|
|
}
|
|
}
|
|
if (!used) {
|
|
error = (m_y_n[i] - m_y_pred_n[i]) / m_ewt[i];
|
|
rel_norm = sqrt(error * error);
|
|
if (rel_norm > dmax) {
|
|
imax[jnum] = i;
|
|
dmax = rel_norm;
|
|
}
|
|
}
|
|
}
|
|
if (imax[jnum] >= 0) {
|
|
i = imax[jnum];
|
|
printf("\t\t%4d %12.4e %12.4e %12.4e %12.4e %12.4e\n",
|
|
i, dmax, m_y_n[i], m_y_pred_n[i], m_ewt[i], m_ydot_n[i]);
|
|
}
|
|
}
|
|
printf("\t\t");
|
|
print_line("-", 70);
|
|
}
|
|
#endif
|
|
rel_norm = 0.0;
|
|
for (i = 0; i < m_neq; i++) {
|
|
error = (m_y_n[i] - m_y_pred_n[i]) / m_ewt[i];
|
|
rel_norm += (error * error);
|
|
}
|
|
rel_norm = sqrt(rel_norm / m_neq);
|
|
return rel_norm;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* Time step control function for the selection of the time step size based on
|
|
* a desired accuracy of time integration and on an estimate of the relative
|
|
* error of the time integration process. This routine can be called for a
|
|
* first order - forward Euler/backward Euler predictor/ corrector and for a
|
|
* second order Adams- Bashforth/Trapezoidal Rule predictor/corrector. See
|
|
* Nachos documentation Sand86-1816 and Gresho, Lee, Sani LLNL report UCRL -
|
|
* 83282 for more information.
|
|
*
|
|
* variables:
|
|
*
|
|
* on input:
|
|
*
|
|
* order - indicates order of method
|
|
* = 1 -> first order forward Euler/backward Euler
|
|
* predictor/corrector
|
|
* = 2 -> second order forward Adams-Bashforth/Trapezoidal
|
|
* rule predictor/corrector
|
|
*
|
|
* delta_t_n - Magnitude of time step at time t_n
|
|
* delta_t_nm1 - Magnitude of time step at time t_n-1
|
|
* rel_error - Generic realtive error tolerance
|
|
* time_error_factor - Estimated value of the time step truncation error
|
|
* factor. This value is a ratio of the computed
|
|
* error norms. The premultiplying constants
|
|
* and the power are not yet applied to normalize the
|
|
* predictor/corrector ratio. (see output value)
|
|
*
|
|
* on output:
|
|
*
|
|
* return - delta_t for the next time step
|
|
* If delta_t is negative, then the current time step is
|
|
* rejected because the time-step truncation error is
|
|
* too large. The return value will contain the negative
|
|
* of the recommended next time step.
|
|
*
|
|
* time_error_factor - This output value is normalized so that
|
|
* values greater than one indicate the current time
|
|
* integration error is greater than the user
|
|
* specified magnitude.
|
|
*/
|
|
double BEulerInt::time_step_control(int order, double time_error_factor)
|
|
{
|
|
double factor = 0.0, power = 0.0, delta_t;
|
|
const char* yo = "time_step_control";
|
|
|
|
/*
|
|
* Special case time_error_factor so that zeroes don't cause a problem.
|
|
*/
|
|
time_error_factor = MAX(1.0E-50, time_error_factor);
|
|
|
|
/*
|
|
* Calculate the factor for the change in magnitude of time step.
|
|
*/
|
|
switch (order) {
|
|
case 1:
|
|
factor = 1.0/(2.0 *(time_error_factor));
|
|
power = 0.5;
|
|
break;
|
|
case 2:
|
|
factor = 1.0/(3.0 * (1.0 + delta_t_nm1 / delta_t_n)
|
|
* (time_error_factor));
|
|
power = 0.3333333333333333;
|
|
}
|
|
factor = pow(factor, power);
|
|
if (factor < 0.5) {
|
|
if (m_print_flag > 1) {
|
|
printf("\t%s: WARNING - Current time step will be chucked\n", yo);
|
|
printf("\t\tdue to a time step truncation error failure.\n");
|
|
}
|
|
delta_t = - 0.5 * delta_t_n;
|
|
} else {
|
|
factor = MIN(factor, 1.5);
|
|
delta_t = factor * delta_t_n;
|
|
}
|
|
return delta_t;
|
|
} /************ END of time_step_control()********************************/
|
|
//================================================================================================
|
|
/**************************************************************************
|
|
*
|
|
* integrate():
|
|
*
|
|
* defaults are located in the .h file. They are as follows:
|
|
* time_init = 0.0
|
|
*/
|
|
double BEulerInt::integrateRJE(double tout, double time_init)
|
|
{
|
|
double time_current;
|
|
bool weAreNotFinished = true;
|
|
m_time_final = tout;
|
|
int flag = SUCCESS;
|
|
/**
|
|
* Initialize the time step number to zero. step will increment so that
|
|
* the first time step is number 1
|
|
*/
|
|
m_time_step_num = 0;
|
|
|
|
|
|
/*
|
|
* Do the integration a step at a time
|
|
*/
|
|
int istep = 0;
|
|
int printStep = 0;
|
|
bool doPrintSoln = false;
|
|
time_current = time_init;
|
|
time_n = time_init;
|
|
time_nm1 = time_init;
|
|
time_nm2 = time_init;
|
|
m_func->evalTimeTrackingEqns(time_current, 0.0, m_y_n, m_ydot_n);
|
|
double print_time = getPrintTime(time_current);
|
|
if (print_time == time_current) {
|
|
m_func->writeSolution(4, time_current, delta_t_n,
|
|
istep, m_y_n, m_ydot_n);
|
|
}
|
|
/*
|
|
* We print out column headers here for the case of
|
|
*/
|
|
if (m_print_flag == 1) {
|
|
print_lvl1_Header(0);
|
|
}
|
|
/*
|
|
* Call a different user routine at the end of each step,
|
|
* that will probably print to a file.
|
|
*/
|
|
m_func->user_out2(0, time_current, 0.0, m_y_n, m_ydot_n);
|
|
|
|
do {
|
|
|
|
print_time = getPrintTime(time_current);
|
|
if (print_time >= tout) {
|
|
print_time = tout;
|
|
}
|
|
|
|
/************************************************************
|
|
* Step the solution
|
|
*/
|
|
time_current = step(tout);
|
|
istep++;
|
|
printStep++;
|
|
/***********************************************************/
|
|
if (time_current < 0.0) {
|
|
if (time_current == -1234.) {
|
|
time_current = 0.0;
|
|
} else {
|
|
time_current = -time_current;
|
|
}
|
|
flag = FAILURE;
|
|
}
|
|
|
|
if (flag != FAILURE) {
|
|
bool retn =
|
|
m_func->evalStoppingCritera(time_current, delta_t_n,
|
|
m_y_n, m_ydot_n);
|
|
if (retn) {
|
|
weAreNotFinished = false;
|
|
doPrintSoln = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* determine conditional printing of soln
|
|
*/
|
|
if (time_current >= print_time) {
|
|
doPrintSoln = true;
|
|
}
|
|
if (m_printSolnStepInterval == printStep) {
|
|
doPrintSoln = true;
|
|
}
|
|
if (m_printSolnFirstSteps > istep) {
|
|
doPrintSoln = true;
|
|
}
|
|
|
|
/*
|
|
* Evaluate time integrated quantities that are calculated at the
|
|
* end of every successful time step.
|
|
*/
|
|
if (flag != FAILURE) {
|
|
m_func->evalTimeTrackingEqns(time_current, delta_t_n,
|
|
m_y_n, m_ydot_n);
|
|
}
|
|
|
|
/*
|
|
* Call the printout routine.
|
|
*/
|
|
if (doPrintSoln) {
|
|
m_func->writeSolution(1, time_current, delta_t_n,
|
|
istep, m_y_n, m_ydot_n);
|
|
printStep = 0;
|
|
doPrintSoln = false;
|
|
if (m_print_flag == 1) {
|
|
print_lvl1_Header(1);
|
|
}
|
|
}
|
|
/*
|
|
* Call a different user routine at the end of each step,
|
|
* that will probably print to a file.
|
|
*/
|
|
if (flag == FAILURE) {
|
|
m_func->user_out2(-1, time_current, delta_t_n, m_y_n, m_ydot_n);
|
|
} else {
|
|
m_func->user_out2(1, time_current, delta_t_n, m_y_n, m_ydot_n);
|
|
}
|
|
|
|
} while (time_current < tout &&
|
|
m_time_step_attempts < m_max_time_step_attempts &&
|
|
flag == SUCCESS && weAreNotFinished);
|
|
|
|
/*
|
|
* Check current time against the max solution time.
|
|
*/
|
|
if (time_current >= tout) {
|
|
printf("Simulation completed time integration in %d time steps\n",
|
|
m_time_step_num);
|
|
printf("Final Time: %e\n\n", time_current);
|
|
} else if (m_time_step_attempts >= m_max_time_step_attempts) {
|
|
printf("Simulation ran into time step attempt limit in"
|
|
"%d time steps\n",
|
|
m_time_step_num);
|
|
printf("Final Time: %e\n\n", time_current);
|
|
} else if (flag == FAILURE) {
|
|
printf("ERROR: time stepper failed at time = %g\n", time_current);
|
|
}
|
|
|
|
/*
|
|
* Print out the final results and counters.
|
|
*/
|
|
print_final(time_n, flag, m_time_step_num, m_numTotalNewtIts,
|
|
m_numTotalLinearSolves, m_numTotalConvFails,
|
|
m_numTotalTruncFails, m_nfe, m_nJacEval);
|
|
|
|
/*
|
|
* Call a different user routine at the end of each step,
|
|
* that will probably print to a file.
|
|
*/
|
|
m_func->user_out2(2, time_current, delta_t_n, m_y_n, m_ydot_n);
|
|
|
|
|
|
if (flag != SUCCESS) {
|
|
throw BEulerErr(" BEuler error encountered.");
|
|
}
|
|
return time_current;
|
|
}
|
|
|
|
/**************************************************************************
|
|
*
|
|
* step():
|
|
*
|
|
* This routine advances the calculations one step using a predictor
|
|
* corrector approach. We use an implicit algorithm here.
|
|
*
|
|
*/
|
|
double BEulerInt::step(double t_max)
|
|
{
|
|
double CJ;
|
|
int one = 1;
|
|
bool step_failed = false;
|
|
bool giveUp = false;
|
|
bool convFailure = false;
|
|
const char* rslt;
|
|
double time_error_factor = 0.0;
|
|
double normFilter = 0.0;
|
|
int numTSFailures = 0;
|
|
int bktr_stps = 0;
|
|
int nonlinearloglevel = m_print_flag;
|
|
int num_newt_its = 0;
|
|
int aztec_its = 0;
|
|
string comment;
|
|
/*
|
|
* Increment the time counter - May have to be taken back,
|
|
* if time step is found to be faulty.
|
|
*/
|
|
m_time_step_num++;
|
|
|
|
/**
|
|
* Loop here until we achieve a successful step or we set the giveUp
|
|
* flag indicating that repeated errors have occurred.
|
|
*/
|
|
do {
|
|
m_time_step_attempts++;
|
|
comment.clear();
|
|
|
|
/*
|
|
* Possibly adjust the delta_t_n value for this time step from the
|
|
* recommended delta_t_np1 value determined in the previous step
|
|
* due to maximum time step constraints or other occurences,
|
|
* known to happen at a given time.
|
|
*/
|
|
if ((time_n + delta_t_np1) >= t_max) {
|
|
delta_t_np1 =t_max - time_n;
|
|
}
|
|
|
|
if (delta_t_np1 >= delta_t_max) {
|
|
delta_t_np1 = delta_t_max;
|
|
}
|
|
|
|
/*
|
|
* Increment the delta_t counters and the time for the current
|
|
* time step.
|
|
*/
|
|
|
|
delta_t_nm2 = delta_t_nm1;
|
|
delta_t_nm1 = delta_t_n;
|
|
delta_t_n = delta_t_np1;
|
|
time_n += delta_t_n;
|
|
|
|
/*
|
|
* Determine the integration order of the current step.
|
|
*
|
|
* Special case for start-up of time integration procedure
|
|
* First time step = Do a predictor step as we
|
|
* have recently added an initial
|
|
* ydot input option. And, setting ydot=0
|
|
* is equivalent to not doing a
|
|
* predictor step.
|
|
* Second step = If 2nd order method, do a first order
|
|
* step for this time-step, only.
|
|
*
|
|
* If 2nd order method with a constant time step, the
|
|
* first and second steps are 1/10 the specified step, and
|
|
* the third step is 8/10 the specified step. This reduces
|
|
* the error asociated with using lower order
|
|
* integration on the first two steps. (RCS 11-6-97)
|
|
*
|
|
* If the previous time step failed for one reason or another,
|
|
* do a linear step. It's more robust.
|
|
*/
|
|
if (m_time_step_num == 1) {
|
|
m_order = 1; /* Backward Euler */
|
|
} else if (m_time_step_num == 2) {
|
|
m_order = 1; /* Forward/Backward Euler */
|
|
} else if (step_failed) {
|
|
m_order = 1; /* Forward/Backward Euler */
|
|
} else if (m_time_step_num > 2) {
|
|
m_order = 1; /* Specified
|
|
Predictor/Corrector
|
|
- not implemented */
|
|
}
|
|
|
|
/*
|
|
* Print out an initial statement about the step.
|
|
*/
|
|
if (m_print_flag > 1) {
|
|
print_time_step1(m_order, m_time_step_num, time_n, delta_t_n,
|
|
delta_t_nm1, step_failed, m_failure_counter);
|
|
}
|
|
|
|
/*
|
|
* Calculate the predicted solution, m_y_pred_n, for the current
|
|
* time step.
|
|
*/
|
|
calc_y_pred(m_order);
|
|
|
|
/*
|
|
* HKM - Commented this out. I may need it for particles later.
|
|
* If Solution bounds checking is turned on, we need to crop the
|
|
* predicted solution to make sure bounds are enforced
|
|
*
|
|
*
|
|
* cropNorm = 0.0;
|
|
* if (Cur_Realm->Realm_Nonlinear.Constraint_Backtracking_Flag ==
|
|
* Constraint_Backtrack_Enable) {
|
|
* cropNorm = cropPredictor(mesh, x_pred_n, abs_time_error,
|
|
* m_reltol);
|
|
*/
|
|
|
|
/*
|
|
* Save the old solution, before overwriting with the new solution
|
|
* - use
|
|
*/
|
|
mdp_copy_dbl_1(m_y_nm1, m_y_n, m_neq);
|
|
|
|
/*
|
|
* Use the predicted value as the initial guess for the corrector
|
|
* loop, for
|
|
* every step other than the first step.
|
|
*/
|
|
if (m_order > 0) {
|
|
mdp_copy_dbl_1(m_y_n, m_y_pred_n, m_neq);
|
|
}
|
|
|
|
/*
|
|
* Save the old time derivative, if necessary, before it is
|
|
* overwritten.
|
|
* This overwrites ydot_nm1, losing information from the previous time
|
|
* step.
|
|
*/
|
|
mdp_copy_dbl_1(m_ydot_nm1, m_ydot_n, m_neq);
|
|
|
|
/*
|
|
* Calculate the new time derivative, ydot_n, that is consistent
|
|
* with the
|
|
* initial guess for the corrected solution vector.
|
|
*
|
|
*/
|
|
calc_ydot(m_order, m_y_n, m_ydot_n);
|
|
|
|
/*
|
|
* Calculate CJ, the coefficient for the jacobian corresponding to the
|
|
* derivative of the residual wrt to the acceleration vector.
|
|
*/
|
|
if (m_order < 2) {
|
|
CJ = 1.0 / delta_t_n;
|
|
} else {
|
|
CJ = 2.0 / delta_t_n;
|
|
}
|
|
|
|
/*
|
|
* Calculate a new Solution Error Weighting vector
|
|
*/
|
|
setSolnWeights();
|
|
|
|
/*
|
|
* Solve the system of equations at the current time step.
|
|
* Note - x_corr_n and x_dot_n are considered to be updated,
|
|
* on return from this solution.
|
|
*/
|
|
int ierror = solve_nonlinear_problem(m_y_n, m_ydot_n,
|
|
CJ, time_n, *tdjac_ptr, num_newt_its,
|
|
aztec_its, bktr_stps,
|
|
nonlinearloglevel);
|
|
/*
|
|
* Set the appropriate flags if a convergence failure is detected.
|
|
*/
|
|
if (ierror < 0) { /* Step failed */
|
|
convFailure = true;
|
|
step_failed = true;
|
|
rslt = "fail";
|
|
m_numTotalConvFails++;
|
|
m_failure_counter +=3;
|
|
if (m_print_flag > 1) {
|
|
printf("\tStep is Rejected, nonlinear problem didn't converge,"
|
|
"ierror = %d\n", ierror);
|
|
}
|
|
} else { /* Step succeeded */
|
|
convFailure = false;
|
|
step_failed = false;
|
|
rslt = "done";
|
|
|
|
/*
|
|
* Apply a filter to a new successful step
|
|
*/
|
|
normFilter = filterNewStep(time_n, m_y_n, m_ydot_n);
|
|
if (normFilter > 1.0) {
|
|
convFailure = true;
|
|
step_failed = true;
|
|
rslt = "filt";
|
|
if (m_print_flag > 1) {
|
|
printf("\tStep is Rejected, too large filter adjustment = %g\n",
|
|
normFilter);
|
|
}
|
|
} else if (normFilter > 0.0) {
|
|
if (normFilter > 0.3) {
|
|
if (m_print_flag > 1) {
|
|
printf("\tStep was filtered, norm = %g, next "
|
|
"time step adjusted\n", normFilter);
|
|
}
|
|
} else {
|
|
if (m_print_flag > 1) {
|
|
printf("\tStep was filtered, norm = %g\n", normFilter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Calculate the time step truncation error for the current step.
|
|
*/
|
|
if (!step_failed) {
|
|
time_error_factor = time_error_norm();
|
|
} else {
|
|
time_error_factor = 1000.;
|
|
}
|
|
|
|
/*
|
|
* Dynamic time step control- delta_t_n, delta_t_nm1 are set here.
|
|
*/
|
|
if (step_failed) {
|
|
/*
|
|
* For convergence failures, decrease the step-size by a factor of
|
|
* 4 and try again.
|
|
*/
|
|
delta_t_np1 = 0.25 * delta_t_n;
|
|
} else if (m_method == BEulerVarStep) {
|
|
|
|
/*
|
|
* If we are doing a predictor/corrector method, and we are
|
|
* past a certain number of time steps given by the input file
|
|
* then either correct the DeltaT for the next time step or
|
|
*
|
|
*/
|
|
if ((m_order > 0) &&
|
|
(m_time_step_num > m_numInitialConstantDeltaTSteps)) {
|
|
delta_t_np1 = time_step_control(m_order, time_error_factor);
|
|
if (normFilter > 0.1) {
|
|
if (delta_t_np1 > delta_t_n) {
|
|
delta_t_np1 = delta_t_n;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check for Current time step failing due to violation of
|
|
* time step
|
|
* truncation bounds.
|
|
*/
|
|
if (delta_t_np1 < 0.0) {
|
|
m_numTotalTruncFails++;
|
|
step_failed = true;
|
|
delta_t_np1 = -delta_t_np1;
|
|
m_failure_counter += 2;
|
|
comment += "TIME TRUNC FAILURE";
|
|
rslt = "TRNC";
|
|
}
|
|
|
|
/*
|
|
* Prevent churning of the time step by not increasing the
|
|
* time step,
|
|
* if the recent "History" of the time step behavior is still bad
|
|
*/
|
|
else if (m_failure_counter > 0) {
|
|
delta_t_np1 = MIN(delta_t_np1, delta_t_n);
|
|
}
|
|
} else {
|
|
delta_t_np1 = delta_t_n;
|
|
}
|
|
|
|
/* Decrease time step if a lot of Newton Iterations are
|
|
* taken.
|
|
* The idea being if more or less Newton iteration are taken
|
|
* than the target number of iterations, then adjust the time
|
|
* step downwards so that the target number of iterations or lower
|
|
* is achieved. This
|
|
* should prevent step failure by too many Newton iterations because
|
|
* the time step becomes too large. CCO
|
|
* hkm -> put in num_new_its min of 3 because the time step
|
|
* was being altered even when num_newt_its == 1
|
|
*/
|
|
int max_Newton_steps = 10000;
|
|
int target_num_iter = 5;
|
|
if (num_newt_its > 3000 && !step_failed) {
|
|
if (max_Newton_steps != target_num_iter) {
|
|
double iter_diff = num_newt_its - target_num_iter;
|
|
double iter_adjust_zone = max_Newton_steps - target_num_iter;
|
|
double target_time_step = delta_t_n
|
|
*(1.0 - iter_diff*fabs(iter_diff)/
|
|
((2.0*iter_adjust_zone*iter_adjust_zone)));
|
|
target_time_step = MAX(0.5*delta_t_n, target_time_step);
|
|
if (target_time_step < delta_t_np1) {
|
|
printf("\tNext time step will be decreased from %g to %g"
|
|
" because of new its restraint\n",
|
|
delta_t_np1, target_time_step);
|
|
delta_t_np1 = target_time_step;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/*
|
|
* The final loop in the time stepping algorithm depends on whether the
|
|
* current step was a success or not.
|
|
*/
|
|
if (step_failed) {
|
|
/*
|
|
* Increment the counter indicating the number of consecutive
|
|
* failures
|
|
*/
|
|
numTSFailures++;
|
|
/*
|
|
* Print out a statement about the failure of the time step.
|
|
*/
|
|
if (m_print_flag > 1) {
|
|
print_time_fail(convFailure, m_time_step_num, time_n, delta_t_n,
|
|
delta_t_np1, time_error_factor);
|
|
} else if (m_print_flag == 1) {
|
|
print_lvl1_summary(m_time_step_num, time_n, rslt, delta_t_n,
|
|
num_newt_its, aztec_its, bktr_stps,
|
|
time_error_factor,
|
|
comment.c_str());
|
|
}
|
|
|
|
/*
|
|
* Change time step counters back to the previous step before
|
|
* the failed
|
|
* time step occurred.
|
|
*/
|
|
time_n -= delta_t_n;
|
|
delta_t_n = delta_t_nm1;
|
|
delta_t_nm1 = delta_t_nm2;
|
|
|
|
/*
|
|
* Replace old solution vector and time derivative solution vector.
|
|
*/
|
|
dcopy_(&m_neq, m_y_nm1, &one, m_y_n, &one);
|
|
dcopy_(&m_neq, m_ydot_nm1, &one, m_ydot_n, &one);
|
|
/*
|
|
* Decide whether to bail on the whole loop
|
|
*/
|
|
if (numTSFailures > 35) {
|
|
giveUp = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do processing for a successful step.
|
|
*/
|
|
else {
|
|
|
|
/*
|
|
* Decrement the number of consequative failure counter.
|
|
*/
|
|
m_failure_counter = MAX(0, m_failure_counter-1);
|
|
|
|
/*
|
|
* Print out final results of a successfull time step.
|
|
*/
|
|
if (m_print_flag > 1) {
|
|
print_time_step2(m_time_step_num, m_order, time_n, time_error_factor,
|
|
delta_t_n, delta_t_np1);
|
|
} else if (m_print_flag == 1) {
|
|
print_lvl1_summary(m_time_step_num, time_n, " ", delta_t_n,
|
|
num_newt_its, aztec_its, bktr_stps, time_error_factor,
|
|
comment.c_str());
|
|
}
|
|
|
|
/*
|
|
* Output information at the end of every successful time step, if
|
|
* requested.
|
|
*
|
|
* fill in
|
|
*/
|
|
|
|
|
|
}
|
|
} while (step_failed && !giveUp);
|
|
|
|
/*
|
|
* Send back the overall result of the time step.
|
|
*/
|
|
if (step_failed) {
|
|
if (time_n == 0.0) {
|
|
return -1234.0;
|
|
}
|
|
return -time_n;
|
|
}
|
|
return time_n;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// Constants
|
|
//-----------------------------------------------------------
|
|
|
|
const double DampFactor = 4;
|
|
const int NDAMP = 10;
|
|
|
|
|
|
//-----------------------------------------------------------
|
|
// MultiNewton methods
|
|
//-----------------------------------------------------------
|
|
/**
|
|
* L2 Norm of a delta in the solution
|
|
*
|
|
* The second argument has a default of false. However,
|
|
* if true, then a table of the largest values is printed
|
|
* out to standard output.
|
|
*/
|
|
double BEulerInt::soln_error_norm(const double* const delta_y,
|
|
bool printLargest)
|
|
{
|
|
int i;
|
|
double sum_norm = 0.0, error;
|
|
for (i = 0; i < m_neq; i++) {
|
|
error = delta_y[i] / m_ewt[i];
|
|
sum_norm += (error * error);
|
|
}
|
|
sum_norm = sqrt(sum_norm / m_neq);
|
|
if (printLargest) {
|
|
const int num_entries = 8;
|
|
double dmax1, normContrib;
|
|
int j;
|
|
int* imax = mdp_alloc_int_1(num_entries, -1);
|
|
printf("\t\tPrintout of Largest Contributors to norm "
|
|
"of value (%g)\n", sum_norm);
|
|
printf("\t\t I ysoln deltaY weightY "
|
|
"Error_Norm**2\n");
|
|
printf("\t\t ");
|
|
print_line("-", 80);
|
|
for (int jnum = 0; jnum < num_entries; jnum++) {
|
|
dmax1 = -1.0;
|
|
for (i = 0; i < m_neq; i++) {
|
|
bool used = false;
|
|
for (j = 0; j < jnum; j++) {
|
|
if (imax[j] == i) {
|
|
used = true;
|
|
}
|
|
}
|
|
if (!used) {
|
|
error = delta_y[i] / m_ewt[i];
|
|
normContrib = sqrt(error * error);
|
|
if (normContrib > dmax1) {
|
|
imax[jnum] = i;
|
|
dmax1 = normContrib;
|
|
}
|
|
}
|
|
}
|
|
i = imax[jnum];
|
|
if (i >= 0) {
|
|
printf("\t\t %4d %12.4e %12.4e %12.4e %12.4e\n",
|
|
i, m_y_n[i], delta_y[i], m_ewt[i], dmax1);
|
|
}
|
|
}
|
|
printf("\t\t ");
|
|
print_line("-", 80);
|
|
mdp_safe_free((void**) &imax);
|
|
}
|
|
return sum_norm;
|
|
}
|
|
#ifdef DEBUG_HKM_JAC
|
|
SquareMatrix jacBack();
|
|
#endif
|
|
/**************************************************************************
|
|
*
|
|
* doNewtonSolve():
|
|
*
|
|
* Compute the undamped Newton step. The residual function is
|
|
* evaluated at the current time, t_n, at the current values of the
|
|
* solution vector, m_y_n, and the solution time derivative, m_ydot_n,
|
|
* but the Jacobian is not recomputed.
|
|
*/
|
|
void BEulerInt::doNewtonSolve(double time_curr, double* y_curr,
|
|
double* ydot_curr, double* delta_y,
|
|
GeneralMatrix& jac, int loglevel)
|
|
{
|
|
int irow, jcol;
|
|
|
|
m_func->evalResidNJ(time_curr, delta_t_n, y_curr,
|
|
ydot_curr, delta_y, Base_ResidEval);
|
|
m_nfe++;
|
|
int sz = m_func->nEquations();
|
|
for (int n = 0; n < sz; n++) {
|
|
delta_y[n] = -delta_y[n];
|
|
}
|
|
|
|
|
|
/*
|
|
* Column scaling -> We scale the columns of the Jacobian
|
|
* by the nominal important change in the solution vector
|
|
*/
|
|
if (m_colScaling) {
|
|
if (!jac.factored()) {
|
|
/*
|
|
* Go get new scales
|
|
*/
|
|
setColumnScales();
|
|
|
|
/*
|
|
* Scale the new Jacobian
|
|
*/
|
|
double* jptr = &(*(jac.begin()));
|
|
for (jcol = 0; jcol < m_neq; jcol++) {
|
|
for (irow = 0; irow < m_neq; irow++) {
|
|
*jptr *= m_colScales[jcol];
|
|
jptr++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_matrixConditioning) {
|
|
if (jac.factored()) {
|
|
m_func->matrixConditioning(0, sz, delta_y);
|
|
} else {
|
|
double* jptr = &(*(jac.begin()));
|
|
m_func->matrixConditioning(jptr, sz, delta_y);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* row sum scaling -> Note, this is an unequivical success
|
|
* at keeping the small numbers well balanced and
|
|
* nonnegative.
|
|
*/
|
|
if (m_rowScaling) {
|
|
if (! jac.factored()) {
|
|
/*
|
|
* Ok, this is ugly. jac.begin() returns an vector<double> iterator
|
|
* to the first data location.
|
|
* Then &(*()) reverts it to a double *.
|
|
*/
|
|
double* jptr = &(*(jac.begin()));
|
|
for (irow = 0; irow < m_neq; irow++) {
|
|
m_rowScales[irow] = 0.0;
|
|
}
|
|
for (jcol = 0; jcol < m_neq; jcol++) {
|
|
for (irow = 0; irow < m_neq; irow++) {
|
|
m_rowScales[irow] += fabs(*jptr);
|
|
jptr++;
|
|
}
|
|
}
|
|
|
|
jptr = &(*(jac.begin()));
|
|
for (jcol = 0; jcol < m_neq; jcol++) {
|
|
for (irow = 0; irow < m_neq; irow++) {
|
|
*jptr /= m_rowScales[irow];
|
|
jptr++;
|
|
}
|
|
}
|
|
}
|
|
for (irow = 0; irow < m_neq; irow++) {
|
|
delta_y[irow] /= m_rowScales[irow];
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_HKM_JAC
|
|
bool printJacContributions = false;
|
|
if (m_time_step_num > 304) {
|
|
printJacContributions = false;
|
|
}
|
|
int focusRow = 10;
|
|
int numRows = 2;
|
|
double RRow[2];
|
|
bool freshJac = true;
|
|
RRow[0] = delta_y[focusRow];
|
|
RRow[1] = delta_y[focusRow+1];
|
|
double Pcutoff = 1.0E-70;
|
|
if (!jac.m_factored) {
|
|
jacBack = jac;
|
|
} else {
|
|
freshJac = false;
|
|
}
|
|
#endif
|
|
/*
|
|
* Solve the system -> This also involves inverting the
|
|
* matrix
|
|
*/
|
|
(void) jac.solve(delta_y);
|
|
|
|
|
|
/*
|
|
* reverse the column scaling if there was any.
|
|
*/
|
|
if (m_colScaling) {
|
|
for (irow = 0; irow < m_neq; irow++) {
|
|
delta_y[irow] *= m_colScales[irow];
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_HKM_JAC
|
|
if (printJacContributions) {
|
|
for (int iNum = 0; iNum < numRows; iNum++) {
|
|
if (iNum > 0) {
|
|
focusRow++;
|
|
}
|
|
double dsum = 0.0;
|
|
vector_fp& Jdata = jacBack.data();
|
|
double dRow = Jdata[m_neq * focusRow + focusRow];
|
|
printf("\n Details on delta_Y for row %d \n", focusRow);
|
|
printf(" Value before = %15.5e, delta = %15.5e,"
|
|
"value after = %15.5e\n", y_curr[focusRow],
|
|
delta_y[focusRow],
|
|
y_curr[focusRow] + delta_y[focusRow]);
|
|
if (!freshJac) {
|
|
printf(" Old Jacobian\n");
|
|
}
|
|
printf(" col delta_y aij "
|
|
"contrib \n");
|
|
printf("--------------------------------------------------"
|
|
"---------------------------------------------\n");
|
|
printf(" Res(%d) %15.5e %15.5e %15.5e (Res = %g)\n",
|
|
focusRow, delta_y[focusRow],
|
|
dRow, RRow[iNum] / dRow, RRow[iNum]);
|
|
dsum += RRow[iNum] / dRow;
|
|
for (int ii = 0; ii < m_neq; ii++) {
|
|
if (ii != focusRow) {
|
|
double aij = Jdata[m_neq * ii + focusRow];
|
|
double contrib = aij * delta_y[ii] * (-1.0) / dRow;
|
|
dsum += contrib;
|
|
if (fabs(contrib) > Pcutoff) {
|
|
printf("%6d %15.5e %15.5e %15.5e\n", ii,
|
|
delta_y[ii] , aij, contrib);
|
|
}
|
|
}
|
|
}
|
|
printf("--------------------------------------------------"
|
|
"---------------------------------------------\n");
|
|
printf(" %15.5e %15.5e\n",
|
|
delta_y[focusRow], dsum);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
m_numTotalLinearSolves++;
|
|
}
|
|
|
|
//================================================================================================
|
|
// Bound the Newton step while relaxing the solution
|
|
/*
|
|
* Return the factor by which the undamped Newton step 'step0'
|
|
* must be multiplied in order to keep all solution components in
|
|
* all domains between their specified lower and upper bounds.
|
|
* Other bounds may be applied here as well.
|
|
*
|
|
* Currently the bounds are hard coded into this routine:
|
|
*
|
|
* Minimum value for all variables: - 0.01 * m_ewt[i]
|
|
* Maximum value = none.
|
|
*
|
|
* Thus, this means that all solution components are expected
|
|
* to be numerical greater than zero in the limit of time step
|
|
* truncation errors going to zero.
|
|
*
|
|
* Delta bounds: The idea behind these is that the Jacobian
|
|
* couldn't possibly be representative if the
|
|
* variable is changed by a lot. (true for
|
|
* nonlinear systems, false for linear systems)
|
|
* Maximum increase in variable in any one newton iteration:
|
|
* factor of 2
|
|
* Maximum decrease in variable in any one newton iteration:
|
|
* factor of 5
|
|
*
|
|
* @param y Current value of the solution
|
|
* @param step0 Current raw step change in y[]
|
|
* @param loglevel Log level. This routine produces output if loglevel
|
|
* is greater than one
|
|
*
|
|
* @return Returns the damping coefficient
|
|
*/
|
|
double BEulerInt::boundStep(const double* const y,
|
|
const double* const step0, int loglevel)
|
|
{
|
|
int i, i_lower = -1, ifbd = 0, i_fbd = 0;
|
|
double fbound = 1.0, f_lowbounds = 1.0, f_delta_bounds = 1.0;
|
|
double ff, y_new, ff_alt;
|
|
for (i = 0; i < m_neq; i++) {
|
|
y_new = y[i] + step0[i];
|
|
if ((y_new < (-0.01 * m_ewt[i])) && y[i] >= 0.0) {
|
|
ff = 0.9 * (y[i] / (y[i] - y_new));
|
|
if (ff < f_lowbounds) {
|
|
f_lowbounds = ff;
|
|
i_lower = i;
|
|
}
|
|
}
|
|
/*
|
|
* Now do a delta bounds
|
|
* Increase variables by a factor of 2 only
|
|
* decrease variables by a factor of 5 only
|
|
*/
|
|
ff = 1.0;
|
|
if ((fabs(y_new) > 2.0 * fabs(y[i])) &&
|
|
(fabs(y_new-y[i]) > m_ewt[i])) {
|
|
ff = fabs(y[i]/(y_new - y[i]));
|
|
ff_alt = fabs(m_ewt[i] / (y_new - y[i]));
|
|
ff = MAX(ff, ff_alt);
|
|
ifbd = 1;
|
|
}
|
|
if ((fabs(5.0 * y_new) < fabs(y[i])) &&
|
|
(fabs(y_new - y[i]) > m_ewt[i])) {
|
|
ff = y[i]/(y_new-y[i]) * (1.0 - 5.0)/5.0;
|
|
ff_alt = fabs(m_ewt[i] / (y_new - y[i]));
|
|
ff = MAX(ff, ff_alt);
|
|
ifbd = 0;
|
|
}
|
|
if (ff < f_delta_bounds) {
|
|
f_delta_bounds = ff;
|
|
i_fbd = ifbd;
|
|
}
|
|
f_delta_bounds = MIN(f_delta_bounds, ff);
|
|
}
|
|
fbound = MIN(f_lowbounds, f_delta_bounds);
|
|
/*
|
|
* Report on any corrections
|
|
*/
|
|
if (loglevel > 1) {
|
|
if (fbound != 1.0) {
|
|
if (f_lowbounds < f_delta_bounds) {
|
|
printf("\t\tboundStep: Variable %d causing lower bounds "
|
|
"damping of %g\n",
|
|
i_lower, f_lowbounds);
|
|
} else {
|
|
if (ifbd) {
|
|
printf("\t\tboundStep: Decrease of Variable %d causing "
|
|
"delta damping of %g\n",
|
|
i_fbd, f_delta_bounds);
|
|
} else {
|
|
printf("\t\tboundStep: Increase of variable %d causing"
|
|
"delta damping of %g\n",
|
|
i_fbd, f_delta_bounds);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return fbound;
|
|
}
|
|
//================================================================================================
|
|
/**************************************************************************
|
|
*
|
|
* dampStep():
|
|
*
|
|
* On entry, step0 must contain an undamped Newton step for the
|
|
* solution x0. This method attempts to find a damping coefficient
|
|
* such that the next undamped step would have a norm smaller than
|
|
* that of step0. If successful, the new solution after taking the
|
|
* damped step is returned in y1, and the undamped step at y1 is
|
|
* returned in step1.
|
|
*/
|
|
int BEulerInt::dampStep(double time_curr, const double* y0,
|
|
const double* ydot0, const double* step0,
|
|
double* y1, double* ydot1, double* step1,
|
|
double& s1, GeneralMatrix& jac,
|
|
int& loglevel, bool writetitle,
|
|
int& num_backtracks)
|
|
{
|
|
|
|
|
|
// Compute the weighted norm of the undamped step size step0
|
|
double s0 = soln_error_norm(step0);
|
|
|
|
// Compute the multiplier to keep all components in bounds
|
|
// A value of one indicates that there is no limitation
|
|
// on the current step size in the nonlinear method due to
|
|
// bounds constraints (either negative values of delta
|
|
// bounds constraints.
|
|
double fbound = boundStep(y0, step0, loglevel);
|
|
|
|
// if fbound is very small, then y0 is already close to the
|
|
// boundary and step0 points out of the allowed domain. In
|
|
// this case, the Newton algorithm fails, so return an error
|
|
// condition.
|
|
if (fbound < 1.e-10) {
|
|
if (loglevel > 1) {
|
|
printf("\t\t\tdampStep: At limits.\n");
|
|
}
|
|
return -3;
|
|
}
|
|
|
|
//--------------------------------------------
|
|
// Attempt damped step
|
|
//--------------------------------------------
|
|
|
|
// damping coefficient starts at 1.0
|
|
double damp = 1.0;
|
|
int j, m;
|
|
double ff;
|
|
num_backtracks = 0;
|
|
for (m = 0; m < NDAMP; m++) {
|
|
|
|
ff = fbound*damp;
|
|
|
|
// step the solution by the damped step size
|
|
/*
|
|
* Whenever we update the solution, we must also always
|
|
* update the time derivative.
|
|
*/
|
|
for (j = 0; j < m_neq; j++) {
|
|
y1[j] = y0[j] + ff*step0[j];
|
|
// HKM setting intermediate y's to zero was a tossup.
|
|
// slightly different, equivalent results
|
|
//#ifdef DEBUG_HKM
|
|
// y1[j] = MAX(0.0, y1[j]);
|
|
//#endif
|
|
}
|
|
calc_ydot(m_order, y1, ydot1);
|
|
|
|
// compute the next undamped step, step1[], that would result
|
|
// if y1[] were accepted.
|
|
|
|
doNewtonSolve(time_curr, y1, ydot1, step1, jac, loglevel);
|
|
|
|
#ifdef DEBUG_HKM
|
|
for (j = 0; j < m_neq; j++) {
|
|
checkFinite(step1[j]);
|
|
checkFinite(y1[j]);
|
|
}
|
|
#endif
|
|
// compute the weighted norm of step1
|
|
s1 = soln_error_norm(step1);
|
|
|
|
// write log information
|
|
if (loglevel > 3) {
|
|
print_solnDelta_norm_contrib((const double*) step0,
|
|
"DeltaSolnTrial",
|
|
(const double*) step1,
|
|
"DeltaSolnTrialTest",
|
|
"dampNewt: Important Entries for "
|
|
"Weighted Soln Updates:",
|
|
y0, y1, ff, 5);
|
|
}
|
|
if (loglevel > 1) {
|
|
printf("\t\t\tdampNewt: s0 = %g, s1 = %g, fbound = %g,"
|
|
"damp = %g\n", s0, s1, fbound, damp);
|
|
}
|
|
#ifdef DEBUG_HKM
|
|
if (loglevel > 2) {
|
|
if (s1 > 1.00000001 * s0 && s1 > 1.0E-5) {
|
|
printf("WARNING: Possible Jacobian Problem "
|
|
"-> turning on more debugging for this step!!!\n");
|
|
print_solnDelta_norm_contrib((const double*) step0,
|
|
"DeltaSolnTrial",
|
|
(const double*) step1,
|
|
"DeltaSolnTrialTest",
|
|
"dampNewt: Important Entries for "
|
|
"Weighted Soln Updates:",
|
|
y0, y1, ff, 5);
|
|
loglevel = 4;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// if the norm of s1 is less than the norm of s0, then
|
|
// accept this damping coefficient. Also accept it if this
|
|
// step would result in a converged solution. Otherwise,
|
|
// decrease the damping coefficient and try again.
|
|
|
|
if (s1 < 1.0E-5 || s1 < s0) {
|
|
if (loglevel > 2) {
|
|
if (s1 > s0) {
|
|
if (s1 > 1.0) {
|
|
printf("\t\t\tdampStep: current trial step and damping"
|
|
" coefficient accepted because test step < 1\n");
|
|
printf("\t\t\t s1 = %g, s0 = %g\n", s1, s0);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
} else {
|
|
if (loglevel > 1) {
|
|
printf("\t\t\tdampStep: current step rejected: (s1 = %g > "
|
|
"s0 = %g)", s1, s0);
|
|
if (m < (NDAMP-1)) {
|
|
printf(" Decreasing damping factor and retrying");
|
|
} else {
|
|
printf(" Giving up!!!");
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
num_backtracks++;
|
|
damp /= DampFactor;
|
|
}
|
|
|
|
// If a damping coefficient was found, return 1 if the
|
|
// solution after stepping by the damped step would represent
|
|
// a converged solution, and return 0 otherwise. If no damping
|
|
// coefficient could be found, return -2.
|
|
if (m < NDAMP) {
|
|
if (s1 > 1.0) {
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
} else {
|
|
if (s1 < 0.5 && (s0 < 0.5)) {
|
|
return 1;
|
|
}
|
|
if (s1 < 1.0) {
|
|
return 0;
|
|
}
|
|
return -2;
|
|
}
|
|
}
|
|
//================================================================================================
|
|
// Solve a nonlinear system
|
|
/*
|
|
* Find the solution to F(X, xprime) = 0 by damped Newton iteration. On
|
|
* entry, y_comm[] contains an initial estimate of the solution and
|
|
* ydot_comm[] contains an estimate of the derivative.
|
|
* On successful return, y_comm[] contains the converged solution
|
|
* and ydot_comm[] contains the derivative
|
|
*
|
|
*
|
|
* @param y_comm[] Contains the input solution. On output y_comm[] contains
|
|
* the converged solution
|
|
* @param ydot_comm Contains the input derivative solution. On output y_comm[] contains
|
|
* the converged derivative solution
|
|
* @param CJ Inverse of the time step
|
|
* @param time_curr Current value of the time
|
|
* @param jac Jacobian
|
|
* @param num_newt_its number of newton iterations
|
|
* @param num_linear_solves number of linear solves
|
|
* @param num_backtracks number of backtracs
|
|
* @param loglevel Log level
|
|
*/
|
|
int BEulerInt::solve_nonlinear_problem(double* const y_comm,
|
|
double* const ydot_comm, double CJ,
|
|
double time_curr,
|
|
GeneralMatrix& jac,
|
|
int& num_newt_its,
|
|
int& num_linear_solves,
|
|
int& num_backtracks,
|
|
int loglevel)
|
|
{
|
|
int m = 0;
|
|
bool forceNewJac = false;
|
|
double s1=1.e30;
|
|
|
|
double* y_curr = mdp_alloc_dbl_1(m_neq, 0.0);
|
|
double* ydot_curr = mdp_alloc_dbl_1(m_neq, 0.0);
|
|
double* stp = mdp_alloc_dbl_1(m_neq, 0.0);
|
|
double* stp1 = mdp_alloc_dbl_1(m_neq, 0.0);
|
|
double* y_new = mdp_alloc_dbl_1(m_neq, 0.0);
|
|
double* ydot_new = mdp_alloc_dbl_1(m_neq, 0.0);
|
|
|
|
mdp_copy_dbl_1(y_curr, y_comm, m_neq);
|
|
mdp_copy_dbl_1(ydot_curr, ydot_comm, m_neq);
|
|
|
|
bool frst = true;
|
|
num_newt_its = 0;
|
|
num_linear_solves = - m_numTotalLinearSolves;
|
|
num_backtracks = 0;
|
|
int i_backtracks;
|
|
|
|
while (1 > 0) {
|
|
|
|
/*
|
|
* Increment Newton Solve counter
|
|
*/
|
|
m_numTotalNewtIts++;
|
|
num_newt_its++;
|
|
|
|
|
|
if (loglevel > 1) {
|
|
printf("\t\tSolve_Nonlinear_Problem: iteration %d:\n",
|
|
num_newt_its);
|
|
}
|
|
|
|
// Check whether the Jacobian should be re-evaluated.
|
|
|
|
forceNewJac = true;
|
|
|
|
if (forceNewJac) {
|
|
if (loglevel > 1) {
|
|
printf("\t\t\tGetting a new Jacobian and solving system\n");
|
|
}
|
|
beuler_jac(jac, m_resid, time_curr, CJ, y_curr, ydot_curr,
|
|
num_newt_its);
|
|
} else {
|
|
if (loglevel > 1) {
|
|
printf("\t\t\tSolving system with old jacobian\n");
|
|
}
|
|
}
|
|
|
|
// compute the undamped Newton step
|
|
doNewtonSolve(time_curr, y_curr, ydot_curr, stp, jac, loglevel);
|
|
|
|
// damp the Newton step
|
|
m = dampStep(time_curr, y_curr, ydot_curr, stp, y_new, ydot_new,
|
|
stp1, s1, jac, loglevel, frst, i_backtracks);
|
|
frst = false;
|
|
num_backtracks += i_backtracks;
|
|
|
|
/*
|
|
* Impose the minimum number of newton iterations critera
|
|
*/
|
|
if (num_newt_its < m_min_newt_its) {
|
|
if (m == 1) {
|
|
m = 0;
|
|
}
|
|
}
|
|
/*
|
|
* Impose max newton iteration
|
|
*/
|
|
if (num_newt_its > 20) {
|
|
m = -1;
|
|
if (loglevel > 1) {
|
|
printf("\t\t\tDampnewton unsuccessful (max newts exceeded) sfinal = %g\n", s1);
|
|
}
|
|
}
|
|
|
|
if (loglevel > 1) {
|
|
if (m == 1) {
|
|
printf("\t\t\tDampNewton iteration successful, nonlin "
|
|
"converged sfinal = %g\n", s1);
|
|
} else if (m == 0) {
|
|
printf("\t\t\tDampNewton iteration successful, get new"
|
|
"direction, sfinal = %g\n", s1);
|
|
} else {
|
|
printf("\t\t\tDampnewton unsuccessful sfinal = %g\n", s1);
|
|
}
|
|
}
|
|
|
|
// If we are converged, then let's use the best solution possible
|
|
// for an end result. We did a resolve in dampStep(). Let's update
|
|
// the solution to reflect that.
|
|
// HKM 5/16 -> Took this out, since if the last step was a
|
|
// damped step, then adding stp1[j] is undamped, and
|
|
// may lead to oscillations. It kind of defeats the
|
|
// purpose of dampStep() anyway.
|
|
// if (m == 1) {
|
|
// for (int j = 0; j < m_neq; j++) {
|
|
// y_new[j] += stp1[j];
|
|
// HKM setting intermediate y's to zero was a tossup.
|
|
// slightly different, equivalent results
|
|
// #ifdef DEBUG_HKM
|
|
// y_new[j] = MAX(0.0, y_new[j]);
|
|
// #endif
|
|
// }
|
|
// }
|
|
|
|
bool m_filterIntermediate = false;
|
|
if (m_filterIntermediate) {
|
|
if (m == 0) {
|
|
(void) filterNewStep(time_n, y_new, ydot_new);
|
|
}
|
|
}
|
|
// Exchange new for curr solutions
|
|
if (m == 0 || m == 1) {
|
|
mdp_copy_dbl_1(y_curr, y_new, m_neq);
|
|
calc_ydot(m_order, y_curr, ydot_curr);
|
|
}
|
|
|
|
// convergence
|
|
if (m == 1) {
|
|
goto done;
|
|
}
|
|
|
|
// If dampStep fails, first try a new Jacobian if an old
|
|
// one was being used. If it was a new Jacobian, then
|
|
// return -1 to signify failure.
|
|
else if (m < 0) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
// Copy into the return vectors
|
|
mdp_copy_dbl_1(y_comm, y_curr, m_neq);
|
|
mdp_copy_dbl_1(ydot_comm, ydot_curr, m_neq);
|
|
// Increment counters
|
|
num_linear_solves += m_numTotalLinearSolves;
|
|
// Free memory
|
|
mdp_safe_free((void**) &y_curr);
|
|
mdp_safe_free((void**) &ydot_curr);
|
|
mdp_safe_free((void**) &stp);
|
|
mdp_safe_free((void**) &stp1);
|
|
mdp_safe_free((void**) &y_new);
|
|
mdp_safe_free((void**) &ydot_new);
|
|
|
|
double time_elapsed = 0.0;
|
|
if (loglevel > 1) {
|
|
if (m == 1) {
|
|
printf("\t\tNonlinear problem solved successfully in "
|
|
"%d its, time elapsed = %g sec\n",
|
|
num_newt_its, time_elapsed);
|
|
}
|
|
}
|
|
return m;
|
|
}
|
|
//================================================================================================
|
|
/*
|
|
*
|
|
*
|
|
*/
|
|
void BEulerInt::
|
|
print_solnDelta_norm_contrib(const double* const solnDelta0,
|
|
const char* const s0,
|
|
const double* const solnDelta1,
|
|
const char* const s1,
|
|
const char* const title,
|
|
const double* const y0,
|
|
const double* const y1,
|
|
double damp,
|
|
int num_entries)
|
|
{
|
|
int i, j, jnum;
|
|
bool used;
|
|
double dmax0, dmax1, error, rel_norm;
|
|
printf("\t\t%s currentDamp = %g\n", title, damp);
|
|
printf("\t\t I ysoln %10s ysolnTrial "
|
|
"%10s weight relSoln0 relSoln1\n", s0, s1);
|
|
int* imax = mdp_alloc_int_1(num_entries, -1);
|
|
printf("\t\t ");
|
|
print_line("-", 90);
|
|
for (jnum = 0; jnum < num_entries; jnum++) {
|
|
dmax1 = -1.0;
|
|
for (i = 0; i < m_neq; i++) {
|
|
used = false;
|
|
for (j = 0; j < jnum; j++) {
|
|
if (imax[j] == i) {
|
|
used = true;
|
|
}
|
|
}
|
|
if (!used) {
|
|
error = solnDelta0[i] / m_ewt[i];
|
|
rel_norm = sqrt(error * error);
|
|
error = solnDelta1[i] / m_ewt[i];
|
|
rel_norm += sqrt(error * error);
|
|
if (rel_norm > dmax1) {
|
|
imax[jnum] = i;
|
|
dmax1 = rel_norm;
|
|
}
|
|
}
|
|
}
|
|
if (imax[jnum] >= 0) {
|
|
i = imax[jnum];
|
|
error = solnDelta0[i] / m_ewt[i];
|
|
dmax0 = sqrt(error * error);
|
|
error = solnDelta1[i] / m_ewt[i];
|
|
dmax1 = sqrt(error * error);
|
|
printf("\t\t %4d %12.4e %12.4e %12.4e %12.4e "
|
|
"%12.4e %12.4e %12.4e\n",
|
|
i, y0[i], solnDelta0[i], y1[i],
|
|
solnDelta1[i], m_ewt[i], dmax0, dmax1);
|
|
}
|
|
}
|
|
printf("\t\t ");
|
|
print_line("-", 90);
|
|
mdp_safe_free((void**) &imax);
|
|
}
|
|
//===============================================================================================
|
|
|
|
} // End of namespace Cantera
|
|
|