538 lines
15 KiB
C++
538 lines
15 KiB
C++
// tasystem.cpp
|
|
//
|
|
// last-edit-by: J.A. de Jong
|
|
//
|
|
// Description:
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
#define TRACERPLUS (-10)
|
|
#include "segment.h"
|
|
#include "tasystem.h"
|
|
#include "triplets.h"
|
|
#include "jacobian.h"
|
|
#include "tasmet_utils.h"
|
|
#include "tasmet_assert.h"
|
|
#include "tasmet_exception.h"
|
|
#include "tasmet_constants.h"
|
|
#include "duct.h"
|
|
#include "ductbc.h"
|
|
|
|
#include <hdf5.h>
|
|
// Lite version of the library
|
|
#include <hdf5_hl.h>
|
|
|
|
TaSystem::TaSystem(const pb::System& sys):
|
|
GlobalConf(sys.nf(),sys.freq())
|
|
{
|
|
TRACE(14,"TaSystem::TaSystem()");
|
|
|
|
if(sys.systemtype() != pb::TaSystem) {
|
|
throw TaSMETError("Invalid system type for TaSystem");
|
|
}
|
|
|
|
// Checking parameters T0 and p0 also happens in newGas method
|
|
_gas = std::unique_ptr<Gas>(Gas::newGas(sys.gastype(),sys.t0(),sys.p0()));
|
|
if(!_gas) throw TaSMETBadAlloc();
|
|
|
|
// Create all ducts
|
|
for(const auto& d : sys.ducts()) {
|
|
// d.first: id
|
|
// d.second: duct description
|
|
try {
|
|
_segs[d.first] = new Duct(*this,d.first,d.second);
|
|
if(!_segs[d.first]) throw TaSMETBadAlloc();
|
|
}
|
|
catch(TaSMETError e) {
|
|
// Cleanup the already successfully created Ducts and rethrow
|
|
cleanup();
|
|
throw e;
|
|
}
|
|
}
|
|
// Create all ductbcs
|
|
for(const auto& d : sys.ductbcs()) {
|
|
// d.first: id
|
|
// d.second: duct description
|
|
try {
|
|
_segs[d.first] = DuctBc::newDuctBc(d.first,
|
|
*this,
|
|
d.second);
|
|
|
|
if(!_segs[d.first]) throw TaSMETBadAlloc();
|
|
}
|
|
catch(TaSMETError e) {
|
|
// Cleanup the already successfully created Ducts and rethrow
|
|
cleanup();
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
initSolRes();
|
|
|
|
}
|
|
TaSystem::TaSystem(const TaSystem& o):
|
|
GlobalConf(o), // Share a ptr to the Global conf
|
|
_gas(o._gas->copy())
|
|
{
|
|
TRACE(25,"TaSystem::TaSystem(TaSystem&) copy");
|
|
|
|
// Take over the pointers to the segments
|
|
_segs = o._segs;
|
|
|
|
for(auto& seg: _segs){
|
|
seg.second = seg.second->copy(*this);
|
|
if(!seg.second) throw TaSMETBadAlloc();
|
|
}
|
|
|
|
initSolRes(); // This empties the
|
|
// solution. Therefore we copy it
|
|
// after this phase.
|
|
|
|
_solution = o._solution;
|
|
|
|
}
|
|
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 {
|
|
// Tells the TaSystem which Dof should be overwritten with the
|
|
// mass arbitration equation. The special value of -1 means, that
|
|
// mass is not arbitrated. This can be the case if for example a
|
|
// pressure boundary condition is present.
|
|
int arbitrateMassEq=-1;
|
|
bool masseq_found = false;
|
|
|
|
us total_neqs = 0, i=0;
|
|
|
|
// Iterate over all segments to find a single one segment Dof that is
|
|
// willing to arbitrate the mass in the system. This can, of
|
|
// course, be only be one segment.
|
|
int local_mass_eq = -1;
|
|
|
|
const Segment* seg;
|
|
for(auto& seg_: _segs) {
|
|
seg = seg_.second;
|
|
|
|
if(masseq_found && (local_mass_eq = seg->arbitrateMassEq())) {
|
|
throw TaSMETError("Error: only one Segment is allowed to"
|
|
" arbitrate the system mass");
|
|
}
|
|
else if(local_mass_eq != -1){
|
|
|
|
arbitrateMassEq = total_neqs+local_mass_eq;
|
|
|
|
masseq_found = true;
|
|
}
|
|
|
|
total_neqs += neqs(i);
|
|
i++;
|
|
}
|
|
return arbitrateMassEq;
|
|
}
|
|
void TaSystem::residualJac(ResidualJac& resjac) const {
|
|
TRACE(15,"TaSystem::residual()");
|
|
|
|
int arbitrateMassEq = -1;//getArbitrateMassEq(neqs);
|
|
|
|
// This is the mass in the sytem. Only counted when there is an
|
|
// equation present which arbitrates the mass in the system
|
|
d mass=0;
|
|
|
|
#ifdef TASMET_DEBUG
|
|
_residual *= arma::datum::nan;
|
|
#endif
|
|
|
|
us segid;
|
|
const Segment* seg;
|
|
for(auto seg_: _segs) {
|
|
segid = seg_.first;
|
|
seg = seg_.second;
|
|
|
|
// Put the residual of the segment in the over-all residual
|
|
seg->residualJac(_residual_eqs[segid],_jac[segid]);
|
|
|
|
// Count the mass, add it
|
|
if(arbitrateMassEq!=-1) {
|
|
mass += seg->getMass();
|
|
}
|
|
|
|
}
|
|
|
|
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");
|
|
|
|
#ifdef TASMET_DEBUG
|
|
assert(arbitrateMassEq< (int) total_neqs);
|
|
#endif // TASMET_DEBUG
|
|
|
|
// Exchange equation if we need to arbitrate mass
|
|
if(arbitrateMassEq>=0) {
|
|
_residual(arbitrateMassEq)=mass - _mass;
|
|
}
|
|
|
|
// Store the Jacobian and the residual
|
|
resjac.jacobian = sdmat(triplets);
|
|
resjac.residual = _residual;
|
|
|
|
}
|
|
vd TaSystem::getSolution() const {
|
|
return _solution;
|
|
}
|
|
void TaSystem::updateSolution(const vd& solution) {
|
|
|
|
if(_solution.size() == solution.size()) {
|
|
_solution = solution;
|
|
}
|
|
else {
|
|
throw TaSMETError("Solution vector size does not match.");
|
|
}
|
|
|
|
}
|
|
const SegPositionMapper& TaSystem::getSolution(const us seg_id) const {
|
|
return _solution_dofs.at(seg_id);
|
|
}
|
|
|
|
vus TaSystem::getNDofs() const {
|
|
TRACE(0,"TaSystem::getNDofs()");
|
|
vus Ndofs(_segs.size());
|
|
for (auto seg : _segs) {
|
|
Ndofs(seg.first)=seg.second->getNDofs();
|
|
}
|
|
return Ndofs;
|
|
}
|
|
vus TaSystem::getNEqs() const {
|
|
TRACE(15,"TaSystem::getNEqs()");
|
|
vus Neqs(_segs.size());
|
|
for (auto seg :_segs) {
|
|
Neqs(seg.first)=seg.second->getNEqs();
|
|
}
|
|
return Neqs;
|
|
}
|
|
void TaSystem::show(us detailnr){
|
|
TRACE(25,"TaSystem::show()");
|
|
|
|
cout << "########################## Showing TaSystem...\n";
|
|
cout << "Showing Global configuration...\n";
|
|
GlobalConf::show();
|
|
|
|
if(detailnr>0){
|
|
for(auto seg:_segs){
|
|
cout << "Showing segment with ID " << seg.first << "\n";
|
|
seg.second->show(detailnr);
|
|
}
|
|
} // detailnr>0
|
|
}
|
|
// TripletList TaSystem::jacTriplets() const {
|
|
|
|
// TRACE(14,"TaSystem::jacobian()");
|
|
// vus ndofs=getNDofs();
|
|
// vus neqs=getNEqs();
|
|
// us total_ndofs = arma::sum(ndofs);
|
|
// us total_neqs = arma::sum(neqs);
|
|
// us i=0,dofnr=0,eqnr=0;
|
|
// Jacobian j(total_ndofs);
|
|
|
|
// int arbitrateMassEq = getArbitrateMassEq(neqs);
|
|
// vd dmtotdx;
|
|
// if(arbitrateMassEq > -1)
|
|
// dmtotdx = vd(total_ndofs);
|
|
|
|
// const Segment* seg;
|
|
// for(auto& seg_ :_segs) {
|
|
// seg = seg_.second;
|
|
// seg->jac(*this,j,dofnr,eqnr);
|
|
// if(arbitrateMassEq > -1) {
|
|
// seg->dmtotdx(*this,dmtotdx,dofnr);
|
|
// }
|
|
// eqnr += neqs(i);
|
|
// dofnr += ndofs(i);
|
|
// i++;
|
|
// }
|
|
|
|
// if(dofnr!=eqnr){
|
|
// throw TaSMETError("System of equations is over/underdetermined");
|
|
// }
|
|
|
|
// // Convert to tripletlist
|
|
// TripletList jac=j;
|
|
|
|
// assert(arbitrateMassEq< (int) total_neqs);
|
|
|
|
// // Exchange equation if we need to arbitrate mass
|
|
// if(arbitrateMassEq!=-1) {
|
|
// // Replace this equation with global mass conservation
|
|
// jac.zeroOutRow(arbitrateMassEq);
|
|
|
|
// us dmtotdxsize=dmtotdx.size();
|
|
// for(us k=0;k<dmtotdxsize;k++)
|
|
// if(dmtotdx(k)!=0){
|
|
// // TRACE(20,"k: " << k);
|
|
// // TRACE(20,"dmtotdx:"<< dmtotdx(k));
|
|
// jac.addTriplet(Triplet(arbitrateMassEq,k,dmtotdx(k)));
|
|
// }
|
|
// }
|
|
|
|
// return jac;
|
|
// }
|
|
// void TaSystem::resetHarmonics(){
|
|
// for(auto seg: _segs) {
|
|
// seg.second->resetHarmonics();
|
|
// }
|
|
// }
|
|
// dmat TaSystem::showJac(){
|
|
|
|
// TRACE(15,"TaSystem::showJac()");
|
|
|
|
// return dmat(jacobian());
|
|
// }
|
|
TaSystem::~TaSystem() {
|
|
TRACE(25,"~TaSystem()");
|
|
cleanup();
|
|
}
|
|
void TaSystem::cleanup() {
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void TaSystem::exportHDF5(const string& filename) const {
|
|
|
|
TRACE(15,"TaSystem::exportHDF5");
|
|
hid_t file_id;
|
|
herr_t status;
|
|
// hid_t error_stack = H5Eget_current_stack();
|
|
|
|
/* Save old error handler */
|
|
// herr_t (*old_func)(void*);
|
|
// void *old_client_data;
|
|
|
|
// H5Eget_auto(error_stack, &old_func, &old_client_data);
|
|
/* Turn off error handling */
|
|
// H5Eset_auto(error_stack, NULL, NULL);
|
|
// H5Eset_auto( NULL, NULL);
|
|
|
|
file_id = H5Fcreate (filename.c_str(),
|
|
H5F_ACC_TRUNC, // Truncate any existing file
|
|
H5P_DEFAULT,
|
|
H5P_DEFAULT);
|
|
|
|
/* Restore previous error handler */
|
|
// H5Eset_auto(error_stack, old_func, old_client_data); /// Disable HDF5 error printing
|
|
|
|
if(file_id < 0)
|
|
throw TaSMETError("Creation of export file failed. This is probably "
|
|
"because the postprocessor is already running. "
|
|
"However, this could also mean that the file is blocked "
|
|
"by another program, or you have insufficient "
|
|
"privileges to create the postprocessing file."
|
|
);
|
|
|
|
/// Enable HDF5 error printing
|
|
d freq = getfreq();
|
|
H5LTset_attribute_double(file_id,
|
|
"/",
|
|
"freq",
|
|
&freq,1);
|
|
|
|
|
|
int Ns = this->Ns();
|
|
H5LTset_attribute_int(file_id,
|
|
"/",
|
|
"Ns",
|
|
&Ns,1);
|
|
|
|
vd timeInstances = this->timeInstances();
|
|
H5LTset_attribute_double(file_id,
|
|
"/",
|
|
"t",
|
|
timeInstances.memptr(),
|
|
timeInstances.size());
|
|
|
|
d p0 = _gas->p0();
|
|
H5LTset_attribute_double(file_id,
|
|
"/",
|
|
"p0",
|
|
&p0,1);
|
|
|
|
d T0 = _gas->T0();
|
|
H5LTset_attribute_double(file_id,
|
|
"/",
|
|
"T0",
|
|
&T0,1);
|
|
|
|
|
|
|
|
const string& gasName = _gas->gasName();
|
|
H5LTset_attribute_string(file_id,
|
|
"/",
|
|
"gas",
|
|
gasName.c_str());
|
|
|
|
for(const auto& seg_ : _segs) {
|
|
// Create a group for each segment
|
|
string group = string("/") + std::to_string(seg_.first);
|
|
VARTRACE(15,group);
|
|
hid_t grp_id = H5Gcreate(file_id,
|
|
group.c_str(),
|
|
H5P_DEFAULT,
|
|
H5P_DEFAULT,
|
|
H5P_DEFAULT);
|
|
|
|
if(grp_id < 0)
|
|
throw TaSMETError("Group creation failed");
|
|
|
|
|
|
seg_.second->exportHDF5(grp_id);
|
|
|
|
// Close the group
|
|
H5Gclose(grp_id);
|
|
|
|
}
|
|
|
|
status = H5Fclose(file_id);
|
|
H5Eprint(status,0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|