#include "sopnamsp.h" #include "difeq.h" #include "ctimer.h" //++ // Class DiffEqSolver // Lib Outils++ // include difeq.h // // Classe abstraite de résolveur d'équation différentielles. // Beaucoup de méthodes renvoient l'objet afin de pouvoir // utiliser une notation chaînée du type //| s.Step(...).Start(...).Solve(...) //-- //++ // Links Implementations // RK4DiffEq // RK4CDiffEq //-- //++ // Titre Constructeurs //-- //++ DiffEqSolver::DiffEqSolver() // // Constructeur vide. //-- : mFunc(NULL), mOwnFunc(false), mYStart(1), mXStart(0), mStep(0.01) {} //++ DiffEqSolver::DiffEqSolver(DiffEqFunction* f) // // Constructeur général. L'équation est donnée sous forme // de DiffEqFunction //-- : mFunc(f), mOwnFunc(false), mYStart(mFunc->NFuncReal()), mXStart(0), mStep(0.01) {} //++ DiffEqSolver::DiffEqSolver(DIFEQFCN1 f) // // Constructeur pour le cas particulier d'une équation du premier // ordre. Voir DiffEqFcn1. La fonction f correspond à l'équation // y' = f(y). //-- : mFunc(new DiffEqFcn1(f)), mOwnFunc(true), mYStart(mFunc->NFuncReal()), mXStart(0), mStep(0.01) {} DiffEqSolver::~DiffEqSolver() { if (mOwnFunc) delete mFunc; } //++ // Titre Méthodes //-- //++ DiffEqSolver& DiffEqSolver::Func(DiffEqFunction* f) // // Permet de spécifier l'équation différentielle, sous la forme // d'une DiffEqFunction. Retourne l'objet DiffEqSolver : notation // chaînée possible //-- { if (mFunc && mOwnFunc) delete mFunc; mFunc = f; mOwnFunc = false; return *this; } //++ DiffEqSolver& DiffEqSolver::Func(DIFEQFCN1 f) // // Permet de spécifier l'équation différentielle, sous la forme // d'une fonction f telle que y' = f(y). Retourne l'objet DiffEqSolver : // notation chaînée possible. //-- { if (mFunc && mOwnFunc) delete mFunc; mFunc = new DiffEqFcn1(f); mOwnFunc = true; return *this; } //++ DiffEqSolver& DiffEqSolver::Step(double step) // // Spécifie le pas d'intégration de l'équation différentielle (pour // les méthodes où ce pas a un sens). La valeur par défaut est 0.01. // Retourne l'objet DiffEqSolver : notation chaînée possible. //-- { mStep = step; return *this; } //++ DiffEqSolver& DiffEqSolver::StartV(Vector const& yi, double t) // // Spécifie le point de départ de l'intégration de l'équadif. // Le vecteur y contient les valeurs intiales. Voir la description // de chaque fonction-objet-équation différentielle pour connaître // le rôle de chaque élément du vecteur. t est la valeur initiale // du temps. // La dimension de y doit correspondre au nombre apparent de fonctions. // Retourne l'objet DiffEqSolver : notation chaînée possible. //-- { ASSERT(mFunc != NULL); ASSERT(yi.NElts() == mFunc->NFunc()); // Nombre apparent mYStart = yi; mXStart = t; mFunc->AdjustStart(mYStart,t); ASSERT(mYStart.NElts() == mFunc->NFuncReal()); return *this; } //++ DiffEqSolver& DiffEqSolver::Start1(double y, double t) // // Spécifie le point de départ de l'intégration de l'équadif, // pour une équation y' = f(y). On donne le temps et la valeur de y. // Retourne l'objet DiffEqSolver : notation chaînée possible. //-- { ASSERT(mFunc != NULL); ASSERT(mFunc->NFunc() == 1); // Nombre apparent mYStart.Realloc(1); mYStart(0) = y; mXStart = t; mFunc->AdjustStart(mYStart,t); ASSERT(mYStart.NElts() == mFunc->NFuncReal()); return *this; } //++ DiffEqSolver& DiffEqSolver::Start(double const* yi, double t) // // Spécifie le point de départ de l'intégration de l'équadif. // Le tableau yi contient les valeurs intiales. Voir la description // de chaque fonction-objet-équation différentielle pour connaître // le rôle de chaque élément du tableau. t est la valeur initiale // du temps. // Le nombre d'éléments de yi doit correspondre au nombre apparent // de fonctions. // Retourne l'objet DiffEqSolver : notation chaînée possible. //-- { mYStart.Realloc(mFunc->NFunc()); for (int i=0; iNFunc(); i++) mYStart(i) = yi[i]; mXStart = t; mFunc->AdjustStart(mYStart,t); ASSERT(mYStart.NElts() == mFunc->NFuncReal()); return *this; } //++ // virtual void DiffEqSolver::SolveArr(Matrix& y, double* t, double tf, int n)=0 // Lance la résolution de l'équadif, jusqu'à la valeur // tf du temps. N valeurs intermédiaires sont retournées dans // les tableaux y et t : // t[i] est le temps au pas i, pour 0<=iNFuncReal()); SolveArr(m, &t, tf, 1); yf.Realloc(mFunc->NFunc()); for (int i=0; iNFunc(); i++) yf(i) = m(0,i); } //++ void DiffEqSolver::Solve1(double& yf, double tf) // // Lance la résolution de l'équadif, jusqu'à la valeur // tf du temps, pour une équation du premier ordre y' = f(y). // La valeur finale de y est retournée dans yf. //-- { ASSERT(mFunc->NFunc() == 1); double t; Matrix m(1,mFunc->NFuncReal()); SolveArr(m, &t, tf, 1); yf = m(0,0); } //++ void DiffEqSolver::Solve(double* yf, double tf) // // Lance la résolution de l'équadif, jusqu'à la valeur // tf du temps. Les valeurs finales sont retournées dans yf. // Voir la description // de chaque fonction-objet-équation différentielle pour connaître // le rôle de chaque élément de yf. // Le nombre d'éléments de yf doit correspondre au nombre apparent // de fonctions. //-- { double t; Matrix m(1, mFunc->NFuncReal()); SolveArr(m, &t, tf, 1); for (int i=0; iNFunc(); i++) yf[i] = m(0,i); } //++ void DiffEqSolver::SolveArr1(double* y, double* t, double tf, int n) // // Lance la résolution de l'équadif (du premier ordre), jusqu'à la valeur // tf du temps. N valeurs intermediaires sont retournées dans // les tableaux y et t : // t[i] est le temps au pas i, pour 0<=iNFunc() == 1); Matrix m(n, mFunc->NFuncReal()); SolveArr(m, t, tf, n); for (int i=0; iNFuncReal()); SolveArr(m, t, tf, n); for (int i=0; iNFunc(); j++) y[i][j] = m(i,j); } //++ // Class DiffEqFunction // Lib Outils++ // include difeq.h // // Classe de fonctions pour la résolution d'équations différentielles. // On résoud de facon générale un système de n équations différentielles // du premier ordre, donnant les dérivées fpi de n fonctions fi. // Cette méthode permet de résoudre toutes les sortes d'équations : // pour une équation du second ordre on crée une fonction intermédiaire // qui est la dérivée de la fonction cherchée. //-- //++ // DiffEqFunction::DiffEqFunction(int n) // Constructeur. N est le nombre de fonctions dans le // système. //-- //++ // DiffEqFunction::DiffEqFunction(int n, int napp) // Constructeur. N est le nombre réel de fonctions dans le // système, et napp le nombre apparent (pour l'utilisateur). // En effet, dans certains cas, on peut avoir besoin de fonctions // supplémentaires masquées à l'utilisateur, par exemple la fonction // constante valant 1, si l'équadif fait intervenir le temps (t). //-- //++ // virtual void ComputeV(Vector& fpi, Vector const& fi) // Calcule les valeurs des dérivées fpi à partir des valeurs // des fonctions fi. A redéfinir. //-- //++ // virtual void Compute(double& fp, double f) // Dans le cas où il y a une seule fonction, calcule la dérivée // fp à partir de la valeur de la fonction f. A redéfinir. //-- //++ // int NFunc() // Nombre apparent de fonctions dans le système //-- //++ // int NFuncReal() {return mNFunc;} // Nombre réel de fonctions dans le système //-- //++ // virtual void AdjustStart(Vector& start, double tstart) // Pour ajuster le vecteur de départ quand il y a des // fonctions à usage interne... //-- //++ // Class DiffEqFcn1 // Lib Outils++ // include difeq.h // // Objet-fonction générique pour la résolution d'équations // différentielles de la forme y' = f(y). // On fournit une fonction de type //| typedef double(*DIFEQFCN1)(double); // qui retourne y' en fonction de y. //-- //++ // Links Parents // DiffEqFunction //-- //++ DiffEqFcn1::DiffEqFcn1(DIFEQFCN1 fcn) // // Constructeur. On fournit la fonction f tq y'=f(y) //-- : DiffEqFunction(1), mFcn(fcn) {} void DiffEqFcn1::Compute(double& fp, double f) { fp = (*mFcn)(f); } //++ // Class DiffEqFcnT1 // Lib Outils++ // include difeq.h // // Objet-fonction générique pour la résolution d'équations // différentielles de la forme y' = f(y,t). // On fournit une fonction de type //| typedef double(*DIFEQFCNT1)(double, double); // qui retourne y' en fonction de y et t. // Note : le système résolu est alors en fait //| y'[0] = fcn(y[0], y[1]) //| y'[1] = 1 //-- //++ // Links Parents // DiffEqFunction //-- //++ DiffEqFcnT1::DiffEqFcnT1(DIFEQFCNT1 fcn) // // Constructeur. On fournit la fonction f tq y' = f(y,t) //-- : DiffEqFunction(2, 1), mFcn(fcn) {} void DiffEqFcnT1::ComputeV(Vector& fpi, Vector const& fi) { fpi(0) = (*mFcn)(fi(0), fi(1)); fpi(1) = 1; } void DiffEqFcnT1::AdjustStart(Vector& start, double tstart) { start.Realloc(2); start(1) = tstart; } //++ // Class DiffEqFcn2 // Lib Outils++ // include difeq.h // // Objet-fonction générique pour la résolution d'équations // différentielles de la forme y'' = f(y',y). // On fournit une fonction de type //| typedef double(*DIFEQFCN2)(double, double); // qui retourne y'' en fonction de y' et y. // Note : le système résolu est en fait //| y'[0] = y[1] //| y'[1] = f(y[1], y[0]) //-- //++ // Links Parents // DiffEqFunction //-- //++ DiffEqFcn2::DiffEqFcn2(DIFEQFCN2 fcn) // // Constructeur. On fournit la fonction f tq y''=f(y',y) //-- : DiffEqFunction(2), mFcn(fcn) {} void DiffEqFcn2::ComputeV(Vector& fpi, Vector const& fi) { fpi(0) = fi(1); fpi(1) = (*mFcn)(fi(1), fi(0)); } //++ // Class DiffEqFcnT2 // Lib Outils++ // include difeq.h // // Objet-fonction générique pour la résolution d'équations // différentielles de la forme y'' = f(y',y,t). // On fournit une fonction de type //| typedef double(*DIFEQFCNT2)(double, double, double); // qui retourne y'' en fonction de y', y et t. // Note : le système résolu est alors en fait //| y'[0] = y[1] //| y'[1] = f(y[1], y[0], y[2]) //| y'[2] = 1 //-- //++ // Links Parents // DiffEqFunction //-- //++ DiffEqFcnT2::DiffEqFcnT2(DIFEQFCNT2 fcn) // // Constructeur. On fournit la fonction f tq y'' = f(y',y,t) //-- : DiffEqFunction(3,2), mFcn(fcn) {} void DiffEqFcnT2::ComputeV(Vector& fpi, Vector const& fi) { fpi(0) = fi(1); fpi(1) = (*mFcn)(fi(1), fi(0), fi(2)); fpi(2) = 1; } void DiffEqFcnT2::AdjustStart(Vector& start, double tstart) { start.Realloc(3); start(2) = tstart; } //++ // Class DiffEqFcnV // Lib Outils++ // include difeq.h // // Objet-fonction générique pour la résolution d'équations // différentielles 3D de la forme y'' = f(y',y), ou y est // un vecteur de dimension 3. // On fournit une fonction de type //| typedef void(*DIFEQFCNV)(Vector& y2, Vector const& y1, //| Vector const& y); // qui retourne y'' en fonction de y' et y. // Note : le système résolu est alors en fait //| v(0-2) = y, v(3-5) = y' //| //| v'[0] = v[3] //| v'[1] = v[4] //| v'[2] = v[5] //| v'[3-5] = f(v[3-5], v[0-2]) //-- //++ // Links Parents // DiffEqFunction //-- DiffEqFcnV::DiffEqFcnV(DIFEQFCNV fcn) : DiffEqFunction(6), mFcn(fcn), tmp1(3), tmp2(3), tmp3(3) {} void DiffEqFcnV::ComputeV(Vector& fpi, Vector const& fi) { fpi(0) = fi(3); fpi(1) = fi(4); fpi(2) = fi(5); tmp2(0) = fi(3); tmp2(1) = fi(4); tmp2(2) = fi(5); tmp3(0) = fi(0); tmp3(1) = fi(1); tmp3(2) = fi(2); (*mFcn)(tmp1, tmp2, tmp3); fpi(3) = tmp1(0); fpi(4) = tmp1(1); fpi(5) = tmp1(2); } //++ // Class RK4DiffEq // Lib Outils++ // include difeq.h // // Classe de résolution d'équadif par la méthode de // Runge-Kutta d'ordre 4. // Voir DiffEqSolver pour les méthodes. //-- //++ // Links Parents // DiffEqSolver //-- //++ // Titre Constructeurs //-- RK4DiffEq::RK4DiffEq() : DiffEqSolver(), k1(10), k2(10), k3(10), k4(10) {} //++ RK4DiffEq::RK4DiffEq(DiffEqFunction* f) // // Constructeur général //-- : DiffEqSolver(f), k1(f->NFuncReal()), k2(f->NFuncReal()), k3(f->NFuncReal()), k4(f->NFuncReal()) {} //++ RK4DiffEq::RK4DiffEq(DIFEQFCN1 f) // // Constructeur pour y' = f(y) //-- : DiffEqSolver(f), k1(1), k2(1), k3(1), k4(1) {} void RK4DiffEq::SolveArr(Matrix& y, double* t, double tf, int n) { //TIMEF; // On calcule le nombre de sous-pas par pas int nStep = (mStep > 0) ? int((tf - mXStart)/n/mStep) : 1; if (nStep <= 1) nStep = 1; double dx = (tf - mXStart)/(n*nStep); Vector yt(mYStart,false); k1.Realloc(mFunc->NFuncReal()); k2.Realloc(mFunc->NFuncReal()); k3.Realloc(mFunc->NFuncReal()); k4.Realloc(mFunc->NFuncReal()); for (int i=0; iNFunc(); k++) y(i,k) = yt(k); t[i] = (i+1)*dx*nStep + mXStart; } } void RK4DiffEq::RKStep(Vector& newY, Vector const& y0, double dt) { mFunc->ComputeV(k1, y0); k1 *= dt; mFunc->ComputeV(k2, y0 + k1/2.); k2 *= dt; mFunc->ComputeV(k3, y0 + k2/2.); k3 *= dt; mFunc->ComputeV(k4, y0 + k3); k4 *= dt; newY = y0 + (k1 + k2*2. + k3*2. + k4)/6.; }