Moved the external libraries to separate library files so that libcantera.a just contains its own namespace externals. Fixed several errors in the equilibrium program that occurred during the port. (int to size_t issues). Moved some equilibrium program headers to the include file system, so that it can link with equilibrium program. Worked on Cantera.mak. Needs more work. Fixed an issue with the Residual virtual base classes within numerics. They didn't inherit due to int to size_t migration. This caused numerous test problems to fail (issue with backwards compatibility - do we want it and how much do we want?). Added csvdiff back so that it's available for shell environment runtests.
2394 lines
78 KiB
C++
2394 lines
78 KiB
C++
/**
|
|
* @file BEulerInt.cpp
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Copyright 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 "cantera/numerics/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, size_t n, double* abstol)
|
|
{
|
|
m_itol = 1;
|
|
if (!m_abstol) {
|
|
m_abstol = mdp_alloc_dbl_1(m_neq, MDP_DBL_NOINIT);
|
|
}
|
|
if (static_cast<int>(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] = std::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 = std::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 = std::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 = std::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 = std::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 = std::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 = std::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 unequivocal 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 = std::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 = std::max(ff, ff_alt);
|
|
ifbd = 0;
|
|
}
|
|
if (ff < f_delta_bounds) {
|
|
f_delta_bounds = ff;
|
|
i_fbd = ifbd;
|
|
}
|
|
f_delta_bounds = std::min(f_delta_bounds, ff);
|
|
}
|
|
fbound = std::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
|
|
|