⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 variablestepdoubleintegration.cpp

📁 用于求解二阶常微分方程的变步长多步积分法
💻 CPP
📖 第 1 页 / 共 2 页
字号:
#include "stdafx.h"
#include "VariableStepDoubleIntegration.h"

CVariableStepDoubleIntegration::CVariableStepDoubleIntegration(void)
{
	m_iMaxIntpNums = 9;

	m_dAbsErr = 1e-14;
	m_dRelErr = 1e-6;

	double dGammaStars[] = {-0.5, -0.08333333333333333, 
							-0.04166666666666667, -0.02638888888888891, 
							-0.01874999999999999, -0.01426917989417986,
							-0.01136739417989419, -0.009356536596119958, 
							-0.007892554012345676};
	double dLamdaStars[] = {-1.0, 0.08333333333333333,
							 0.0, -0.004166666666666666,
							 -0.004166666666666666, -0.003654100529100521,
							 -0.0031415343911534404, -0.002708608906525564,
							 -0.002355324074074072};

	m_pGammaStars = dGammaStars;
	m_pLamdaStars = dLamdaStars;

	errno_t err;

	// Open for write 
	if((err = fopen_s(&m_pStream, "data2.txt", "w+")) != 0)
	{
		printf("The file 'data2.txt' was not opened\n");
	}
	else
	{
		printf("The file 'data2.txt' was opened\n");
	}
}

CVariableStepDoubleIntegration::~CVariableStepDoubleIntegration(void)
{
	// Close stream if it is not NULL 
	if(m_pStream)
	{
		if(fclose(m_pStream))
		{
			printf("The file 'data2.txt' was not closed\n");
		}
	}
}

void CVariableStepDoubleIntegration::_2ndODEs(int iEqNums, double dTime, double *pPos, double *pVel, double *pAcc)
{

	// the following is for y''=-y
	for(int i=0; i<iEqNums; i++)
	{
		pAcc[i] = -pPos[i];
	}

/*
	// the following is for y''=u(1-y^2)y'-y
	double dMu = 1;
	for(int i=0; i<iEqNums; i++)
	{
		pAcc[i] = dMu*(1-pPos[i]*pPos[i])*pVel[i] - pPos[i];
	}
*/
}

