From 0136deb88e007e5034ff403f46897e36ff0f3bc5 Mon Sep 17 00:00:00 2001 From: mattijs Date: Tue, 22 May 2012 17:34:03 +0100 Subject: [PATCH 01/10] ENH: extrudeMesh: make edge collape optional --- .../extrude/extrudeMesh/extrudeMesh.C | 22 ++++++++++++++++++- .../extrude/extrudeMesh/extrudeMeshDict | 4 ++++ .../system/extrudeMeshDict | 12 +++++++--- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMesh.C b/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMesh.C index 6cd54bb461..1f72f1d221 100644 --- a/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMesh.C +++ b/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMesh.C @@ -238,6 +238,25 @@ int main(int argc, char *argv[]) dict.lookup("constructFrom") ); + // Any merging of small edges + const scalar mergeTol(dict.lookupOrDefault("mergeTol", 1e-4)); + + Info<< "Extruding from " << ExtrudeModeNames[mode] + << " using model " << model().type() << endl; + if (flipNormals) + { + Info<< "Flipping normals before extruding" << endl; + } + if (mergeTol > 0) + { + Info<< "Collapsing edges < " << mergeTol << " of bounding box" << endl; + } + else + { + Info<< "Not collapsing any edges after extrusion" << endl; + } + Info<< endl; + // Generated mesh (one of either) autoPtr meshFromMesh; @@ -715,7 +734,7 @@ int main(int argc, char *argv[]) const boundBox& bb = mesh.bounds(); const vector span = bb.span(); - const scalar mergeDim = 1e-4 * bb.minDim(); + const scalar mergeDim = mergeTol * bb.minDim(); Info<< "Mesh bounding box : " << bb << nl << " with span : " << span << nl @@ -726,6 +745,7 @@ int main(int argc, char *argv[]) // Collapse edges // ~~~~~~~~~~~~~~ + if (mergeDim > 0) { Info<< "Collapsing edges < " << mergeDim << " ..." << nl << endl; diff --git a/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMeshDict b/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMeshDict index e075acbc4e..ecbc647797 100644 --- a/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMeshDict +++ b/applications/utilities/mesh/generation/extrude/extrudeMesh/extrudeMeshDict @@ -88,4 +88,8 @@ sigmaRadialCoeffs // degree wedges. mergeFaces false; //true; +// Merge small edges. Fraction of bounding box. +mergeTol 0; + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/tutorials/incompressible/pimpleDyMFoam/wingMotion/wingMotion2D_simpleFoam/system/extrudeMeshDict b/tutorials/incompressible/pimpleDyMFoam/wingMotion/wingMotion2D_simpleFoam/system/extrudeMeshDict index b695ab7e5d..0bcac7b5ce 100644 --- a/tutorials/incompressible/pimpleDyMFoam/wingMotion/wingMotion2D_simpleFoam/system/extrudeMeshDict +++ b/tutorials/incompressible/pimpleDyMFoam/wingMotion/wingMotion2D_simpleFoam/system/extrudeMeshDict @@ -10,12 +10,14 @@ FoamFile version 2.0; format ascii; class dictionary; - object extrudeProperties; + object extrudeMeshDict; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // What to extrude: // patch : from patch of another case ('sourceCase') +// mesh : as above but with original case included +// surface : from externally read surface constructFrom patch; sourceCase "../wingMotion_snappyHexMesh"; @@ -24,7 +26,8 @@ sourcePatches (front); // If construct from patch: patch to use for back (can be same as sourcePatch) exposedPatchName back; -// Flip surface normals before usage. +// Flip surface normals before usage. Valid only for extrude from surface or +// patch. flipNormals false; //- Linear extrusion in point-normal direction @@ -41,7 +44,10 @@ linearNormalCoeffs // Do front and back need to be merged? Usually only makes sense for 360 // degree wedges. -mergeFaces false; +mergeFaces false; //true; + +// Merge small edges. Fraction of bounding box. +mergeTol 0; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // From 46764219625a07b70ea7f3f6c38123b3856f07ee Mon Sep 17 00:00:00 2001 From: mattijs Date: Tue, 22 May 2012 17:34:37 +0100 Subject: [PATCH 02/10] ENH: foamFormatConvert: convert cvMesh's internalDelaunayVertices --- .../foamFormatConvert/foamFormatConvert.C | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/applications/utilities/miscellaneous/foamFormatConvert/foamFormatConvert.C b/applications/utilities/miscellaneous/foamFormatConvert/foamFormatConvert.C index 6f3f040a0e..8104f9e7c3 100644 --- a/applications/utilities/miscellaneous/foamFormatConvert/foamFormatConvert.C +++ b/applications/utilities/miscellaneous/foamFormatConvert/foamFormatConvert.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation + \\ / A nd | Copyright (C) 2011-2012 OpenFOAM Foundation \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -206,6 +206,14 @@ int main(int argc, char *argv[]) runTime ); + // cvMesh vertices + writeMeshObject + ( + "internalDelaunayVertices", + regionPrefix, + runTime + ); + if (runTime.writeFormat() == IOstream::ASCII) { // Only do zones when converting from binary to ascii From cb30c8389299a70575bcffcc95a0ddccda3f4437 Mon Sep 17 00:00:00 2001 From: mattijs Date: Wed, 23 May 2012 11:24:00 +0100 Subject: [PATCH 03/10] ENH: foamPack: enforce presence of .build --- bin/foamPack | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/bin/foamPack b/bin/foamPack index 9688cd039e..e3d090e2f2 100755 --- a/bin/foamPack +++ b/bin/foamPack @@ -3,7 +3,7 @@ # ========= | # \\ / F ield | OpenFOAM: The Open Source CFD Toolbox # \\ / O peration | -# \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation +# \\ / A nd | Copyright (C) 2011-2012 OpenFOAM Foundation # \\/ M anipulation | #------------------------------------------------------------------------------ # License @@ -107,15 +107,23 @@ then echo "pack manually" 1>&2 foamPackSource $packDir $packFile else - echo "pack with git-archive" 1>&2 - ( cd $packDir && git archive --format=tar --prefix=$packDir/ HEAD) > $packBase.tar + if [ ! -f $packDir/.build ] + then + echo "Error: $packDir/.build does not exists" 1>&2 + echo " Please update this by running e.g. 'wmakePrintBuild -update' in $packDir" 1>&2 + exit 2 + fi - echo "add in time-stamp and lnInclude directories" 1>&2 - tar cf $packBase.tar2 $packDir/.timeStamp $packDir/.build `find -H $packDir -type d -name lnInclude` - tar Af $packBase.tar $packBase.tar2 - echo "gzip tar file" 1>&2 - gzip -c9 $packBase.tar > $packFile + echo "pack with git-archive" 1>&2 && + ( cd $packDir && git archive --format=tar --prefix=$packDir/ HEAD) > $packBase.tar && + + echo "add in time-stamp and lnInclude directories" 1>&2 && + tar cf $packBase.tar2 $packDir/.timeStamp $packDir/.build `find -H $packDir -type d -name lnInclude` && + tar Af $packBase.tar $packBase.tar2 && + + echo "gzip tar file" 1>&2 && + gzip -c9 $packBase.tar > $packFile && rm -f $packBase.tar $packBase.tar2 2>/dev/null fi From 89bcfe0441861a6a39fd1035b63963fcf1a540bd Mon Sep 17 00:00:00 2001 From: mattijs Date: Wed, 23 May 2012 18:12:39 +0100 Subject: [PATCH 04/10] BUG: ptScotchDecomp: handle zero-sized domains --- .../ptscotchDecomp/dummyPtscotchDecomp.C | 20 +- .../decompose/ptscotchDecomp/ptscotchDecomp.C | 494 +++++++++--------- .../decompose/ptscotchDecomp/ptscotchDecomp.H | 26 +- 3 files changed, 278 insertions(+), 262 deletions(-) diff --git a/src/dummyThirdParty/ptscotchDecomp/dummyPtscotchDecomp.C b/src/dummyThirdParty/ptscotchDecomp/dummyPtscotchDecomp.C index 701de4e23a..36d405d597 100644 --- a/src/dummyThirdParty/ptscotchDecomp/dummyPtscotchDecomp.C +++ b/src/dummyThirdParty/ptscotchDecomp/dummyPtscotchDecomp.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation + \\ / A nd | Copyright (C) 2011-2012 OpenFOAM Foundation \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -58,7 +58,7 @@ void Foam::ptscotchDecomp::check(const int retVal, const char* str) {} -Foam::label Foam::ptscotchDecomp::decomposeZeroDomains +Foam::label Foam::ptscotchDecomp::decompose ( const fileName& meshPath, const List& initxadj, @@ -72,6 +72,7 @@ Foam::label Foam::ptscotchDecomp::decomposeZeroDomains ( "label ptscotchDecomp::decompose" "(" + "onst fileName&," "const List&, " "const List&, " "const scalarField&, " @@ -84,8 +85,10 @@ Foam::label Foam::ptscotchDecomp::decomposeZeroDomains Foam::label Foam::ptscotchDecomp::decompose ( const fileName& meshPath, - const List& adjncy, - const List& xadj, + const int adjncySize, + const int adjncy[], + const int xadjSize, + const int xadj[], const scalarField& cWeights, List& finalDecomp ) const @@ -94,9 +97,12 @@ Foam::label Foam::ptscotchDecomp::decompose ( "label ptscotchDecomp::decompose" "(" - "const List&, " - "const List&, " - "const scalarField&, " + "const fileName&," + "const int," + "const int," + "const int," + "const int," + "const scalarField&," "List&" ")" ) << notImplementedMessage << exit(FatalError); diff --git a/src/parallel/decompose/ptscotchDecomp/ptscotchDecomp.C b/src/parallel/decompose/ptscotchDecomp/ptscotchDecomp.C index e7c4995a36..81d3851837 100644 --- a/src/parallel/decompose/ptscotchDecomp/ptscotchDecomp.C +++ b/src/parallel/decompose/ptscotchDecomp/ptscotchDecomp.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation + \\ / A nd | Copyright (C) 2011-2012 OpenFOAM Foundation \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -103,9 +103,9 @@ License - Note: writes out .dgr files for debugging. Run with e.g. + Note: writeGraph=true : writes out .dgr files for debugging. Run with e.g. - mpirun -np 4 dgpart 2 'processor%r.grf' + mpirun -np 4 dgpart 2 'region0_%r.dgr' - %r gets replaced by current processor rank - decompose into 2 domains @@ -167,192 +167,192 @@ void Foam::ptscotchDecomp::check(const int retVal, const char* str) } -//- Does prevention of 0 cell domains and calls ptscotch. -Foam::label Foam::ptscotchDecomp::decomposeZeroDomains -( - const fileName& meshPath, - const List& initadjncy, - const List& initxadj, - const scalarField& initcWeights, - - List& finalDecomp -) const -{ - globalIndex globalCells(initxadj.size()-1); - - bool hasZeroDomain = false; - for (label procI = 0; procI < Pstream::nProcs(); procI++) - { - if (globalCells.localSize(procI) == 0) - { - hasZeroDomain = true; - break; - } - } - - if (!hasZeroDomain) - { - return decompose - ( - meshPath, - initadjncy, - initxadj, - initcWeights, - finalDecomp - ); - } - - - if (debug) - { - Info<< "ptscotchDecomp : have graphs with locally 0 cells." - << " trickling down." << endl; - } - - // Make sure every domain has at least one cell - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // (scotch does not like zero sized domains) - // Trickle cells from processors that have them up to those that - // don't. - - - // Number of cells to send to the next processor - // (is same as number of cells next processor has to receive) - List nSendCells(Pstream::nProcs(), 0); - - for (label procI = nSendCells.size()-1; procI >=1; procI--) - { - label nLocalCells = globalCells.localSize(procI); - if (nLocalCells-nSendCells[procI] < 1) - { - nSendCells[procI-1] = nSendCells[procI]-nLocalCells+1; - } - } - - // First receive (so increasing the sizes of all arrays) - - Field xadj(initxadj); - Field adjncy(initadjncy); - scalarField cWeights(initcWeights); - - if (Pstream::myProcNo() >= 1 && nSendCells[Pstream::myProcNo()-1] > 0) - { - // Receive cells from previous processor - IPstream fromPrevProc(Pstream::blocking, Pstream::myProcNo()-1); - - Field prevXadj(fromPrevProc); - Field prevAdjncy(fromPrevProc); - scalarField prevCellWeights(fromPrevProc); - - if (prevXadj.size() != nSendCells[Pstream::myProcNo()-1]) - { - FatalErrorIn("ptscotchDecomp::decompose(..)") - << "Expected from processor " << Pstream::myProcNo()-1 - << " connectivity for " << nSendCells[Pstream::myProcNo()-1] - << " nCells but only received " << prevXadj.size() - << abort(FatalError); - } - - // Insert adjncy - prepend(prevAdjncy, adjncy); - // Adapt offsets and prepend xadj - xadj += prevAdjncy.size(); - prepend(prevXadj, xadj); - // Weights - prepend(prevCellWeights, cWeights); - } - - - // Send to my next processor - - if (nSendCells[Pstream::myProcNo()] > 0) - { - // Send cells to next processor - OPstream toNextProc(Pstream::blocking, Pstream::myProcNo()+1); - - int nCells = nSendCells[Pstream::myProcNo()]; - int startCell = xadj.size()-1 - nCells; - int startFace = xadj[startCell]; - int nFaces = adjncy.size()-startFace; - - // Send for all cell data: last nCells elements - // Send for all face data: last nFaces elements - toNextProc - << Field::subField(xadj, nCells, startCell)-startFace - << Field::subField(adjncy, nFaces, startFace) - << - ( - cWeights.size() - ? static_cast - ( - scalarField::subField(cWeights, nCells, startCell) - ) - : scalarField(0) - ); - - // Remove data that has been sent - if (cWeights.size()) - { - cWeights.setSize(cWeights.size()-nCells); - } - adjncy.setSize(adjncy.size()-nFaces); - xadj.setSize(xadj.size() - nCells); - } - - - // Do decomposition as normal. Sets finalDecomp. - label result = decompose(meshPath, adjncy, xadj, cWeights, finalDecomp); - - - if (debug) - { - Info<< "ptscotchDecomp : have graphs with locally 0 cells." - << " trickling up." << endl; - } - - - // If we sent cells across make sure we undo it - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - // Receive back from next processor if I sent something - if (nSendCells[Pstream::myProcNo()] > 0) - { - IPstream fromNextProc(Pstream::blocking, Pstream::myProcNo()+1); - - List nextFinalDecomp(fromNextProc); - - if (nextFinalDecomp.size() != nSendCells[Pstream::myProcNo()]) - { - FatalErrorIn("parMetisDecomp::decompose(..)") - << "Expected from processor " << Pstream::myProcNo()+1 - << " decomposition for " << nSendCells[Pstream::myProcNo()] - << " nCells but only received " << nextFinalDecomp.size() - << abort(FatalError); - } - - append(nextFinalDecomp, finalDecomp); - } - - // Send back to previous processor. - if (Pstream::myProcNo() >= 1 && nSendCells[Pstream::myProcNo()-1] > 0) - { - OPstream toPrevProc(Pstream::blocking, Pstream::myProcNo()-1); - - int nToPrevious = nSendCells[Pstream::myProcNo()-1]; - - toPrevProc << - SubList - ( - finalDecomp, - nToPrevious, - finalDecomp.size()-nToPrevious - ); - - // Remove locally what has been sent - finalDecomp.setSize(finalDecomp.size()-nToPrevious); - } - return result; -} +////- Does prevention of 0 cell domains and calls ptscotch. +//Foam::label Foam::ptscotchDecomp::decomposeZeroDomains +//( +// const fileName& meshPath, +// const List& initadjncy, +// const List& initxadj, +// const scalarField& initcWeights, +// +// List& finalDecomp +//) const +//{ +// globalIndex globalCells(initxadj.size()-1); +// +// bool hasZeroDomain = false; +// for (label procI = 0; procI < Pstream::nProcs(); procI++) +// { +// if (globalCells.localSize(procI) == 0) +// { +// hasZeroDomain = true; +// break; +// } +// } +// +// if (!hasZeroDomain) +// { +// return decompose +// ( +// meshPath, +// initadjncy, +// initxadj, +// initcWeights, +// finalDecomp +// ); +// } +// +// +// if (debug) +// { +// Info<< "ptscotchDecomp : have graphs with locally 0 cells." +// << " trickling down." << endl; +// } +// +// // Make sure every domain has at least one cell +// // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// // (scotch does not like zero sized domains) +// // Trickle cells from processors that have them up to those that +// // don't. +// +// +// // Number of cells to send to the next processor +// // (is same as number of cells next processor has to receive) +// List nSendCells(Pstream::nProcs(), 0); +// +// for (label procI = nSendCells.size()-1; procI >=1; procI--) +// { +// label nLocalCells = globalCells.localSize(procI); +// if (nLocalCells-nSendCells[procI] < 1) +// { +// nSendCells[procI-1] = nSendCells[procI]-nLocalCells+1; +// } +// } +// +// // First receive (so increasing the sizes of all arrays) +// +// Field xadj(initxadj); +// Field adjncy(initadjncy); +// scalarField cWeights(initcWeights); +// +// if (Pstream::myProcNo() >= 1 && nSendCells[Pstream::myProcNo()-1] > 0) +// { +// // Receive cells from previous processor +// IPstream fromPrevProc(Pstream::blocking, Pstream::myProcNo()-1); +// +// Field prevXadj(fromPrevProc); +// Field prevAdjncy(fromPrevProc); +// scalarField prevCellWeights(fromPrevProc); +// +// if (prevXadj.size() != nSendCells[Pstream::myProcNo()-1]) +// { +// FatalErrorIn("ptscotchDecomp::decompose(..)") +// << "Expected from processor " << Pstream::myProcNo()-1 +// << " connectivity for " << nSendCells[Pstream::myProcNo()-1] +// << " nCells but only received " << prevXadj.size() +// << abort(FatalError); +// } +// +// // Insert adjncy +// prepend(prevAdjncy, adjncy); +// // Adapt offsets and prepend xadj +// xadj += prevAdjncy.size(); +// prepend(prevXadj, xadj); +// // Weights +// prepend(prevCellWeights, cWeights); +// } +// +// +// // Send to my next processor +// +// if (nSendCells[Pstream::myProcNo()] > 0) +// { +// // Send cells to next processor +// OPstream toNextProc(Pstream::blocking, Pstream::myProcNo()+1); +// +// int nCells = nSendCells[Pstream::myProcNo()]; +// int startCell = xadj.size()-1 - nCells; +// int startFace = xadj[startCell]; +// int nFaces = adjncy.size()-startFace; +// +// // Send for all cell data: last nCells elements +// // Send for all face data: last nFaces elements +// toNextProc +// << Field::subField(xadj, nCells, startCell)-startFace +// << Field::subField(adjncy, nFaces, startFace) +// << +// ( +// cWeights.size() +// ? static_cast +// ( +// scalarField::subField(cWeights, nCells, startCell) +// ) +// : scalarField(0) +// ); +// +// // Remove data that has been sent +// if (cWeights.size()) +// { +// cWeights.setSize(cWeights.size()-nCells); +// } +// adjncy.setSize(adjncy.size()-nFaces); +// xadj.setSize(xadj.size() - nCells); +// } +// +// +// // Do decomposition as normal. Sets finalDecomp. +// label result = decompose(meshPath, adjncy, xadj, cWeights, finalDecomp); +// +// +// if (debug) +// { +// Info<< "ptscotchDecomp : have graphs with locally 0 cells." +// << " trickling up." << endl; +// } +// +// +// // If we sent cells across make sure we undo it +// // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// // Receive back from next processor if I sent something +// if (nSendCells[Pstream::myProcNo()] > 0) +// { +// IPstream fromNextProc(Pstream::blocking, Pstream::myProcNo()+1); +// +// List nextFinalDecomp(fromNextProc); +// +// if (nextFinalDecomp.size() != nSendCells[Pstream::myProcNo()]) +// { +// FatalErrorIn("parMetisDecomp::decompose(..)") +// << "Expected from processor " << Pstream::myProcNo()+1 +// << " decomposition for " << nSendCells[Pstream::myProcNo()] +// << " nCells but only received " << nextFinalDecomp.size() +// << abort(FatalError); +// } +// +// append(nextFinalDecomp, finalDecomp); +// } +// +// // Send back to previous processor. +// if (Pstream::myProcNo() >= 1 && nSendCells[Pstream::myProcNo()-1] > 0) +// { +// OPstream toPrevProc(Pstream::blocking, Pstream::myProcNo()-1); +// +// int nToPrevious = nSendCells[Pstream::myProcNo()-1]; +// +// toPrevProc << +// SubList +// ( +// finalDecomp, +// nToPrevious, +// finalDecomp.size()-nToPrevious +// ); +// +// // Remove locally what has been sent +// finalDecomp.setSize(finalDecomp.size()-nToPrevious); +// } +// return result; +//} // Call scotch with options from dictionary. @@ -362,13 +362,42 @@ Foam::label Foam::ptscotchDecomp::decompose const List& adjncy, const List& xadj, const scalarField& cWeights, + List& finalDecomp +) const +{ + List dummyAdjncy(1); + List dummyXadj(1); + dummyXadj[0] = 0; + + return decompose + ( + meshPath, + adjncy.size(), + (adjncy.size() ? adjncy.begin() : dummyAdjncy.begin()), + xadj.size(), + (xadj.size() ? xadj.begin() : dummyXadj.begin()), + cWeights, + finalDecomp + ); +} + + +// Call scotch with options from dictionary. +Foam::label Foam::ptscotchDecomp::decompose +( + const fileName& meshPath, + const int adjncySize, + const int adjncy[], + const int xadjSize, + const int xadj[], + const scalarField& cWeights, List& finalDecomp ) const { if (debug) { - Pout<< "ptscotchDecomp : entering with xadj:" << xadj.size() << endl; + Pout<< "ptscotchDecomp : entering with xadj:" << xadjSize << endl; } // Dump graph @@ -387,7 +416,7 @@ Foam::label Foam::ptscotchDecomp::decompose Pout<< "Dumping Scotch graph file to " << str.name() << endl << "Use this in combination with dgpart." << endl; - globalIndex globalCells(xadj.size()-1); + globalIndex globalCells(xadjSize-1); // Distributed graph file (.grf) label version = 2; @@ -400,17 +429,17 @@ Foam::label Foam::ptscotchDecomp::decompose // Total number of vertices (vertglbnbr) str << globalCells.size(); // Total number of connections (edgeglbnbr) - str << ' ' << returnReduce(xadj[xadj.size()-1], sumOp