1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2008 OpenCFD Ltd.
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
11 OpenFOAM is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation; either version 2 of the License, or (at your
14 option) any later version.
16 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM; if not, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 \*---------------------------------------------------------------------------*/
27 #include "globalMeshData.H"
30 #include "PstreamCombineReduceOps.H"
31 #include "processorPolyPatch.H"
32 #include "demandDrivenData.H"
33 #include "globalPoints.H"
34 //#include "geomGlobalPoints.H"
35 #include "labelIOList.H"
36 #include "PackedList.H"
37 #include "mergePoints.H"
38 #include "matchPoints.H"
41 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
45 defineTypeNameAndDebug(globalMeshData, 0);
48 // Geometric matching tolerance. Factor of mesh bounding box.
49 const Foam::scalar Foam::globalMeshData::matchTol_ = 1E-8;
52 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
54 // Collect processor patch addressing.
55 void Foam::globalMeshData::initProcAddr()
57 processorPatchIndices_.setSize(mesh_.boundaryMesh().size());
58 processorPatchIndices_ = -1;
60 processorPatchNeighbours_.setSize(mesh_.boundaryMesh().size());
61 processorPatchNeighbours_ = -1;
63 // Construct processor patch indexing. processorPatchNeighbours_ only
64 // set if running in parallel!
65 processorPatches_.setSize(mesh_.boundaryMesh().size());
67 label nNeighbours = 0;
69 forAll (mesh_.boundaryMesh(), patchi)
71 if (isA<processorPolyPatch>(mesh_.boundaryMesh()[patchi]))
73 processorPatches_[nNeighbours] = patchi;
74 processorPatchIndices_[patchi] = nNeighbours++;
77 processorPatches_.setSize(nNeighbours);
80 if (Pstream::parRun())
82 // Send indices of my processor patches to my neighbours
83 forAll (processorPatches_, i)
85 label patchi = processorPatches_[i];
90 refCast<const processorPolyPatch>
92 mesh_.boundaryMesh()[patchi]
96 toNeighbour << processorPatchIndices_[patchi];
99 forAll(processorPatches_, i)
101 label patchi = processorPatches_[i];
103 IPstream fromNeighbour
106 refCast<const processorPolyPatch>
108 mesh_.boundaryMesh()[patchi]
112 fromNeighbour >> processorPatchNeighbours_[patchi];
118 // Given information about locally used edges allocate global shared edges.
119 void Foam::globalMeshData::countSharedEdges
121 const HashTable<labelList, edge, Hash<edge> >& procSharedEdges,
122 HashTable<label, edge, Hash<edge> >& globalShared,
126 // Count occurrences of procSharedEdges in global shared edges table.
129 HashTable<labelList, edge, Hash<edge> >::const_iterator iter =
130 procSharedEdges.begin();
131 iter != procSharedEdges.end();
135 const edge& e = iter.key();
137 HashTable<label, edge, Hash<edge> >::iterator globalFnd =
138 globalShared.find(e);
140 if (globalFnd == globalShared.end())
142 // First time occurrence of this edge. Check how many we are adding.
143 if (iter().size() == 1)
145 // Only one edge. Mark with special value.
146 globalShared.insert(e, -1);
150 // Edge used more than once (even by local shared edges alone)
151 // so allocate proper shared edge label.
152 globalShared.insert(e, sharedEdgeI++);
157 if (globalFnd() == -1)
159 // Second time occurence of this edge. Assign proper
161 globalFnd() = sharedEdgeI++;
168 // Shared edges are shared between multiple processors. By their nature both
169 // of their endpoints are shared points. (but not all edges using two shared
170 // points are shared edges! There might e.g. be an edge between two unrelated
171 // clusters of shared points)
172 void Foam::globalMeshData::calcSharedEdges() const
174 if (nGlobalEdges_ != -1 || sharedEdgeLabelsPtr_ || sharedEdgeAddrPtr_)
176 FatalErrorIn("globalMeshData::calcSharedEdges()")
177 << "Shared edge addressing already done" << abort(FatalError);
181 const labelList& sharedPtAddr = sharedPointAddr();
182 const labelList& sharedPtLabels = sharedPointLabels();
184 // Since don't want to construct pointEdges for whole mesh create
185 // Map for all shared points.
186 Map<label> meshToShared(2*sharedPtLabels.size());
187 forAll(sharedPtLabels, i)
189 meshToShared.insert(sharedPtLabels[i], i);
192 // Find edges using shared points. Store correspondence to local edge
193 // numbering. Note that multiple local edges can have the same shared
194 // points! (for cyclics or separated processor patches)
195 HashTable<labelList, edge, Hash<edge> > localShared
197 2*sharedPtAddr.size()
200 const edgeList& edges = mesh_.edges();
204 const edge& e = edges[edgeI];
206 Map<label>::const_iterator e0Fnd = meshToShared.find(e[0]);
208 if (e0Fnd != meshToShared.end())
210 Map<label>::const_iterator e1Fnd = meshToShared.find(e[1]);
212 if (e1Fnd != meshToShared.end())
214 // Found edge which uses shared points. Probably shared.
216 // Construct the edge in shared points (or rather global indices
217 // of the shared points)
220 sharedPtAddr[e0Fnd()],
221 sharedPtAddr[e1Fnd()]
224 HashTable<labelList, edge, Hash<edge> >::iterator iter =
225 localShared.find(sharedEdge);
227 if (iter == localShared.end())
229 // First occurrence of this point combination. Store.
230 localShared.insert(sharedEdge, labelList(1, edgeI));
234 // Add this edge to list of edge labels.
235 labelList& edgeLabels = iter();
237 label sz = edgeLabels.size();
238 edgeLabels.setSize(sz+1);
239 edgeLabels[sz] = edgeI;
246 // Now we have a table on every processors which gives its edges which use
247 // shared points. Send this all to the master and have it allocate
248 // global edge numbers for it. But only allocate a global edge number for
249 // edge if it is used more than once!
250 // Note that we are now sending the whole localShared to the master whereas
251 // we only need the local count (i.e. the number of times a global edge is
252 // used). But then this only gets done once so not too bothered about the
253 // extra global communication.
255 HashTable<label, edge, Hash<edge> > globalShared(nGlobalPoints());
257 if (Pstream::master())
259 label sharedEdgeI = 0;
261 // Merge my shared edges into the global list
264 Pout<< "globalMeshData::calcSharedEdges : Merging in from proc0 : "
265 << localShared.size() << endl;
267 countSharedEdges(localShared, globalShared, sharedEdgeI);
269 // Receive data from slaves and insert
270 if (Pstream::parRun())
274 int slave=Pstream::firstSlave();
275 slave<=Pstream::lastSlave();
279 // Receive the edges using shared points from the slave.
280 IPstream fromSlave(Pstream::blocking, slave);
281 HashTable<labelList, edge, Hash<edge> > procSharedEdges
288 Pout<< "globalMeshData::calcSharedEdges : "
289 << "Merging in from proc"
290 << Foam::name(slave) << " : " << procSharedEdges.size()
293 countSharedEdges(procSharedEdges, globalShared, sharedEdgeI);
297 // Now our globalShared should have some edges with -1 as edge label
298 // These were only used once so are not proper shared edges.
301 HashTable<label, edge, Hash<edge> > oldSharedEdges(globalShared);
303 globalShared.clear();
307 HashTable<label, edge, Hash<edge> >::const_iterator iter =
308 oldSharedEdges.begin();
309 iter != oldSharedEdges.end();
315 globalShared.insert(iter.key(), iter());
320 Pout<< "globalMeshData::calcSharedEdges : Filtered "
321 << oldSharedEdges.size()
322 << " down to " << globalShared.size() << endl;
327 // Send back to slaves.
328 if (Pstream::parRun())
332 int slave=Pstream::firstSlave();
333 slave<=Pstream::lastSlave();
337 // Receive the edges using shared points from the slave.
338 OPstream toSlave(Pstream::blocking, slave);
339 toSlave << globalShared;
345 // Send local edges to master
347 OPstream toMaster(Pstream::blocking, Pstream::masterNo());
349 toMaster << localShared;
351 // Receive merged edges from master.
353 IPstream fromMaster(Pstream::blocking, Pstream::masterNo());
355 fromMaster >> globalShared;
359 // Now use the global shared edges list (globalShared) to classify my local
360 // ones (localShared)
362 nGlobalEdges_ = globalShared.size();
364 DynamicList<label> dynSharedEdgeLabels(globalShared.size());
365 DynamicList<label> dynSharedEdgeAddr(globalShared.size());
369 HashTable<labelList, edge, Hash<edge> >::const_iterator iter =
371 iter != localShared.end();
375 const edge& e = iter.key();
377 HashTable<label, edge, Hash<edge> >::const_iterator edgeFnd =
378 globalShared.find(e);
380 if (edgeFnd != globalShared.end())
382 // My local edge is indeed a shared one. Go through all local edge
383 // labels with this point combination.
384 const labelList& edgeLabels = iter();
386 forAll(edgeLabels, i)
388 // Store label of local mesh edge
389 dynSharedEdgeLabels.append(edgeLabels[i]);
391 // Store label of shared edge
392 dynSharedEdgeAddr.append(edgeFnd());
396 dynSharedEdgeLabels.shrink();
397 sharedEdgeLabelsPtr_ = new labelList();
398 labelList& sharedEdgeLabels = *sharedEdgeLabelsPtr_;
399 sharedEdgeLabels.transfer(dynSharedEdgeLabels);
400 dynSharedEdgeLabels.clear();
402 dynSharedEdgeAddr.shrink();
403 sharedEdgeAddrPtr_ = new labelList();
404 labelList& sharedEdgeAddr = *sharedEdgeAddrPtr_;
405 sharedEdgeAddr.transfer(dynSharedEdgeAddr);
406 dynSharedEdgeAddr.clear();
410 Pout<< "globalMeshData : nGlobalEdges_:" << nGlobalEdges_ << nl
411 << "globalMeshData : sharedEdgeLabels:" << sharedEdgeLabels.size()
413 << "globalMeshData : sharedEdgeAddr:" << sharedEdgeAddr.size()
419 // Helper function to count coincident faces. This part used to be
420 // in updateMesh but I've had to move it to a separate function
421 // because of aliasing optimisation errors in icc9.1 on the
423 Foam::label Foam::globalMeshData::countCoincidentFaces
426 const vectorField& separationDist
429 label nCoincident = 0;
431 forAll(separationDist, faceI)
433 if (mag(separationDist[faceI]) < tolDim)
443 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
445 // Construct from polyMesh
446 Foam::globalMeshData::globalMeshData(const polyMesh& mesh)
448 processorTopology(mesh.boundaryMesh()),
450 bb_(vector::zero, vector::zero),
454 processorPatches_(0),
455 processorPatchIndices_(0),
456 processorPatchNeighbours_(0),
458 sharedPointLabels_(0),
460 sharedPointGlobalLabelsPtr_(NULL),
462 sharedEdgeLabelsPtr_(NULL),
463 sharedEdgeAddrPtr_(NULL)
469 // Read constructor given IOobject and a polyMesh reference
470 Foam::globalMeshData::globalMeshData(const IOobject& io, const polyMesh& mesh)
472 processorTopology(mesh.boundaryMesh()),
478 processorPatches_(0),
479 processorPatchIndices_(0),
480 processorPatchNeighbours_(0),
482 sharedPointLabels_(0),
484 sharedPointGlobalLabelsPtr_(NULL),
486 sharedEdgeLabelsPtr_(NULL),
487 sharedEdgeAddrPtr_(NULL)
491 IOdictionary dict(io);
493 dict.lookup("nTotalPoints") >> nTotalPoints_;
494 dict.lookup("nTotalFaces") >> nTotalFaces_;
495 dict.lookup("nTotalCells") >> nTotalCells_;
496 dict.lookup("nGlobalPoints") >> nGlobalPoints_;
497 dict.lookup("sharedPointLabels") >> sharedPointLabels_;
498 dict.lookup("sharedPointAddr") >> sharedPointAddr_;
499 labelList sharedPointGlobalLabels(dict.lookup("sharedPointGlobalLabels"));
501 sharedPointGlobalLabelsPtr_ = new labelList(sharedPointGlobalLabels);
505 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
507 Foam::globalMeshData::~globalMeshData()
513 void Foam::globalMeshData::clearOut()
515 deleteDemandDrivenData(sharedPointGlobalLabelsPtr_);
518 deleteDemandDrivenData(sharedEdgeLabelsPtr_);
519 deleteDemandDrivenData(sharedEdgeAddrPtr_);
523 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
525 // Return shared point global labels.
526 const Foam::labelList& Foam::globalMeshData::sharedPointGlobalLabels() const
528 if (!sharedPointGlobalLabelsPtr_)
530 sharedPointGlobalLabelsPtr_ = new labelList(sharedPointLabels_.size());
531 labelList& sharedPointGlobalLabels = *sharedPointGlobalLabelsPtr_;
535 "pointProcAddressing",
536 mesh_.facesInstance()/mesh_.meshSubDir,
541 if (addrHeader.headerOk())
543 // There is a pointProcAddressing file so use it to get labels
544 // on the original mesh
545 Pout<< "globalMeshData::sharedPointGlobalLabels : "
546 << "Reading pointProcAddressing" << endl;
548 labelIOList pointProcAddressing(addrHeader);
550 forAll(sharedPointLabels_, i)
553 label pointI = sharedPointLabels_[i];
555 // Map to mesh point of original mesh
556 sharedPointGlobalLabels[i] = pointProcAddressing[pointI];
561 Pout<< "globalMeshData::sharedPointGlobalLabels :"
562 << " Setting pointProcAddressing to -1" << endl;
564 sharedPointGlobalLabels = -1;
567 return *sharedPointGlobalLabelsPtr_;
571 // Collect coordinates of shared points. (does parallel communication!)
572 Foam::pointField Foam::globalMeshData::sharedPoints() const
574 // Get all processors to send their shared points to master.
575 // (not very efficient)
577 pointField sharedPoints(nGlobalPoints_);
579 if (Pstream::master())
582 // insert my own data first
583 forAll(sharedPointLabels_, i)
585 label sharedPointI = sharedPointAddr_[i];
587 sharedPoints[sharedPointI] = mesh_.points()[sharedPointLabels_[i]];
590 // Receive data from slaves and insert
593 int slave=Pstream::firstSlave();
594 slave<=Pstream::lastSlave();
598 IPstream fromSlave(Pstream::blocking, slave);
600 labelList nbrSharedPointAddr;
601 pointField nbrSharedPoints;
602 fromSlave >> nbrSharedPointAddr >> nbrSharedPoints;
604 forAll(nbrSharedPointAddr, i)
606 label sharedPointI = nbrSharedPointAddr[i];
608 sharedPoints[sharedPointI] = nbrSharedPoints[i];
615 int slave=Pstream::firstSlave();
616 slave<=Pstream::lastSlave();
624 sharedPoints.size()*sizeof(vector::zero)
626 toSlave << sharedPoints;
634 OPstream toMaster(Pstream::blocking, Pstream::masterNo());
638 << IndirectList<point>(mesh_.points(), sharedPointLabels_)();
641 // Receive sharedPoints
643 IPstream fromMaster(Pstream::blocking, Pstream::masterNo());
644 fromMaster >> sharedPoints;
652 // Collect coordinates of shared points. (does parallel communication!)
653 Foam::pointField Foam::globalMeshData::geometricSharedPoints() const
655 // Get coords of my shared points
656 pointField sharedPoints(sharedPointLabels_.size());
658 forAll(sharedPointLabels_, i)
660 label meshPointI = sharedPointLabels_[i];
662 sharedPoints[i] = mesh_.points()[meshPointI];
665 // Append from all processors
666 combineReduce(sharedPoints, plusEqOp<pointField>());
669 scalar tolDim = matchTol_*mag(bb_.max() - bb_.min());
671 // And see how many are unique
673 pointField mergedPoints;
677 sharedPoints, // coordinates to merge
688 Foam::label Foam::globalMeshData::nGlobalEdges() const
690 if (nGlobalEdges_ == -1)
694 return nGlobalEdges_;
698 const Foam::labelList& Foam::globalMeshData::sharedEdgeLabels() const
700 if (!sharedEdgeLabelsPtr_)
704 return *sharedEdgeLabelsPtr_;
708 const Foam::labelList& Foam::globalMeshData::sharedEdgeAddr() const
710 if (!sharedEdgeAddrPtr_)
714 return *sharedEdgeAddrPtr_;
718 void Foam::globalMeshData::movePoints(const pointField& newPoints)
720 // Topology does not change and we don't store any geometry so nothing
725 // Update all data after morph
726 void Foam::globalMeshData::updateMesh()
728 // Clear out old data
731 // Do processor patch addressing
734 // Bounding box (does communication)
735 bb_ = boundBox(mesh_.points(), true);
737 scalar tolDim = matchTol_*mag(bb_.max() - bb_.min());
741 Pout<< "globalMeshData : bb_:" << bb_
742 << " merge dist:" << tolDim << endl;
747 // Option 1. Topological
749 // Calculate all shared points. This does all the hard work.
750 globalPoints parallelPoints(mesh_);
753 nGlobalPoints_ = parallelPoints.nGlobalPoints();
754 sharedPointLabels_ = parallelPoints.sharedPointLabels();
755 sharedPointAddr_ = parallelPoints.sharedPointAddr();
757 //// Option 2. Geometric
759 // // Calculate all shared points. This does all the hard work.
760 // geomGlobalPoints parallelPoints(mesh_, tolDim);
763 // nGlobalPoints_ = parallelPoints.nGlobalPoints();
764 // sharedPointLabels_ = parallelPoints.sharedPointLabels();
765 // sharedPointAddr_ = parallelPoints.sharedPointAddr();
767 // nGlobalEdges_ = parallelPoints.nGlobalEdges();
768 // sharedEdgeLabels_ = parallelPoints.sharedEdgeLabels();
769 // sharedEdgeAddr_ = parallelPoints.sharedEdgeAddr();
772 // Total number of faces. Start off from all faces. Remove coincident
773 // processor faces (on highest numbered processor) before summing.
774 nTotalFaces_ = mesh_.nFaces();
776 // Do not count processorpatch faces that are coincident.
777 forAll(processorPatches_, i)
779 label patchI = processorPatches_[i];
781 const processorPolyPatch& procPatch =
782 refCast<const processorPolyPatch>(mesh_.boundaryMesh()[patchI]);
784 if (Pstream::myProcNo() > procPatch.neighbProcNo())
786 // Uncount my faces. Handle cyclics separately.
788 if (procPatch.separated())
790 const vectorField& separationDist = procPatch.separation();
792 nTotalFaces_ -= countCoincidentFaces(tolDim, separationDist);
796 // Normal, unseparated processor patch. Remove duplicates.
797 nTotalFaces_ -= procPatch.size();
801 reduce(nTotalFaces_, sumOp<label>());
805 Pout<< "globalMeshData : nTotalFaces_:" << nTotalFaces_ << endl;
809 nTotalCells_ = mesh_.nCells();
810 reduce(nTotalCells_, sumOp<label>());
814 Pout<< "globalMeshData : nTotalCells_:" << nTotalCells_ << endl;
817 nTotalPoints_ = mesh_.nPoints();
819 // Correct points for duplicate ones. We have
820 // - points shared between 2 processor patches only. Count only on
821 // lower numbered processor. Make sure to count only once since points
822 // can be on multiple patches on the same processor.
823 // - globally shared points.
825 if (Pstream::parRun())
827 const label UNSET = 0; // not set
828 const label SHARED = 1; // globally shared
829 const label VISITED = 2; // corrected for
831 // Mark globally shared points
832 PackedList<2> pointStatus(mesh_.nPoints(), UNSET);
834 forAll(sharedPointLabels_, i)
836 label meshPointI = sharedPointLabels_[i];
838 pointStatus.set(meshPointI, SHARED);
841 // Send patch local points
842 forAll(processorPatches_, i)
844 label patchI = processorPatches_[i];
846 const processorPolyPatch& procPatch =
847 refCast<const processorPolyPatch>(mesh_.boundaryMesh()[patchI]);
849 OPstream toNeighbour(Pstream::blocking, procPatch.neighbProcNo());
851 toNeighbour << procPatch.localPoints();
854 // Receive patch local points and uncount if coincident (and not shared)
855 forAll(processorPatches_, i)
857 label patchI = processorPatches_[i];
859 const processorPolyPatch& procPatch =
860 refCast<const processorPolyPatch>(mesh_.boundaryMesh()[patchI]);
862 IPstream fromNeighbour(Pstream::blocking, procPatch.neighbProcNo());
864 pointField nbrPoints(fromNeighbour);
866 if (Pstream::myProcNo() > procPatch.neighbProcNo())
871 procPatch.localPoints(),
873 scalarField(procPatch.nPoints(), tolDim), // tolerance
875 pMap // map from my points to nbrPoints
878 forAll(pMap, patchPointI)
880 label meshPointI = procPatch.meshPoints()[patchPointI];
882 label stat = pointStatus.get(meshPointI);
886 // Mark point as visited so if point is on multiple proc
887 // patches it only gets uncounted once.
888 pointStatus.set(meshPointI, VISITED);
890 if (pMap[patchPointI] != -1)
892 // Points share same coordinate so uncount.
900 reduce(nTotalPoints_, sumOp<label>());
903 // nTotalPoints has not been corrected yet for shared points. For these
904 // just collect all their coordinates and count unique ones.
906 label mySharedPoints = sharedPointLabels_.size();
907 reduce(mySharedPoints, sumOp<label>());
909 // Collect and merge shared points (does parallel communication)
910 pointField geomSharedPoints(geometricSharedPoints());
911 label nGeomSharedPoints = geomSharedPoints.size();
913 // Shared points merged down to mergedPoints size.
914 nTotalPoints_ -= mySharedPoints - nGeomSharedPoints;
918 Pout<< "globalMeshData : nTotalPoints_:" << nTotalPoints_ << endl;
922 // Now we have all info we wanted.
923 // Do some checking (if debug is set)
928 if (Pstream::master())
930 // We have the geometricSharedPoints already so write them.
931 // Ideally would like to write the networks of connected points as
932 // well but this is harder. (Todo)
933 Pout<< "globalMeshData : writing geometrically separated shared"
934 << " points to geomSharedPoints.obj" << endl;
936 OFstream str("geomSharedPoints.obj");
938 forAll(geomSharedPoints, i)
940 const point& pt = geomSharedPoints[i];
942 str << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z()
951 bool Foam::globalMeshData::write() const
958 mesh_.facesInstance(),
964 dict.add("nTotalPoints", nTotalPoints());
965 dict.add("nTotalFaces", nTotalFaces());
966 dict.add("nTotalCells", nTotalCells());
968 dict.add("nGlobalPoints", nGlobalPoints());
969 dict.add("sharedPointLabels", sharedPointLabels());
970 dict.add("sharedPointAddr", sharedPointAddr());
971 dict.add("sharedPointGlobalLabels", sharedPointGlobalLabels());
973 return dict.writeObject
976 IOstream::currentVersion,
977 IOstream::UNCOMPRESSED
982 // * * * * * * * * * * * * * * * Ostream Operators * * * * * * * * * * * * * //
984 Foam::Ostream& Foam::operator<<(Ostream& os, const globalMeshData& p)
986 os << "nTotalPoints " << p.nTotalPoints() << token::END_STATEMENT << nl
987 << "nTotalFaces " << p.nTotalFaces() << token::END_STATEMENT << nl
988 << "nTotalCells " << p.nTotalCells() << token::END_STATEMENT << nl
989 << "nGlobalPoints " << p.nGlobalPoints() << token::END_STATEMENT << nl
990 << "sharedPointLabels " << p.sharedPointLabels()
991 << token::END_STATEMENT << nl
992 << "sharedPointAddr " << p.sharedPointAddr()
993 << token::END_STATEMENT << endl;
999 // ************************************************************************* //