bool CVariableStepDoubleIntegration::Init(int iEqNums, double *pTime, double dEndTime, double *pInitPos, double *pInitVel)
{
	fprintf_s(m_pStream, "time\tvalue\terr\n");
	fprintf_s(m_pStream, "%17.16f\t%17.16f\t%17.16f\n", *pTime, pInitPos[0], sin(*pTime)-pInitPos[0]);

	m_dEndTime = dEndTime;

	m_iIntpNums = 1;
	m_iLastIntpNums = 0;

	// set EPS, relative epsilon, absolute epsilon
	m_dEps = __max(m_dRelErr, m_dAbsErr); // epsilon
	m_dRelEps = m_dRelErr / m_dEps; // relative epsilon
	m_dAbsEps = m_dAbsErr / m_dEps; // absolute epsilon

	// get machine epsilon
	double dMacEps = numeric_limits<double>::epsilon();

	// get initial value of acceleration
	// y'' = f(t, y, y')
	_2ndODEs(iEqNums, *pTime, pInitPos, pInitVel, m_pAcc);

	// get initial step estimate for single and double integration
	m_dSTempErr = m_dDTempErr = 0.0;
	for(int i=0; i<iEqNums; i++)
	{
		m_pLastVel[i] = pInitVel[i];
		m_pLastPos[i] = pInitPos[i];

		m_pPhiN[i*(m_iMaxIntpNums+1)+0] = m_pAcc[i];

		m_pSWeight[i] = fabs(m_pLastVel[i])*m_dRelEps + m_dAbsEps;
		m_pDWeight[i] = fabs(m_pLastPos[i])*m_dRelEps + m_dAbsEps;

		m_dSTempErr += (m_pAcc[i]/m_pSWeight[i]) * (m_pAcc[i]/m_pSWeight[i]);
		m_dDTempErr += (pInitVel[i]/m_pDWeight[i]) * (pInitVel[i]/m_pDWeight[i]);
	}

	m_dSTempErr = sqrt(m_dSTempErr);
	m_dDTempErr = sqrt(m_dDTempErr);

	// get initial step estimate for single and double integration
	double dSStepSize = 0.25 * sqrt(m_dEps/m_dSTempErr);
	double dDStepSize = 0.25 * sqrt(m_dEps/m_dDTempErr);

	m_dIntStep = __max(__min(__min(dSStepSize, dDStepSize), fabs(m_dEndTime-*pTime)), 4.0*dMacEps*fabs(*pTime));

	// set step size in the right direction
	m_iIntDir = (m_dEndTime>=*pTime) ? 1 : -1;
	if(-1 == m_iIntDir)
	{	
		m_dIntStep = -m_dIntStep;
	}

	m_iFail = 0;
	bool bRepeat = true;

	while(bRepeat && m_iFail < 10)
	{
		bRepeat = false;

		m_dIntStepSq = m_dIntStep*m_dIntStep;
		m_pIntStepArray[m_iIntpNums-1] = m_dIntStep;

		// do first step with general formulation
		// predict
		for(int i=0; i<iEqNums; i++)
		{
			pInitVel[i] = m_pLastVel[i] + m_dIntStep*m_pPhiN[i*(m_iMaxIntpNums+1)+0];
			pInitPos[i] = m_pLastPos[i] + m_dIntStep*m_pLastVel[i] + m_dIntStepSq*m_pPhiN[i*(m_iMaxIntpNums+1)+0]/2.0;
		}

		// advance time
		*pTime += m_dIntStep; 

		// evaluate		
		_2ndODEs(iEqNums, *pTime, pInitPos, pInitVel, m_pAcc);

		// get differences and error estimate		
		m_dSTempErr = m_dDTempErr = 0.0;
		for(int i=0; i<iEqNums; i++)
		{
			// get differences
			for(int m=0; m<m_iIntpNums+1; m++)
			{
				m_pPhi[i*(m_iMaxIntpNums+1)+m] = (0==m) ? m_pAcc[i] : m_pPhi[i*(m_iMaxIntpNums+1)+m-1]-m_pPhiN[i*(m_iMaxIntpNums+1)+m-1];
			}

			// get error estimate
			m_pSWeight[i] = fabs(m_pLastVel[i])*m_dRelEps + m_dAbsEps;
			m_pDWeight[i] = fabs(m_pLastPos[i])*m_dRelEps + m_dAbsEps;

			m_dSTempErr += ((m_pPhi[i*(m_iMaxIntpNums+1)+1])/m_pSWeight[i]) * ((m_pPhi[i*(m_iMaxIntpNums+1)+1])/m_pSWeight[i]);
			m_dDTempErr += ((m_pPhi[i*(m_iMaxIntpNums+1)+1])/m_pDWeight[i]) * ((m_pPhi[i*(m_iMaxIntpNums+1)+1])/m_pDWeight[i]);
		}
		
		m_dSTempErr = sqrt(m_dSTempErr);
		m_dDTempErr = sqrt(m_dDTempErr);

		m_dSErr = fabs(m_dIntStep)*m_dSTempErr/2.0;
		m_dDErr = m_dIntStepSq*m_dDTempErr/3.0;

		if(m_dSErr>m_dEps || m_dDErr>m_dEps)
		{
			// set everything back and try again with half step
			bRepeat = true;
			m_iFail++;
			*pTime -= m_dIntStep;
			m_dIntStep /= 2.0;
		}
		else if(0 == m_iFail)
		{
			// step succeeded, try again with a larger step, 
			// to find maximum initial step
			bRepeat = true;
			*pTime -= m_dIntStep;
			m_dIntStep *= 2.0;
		}
	}
	// end of initial step loop

	if(bRepeat)
	{
		printf("Unable to take first step after 10 tries, giving up!");

		return false;
	}

	// reset fail count
	m_iFail = 0;

	// correct
	for(int i=0; i<iEqNums; i++)
	{
		pInitVel[i] += m_dIntStep*m_pPhi[i*(m_iMaxIntpNums+1)+1]/2.0;
		pInitPos[i] += m_dIntStepSq*m_pPhi[i*(m_iMaxIntpNums+1)+1]/6.0;
	}	

	// re-evaluate and get differences
	_2ndODEs(iEqNums, *pTime, pInitPos, pInitVel, m_pAcc);

	// get differences
	for(int i=0; i<iEqNums; i++)
	{
		for(int m=0; m<m_iIntpNums+1; m++)
		{
			m_pPhi[i*(m_iMaxIntpNums+1)+m] = (0==m) ? m_pAcc[i] : m_pPhi[i*(m_iMaxIntpNums+1)+m-1]-m_pPhiN[i*(m_iMaxIntpNums+1)+m-1];
		}
	}

	// increment order, double step size on next step
	m_iLastIntpNums = m_iIntpNums++;
	m_dRho = 2.0;

	// copy latest differences from m_pPhi to m_pPhiN,
	// and set integration state and time to current values
	for(int i=0; i<iEqNums; i++)
	{
		// copy latest differences from m_pPhi to m_pPhiN
		for(int m=0; m<m_iLastIntpNums+1; m++)
		{
			m_pPhiN[i*(m_iMaxIntpNums+1)+m] = m_pPhi[i*(m_iMaxIntpNums+1)+m];
		}		

		// set integration state to current values
		m_pIntVel[i] = pInitVel[i];
		m_pIntPos[i] = pInitPos[i];
	}

	// set integration time
	m_dIntTime = *pTime;

	m_pPsi[0] = m_dIntStep;

	fprintf_s(m_pStream, "%17.16f\t%17.16f\t%17.16f\n", m_dIntTime, m_pIntPos[0], sin(m_dIntTime)-m_pIntPos[0]);

	printf("The step size is :%f\n", m_dIntStep);

	return true;
}

