SundanceInhomogeneousNodalDOFMap.cpp
Go to the documentation of this file.
00001 /* @HEADER@ */
00002 // ************************************************************************
00003 // 
00004 //                              Sundance
00005 //                 Copyright (2005) Sandia Corporation
00006 // 
00007 // Copyright (year first published) Sandia Corporation.  Under the terms 
00008 // of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government 
00009 // retains certain rights in this software.
00010 // 
00011 // This library is free software; you can redistribute it and/or modify
00012 // it under the terms of the GNU Lesser General Public License as
00013 // published by the Free Software Foundation; either version 2.1 of the
00014 // License, or (at your option) any later version.
00015 //  
00016 // This library is distributed in the hope that it will be useful, but
00017 // WITHOUT ANY WARRANTY; without even the implied warranty of
00018 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019 // Lesser General Public License for more details.
00020 //                                                                                 
00021 // You should have received a copy of the GNU Lesser General Public
00022 // License along with this library; if not, write to the Free Software
00023 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00024 // USA                                                                                
00025 // Questions? Contact Kevin Long (krlong@sandia.gov), 
00026 // Sandia National Laboratories, Livermore, California, USA
00027 // 
00028 // ************************************************************************
00029 /* @HEADER@ */
00030 
00031 #include "SundanceMap.hpp"
00032 #include "PlayaTabs.hpp"
00033 #include "SundanceOut.hpp"
00034 #include "SundanceOrderedTuple.hpp"
00035 #include "SundanceInhomogeneousNodalDOFMap.hpp"
00036 #include "SundanceLagrange.hpp"
00037 #include "PlayaMPIContainerComm.hpp"
00038 #include "Teuchos_TimeMonitor.hpp"
00039 
00040 using namespace Sundance;
00041 using namespace Teuchos;
00042 using Playa::MPIComm;
00043 using Playa::MPIContainerComm;
00044 
00045 
00046 InhomogeneousNodalDOFMap
00047 ::InhomogeneousNodalDOFMap(const Mesh& mesh, 
00048   const Array<Map<Set<int>, CellFilter> >& funcSetToDomainMap,
00049   int setupVerb)
00050   : DOFMapBase(mesh, setupVerb),
00051     dim_(mesh.spatialDim()),
00052     basis_(rcp(new Lagrange(1))),
00053     nTotalFuncs_(),
00054     funcDomains_(),
00055     nodeDofs_(),
00056     elemDofs_(),
00057     nodeToFuncSetIndexMap_(mesh.numCells(0)),
00058     elemToFuncSetIndexMap_(mesh.numCells(dim_)),
00059     elemFuncSets_(),
00060     nodalFuncSets_(),
00061     nodeToOffsetMap_(mesh.numCells(0)),
00062     elemToOffsetMap_(mesh.numCells(0)),
00063     funcIndexWithinNodeFuncSet_(),
00064     elemStructure_(),
00065     nodeStructure_()
00066 {
00067   SUNDANCE_MSG1(setupVerb, "in InhomogeneousNodalDOFMap ctor");
00068   SUNDANCE_MSG4(setupVerb, "func set to domain map " << funcSetToDomainMap);
00069 
00070   /* count the total number of functions across all subdomains */
00071   Set<int> allFuncs;
00072   for (int d=dim_; d>=0; d--)
00073   {
00074     for (Map<Set<int>, CellFilter>::const_iterator 
00075            i=funcSetToDomainMap[d].begin(); i!=funcSetToDomainMap[d].end(); i++)
00076     {
00077       allFuncs.merge(i->first);
00078     }
00079   }
00080 
00081   
00082   nTotalFuncs_ = allFuncs.size();
00083   SUNDANCE_MSG2(setupVerb, "found " << nTotalFuncs_ << " functions");
00084   
00085 
00086   /* get flat arrays of subdomains and function arrays */
00087   Array<CellFilter> subdomains;
00088   Array<CellFilter> maxSubdomains;
00089   Array<Array<int> > funcArrays;
00090   Array<Set<int> > funcSets;
00091 
00092   for (int d=dim_; d>=0; d--)
00093   {
00094     for (Map<Set<int>, CellFilter>::const_iterator 
00095            i=funcSetToDomainMap[d].begin(); i!=funcSetToDomainMap[d].end(); i++)
00096     {
00097       subdomains.append(i->second);
00098       if (d==dim_) 
00099       {
00100         elemFuncSets_.append(i->first);
00101         maxSubdomains.append(i->second);
00102       }
00103 
00104       Array<int> myFuncs = i->first.elements();
00105       funcArrays.append(myFuncs);
00106       funcSets.append(i->first);
00107     }
00108   }
00109 
00110 
00111   /* determine the functions appearing at each node */
00112 
00113 
00114 
00115   Array<Array<int> > cellLIDs(subdomains.size());
00116   Array<Array<int> > facetLIDs(subdomains.size());
00117   Array<Array<int> > facetOrientations(subdomains.size());
00118   Array<Set<int> > nodeToFuncSetMap(mesh.numCells(0));
00119 
00120   for (int r=0; r<subdomains.size(); r++)
00121   {
00122     int d = subdomains[r].dimension(mesh);
00123     CellSet cells = subdomains[r].getCells(mesh);
00124     SUNDANCE_MSG4(setupVerb, "domain " << subdomains[r] << " has functions "
00125       << funcSets[r]);
00126       
00127     for (CellIterator c=cells.begin(); c!=cells.end(); c++)
00128     {
00129       int cellLID = *c;
00130       cellLIDs[r].append(cellLID);
00131     }
00132     if (cellLIDs[r].size()==0) continue; 
00133     int nFacets = mesh.numFacets(d, cellLIDs[r][0], 0);
00134     if (d==0)
00135     {
00136       for (int c=0; c<cellLIDs[r].size(); c++)
00137       {
00138         int fLID = cellLIDs[r][c];
00139         nodeToFuncSetMap[fLID].merge(funcSets[r]);
00140       }
00141     }
00142     else
00143     {
00144       Array<int>& facetLID = facetLIDs[r];
00145       facetLID.resize(nFacets*cellLIDs[r].size());
00146       facetOrientations[r].resize(nFacets*cellLIDs[r].size());
00147       mesh.getFacetLIDs(d, cellLIDs[r], 0, facetLID, facetOrientations[r]);
00148       for (int c=0; c<cellLIDs[r].size(); c++)
00149       {
00150         for (int f=0; f<nFacets; f++)
00151         {
00152           int fLID = facetLID[c*nFacets+f];
00153           nodeToFuncSetMap[fLID].merge(funcSets[r]);
00154         }
00155       }
00156     }
00157   }
00158 
00159   /* find the distinct combinations of functions at the nodes */
00160 
00161   Map<Set<int>, int> fsToFSIndexMap;
00162   Array<int> funcSetNodeCounts;
00163   int nNodes = mesh.numCells(0);
00164 
00165   for (int n=0; n<nNodes; n++)
00166   {
00167     const Set<int>& f = nodeToFuncSetMap[n];
00168     int funcComboIndex;
00169     if (!fsToFSIndexMap.containsKey(f)) 
00170     {
00171       funcComboIndex = nodalFuncSets_.size();
00172       fsToFSIndexMap.put(f, funcComboIndex);
00173       nodalFuncSets_.append(f);
00174       funcSetNodeCounts.append(1);
00175       nodeToOffsetMap_[n] = 0;
00176     }
00177     else
00178     {
00179       funcComboIndex = fsToFSIndexMap.get(f);
00180       nodeToOffsetMap_[n] = f.size() * funcSetNodeCounts[funcComboIndex];
00181       funcSetNodeCounts[funcComboIndex]++;
00182     }
00183     nodeToFuncSetIndexMap_[n] = funcComboIndex;
00184   }
00185 
00186   SUNDANCE_MSG2(setupVerb, "nodal func sets = " << nodalFuncSets_);
00187 
00188   nodeDofs_.resize(nodalFuncSets_.size());
00189   nodeStructure_.resize(nodalFuncSets_.size());
00190   funcIndexWithinNodeFuncSet_.resize(nodalFuncSets_.size());
00191 
00192   for (int f=0; f<nodalFuncSets_.size(); f++)
00193   {
00194     Array<int> funcs = nodalFuncSets_[f].elements();
00195     int nFuncs = funcs.size();
00196     funcIndexWithinNodeFuncSet_[f].resize(nTotalFuncs_, -1);
00197     for (int i=0; i<nFuncs; i++)
00198     {
00199       funcIndexWithinNodeFuncSet_[f][funcs[i]] = i;
00200     }
00201     nodeDofs_[f].resize(nFuncs * funcSetNodeCounts[f], -1);
00202     Array<RCP<BasisDOFTopologyBase> > nodeBases = tuple(basis_);
00203     Array<Array<int> > nodeFuncs = tuple(nodalFuncSets_[f].elements());
00204     nodeStructure_[f] = rcp(new MapStructure(nTotalFuncs_, 
00205         nodeBases, nodeFuncs));
00206   }
00207 
00208 
00209 
00210   /* second pass to assign node DOFs */
00211   int nextDOF = 0;
00212   Array<int> hasProcessedNode(nNodes);
00213   Array<Array<int> > remoteNodes(mesh.comm().getNProc());
00214 
00215   for (int r=0; r<subdomains.size(); r++)
00216   {
00217     int d = subdomains[r].dimension(mesh);
00218 
00219     if (cellLIDs[r].size()==0) continue; 
00220 
00221     if (d==0)
00222     {
00223       for (int c=0; c<cellLIDs[r].size(); c++)
00224       {
00225         int fLID = cellLIDs[r][c];
00226         int funcSetIndex = nodeToFuncSetIndexMap_[fLID];
00227         int nFuncs = nodalFuncSets_[funcSetIndex].size();
00228         int dofOffset = nodeToOffsetMap_[fLID];
00229         if (!hasProcessedNode[fLID])
00230         {
00231           assignNode(fLID, funcSetIndex, dofOffset, nFuncs,
00232             remoteNodes, hasProcessedNode, nextDOF);
00233         }
00234       }
00235     }
00236     else
00237     {
00238       Array<int>& facetLID = facetLIDs[r];
00239       int nFacets = mesh.numFacets(d, cellLIDs[r][0], 0);      
00240       for (int c=0; c<cellLIDs[r].size(); c++)
00241       {
00242         for (int f=0; f<nFacets; f++)
00243         {
00244           int fLID = facetLID[c*nFacets+f];
00245           int funcSetIndex = nodeToFuncSetIndexMap_[fLID];
00246           int dofOffset = nodeToOffsetMap_[fLID];
00247           int nFuncs = nodalFuncSets_[funcSetIndex].size();
00248           if (!hasProcessedNode[fLID])
00249           {
00250             assignNode(fLID, funcSetIndex, dofOffset, nFuncs,
00251               remoteNodes, hasProcessedNode, nextDOF);
00252           }
00253         }
00254       }
00255     }
00256   }
00257 
00258 
00259   /* Compute offsets for each processor */
00260   int localCount = nextDOF;
00261   computeOffsets(localCount);
00262   
00263   /* Resolve remote DOF numbers */
00264   shareRemoteDOFs(remoteNodes);
00265 
00266   /* Build dof tables for elements */
00267   elemDofs_.resize(maxSubdomains.size());
00268   int count = 0;
00269   Array<Array<int> > elemFuncArrays;
00270   
00271   funcDomains_.resize(nTotalFuncs_);
00272 
00273   for (int r=0; r<subdomains.size(); r++)
00274   {
00275     int d = subdomains[r].dimension(mesh);
00276     if (d != dim_) continue;
00277     if (cellLIDs[r].size()==0) continue; 
00278     int nFacets = mesh.numFacets(d, cellLIDs[r][0], 0);      
00279     Array<int>& facetLID = facetLIDs[r];
00280     Array<Array<int> > dofs(1);
00281     dofs[0].resize(funcArrays[r].size() * cellLIDs[r].size() * nFacets);
00282     getFunctionDofs(d, cellLIDs[r], facetLID, funcArrays[r], dofs);
00283     elemDofs_[count] = dofs[0];
00284     elemFuncArrays.append(funcArrays[r]);
00285     Array<RCP<BasisDOFTopologyBase> > elemBases = tuple(basis_);
00286     Array<Array<int> > elemFuncs = tuple(elemFuncSets_[count].elements());
00287     elemStructure_.append(rcp(new MapStructure(nTotalFuncs_, 
00288           elemBases, elemFuncs)));
00289     for (int c=0; c<cellLIDs[r].size(); c++)
00290     {
00291       elemToFuncSetIndexMap_[cellLIDs[r][c]] = count;
00292     }
00293     for (int i=0; i<elemFuncs[0].size(); i++)
00294     {
00295       funcDomains_[elemFuncs[0][i]] = funcDomains_[elemFuncs[0][i]] + subdomains[r];
00296     }
00297     count++;
00298   }
00299   
00300 }
00301 
00302 
00303 void InhomogeneousNodalDOFMap::getFunctionDofs(int cellDim,
00304   const Array<int>& cellLID,
00305   const Array<int>& facetLID,
00306   const Array<int>& funcs,
00307   Array<Array<int> >& dofs) const
00308 {
00309   Array<int>& dofChunk = dofs[0];
00310   dofChunk.clear();
00311 
00312   if (cellDim != 0)
00313   {
00314     int nFacets = mesh().numFacets(cellDim, cellLID[0], 0);
00315     dofChunk.resize(funcs.size() * cellLID.size() * nFacets, -1);
00316       
00317       
00318     for (int c=0; c<cellLID.size(); c++)
00319     {
00320       for (int f=0; f<nFacets; f++)
00321       {
00322         int fLID = facetLID[c*nFacets + f];
00323         int fci = nodeToFuncSetIndexMap_[fLID];
00324         int nodeOffset = nodeToOffsetMap_[fLID];
00325         for (int i=0; i<funcs.size(); i++)
00326         {
00327           int funcIndex = funcIndexWithinNodeFuncSet_[fci][funcs[i]];
00328           if (funcIndex >= 0)
00329           {
00330             dofChunk[(c*funcs.size()+i)*nFacets + f] 
00331             = nodeDofs_[fci][nodeOffset+funcIndex];
00332         
00333           }
00334         }
00335       }
00336     }
00337   }
00338   else
00339   {
00340     dofChunk.resize(funcs.size() * cellLID.size(), -1);
00341 
00342     for (int c=0; c<cellLID.size(); c++)
00343     {
00344       int fci = nodeToFuncSetIndexMap_[cellLID[c]];
00345       int nodeOffset = nodeToOffsetMap_[cellLID[c]];
00346       for (int i=0; i<funcs.size(); i++)
00347       {
00348         int funcIndex = funcIndexWithinNodeFuncSet_[fci][funcs[i]];
00349         if (funcIndex >= 0)
00350         {
00351           dofChunk[c*funcs.size()+ i] 
00352             = nodeDofs_[fci][nodeOffset+funcIndex];
00353         }
00354       }
00355     }
00356   }
00357 }
00358 
00359 void InhomogeneousNodalDOFMap::assignNode(int fLID,
00360   int funcSetIndex,
00361   int dofOffset,
00362   int nFuncs,
00363   Array<Array<int> >& remoteNodes,
00364   Array<int>& hasProcessedCell,
00365   int& nextDOF)
00366 {
00367   /* the facet may be owned by another processor */
00368   int owner;
00369   if (isRemote(0, fLID, owner))
00370   {
00371     int facetGID 
00372       = mesh().mapLIDToGID(0, fLID);
00373     remoteNodes[owner].append(facetGID);
00374       
00375   }
00376   else /* we can assign a DOF locally */
00377   {
00378     /* assign DOFs */
00379     Array<int>& myDofs = nodeDofs_[funcSetIndex];
00380     for (int i=0; i<nFuncs; i++)
00381     {
00382       myDofs[dofOffset + i] = nextDOF;
00383       nextDOF++;
00384     }
00385   }
00386   hasProcessedCell[fLID] = 1;
00387 }
00388 
00389 void InhomogeneousNodalDOFMap::computeOffsets(int localCount)
00390 {
00391   TEUCHOS_TEST_FOR_EXCEPTION(mesh().comm().getNProc() != 1,
00392     std::runtime_error,
00393     "parallel inhomogeneous DOF maps not yet supported");
00394   
00395   int totalDOFCount = localCount;
00396   int myOffset = 0;
00397   setLowestLocalDOF(myOffset);
00398   setNumLocalDOFs(localCount);
00399   setTotalNumDOFs(totalDOFCount);
00400 }
00401 
00402 void InhomogeneousNodalDOFMap::shareRemoteDOFs(const Array<Array<int> >& remoteNodes)
00403 {
00404   TEUCHOS_TEST_FOR_EXCEPTION(mesh().comm().getNProc() != 1,
00405     std::runtime_error,
00406     "parallel inhomogeneous DOF maps not yet supported");
00407 }
00408 
00409 RCP<const Set<int> >
00410 InhomogeneousNodalDOFMap::allowedFuncsOnCellBatch(int cellDim,
00411   const Array<int>& cellLID) const 
00412 {
00413   Set<int> rtn;
00414 
00415   if (cellDim==0)
00416   {
00417     rtn = nodalFuncSets_[nodeToFuncSetIndexMap_[cellLID[0]]];
00418     for (int c=0; c<cellLID.size(); c++) 
00419     {
00420       const Set<int>& s = nodalFuncSets_[nodeToFuncSetIndexMap_[cellLID[c]]];
00421       rtn = rtn.intersection(s);
00422     }
00423   }
00424   else if (cellDim==dim_)
00425   {
00426     rtn = elemFuncSets_[elemToFuncSetIndexMap_[cellLID[0]]];
00427     for (int c=0; c<cellLID.size(); c++) 
00428     {
00429       const Set<int>& s = elemFuncSets_[elemToFuncSetIndexMap_[cellLID[c]]];
00430       rtn = rtn.intersection(s);
00431     }
00432   }
00433   else
00434   {
00435     Array<int> facetLID;
00436     Array<int> facetOrientations;
00437     mesh().getFacetLIDs(cellDim, cellLID, 0, facetLID, facetOrientations);
00438     rtn = nodalFuncSets_[nodeToFuncSetIndexMap_[facetLID[0]]];
00439     for (int f=0; f<facetLID.size(); f++)
00440     {
00441       const Set<int>& s = nodalFuncSets_[nodeToFuncSetIndexMap_[facetLID[f]]];
00442       rtn = rtn.intersection(s);
00443     }
00444   }
00445   return rcp(new Set<int>(rtn));
00446 }
00447 
00448 
00449 RCP<const MapStructure> 
00450 InhomogeneousNodalDOFMap::getDOFsForCellBatch(int cellDim,
00451   const Array<int>& cellLID,
00452   const Set<int>& requestedFuncSet,
00453   Array<Array<int> >& dofs,
00454   Array<int>& nNodes,
00455   int verb) const 
00456 {
00457   TimeMonitor timer(batchedDofLookupTimer());
00458   Tabs tab0;
00459 
00460   SUNDANCE_MSG2(verb, tab0 << "in InhomNodalDOFMap::getDOFsForCellBatch()");
00461 
00462   if (cellDim==0)
00463   {
00464     Tabs tab1;
00465     SUNDANCE_MSG2(verb, tab1 << "cell dim = " << cellDim);
00466     bool isHomogeneous = true;
00467     int firstFuncSet = nodeToFuncSetIndexMap_[cellLID[0]];
00468     for (int c=0; c<cellLID.size(); c++)
00469     {
00470       if (nodeToFuncSetIndexMap_[cellLID[c]] != firstFuncSet) 
00471       {
00472         isHomogeneous = false;
00473         break;
00474       }
00475     }
00476 
00477     dofs.resize(1);
00478     nNodes.resize(1);
00479     nNodes[0] = 1;
00480     Array<int> dummyFacets;
00481 
00482     if (isHomogeneous)
00483     {
00484       const Set<int>& funcSet = nodalFuncSets_[firstFuncSet];
00485       TEUCHOS_TEST_FOR_EXCEPT(requestedFuncSet.setDifference(funcSet).size() != 0);
00486       Array<int> funcs = funcSet.elements();
00487       getFunctionDofs(cellDim, cellLID, dummyFacets, funcs, dofs);
00488       return nodeStructure_[firstFuncSet];
00489     }
00490     else
00491     {
00492       Array<int> funcs = requestedFuncSet.elements();
00493       getFunctionDofs(cellDim, cellLID, dummyFacets, funcs, dofs);
00494       RCP<const MapStructure> rtn 
00495         = rcp(new MapStructure(nTotalFuncs_, basis_, tuple(funcs)));
00496       return rtn;
00497     }
00498   }
00499   else if (cellDim==dim_)
00500   {
00501     Tabs tab1;
00502     SUNDANCE_MSG2(verb, tab1 << "cell dim = " << cellDim);
00503     bool isHomogeneous = true;
00504     int firstFuncSet = elemToFuncSetIndexMap_[cellLID[0]];
00505     SUNDANCE_MSG2(verb, tab1 << "first func set = " << firstFuncSet);
00506     for (int c=0; c<cellLID.size(); c++)
00507     {
00508       if (elemToFuncSetIndexMap_[cellLID[c]] != firstFuncSet) 
00509       {
00510         isHomogeneous = false;
00511         break;
00512       }
00513     }
00514 
00515     dofs.resize(1);
00516     nNodes.resize(1);
00517     nNodes[0] = mesh().numFacets(cellDim, cellLID[0], 0);
00518 
00519     if (isHomogeneous)
00520     {
00521       Tabs tab2;
00522       Array<int> facetLID;
00523       Array<int> facetOrientations;
00524       mesh().getFacetLIDs(cellDim, cellLID, 0, facetLID, facetOrientations);
00525       if (verb >= 2)
00526       {
00527         Out::os() << tab2 << "cellLID = " << cellLID << std::endl;
00528         Out::os() << tab2 << "facetLID = " << facetLID << std::endl;
00529         Out::os() << tab2 << "elem func sets = " << elemFuncSets_ << std::endl;
00530       }
00531       const Set<int>& funcSet = elemFuncSets_[firstFuncSet];
00532       TEUCHOS_TEST_FOR_EXCEPT(requestedFuncSet.setDifference(funcSet).size() != 0);
00533       Array<int> funcs = funcSet.elements();
00534       getFunctionDofs(cellDim, cellLID, facetLID, funcs, dofs);
00535       return elemStructure_[firstFuncSet];
00536     }
00537     else
00538     {
00539       Array<int> facetLID;
00540       Array<int> facetOrientations;
00541       mesh().getFacetLIDs(cellDim, cellLID, 0, facetLID, facetOrientations);
00542       Array<int> funcs = requestedFuncSet.elements();
00543       getFunctionDofs(cellDim, cellLID, facetLID, funcs, dofs);
00544       RCP<const MapStructure> rtn 
00545         = rcp(new MapStructure(nTotalFuncs_, basis_, tuple(funcs)));
00546       return rtn;
00547     }
00548   }
00549   else
00550   {
00551     Tabs tab1;
00552     SUNDANCE_MSG2(verb, tab1 << "cell dim = " << cellDim);
00553     Array<int> facetLID;
00554     Array<int> facetOrientations;
00555     mesh().getFacetLIDs(cellDim, cellLID, 0, facetLID, facetOrientations);
00556     dofs.resize(1);
00557     nNodes.resize(1);
00558     nNodes[0] = mesh().numFacets(cellDim, cellLID[0], 0);
00559 
00560     Array<int> funcs = requestedFuncSet.elements();
00561 
00562     getFunctionDofs(cellDim, cellLID, facetLID, funcs, dofs);
00563     RCP<const MapStructure> rtn 
00564       = rcp(new MapStructure(nTotalFuncs_, basis_, tuple(funcs)));
00565     return rtn;
00566   }
00567 }
00568 
00569 
00570 
00571 void InhomogeneousNodalDOFMap::print(std::ostream& os) const
00572 {
00573   Tabs tab0;
00574   std::cout << tab0 << "dof map: " << std::endl;
00575   for (int d=0; d<=dim_; d++)
00576   {
00577     Tabs tab1;
00578     std::cout << tab1 << d << "-cells: " << std::endl;
00579     for (int n=0; n<mesh().numCells(d); n++)
00580     {
00581       Tabs tab2;
00582       std::cout << tab2 << "d=" << d << " cell=" << n << std::endl;
00583       RCP<const Set<int> > funcs = allowedFuncsOnCellBatch(d, tuple(n));
00584       for (Set<int>::const_iterator f=funcs->begin(); f!=funcs->end(); f++)
00585       {
00586         Tabs tab3;
00587         Array<int> dofs;
00588         getDOFsForCell(d, n, *f, dofs);
00589         std::cout << tab3 << " f=" << *f << " dofs=" << dofs << std::endl;
00590       }
00591     }
00592   }
00593 }

Site Contact