Working! New way of obtaining Jacobian succesfully implemented. Newton-Raphson solver working

This commit is contained in:
Anne de Jong 2017-01-26 15:03:34 +01:00
parent 328905b461
commit c81e17cecc
20 changed files with 416 additions and 399 deletions

View File

@ -65,9 +65,8 @@ add_library(tasmet_src
# sys/enginesystem.cpp # sys/enginesystem.cpp
sys/globalconf.cpp sys/globalconf.cpp
sys/tasmet_variable.cpp sys/tasmet_variable.cpp
sys/jaccol.cpp
sys/jacobian.cpp sys/jacobian.cpp
sys/jacrow.cpp
sys/tasystem.cpp sys/tasystem.cpp
sys/triplets.cpp sys/triplets.cpp
) )

View File

@ -60,11 +60,10 @@ Duct::~Duct() {
// delete eq; // delete eq;
// } // }
} }
void Duct::residualJac(arma::subview_col<d> && residual) const { void Duct::residualJac(SegPositionMapper& residual,
SegJacRows& jacrows) const {
TRACE(15,"Duct::residual()"); TRACE(15,"Duct::residualJac()");
const arma::subview_col<d> sol = sys.getSolution(_id);
vd rho,u,T,p,Ts; // Solution at this gp vd rho,u,T,p,Ts; // Solution at this gp
vd rhop,up,Tp,pp,Tsp; // Solution at next gp vd rhop,up,Tp,pp,Tsp; // Solution at next gp
@ -78,14 +77,14 @@ void Duct::residualJac(arma::subview_col<d> && residual) const {
us number_eqs = 4; us number_eqs = 4;
number_eqs += (has_solideq) ? 1 : 0; number_eqs += (has_solideq) ? 1 : 0;
// rho,u,T,p
// Ts maybe
us nvars_per_gp = (_ductpb.stempmodel()
== pb::HeatBalance ? 5 : 4);
VARTRACE(15,number_eqs); VARTRACE(15,number_eqs);
us Ns = sys.Ns(); us Ns = sys.Ns();
us eq_offset = 0; // Equation offset for current gp
us res_offset = 0; // Residual offset for current gp
us res_offsetp = 0; // Residual offset for next gp
us gp_jump = number_eqs * Ns; // The jump per gp
rhop = getvart(constants::rho,0); rhop = getvart(constants::rho,0);
up = getvart(constants::u,0); up = getvart(constants::u,0);
@ -94,11 +93,9 @@ void Duct::residualJac(arma::subview_col<d> && residual) const {
const Gas& gas = sys.gas(); const Gas& gas = sys.gas();
for(us gp=0;gp<ngp()-1;gp++) { us i=0;
eq_offset = gp*Ns*number_eqs; for(us gp=0;gp<ngp()-1;gp++) {
res_offset = eq_offset;
res_offsetp = res_offset + gp_jump;
d dx = x(gp+1)-x(gp); d dx = x(gp+1)-x(gp);
@ -112,8 +109,35 @@ void Duct::residualJac(arma::subview_col<d> && residual) const {
pp = getvart(constants::p,gp+1); pp = getvart(constants::p,gp+1);
cont = ((rhop%up)-(rho%u))/dx; cont = ((rhop%up)-(rho%u))/dx;
*residual.at(i) = cont;
jacrows.at(i)[{_id,gp*nvars_per_gp+constants::u}] =
-diagmat(rho)/dx;
jacrows.at(i)[{_id,gp*nvars_per_gp+constants::rho}] =
-diagmat(u)/dx;
jacrows.at(i)[{_id,(gp+1)*nvars_per_gp+constants::rho}] =
diagmat(up)/dx;
jacrows.at(i)[{_id,(gp+1)*nvars_per_gp+constants::u}] =
diagmat(rhop)/dx;
TRACE(15,"SFSG");
i++;
mom = (rhop%up%up - rho%u%u + pp - p)/dx; mom = (rhop%up%up - rho%u%u + pp - p)/dx;
jacrows.at(i)[{_id,gp*nvars_per_gp+constants::u}] =
-diagmat(rho%u)/dx;
jacrows.at(i)[{_id,gp*nvars_per_gp+constants::rho}] =
-diagmat(u%u)/dx;
jacrows.at(i)[{_id,(gp+1)*nvars_per_gp+constants::rho}] =
diagmat(u%u)/dx;
jacrows.at(i)[{_id,(gp+1)*nvars_per_gp+constants::u}] =
diagmat(up%rhop)/dx;
jacrows.at(i)[{_id,(gp)*nvars_per_gp+constants::p}] =
-eye(Ns,Ns)/dx;
jacrows.at(i)[{_id,(gp+1)*nvars_per_gp+constants::p}] =
eye(Ns,Ns)/dx;
*residual.at(i) = mom;
TRACE(15,"SFSG");
i++;
switch (_ductpb.htmodel()) { switch (_ductpb.htmodel()) {
case pb::Isentropic: { case pb::Isentropic: {
@ -125,33 +149,53 @@ void Duct::residualJac(arma::subview_col<d> && residual) const {
en = p/p0 - pow(rho/rho0,gamma0); en = p/p0 - pow(rho/rho0,gamma0);
*residual.at(i) = en;
jacrows.at(i)[{_id,gp*nvars_per_gp+constants::p}] =
eye(Ns,Ns)/p0;
jacrows.at(i)[{_id,gp*nvars_per_gp+constants::rho}] =
-(gamma0/rho0)*diagmat(pow(rho/rho0,gamma0-1));
TRACE(15,"SFSG");
} }
break; break;
default: default:
tasmet_assert(false,"Not implemented htmodel"); tasmet_assert(false,"Not implemented htmodel");
} }
i++;
TRACE(15,"SFSG");
// Equation of state
st = gas.rho(T,p) - rho; st = gas.rho(T,p) - rho;
TRACE(15,"SFSG");
jacrows.at(i)[{_id,gp*nvars_per_gp+constants::rho}] =
-eye(Ns,Ns);
jacrows.at(i)[{_id,gp*nvars_per_gp+constants::p}] =
diagmat(gas.drhodp(T,p));
jacrows.at(i)[{_id,gp*nvars_per_gp+constants::T}] =
diagmat(gas.drhodT(T,p));
residual.subvec(eq_offset+0*Ns,eq_offset+1*Ns-1) = cont; *residual.at(i) = st;
residual.subvec(eq_offset+1*Ns,eq_offset+2*Ns-1) = mom; TRACE(15,"SFSG");
residual.subvec(eq_offset+2*Ns,eq_offset+3*Ns-1) = en; i++;
residual.subvec(eq_offset+3*Ns,eq_offset+4*Ns-1) = st;
} }
eq_offset += number_eqs*Ns;
// Equation of state for the last node // Equation of state for the last node
st = gas.rho(Tp,pp) - rhop; st = gas.rho(Tp,pp) - rhop;
residual.subvec(eq_offset,eq_offset+Ns-1) = st; *residual.at(i) = st;
jacrows.at(i)[{_id,(ngp()-1)*nvars_per_gp+constants::rho}] =
-eye(Ns,Ns);
jacrows.at(i)[{_id,(ngp()-1)*nvars_per_gp+constants::p}] =
diagmat(gas.drhodp(T,p));
jacrows.at(i)[{_id,(ngp()-1)*nvars_per_gp+constants::T}] =
diagmat(gas.drhodT(T,p));
i++;
// Two more equations for the last grid point in case // Two more equations for the last grid point in case
// the heat transfer model is not a transport equation. // the heat transfer model is not a transport equation.
if(_ductpb.htmodel() == pb::Isentropic) { if(_ductpb.htmodel() == pb::Isentropic) {
eq_offset += Ns;
d T0 = gas.T0(); d T0 = gas.T0();
d p0 = gas.p0(); d p0 = gas.p0();
d rho0 = gas.rho0(); d rho0 = gas.rho0();
@ -159,24 +203,43 @@ void Duct::residualJac(arma::subview_col<d> && residual) const {
en = p/p0 - pow(rho/rho0,gamma0); en = p/p0 - pow(rho/rho0,gamma0);
residual.subvec(eq_offset,eq_offset+Ns-1) = en; jacrows.at(i)[{_id,(ngp()-1)*nvars_per_gp+constants::p}] =
eye(Ns,Ns)/p0;
jacrows.at(i)[{_id,(ngp()-1)*nvars_per_gp+constants::rho}] =
-(gamma0/rho0)*diagmat(pow(rho/rho0,gamma0-1));
*residual.at(i) = en; i++;
} }
} }
PosId Duct::getDof(int varnr,int gp) const {
// Wraparound
if(gp<0) gp+=ngp();
// rho,u,T,p
// Ts maybe
us nvars_per_gp = (_ductpb.stempmodel()
== pb::HeatBalance ? 5 : 4);
return {_id,nvars_per_gp*gp+varnr};
}
vd Duct::getvart(int varnr,int gp) const { vd Duct::getvart(int varnr,int gp) const {
TRACE(15,"Duct::getvart()"); TRACE(14,"Duct::getvart()");
const arma::subview_col<d> sol = sys.getSolution(_id); const SegPositionMapper& sol = sys.getSolution(_id);
us Ns = sys.Ns(); us Ns = sys.Ns();
// Wraparound // Wraparound
if(gp<0) gp+=ngp(); if(gp<0) gp+=ngp();
us vars_per_gp = 4; // rho,u,T,p
vars_per_gp+= (_ductpb.stempmodel() == pb::HeatBalance ? 1 : 0); // Ts maybe
us nvars_per_gp = (_ductpb.stempmodel()
== pb::HeatBalance ? 5 : 4);
return sol.subvec((gp*vars_per_gp+varnr)*Ns, return *sol.at((gp*nvars_per_gp+varnr));
(gp*vars_per_gp+varnr+1)*Ns-1);
} }
vd Duct::getvarx(int varnr,int t) const { vd Duct::getvarx(int varnr,int t) const {
vd res(ngp()); vd res(ngp());
@ -185,48 +248,44 @@ vd Duct::getvarx(int varnr,int t) const {
} }
return res; return res;
} }
vd Duct::initialSolution() const { void Duct::initialSolution(SegPositionMapper& sol) const {
TRACE(15,"Duct::initialSolution()"); TRACE(15,"Duct::initialSolution()");
vd initsol(getNDofs()); VARTRACE(15,sol.size());
VARTRACE(15,initsol.size());
us vars_per_gp = 4; us nvars_per_gp = 4;
vars_per_gp+= (_ductpb.stempmodel() == pb::HeatBalance ? 1 : 0); nvars_per_gp+= (_ductpb.stempmodel() == pb::HeatBalance ? 1 : 0);
us Ns = sys.Ns();
const Gas& gas = sys.gas(); const Gas& gas = sys.gas();
us segdof = 0;
for(us i=0;i<ngp();i++){ for(us i=0;i<ngp();i++){
VARTRACE(15,i); VARTRACE(15,i);
// Initial density // Initial density
*sol.at(segdof) += gas.rho0()+0.1;
initsol.subvec((i*vars_per_gp+0)*Ns,(i*vars_per_gp+1)*Ns-1) = segdof++;
gas.rho0()+.01;
// Initial velocity // Initial velocity
initsol.subvec((i*vars_per_gp+1)*Ns,(i*vars_per_gp+2)*Ns-1) = *sol.at(segdof) += 0.1;
0; segdof++;
// Initial Temperature // Initial Temperature
initsol.subvec((i*vars_per_gp+2)*Ns,(i*vars_per_gp+3)*Ns-1) = *sol.at(segdof) += _Tsprescribed(i);
_Tsprescribed(i); segdof++;
// Initial pressure // Initial pressure
initsol.subvec((i*vars_per_gp+3)*Ns,(i*vars_per_gp+4)*Ns-1) = *sol.at(segdof) += gas.p0();
gas.p0(); segdof++;
// Initial solid temperature, if not prescribed // Initial solid temperature, if not prescribed
if(_ductpb.stempmodel() != pb::Prescribed) { if(_ductpb.stempmodel() != pb::Prescribed) {
initsol.subvec((i*vars_per_gp+4)*Ns,(i*vars_per_gp+5)*Ns-1) = *sol.at(segdof) += _Tsprescribed(i);
_Tsprescribed(i); segdof++;
} }
} }
return initsol;
} }
us Duct::getNEqs() const { us Duct::getNEqs() const {
@ -257,9 +316,9 @@ us Duct::getNDofs() const {
TRACE(15,"Duct::getNDofs()"); TRACE(15,"Duct::getNDofs()");
// rho,u,T,p // rho,u,T,p
us nvars_per_gp = 4;
// Ts maybe // Ts maybe
nvars_per_gp += (_ductpb.stempmodel() == pb::HeatBalance ? 1 : 0); us nvars_per_gp = (_ductpb.stempmodel()
== pb::HeatBalance ? 5 : 4);
return nvars_per_gp*ngp(); return nvars_per_gp*ngp();
} }

View File

@ -12,6 +12,7 @@
#include "duct.pb.h" #include "duct.pb.h"
#include "geom.h" #include "geom.h"
#include "tasmet_constants.h" // For the variable nrs #include "tasmet_constants.h" // For the variable nrs
#include "jacobian.h"
class Equation; class Equation;
class Drag; class Drag;
@ -34,8 +35,9 @@ public:
vd Tsprescribed() const { return _Tsprescribed;} vd Tsprescribed() const { return _Tsprescribed;}
// Initialize the solution to something sensible void initialSolution(SegPositionMapper&) const;
vd initializeSolution(const TaSystem& sys);
virtual Duct* copy(const TaSystem&) const; virtual Duct* copy(const TaSystem&) const;
const Geom& geom() const; const Geom& geom() const;
@ -70,10 +72,10 @@ public:
vd getvarx(int varnr,int t) const; vd getvarx(int varnr,int t) const;
d getvartx(int t,int gp) const; d getvartx(int t,int gp) const;
// Solving
virtual void residualJac(arma::subview_col<d>&& residual) const;
vd initialSolution() const; // Solving
virtual void residualJac(SegPositionMapper& residual,
SegJacRows& jac) const;
// virtual void getSolution(const TaSystem&,const us insertion_start) const; // virtual void getSolution(const TaSystem&,const us insertion_start) const;

View File

@ -35,20 +35,22 @@ AdiabaticWall::~AdiabaticWall() {
AdiabaticWall* AdiabaticWall::copy(const TaSystem& sys) const { AdiabaticWall* AdiabaticWall::copy(const TaSystem& sys) const {
return new AdiabaticWall(sys,*this); return new AdiabaticWall(sys,*this);
} }
vd AdiabaticWall::initialSolution() const {
return vd();
}
void AdiabaticWall::residualJac(arma::subview_col<d>&& residual
) const {
TRACE(15,"AdiabaticWall::residual()"); void AdiabaticWall::residualJac(SegPositionMapper& residual,
SegJacRows& jac) const {
TRACE(15,"AdiabaticWall::residualJac()");
const Duct& duct = getDuct(); const Duct& duct = getDuct();
const pb::Duct& dpb = duct.getDuctPb(); const pb::Duct& dpb = duct.getDuctPb();
us Ns = sys.Ns(); us Ns = sys.Ns();
us i=0;
if(_side == pb::left) { if(_side == pb::left) {
residual.subvec(0,Ns-1) = duct.ut(0); *residual.at(i) = duct.ut(0);
jac.at(i)[duct.getDof(constants::u,0)].eye(Ns,Ns);
if(dpb.htmodel() != pb::Isentropic) { if(dpb.htmodel() != pb::Isentropic) {
// TODO: Put the boundary condition of zero heat flux here // TODO: Put the boundary condition of zero heat flux here
// residual.subvec(Ns,2*Ns-1) = // residual.subvec(Ns,2*Ns-1) =
@ -56,7 +58,8 @@ void AdiabaticWall::residualJac(arma::subview_col<d>&& residual
} }
} }
else { else {
residual.subvec(0,Ns-1) = duct.ut(-1); *residual.at(i) = duct.ut(-1);
jac.at(i)[duct.getDof(constants::u,-1)].eye(Ns,Ns);
if(dpb.htmodel() != pb::Isentropic) { if(dpb.htmodel() != pb::Isentropic) {
// TODO: Put the boundary condition of zero heat flux here // TODO: Put the boundary condition of zero heat flux here
// residual.subvec(Ns,2*Ns-1) = duct.Tt(sys,-1) - _T; // residual.subvec(Ns,2*Ns-1) = duct.Tt(sys,-1) - _T;

View File

@ -34,10 +34,8 @@ public:
~AdiabaticWall(); ~AdiabaticWall();
AdiabaticWall* copy(const TaSystem& sys) const; AdiabaticWall* copy(const TaSystem& sys) const;
vd initialSolution() const; virtual void residualJac(SegPositionMapper& residual,
SegJacRows& jac) const;
virtual void residualJac(arma::subview_col<d>&& residual
) const;
// Return the total number of equations in this segment // Return the total number of equations in this segment
virtual us getNEqs() const; virtual us getNEqs() const;
@ -50,11 +48,6 @@ public:
// Reset amplitude data in higher harmonics // Reset amplitude data in higher harmonics
// virtual void resetHarmonics() = 0; // virtual void resetHarmonics() = 0;
// Fill Jacobian with values from the equations in this
// segment/connector.
virtual void jac(const TaSystem&,Jacobian&,us dof_start,us eq_start) const;
}; };
#endif // ADIABATICWALL_H #endif // ADIABATICWALL_H

View File

@ -31,10 +31,10 @@ DuctBc* DuctBc::newDuctBc(const us id,
return new PressureBc(sys,id,dbc); return new PressureBc(sys,id,dbc);
break; break;
} }
// case pb::AdiabaticWall: { case pb::AdiabaticWall: {
// return new AdiabaticWall(id,sys,dbc); return new AdiabaticWall(sys,id,dbc);
// break; break;
// } }
default: default:
tasmet_assert(false,"Not implemented DuctBc"); tasmet_assert(false,"Not implemented DuctBc");
break; break;

View File

@ -33,6 +33,8 @@ public:
const Duct& getDuct() const; const Duct& getDuct() const;
virtual void initialSolution(SegPositionMapper&) const {}
}; };

View File

@ -69,31 +69,28 @@ PressureBc::~PressureBc() {
PressureBc* PressureBc::copy(const TaSystem& sys) const { PressureBc* PressureBc::copy(const TaSystem& sys) const {
return new PressureBc(sys,*this); return new PressureBc(sys,*this);
} }
vd PressureBc::initialSolution() const {
return vd();
}
void PressureBc::residualJac(arma::subview_col<d>&& residual void PressureBc::residualJac(SegPositionMapper& residual,
) const { SegJacRows& jacrows) const {
TRACE(15,"PressureBc::residual()"); TRACE(15,"PressureBc::residualJac()");
const pb::Duct& dpb = getDuct().getDuctPb(); const pb::Duct& dpb = getDuct().getDuctPb();
us Ns = sys.Ns(); us Ns = sys.Ns();
const Duct& duct = getDuct(); const Duct& duct = getDuct();
if(_side == pb::left) { if(_side == pb::left) {
residual.subvec(0,Ns-1) = duct.pt(0) - _p; *residual.at(0) = duct.pt(0) - _p;
jacrows.at(0)[duct.getDof(constants::p,0)] = eye(Ns,Ns);
if(dpb.htmodel() != pb::Isentropic) { if(dpb.htmodel() != pb::Isentropic) {
residual.subvec(Ns,2*Ns-1) = duct.Tt(0) - _T; // residual.subvec(Ns,2*Ns-1) = duct.Tt(0) - _T;
} }
} }
else { else {
residual.subvec(0,Ns-1) = duct.pt(-1) - _p; *residual.at(0) = duct.pt(-1) - _p;
jacrows.at(0)[duct.getDof(constants::p,-1)] = eye(Ns,Ns);
if(dpb.htmodel() != pb::Isentropic) { if(dpb.htmodel() != pb::Isentropic) {
residual.subvec(Ns,2*Ns-1) = duct.Tt(-1) - _T; // residual.subvec(Ns,2*Ns-1) = duct.Tt(-1) - _T;
} }
} }
} }

View File

@ -38,10 +38,8 @@ public:
PressureBc* copy(const TaSystem&) const; PressureBc* copy(const TaSystem&) const;
vd initialSolution() const; virtual void residualJac(SegPositionMapper& residual,
SegJacRows& jac) const;
virtual void residualJac(arma::subview_col<d>&& residual
) const;
// Return the total number of equations in this segment // Return the total number of equations in this segment
virtual us getNEqs() const; virtual us getNEqs() const;

View File

@ -21,11 +21,11 @@ using pb::air;
using pb::helium; using pb::helium;
using pb::nitrogen; using pb::nitrogen;
#define element_wise(varname) \ #define element_wise(function) \
vd varname_(T.size()); \ vd function_(T.size()); \
for(us i=0;i<T.size();i++) \ for(us i=0;i<T.size();i++) \
varname_ = varname(T(i),p(i));\ function_(i) = function(T(i),p(i)); \
return varname_ return function_

View File

@ -34,26 +34,22 @@ void NewtonRaphson::start_implementation(GradientNonlinearSystem& system,
ResidualJac resjac; ResidualJac resjac;
system.residualJac(resjac); system.residualJac(resjac);
const sdmat& jac = resjac.jacobian;
const vd& residual = resjac.residual;
vd dx; vd dx;
#ifdef DEBUG_TASMET_SYSTEM assert(resjac.jacobian.n_cols==resjac.residual.size());
cout << "Initial residual: " << endl; assert(resjac.jacobian.n_rows==resjac.residual.size());
cout << residual << endl;
#endif // DEBUG_TASMET_SYSTEM
assert(jac.n_cols==residual.size());
assert(jac.n_rows==residual.size());
while (true) { while (true) {
#ifdef DEBUG_TASMET_SYSTEM #ifdef DEBUG_TASMET_SYSTEM
cout << "Residual: "; cout << "Residual: ";
cout << residual << endl; cout << resjac.residual << endl;
cout << dmat(resjac.jacobian) << endl;
#endif // DEBUG_TASMET_SYSTEM #endif // DEBUG_TASMET_SYSTEM
dx = -1*_dampfac*arma::spsolve(jac,residual,"superlu"); TRACE(15,"Solving system of eqs");
dx = -1*_dampfac*arma::spsolve(resjac.jacobian,resjac.residual,"superlu");
TRACE(15,"Solving system of eqs done");
progress.rel_err = norm(dx); progress.rel_err = norm(dx);
@ -64,12 +60,14 @@ void NewtonRaphson::start_implementation(GradientNonlinearSystem& system,
// Obtain a new residual and Jacobian matrix // Obtain a new residual and Jacobian matrix
system.residualJac(resjac); system.residualJac(resjac);
progress.fun_err = norm(residual); progress.fun_err = norm(resjac.residual);
progress.iteration++; progress.iteration++;
action = (*callback)(progress); action = (*callback)(progress);
if(action==Stop){ if(action==Stop){
TRACE(15,"Solution at stop:");
TRACE(15,guess);
break; break;
} }

View File

@ -1,13 +0,0 @@
// jaccol.cpp
//
// last-edit-by: J.A. de Jong
//
// Description:
//
//////////////////////////////////////////////////////////////////////
#include "jaccol.h"
//////////////////////////////////////////////////////////////////////

View File

@ -1,50 +0,0 @@
// jaccol.h
//
// Author: J.A. de Jong
//
// Description:
// A column block of the Jacobian matrix
//////////////////////////////////////////////////////////////////////
#pragma once
#ifndef JACCOL_H
#define JACCOL_H
#include "tasmet_types.h"
/**
* A PosId is a specifier for the position in the global Jacobian
* matrix. Both considering the column as well as the row
*
*/
struct PosId {
int segno;
int segoffset;
};
class JacCol{ // Column block of Jacobian
public:
// For more ease of use, we make the members public.
PosId id; /**< The Id of this DOF */
dmat data; /**< The matrix block */
JacCol(PosId id): id(id){}
JacCol(PosId id,const dmat& data):id(id),data(data){}
JacCol& operator=(const JacCol& o) { id = o.id; data=o.data; return *this; }
JacCol(const JacCol& o): id(o.id),data(o.data){}
void prePostMultiply(const dmat& pre,const dmat& post){ data=pre*data*post;}
// Some operations on the data
JacCol operator-() { return JacCol(id,-data);}
JacCol& operator+=(const dmat& add) {data+=add; return *this;}
JacCol& operator*=(const double d) {data*=d; return *this;}
};
#endif // JACCOL_H
//////////////////////////////////////////////////////////////////////

View File

@ -2,14 +2,6 @@
#include "triplets.h" #include "triplets.h"
#include "tasmet_tracer.h" #include "tasmet_tracer.h"
void Jacobian::operator+=(const Jacobian& other){
TRACE(2,"Jacobian::append()");
jacrows.insert(jacrows.end(),other.jacrows.begin(),other.jacrows.end());
}
void Jacobian::operator+=(const JacRow& other){
TRACE(2,"Jacobian::append()");
jacrows.push_back(other);
}
// Jacobian::operator TripletList() const{ // Jacobian::operator TripletList() const{
// TRACE(18,"Jacobian::operator Tripletlist()"); // TRACE(18,"Jacobian::operator Tripletlist()");

View File

@ -8,25 +8,59 @@
#pragma once #pragma once
#ifndef JACOBIAN_H #ifndef JACOBIAN_H
#define JACOBIAN_H #define JACOBIAN_H
#include "tasmet_types.h"
#include "jacrow.h" #include <unordered_map>
#include <map>
class TripletList; class TripletList;
/**
* A PosId is a specifier for the position in the global Jacobian
* matrix. Both considering the column as well as the row
*
*/
struct PosId {
us segno; /**< This is the segment number */
us segoffset; /**< This is the offset in the segment */
class Jacobian{ bool operator==(const PosId& other) const {
us _ndofs; return (segno==other.segno) &&
public: (segoffset == other.segoffset);
Jacobian(us ndofs):_ndofs(ndofs){} }
vector<JacRow> jacrows;
void operator+=(const Jacobian&); bool operator<(const PosId& other) const {
void operator+=(const JacRow&); if(other.segno < segno) return true;
else if(other.segno > segno) return false;
else return (other.segoffset < segoffset);
operator TripletList() const; }
};
/**
* To use the PosId in an unordered_map, it requires a custom hash
* function. This struct has a custom function to generate a hash from
* the given two integers.
*
*/
struct hash_PosId
{
std::size_t operator() ( const PosId& m ) const
{
const us a = m.segno+1 ;
const us b = m.segoffset+1 ;
return std::hash<us>()( a*b + (a+b) ) ;
}
}; };
typedef std::unordered_map<PosId,dmat,hash_PosId> JacRow;
typedef std::vector<JacRow> SegJacRows;
typedef std::map<us,SegJacRows> Jacobian;
typedef std::vector<vd*> SegPositionMapper;
typedef std::map<us, SegPositionMapper> GlobalPositionMapper;
#endif // JACOBIAN_H #endif // JACOBIAN_H
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View File

@ -1,40 +0,0 @@
#include "jacobian.h"
#include "tasmet_variable.h"
#include "globalconf.h"
JacRow& JacRow::operator+=(const JacCol& j){
TRACE(10,"JacRow::operator+=(const JacCol& j)");
jaccols.push_back(j);
return *this;
}
JacRow& JacRow::operator*=(const d val){
TRACE(2,"Jacobian::operator*=()");
for(JacCol& col: jaccols)
col*=val;
return *this;
}
JacRow& JacRow::operator+=(const JacRow& other){
// Reserve some space
this->jaccols.reserve(this->jaccols.capacity()+other.jaccols.size()-this->jaccols.size());
for(const JacCol& col :other.jaccols)
(*this)+=col;
return *this;
}
JacRow JacRow::operator-() const{
TRACE(15,"JacRow::operator-()");
JacRow result(*this);
for (JacCol& jaccol : result.jaccols)
jaccol*=-1;
return result;
}
void JacRow::prePostMultiply(const dmat& pre,const dmat& post) {
for(JacCol& jaccol: jaccols)
jaccol.prePostMultiply(pre,post);
}

View File

@ -1,55 +0,0 @@
// jacrow.h
//
// Author: J.A. de Jong
//
// Description:
//
//////////////////////////////////////////////////////////////////////
#pragma once
#ifndef JACROW_H
#define JACROW_H
#include <vector>
#include "jaccol.h"
class JacRow{ // Row in Jacobian matrix
public:
PosId id; /**< Specifies the row for this
JacRow */
std::vector<JacCol> jaccols; /**< The columns in this row */
/**
* Initialize a Jacobian row with a single column block
*
* @param id The equation ID
* @param col The added column
*/
JacRow(PosId id,const JacCol& col):JacRow(id){jaccols.push_back(col);}
JacRow(PosId id): id(id){}
JacRow operator-() const;
JacRow& operator+=(const JacCol&);
JacRow& operator+=(const JacRow& jacrow);
JacRow& operator*=(const d val); // Multiply all terms with constant value
/**
* Pre- and postmultiply all Column blocks with the given
* matrices. Hence the new columns will be col_new = pre*col_old*post
*
* @param pre : The pre-multiply matrix
* @param post: The post-multiply matrix
*/
void prePostMultiply(const dmat& pre,const dmat& post);
};
#endif // JACROW_H
//////////////////////////////////////////////////////////////////////

View File

@ -15,9 +15,10 @@
#include "tasmet_types.h" #include "tasmet_types.h"
#include "tasmet_exception.h" #include "tasmet_exception.h"
#include "jacobian.h"
// #include "phaseconstraint.h" // #include "phaseconstraint.h"
class Jacobian;
class GlobalConf; class GlobalConf;
class TaSystem; class TaSystem;
@ -51,9 +52,10 @@ public:
*/ */
virtual Segment* copy(const TaSystem&) const = 0; virtual Segment* copy(const TaSystem&) const = 0;
virtual vd initialSolution() const = 0; virtual void initialSolution(SegPositionMapper&) const = 0;
virtual void residualJac(arma::subview_col<d>&& residual // Here we store the residual virtual void residualJac(SegPositionMapper& residual,
SegJacRows& jacrows// Here we store the residual
) const=0; ) const=0;
// Get and set name // Get and set name

View File

@ -61,46 +61,7 @@ TaSystem::TaSystem(const pb::System& sys):
} }
} }
// Create the initial solution from the segments and store it initSolRes();
// here. Please be careful not to call any virtual functions!
vus ndofs = getNDofs();
vus dofend = arma::cumsum(ndofs);
us total_dofs = arma::sum(ndofs);
vd solution = vd(total_dofs);
us i=0;
const Segment* seg;
for(auto& seg_: _segs) {
seg = seg_.second;
if(ndofs(i)>0) {
if(i==0) {
solution.subvec(0,ndofs(0)-1) =
seg->initialSolution();
}
else {
solution.subvec(dofend(i-1),dofend(i)-1) =
seg->initialSolution();
}
i++;
}
}
// TODO: work directly on the final solution array
_solution = solution;
// Copy solution vector, if valid
// const auto& sol = sys.solution();
// us size = sol.size(), i=0;
// if(size>0) {
// _solution = vd(size);
// for(auto& val: sol) {
// _solution(i) = val;
// i++;
// }
// }
} }
TaSystem::TaSystem(const TaSystem& o): TaSystem::TaSystem(const TaSystem& o):
@ -117,7 +78,89 @@ TaSystem::TaSystem(const TaSystem& o):
seg.second = seg.second->copy(*this); seg.second = seg.second->copy(*this);
if(!seg.second) throw TaSMETBadAlloc(); if(!seg.second) throw TaSMETBadAlloc();
} }
initSolRes();
} }
void TaSystem::initSolRes() {
// Create the initial solution from the segments and store it
// here. Please be careful not to call any virtual functions!
vus ndofs = getNDofs();
vus neqs = getNEqs();
us Ns = this->Ns();
us total_dofs = arma::sum(ndofs);
us total_eqs = arma::sum(neqs);
if(total_dofs != total_eqs) {
throw TaSMETError("Number of equations does not match number of DOFS. "
"This is probably due to missing, or too much boundary conditions");
}
if(total_dofs*Ns > constants::maxndofs) {
stringstream error;
error << "Too many DOFS required. Problem too large. "
"Number of equations computed: ";
error << total_dofs*Ns;
throw TaSMETError(error);
}
_solution = zeros(total_dofs*Ns);
_residual = zeros(total_dofs*Ns)+arma::datum::nan;
d* solptr = _solution.memptr();
d* resptr = _residual.memptr();
us globalposdof=0;
us globalposeq=0;
// Loop over the segments, and for each dof in the segment, we
// allocate a vd, with its memory pointed to from the global
// solution pointer.
for(auto& seg_: _segs) {
us segid = seg_.first;
VARTRACE(15,segid);
for(us i=0;i< ndofs(segid); i++) {
// false: own memory (means the vd does not own the memory
// true: strict true means bound to the memory for its
// lifetime. Does not allow memory adjustments.
_solution_dofs[segid].push_back(new vd(&solptr[globalposdof*Ns],Ns,false,true));
globalposdof++;
VARTRACE(15,i);
}
// Set the initial solution
if(ndofs(segid)>0)
seg_.second->initialSolution(_solution_dofs.at(segid));
for(us i=0;i< neqs(segid); i++) {
// false: own memory (means the vd does not own the memory
// true: strict true means bound to the memory for its
// lifetime. Does not allow memory adjustments.
_residual_eqs[segid].push_back(new vd(&resptr[globalposeq*Ns],Ns,false,true));
_jac[segid].push_back(JacRow());
globalposeq++;
}
}
tasmet_assert(globalposdof == total_dofs,"Bug in assigning solution vectors");
tasmet_assert(globalposeq == total_eqs,"Bug in assigning residual vectors");
}
int TaSystem::getArbitrateMassEq(const vus& neqs) const { int TaSystem::getArbitrateMassEq(const vus& neqs) const {
// Tells the TaSystem which Dof should be overwritten with the // Tells the TaSystem which Dof should be overwritten with the
// mass arbitration equation. The special value of -1 means, that // mass arbitration equation. The special value of -1 means, that
@ -156,57 +199,86 @@ int TaSystem::getArbitrateMassEq(const vus& neqs) const {
void TaSystem::residualJac(ResidualJac& resjac) const { void TaSystem::residualJac(ResidualJac& resjac) const {
TRACE(15,"TaSystem::residual()"); TRACE(15,"TaSystem::residual()");
vus neqs = getNEqs(); int arbitrateMassEq = -1;//getArbitrateMassEq(neqs);
us total_neqs = arma::sum(neqs);
if(total_neqs>constants::maxndofs) {
stringstream error;
error << "Too many DOFS required. Problem too large. Number of equations computed: ";
error << total_neqs;
throw TaSMETError(error);
}
// This vector of indices stores the last equation number + 1 for
// each equation set in a Segment
vus eqsend = arma::cumsum(neqs);
int arbitrateMassEq = getArbitrateMassEq(neqs);
// This is the mass in the sytem. Only counted when there is an // This is the mass in the sytem. Only counted when there is an
// equation present which arbitrates the mass in the system // equation present which arbitrates the mass in the system
d mass=0; d mass=0;
VARTRACE(25,total_neqs);
VARTRACE(25,eqsend);
vd residual(total_neqs);
#ifdef TASMET_DEBUG #ifdef TASMET_DEBUG
residual = arma::datum::nan*ones(total_neqs); _residual *= arma::datum::nan;
#endif #endif
us i=0; us segid;
const Segment* seg; const Segment* seg;
for(auto seg_: _segs) { for(auto seg_: _segs) {
segid = seg_.first;
seg = seg_.second; seg = seg_.second;
if(i==0) {
// Put the residual of the segment in the over-all residual // Put the residual of the segment in the over-all residual
seg->residualJac(residual.subvec(0,eqsend(0)-1)); seg->residualJac(_residual_eqs[segid],_jac[segid]);
}
else {
seg->residualJac(residual.subvec(eqsend(i-1),eqsend(i)-1));
}
// Count the mass, add it // Count the mass, add it
if(arbitrateMassEq!=-1) { if(arbitrateMassEq!=-1) {
mass += seg->getMass(); mass += seg->getMass();
} }
i++;
} }
us Ns = this->Ns();
vus neqs = getNEqs();
vus neqs_sum = arma::cumsum(neqs);
us total_neqs = arma::sum(neqs);
vus ndofs = getNDofs();
vus ndofs_sum = arma::cumsum(ndofs);
us total_ndofs = arma::sum(ndofs);
// Now we create the triplets
TripletList triplets(total_neqs*Ns);
for(auto& segjacrows : _jac) {
// The current segment number
us segno = segjacrows.first;
// The row offset for this segment
us rowoffset = (segno==0) ? 0 : neqs_sum(segno-1)*Ns;
for(auto& jacrow: segjacrows.second) {
for(auto& jaccol : jacrow) {
const PosId& id = jaccol.first;
us coloffset = (id.segno==0) ? 0 : ndofs_sum(segno-1)*Ns;
coloffset += id.segoffset;
coloffset*=Ns;
dmat& colmat = jaccol.second;
for(us i=0;i<Ns;i++){
us row = rowoffset + i;
for(us j=0;j<Ns;j++) {
us col = coloffset + j;
triplets.addTriplet(Triplet(row,col,colmat(i,j)));
}
}
} // For loop over the columns
rowoffset += Ns;
} // For loop over the rows for this segment
} // For loop over the segments
TRACE(15,"Obtained residual from all Segments"); TRACE(15,"Obtained residual from all Segments");
#ifdef TASMET_DEBUG #ifdef TASMET_DEBUG
@ -214,48 +286,33 @@ void TaSystem::residualJac(ResidualJac& resjac) const {
#endif // TASMET_DEBUG #endif // TASMET_DEBUG
// Exchange equation if we need to arbitrate mass // Exchange equation if we need to arbitrate mass
if(arbitrateMassEq!=-1) { if(arbitrateMassEq>=0) {
residual(arbitrateMassEq)=mass - _mass; _residual(arbitrateMassEq)=mass - _mass;
} }
resjac.residual = residual; resjac.jacobian = sdmat(triplets);
resjac.residual = _residual;
} }
vd TaSystem::getSolution() const { vd TaSystem::getSolution() const {
return _solution; return _solution;
} }
const arma::subview_col<d> TaSystem::getSolution(const us seg_id) const { const SegPositionMapper& TaSystem::getSolution(const us seg_id) const {
return _solution_dofs.at(seg_id);
vus ndofs = getNDofs();
vus dofsend = arma::cumsum(ndofs);
VARTRACE(15,dofsend);
VARTRACE(15,seg_id);
if(seg_id == 0) {
VARTRACE(15,_solution.size());
return _solution.subvec(0,dofsend(0)-1);
}
else {
return _solution.subvec(dofsend(seg_id-1),dofsend(seg_id)-1);
}
} }
vus TaSystem::getNDofs() const { vus TaSystem::getNDofs() const {
TRACE(0,"TaSystem::getNDofs()"); TRACE(0,"TaSystem::getNDofs()");
vus Ndofs(_segs.size()); vus Ndofs(_segs.size());
us i=0;
for (auto seg : _segs) { for (auto seg : _segs) {
Ndofs(i)=seg.second->getNDofs(); Ndofs(seg.first)=seg.second->getNDofs();
i++;
} }
return Ndofs; return Ndofs;
} }
vus TaSystem::getNEqs() const { vus TaSystem::getNEqs() const {
TRACE(15,"TaSystem::getNEqs()"); TRACE(15,"TaSystem::getNEqs()");
vus Neqs(_segs.size()); vus Neqs(_segs.size());
us i=0;
for (auto seg :_segs) { for (auto seg :_segs) {
Neqs(i)=seg.second->getNEqs(); Neqs(seg.first)=seg.second->getNEqs();
VARTRACE(15,Neqs(i));
i++;
} }
return Neqs; return Neqs;
} }
@ -341,7 +398,19 @@ TaSystem::~TaSystem() {
cleanup(); cleanup();
} }
void TaSystem::cleanup() { void TaSystem::cleanup() {
purge(_segs); purge(_segs);
for(auto& segdofs: _solution_dofs) {
for(vd* dof : segdofs.second){
delete dof;
}
}
for(auto& segdofs: _residual_eqs) {
for(vd* dof : segdofs.second){
delete dof;
}
}
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View File

@ -11,9 +11,9 @@
#include "system.h" #include "system.h"
#include "globalconf.h" #include "globalconf.h"
#include "triplets.h" #include "triplets.h"
#include "jacobian.h"
#include "gas.h" #include "gas.h"
#include "protobuf/system.pb.h" #include "protobuf/system.pb.h"
#include <map>
#include <memory> #include <memory>
class Segment; class Segment;
@ -28,21 +28,48 @@ protected:
std::unique_ptr<Gas> _gas; std::unique_ptr<Gas> _gas;
mutable
vd _solution; /**< This column vector stores the vd _solution; /**< This column vector stores the
current solution. */ current solution. */
std::vector<vd> _dofs; /**< This vector of column vectors mutable
GlobalPositionMapper _solution_dofs; /**< This vector of column vectors
stores columns of doubles that do stores columns of doubles that do
not own the memory they point not own the memory they point
to. Instead, they use the memory of to. Instead, they use the memory of
_solution.*/ _solution.*/
vus _startdof; // Store the start DOFS for each segment
mutable
vd _residual;
mutable
GlobalPositionMapper _residual_eqs;
mutable
Jacobian _jac; /**< Storing the Jacobian brings the
hugh benefit of only allocating the
memory once. Once this is done, all
values can be updated during each
iteration. */
TaSystem& operator=(const TaSystem& other)=delete; TaSystem& operator=(const TaSystem& other)=delete;
private:
/**
* Initializes the _solution and _solution_dofs vector. This
* should be done during construction of the object. The method is
* called from all constructors. Should be done after construction
* of the segments.
*
*/
void initSolRes();
public: public:
TaSystem(const TaSystem& o); TaSystem(const TaSystem& o);
TaSystem(const pb::System&); TaSystem(const pb::System&);
static TaSystem testSystem() { static TaSystem testSystem() {
return TaSystem(pb::System::default_instance()); return TaSystem(pb::System::default_instance());
} }
@ -67,7 +94,7 @@ public:
vd getSolution() const; vd getSolution() const;
// Obtain the solution vector for the Segment with given id // Obtain the solution vector for the Segment with given id
const arma::subview_col<d> getSolution(const us seg_id) const; const SegPositionMapper& getSolution(const us seg_id) const;
virtual void updateSolution(const vd& sol) {_solution = sol; } // Update the solution virtual void updateSolution(const vd& sol) {_solution = sol; } // Update the solution