bool CVariableStepDoubleIntegration::StartIntegration(int iEqNums, double *pTime, double dEndTime, double *pPos, double *pVel)
{	
	// the following new function is for Init(...)
	m_pAcc = new double[iEqNums];

	m_pLastVel = new double[iEqNums];
	m_pLastPos = new double[iEqNums];

	m_pPhiN = new double[iEqNums*(m_iMaxIntpNums+1)];

	m_pSWeight = new double[iEqNums];
	m_pDWeight = new double[iEqNums];

	m_pIntStepArray = new double[m_iMaxIntpNums];
	m_pPhi = new double[iEqNums*(m_iMaxIntpNums+1)];

	m_pIntVel = new double[iEqNums];
	m_pIntPos = new double[iEqNums];

	m_pPsi = new double[m_iMaxIntpNums];

	// init
	if(!Init(iEqNums, pTime, dEndTime, pPos, pVel))
	{
		return false;
	}

	// the following new function is for integration or while loop
	m_pPos2Back = new double[iEqNums];

	m_pPsiNm1 = new double[m_iMaxIntpNums-1];
	m_pPsiN = new double[m_iMaxIntpNums-1];

	m_pAlpha = new double[m_iMaxIntpNums];

	m_pGCoeff = new double[(m_iMaxIntpNums+2)*(m_iMaxIntpNums+1)];
	m_pGpCoeff = new double[(m_iMaxIntpNums+2)*(m_iMaxIntpNums+1)];

	m_pPhiNSave = new double[iEqNums*m_iMaxIntpNums];
	m_pBeta = new double[m_iMaxIntpNums];

	// integrate to, or past, the request time
	while(((m_dEndTime>m_dIntTime)&&(1==m_iIntDir)) || ((m_dEndTime<m_dIntTime)&&(-1==m_iIntDir)))
	{		
		if(0 == m_iFail)
		{
			for(int i=0; i<iEqNums; i++)
			{
				m_pPos2Back[i] = m_pLastPos[i];
				m_pLastPos[i] = m_pIntPos[i];
				m_pLastVel[i] = m_pIntVel[i];

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -