34#include <unordered_set>
36#define INTEGRAL_LINE_TABULAR_SIZE 50
38#define INTEGRAL_LINE_IS_ELEMENT_TO_PROCESS 0
39#define INTEGRAL_LINE_IS_MESSAGE_SIZE 1
70 struct GhostElementsToSort {
80 inline bool operator<(
const GhostElementsToSort &left,
81 const GhostElementsToSort &right) {
82 if(left.seedIdentifier != right.seedIdentifier) {
83 return left.seedIdentifier < right.seedIdentifier;
85 if(left.forkIdentifier != right.forkIdentifier) {
86 return left.forkIdentifier < right.forkIdentifier;
88 return left.minLocalVertexId < right.minLocalVertexId;
95 static int finishedElement_ __attribute__((aligned(64)));
96 static int addedElement_ __attribute__((aligned(64)));
104 struct ElementToBeSent {
107 double DistanceFromSeed1;
108 double DistanceFromSeed2;
125 template <
class triangulationType = ttk::AbstractTriangulation>
126 int execute(triangulationType *triangulation);
137 template <
class triangulationType = ttk::AbstractTriangulation>
152 template <
class triangulationType>
153 void createTask(
const triangulationType *triangulation,
154 std::vector<ttk::intgl::IntegralLine *> &chunkIntegralLine,
156 int nbElement)
const;
171 template <
class triangulationType>
174 std::vector<ttk::intgl::IntegralLine *> &chunkIntegralLine,
177 std::vector<SimplexId> *seeds)
const;
202 std::vector<ttk::SimplexId> &component,
217 template <
typename triangulationType>
218 int getGlobalIdentifiers(
219 std::vector<ttk::SimplexId> &globalVertexId,
220 std::vector<ttk::SimplexId> &globalCellId,
224 const triangulationType *triangulation);
237 const std::vector<std::vector<ttk::intgl::GhostElementsToSort>>
239 std::vector<ttk::SimplexId> &globalVertexId,
240 std::vector<ttk::SimplexId> &globalCellId);
253 template <
class triangulationType>
254 void storeToSendIfNecessary(
const triangulationType *triangulation,
273 template <
class triangulationType>
275 receiveElement(
const triangulationType *triangulation,
276 intgl::ElementToBeSent &element,
277 std::vector<ttk::intgl::IntegralLine *> &chunkIntegralLine,
282 inline void setToSend(
283 std::vector<std::vector<std::vector<intgl::ElementToBeSent>>> *send) {
287 inline void setGlobalElementCounter(
int counter) {
288 globalElementCounter_ = counter;
291 inline void setNeighbors(
const std::vector<int> &neighbors) {
292 neighbors_ = neighbors;
293 neighborNumber_ = neighbors_.size();
295 for(
int neighbor : neighbors) {
296 neighborsToId_[neighbor] = idx;
305 void createMessageType() {
308 = {getMPIType(
id), getMPIType(
id), MPI_DOUBLE, MPI_DOUBLE,
309 getMPIType(
id), getMPIType(
id), getMPIType(
id), getMPIType(
id)};
310 int lengths[] = {1, 1, 1, 1, 1, 1, 1, 1};
311 const long int mpi_offsets[]
312 = {offsetof(intgl::ElementToBeSent, Id1),
313 offsetof(intgl::ElementToBeSent, Id2),
314 offsetof(intgl::ElementToBeSent, DistanceFromSeed1),
315 offsetof(intgl::ElementToBeSent, DistanceFromSeed2),
316 offsetof(intgl::ElementToBeSent, LocalVertexIdentifier1),
317 offsetof(intgl::ElementToBeSent, LocalVertexIdentifier2),
318 offsetof(intgl::ElementToBeSent, SeedIdentifier),
319 offsetof(intgl::ElementToBeSent, ForkIdentifier)};
320 MPI_Type_create_struct(
321 8, lengths, mpi_offsets, types, &(this->MessageType));
322 MPI_Type_commit(&(this->MessageType));
386 std::vector<std::vector<std::vector<intgl::ElementToBeSent>>> *toSend_{
389 std::unordered_map<int, int> neighborsToId_;
390 std::vector<int> neighbors_;
393 MPI_Datatype MessageType;
400template <
class triangulationType>
401void ttk::IntegralLines::receiveElement(
402 const triangulationType *triangulation,
403 intgl::ElementToBeSent &element,
404 std::vector<ttk::intgl::IntegralLine *> &chunkIntegralLine,
411#ifdef TTK_ENABLE_OPENMP4
412 threadNum = omp_get_thread_num();
415 ttk::intgl::IntegralLine *integralLine
417 ttk::intgl::IntegralLine{
418 std::vector<ttk::SimplexId>(
419 {triangulation->getVertexLocalId(element.Id1),
420 triangulation->getVertexLocalId(element.Id2)}),
422 {element.DistanceFromSeed1, element.DistanceFromSeed2}),
423 std::vector<ttk::SimplexId>(
424 {element.LocalVertexIdentifier1, element.LocalVertexIdentifier2}),
425 element.SeedIdentifier, element.ForkIdentifier});
428 chunkIntegralLine[index] = integralLine;
431 if(index == taskSize - 1) {
433 triangulation, chunkIntegralLine, offsets, taskSize);
440template <
class triangulationType>
441void ttk::IntegralLines::storeToSendIfNecessary(
442 const triangulationType *triangulation,
443 ttk::intgl::IntegralLine *integralLine,
446 if(ttk::isRunningWithMPI()) {
450 = triangulation->getVertexRank(integralLine->
trajectory.back());
452 intgl::ElementToBeSent element
453 = intgl::ElementToBeSent{-1,
462 = triangulation->getVertexGlobalId(integralLine->
trajectory.back());
463 element.Id1 = triangulation->getVertexGlobalId(
467 element.LocalVertexIdentifier2
469 element.LocalVertexIdentifier1
471#ifdef TTK_ENABLE_OPENMP4
473 ->at(neighborsToId_.find(rankArray)->second)[omp_get_thread_num()]
476 toSend_->at(neighborsToId_.find(rankArray)->second)[0].push_back(
490 std::vector<ttk::SimplexId> &component,
495 if(fnext < offsets[component[k]]) {
496 vnext = component[k];
497 fnext = offsets[component[k]];
500 if(fnext > offsets[component[k]]) {
501 vnext = component[k];
502 fnext = offsets[component[k]];
508template <
class triangulationType>
510 const triangulationType *triangulation,
517 triangulation->getVertexPoint(v, p0[0], p0[1], p0[2]);
519 std::vector<std::vector<ttk::SimplexId>> *components;
521 std::vector<std::vector<ttk::SimplexId>> upperComponents;
522 std::vector<std::vector<ttk::SimplexId>> lowerComponents;
524 char const criticalType
526 v, offsets, triangulation, &upperComponents, &lowerComponents);
534 if(ttk::isRunningWithMPI()
536#ifdef TTK_ENABLE_OPENMP4
537#pragma omp atomic update seq_cst
539 ttk::intgl::finishedElement_++;
541 this->storeToSendIfNecessary<triangulationType>(
542 triangulation, integralLine, isMax);
548 components = &(lowerComponents);
550 components = &(upperComponents);
560#ifdef TTK_ENABLE_OPENMP4
561#pragma omp atomic update seq_cst
563 ttk::intgl::finishedElement_++;
564#ifdef TTK_ENABLE_OPENMP4
565#pragma omp atomic update seq_cst
567 ttk::intgl::addedElement_ += numberOfComponents;
570 for(
int i = 0; i < numberOfComponents; i++) {
577 = triangulation->getVertexGlobalId(vnext);
581 triangulation->getVertexPoint(vnext, p1[0], p1[1], p1[2]);
584#ifdef TTK_ENABLE_OPENMP4
585 threadNum = omp_get_thread_num();
590 std::vector<ttk::SimplexId>({v, vnext}),
591 std::vector<double>({distance, distance + distanceFork}),
592 std::vector<ttk::SimplexId>(
597#ifdef TTK_ENABLE_OPENMP4
598#pragma omp task firstprivate(integralLineFork)
602 bool hasBeenSent =
false;
603 this->storeToSendIfNecessary<triangulationType>(
604 triangulation, integralLineFork, hasBeenSent);
608 triangulation, integralLineFork, offsets);
612#ifdef TTK_ENABLE_OPENMP4
621 = triangulation->getVertexNeighborNumber(v);
622 components->push_back(std::vector<ttk::SimplexId>());
625 triangulation->getVertexNeighbor(v, i,
id);
626 components->at(0).push_back(
id);
631 triangulation->getVertexPoint(vnext, p1[0], p1[1], p1[2]);
642 this->storeToSendIfNecessary<triangulationType>(
643 triangulation, integralLine, isMax);
650template <
class triangulationType>
653 const triangulationType *triangulation,
655 const triangulationType *
ttkNotUsed(triangulation),
657 std::vector<ttk::intgl::IntegralLine *> &chunkIntegralLine,
660 std::vector<SimplexId> *seeds)
const {
662 for(
SimplexId j = 0; j < nbElement; j++) {
663 SimplexId const v{seeds->at(j + startingIndex)};
666 seedIdentifier = triangulation->getVertexGlobalId(v);
671#ifdef TTK_ENABLE_OPENMP4
672 threadNum = omp_get_thread_num();
676 std::vector<ttk::SimplexId>(1, v), std::vector<double>(1, 0),
677 std::vector<ttk::SimplexId>(1, 0), seedIdentifier, -1});
681template <
class triangulationType>
683 const triangulationType *triangulation,
684 std::vector<ttk::intgl::IntegralLine *> &chunkIntegralLine,
686 int nbElement)
const {
687#ifdef TTK_ENABLE_OPENMP4
688#pragma omp task firstprivate(chunkIntegralLine)
691 for(
int j = 0; j < nbElement; j++) {
693 triangulation, chunkIntegralLine[j], offsets);
695#ifdef TTK_ENABLE_OPENMP4
700template <
class triangulationType>
705 ttk::intgl::finishedElement_ = 0;
706 ttk::intgl::addedElement_ = 0;
712 std::vector<ttk::intgl::IntegralLine *> chunkIntegralLine(
chunkSize_);
714#ifdef TTK_ENABLE_OPENMP4
716#pragma omp parallel shared( \
717 ttk::intgl::finishedElement_, toSend_, ttk::intgl::addedElement_) \
718 num_threads(threadNumber_)
721#pragma omp parallel num_threads(threadNumber_)
727 for(
SimplexId i = 0; i < taskNumber; ++i) {
731 triangulation, chunkIntegralLine, offsets,
chunkSize_);
736 triangulation, chunkIntegralLine, taskNumber *
chunkSize_, rest,
739 triangulation, chunkIntegralLine, offsets, rest);
741#ifdef TTK_ENABLE_OPENMP4
746 if(ttk::isRunningWithMPI()) {
748 int finishedElementReceived = 0;
749 std::vector<int> sendMessageSize(neighborNumber_);
750 std::vector<int> recvMessageSize(neighborNumber_);
751 std::vector<std::vector<intgl::ElementToBeSent>> send_buf(neighborNumber_);
752 std::vector<std::vector<intgl::ElementToBeSent>> recv_buf(neighborNumber_);
753 for(i = 0; i < neighborNumber_; i++) {
757 std::vector<MPI_Request> requests(2 * neighborNumber_, MPI_REQUEST_NULL);
758 std::vector<MPI_Status> statuses(4 * neighborNumber_);
761 int totalMessageSize;
762 while(keepWorking_) {
763 ttk::intgl::finishedElement_ -= ttk::intgl::addedElement_;
764 ttk::intgl::addedElement_ = 0;
766 MPI_Allreduce(&ttk::intgl::finishedElement_, &finishedElementReceived, 1,
767 MPI_INTEGER, MPI_SUM, ttk::MPIcomm_);
768 ttk::intgl::finishedElement_ = 0;
770 globalElementCounter_ -= finishedElementReceived;
772 if(globalElementCounter_ == 0) {
776 totalMessageSize = 0;
779 for(i = 0; i < neighborNumber_; i++) {
781 send_buf[i].insert(send_buf[i].
end(), toSend_->at(i)[j].begin(),
782 toSend_->at(i)[j].end());
783 toSend_->at(i)[j].clear();
785 sendMessageSize[i] = (int)send_buf[i].size();
786 MPI_Isend(&sendMessageSize[i], 1, MPI_INTEGER, neighbors_.at(i),
787 INTEGRAL_LINE_IS_MESSAGE_SIZE, ttk::MPIcomm_,
789 MPI_Irecv(&recvMessageSize[i], 1, MPI_INTEGER, neighbors_.at(i),
790 INTEGRAL_LINE_IS_MESSAGE_SIZE, ttk::MPIcomm_,
791 &requests[2 * i + 1]);
793 MPI_Waitall(2 * neighborNumber_, requests.data(), MPI_STATUSES_IGNORE);
795 for(i = 0; i < neighborNumber_; i++) {
796 if(recv_buf[i].size() < (
size_t)recvMessageSize[i]) {
797 recv_buf[i].resize(recvMessageSize[i]);
799 if(recvMessageSize[i] > 0) {
800 MPI_Irecv(recv_buf[i].data(), recvMessageSize[i], this->MessageType,
801 neighbors_.at(i), INTEGRAL_LINE_IS_ELEMENT_TO_PROCESS,
802 ttk::MPIcomm_, &requests[2 * i]);
803 totalMessageSize += recvMessageSize[i];
806 if(sendMessageSize[i] > 0) {
807 MPI_Isend(send_buf[i].data(), sendMessageSize[i], this->MessageType,
808 neighbors_.at(i), INTEGRAL_LINE_IS_ELEMENT_TO_PROCESS,
809 ttk::MPIcomm_, &requests[2 * i + 1]);
812 MPI_Waitall(2 * neighborNumber_, requests.data(), MPI_STATUSES_IGNORE);
813 for(i = 0; i < neighborNumber_; i++) {
817#ifdef TTK_ENABLE_OPENMP4
818#pragma omp parallel shared(ttk::intgl::finishedElement_, toSend_) \
819 num_threads(threadNumber_)
827 std::min(totalMessageSize, 50)),
829 chunkIntegralLine.resize(taskSize);
830 for(i = 0; i < neighborNumber_; i++) {
831 for(
int j = 0; j < recvMessageSize[i]; j++) {
832 this->receiveElement<triangulationType>(
833 triangulation, recv_buf[i][j], chunkIntegralLine, index,
839 triangulation, chunkIntegralLine, offsets, index);
841#ifdef TTK_ENABLE_OPENMP4
850 std::stringstream msg;
858template <
typename triangulationType>
859int ttk::IntegralLines::getGlobalIdentifiers(
860 std::vector<ttk::SimplexId> &globalVertexId,
861 std::vector<ttk::SimplexId> &globalCellId,
865 const triangulationType *triangulation) {
872#ifdef TTK_ENABLE_OPENMP4
873#pragma omp parallel for reduction(+ : outputVertexNumber, outputCellNumber, \
874 realCellNumber, realVertexNumber) \
875 schedule(static, 1) private(intervalSize)
881 = integralLines[thread].list_.begin();
882 while(integralLine != integralLines[thread].list_.end()) {
884 if(integralLine->at(i).trajectory.size() > 0) {
886 integralLine->at(i).trajectory.size());
887 outputVertexNumber += intervalSize;
888 outputCellNumber += intervalSize - 1;
889 if(triangulation->getVertexRank(integralLine->at(i).trajectory.at(0))
893 if(integralLine->at(i).trajectory.size() > 1) {
894 realCellNumber += intervalSize - 1;
895 if(triangulation->getVertexRank(
896 integralLine->at(i).trajectory.back())
901 realVertexNumber += intervalSize;
915 MPI_Exscan(&realVertexNumber, &vertIndex, 1, ttk::getMPIType(vertIndex),
916 MPI_SUM, ttk::MPIcomm_);
917 MPI_Exscan(&realCellNumber, &cellIndex, 1, ttk::getMPIType(cellIndex),
918 MPI_SUM, ttk::MPIcomm_);
931 globalVertexId.resize(outputVertexNumber);
932 globalCellId.resize(outputCellNumber);
933 std::vector<std::vector<ttk::intgl::GhostElementsToSort>> unmatchedGhosts(
935 std::vector<std::vector<ttk::SimplexId>> unmatchedGhostsVertexLocalId(
937 std::vector<std::vector<ttk::SimplexId>> unmatchedGhostsEdgeLocalId(
943 = integralLines[thread].list_.begin();
944 while(integralLine != integralLines[thread].list_.end()) {
946 if(integralLine->at(i).trajectory.size() > 0) {
947 if(triangulation->getVertexRank(integralLine->at(i).trajectory.at(0))
949 globalVertexId.at(startVertexId) = -1;
951 globalVertexId.at(startVertexId) = vertIndex;
955 if(integralLine->at(i).trajectory.size() > 1) {
956 if(triangulation->getVertexRank(
957 integralLine->at(i).trajectory.at(0))
959 globalCellId.at(startCellId) = -1;
961 .at(neighborsToId_[triangulation->getVertexRank(
962 integralLine->at(i).trajectory.at(0))])
963 .push_back(ttk::intgl::GhostElementsToSort{
964 vertIndex, integralLine->at(i).localVertexIdentifier.at(0),
965 integralLine->at(i).seedIdentifier,
966 integralLine->at(i).forkIdentifier, -1, startVertexId - 1,
969 globalCellId.at(startCellId) = cellIndex;
973 if(integralLine->at(i).trajectory.size() > 2) {
974 std::iota(globalCellId.begin() + startCellId,
975 globalCellId.begin() + startCellId
976 + integralLine->at(i).trajectory.size() - 2,
978 std::iota(globalVertexId.begin() + startVertexId,
979 globalVertexId.begin() + startVertexId
980 + integralLine->at(i).trajectory.size() - 2,
982 startCellId += integralLine->at(i).trajectory.size() - 2;
983 startVertexId += integralLine->at(i).trajectory.size() - 2;
984 vertIndex += integralLine->at(i).trajectory.size() - 2;
985 cellIndex += integralLine->at(i).trajectory.size() - 2;
987 if(triangulation->getVertexRank(
988 integralLine->at(i).trajectory.back())
990 globalVertexId.at(startVertexId) = -1;
992 .at(neighborsToId_[triangulation->getVertexRank(
993 integralLine->at(i).trajectory.back())])
994 .push_back(ttk::intgl::GhostElementsToSort{
995 globalVertexId.at(startVertexId - 1),
996 integralLine->at(i).localVertexIdentifier.at(
997 integralLine->at(i).localVertexIdentifier.size() - 2),
998 integralLine->at(i).seedIdentifier,
999 integralLine->at(i).forkIdentifier,
1000 globalCellId.at(startCellId - 1), startVertexId,
1003 globalVertexId.at(startVertexId) = vertIndex;
1028 for(
int i = 0; i < neighborNumber_; i++) {
1030 unmatchedGhosts.at(i).end());
1032 this->exchangeGhosts(unmatchedGhosts, globalVertexId, globalCellId);
1038inline int ttk::IntegralLines::exchangeGhosts(
1039 const std::vector<std::vector<ttk::intgl::GhostElementsToSort>>
1041 std::vector<ttk::SimplexId> &globalVertexId,
1042 std::vector<ttk::SimplexId> &globalCellId) {
1044 std::vector<std::vector<ttk::SimplexId>> globalIdsToSend(neighborNumber_);
1045 std::vector<std::vector<ttk::SimplexId>> globalIdsToReceive(neighborNumber_);
1046 for(
int i = 0; i < neighborNumber_; i++) {
1047 globalIdsToSend[i].resize(2 * unmatchedGhosts[i].size());
1048 globalIdsToReceive[i].resize(2 * unmatchedGhosts[i].size());
1051#ifdef TTK_ENABLE_OPENMP4
1052#pragma omp parallel for
1054 for(
int i = 0; i < neighborNumber_; i++) {
1055 for(
size_t j = 0; j < unmatchedGhosts[i].size(); j++) {
1056 globalIdsToSend[i][2 * j] = unmatchedGhosts[i][j].ownedGlobalId;
1057 globalIdsToSend[i][2 * j + 1] = unmatchedGhosts[i][j].globalEdgeId;
1062 for(
int neighbor : neighbors_) {
1063 MPI_Sendrecv(globalIdsToSend[neighborsToId_[neighbor]].data(),
1064 globalIdsToSend[neighborsToId_[neighbor]].size(),
1066 globalIdsToReceive[neighborsToId_[neighbor]].data(),
1067 globalIdsToSend[neighborsToId_[neighbor]].size(),
1068 ttk::getMPIType(
id), neighbor, neighbor, ttk::MPIcomm_,
1073#ifdef TTK_ENABLE_OPENMP4
1074#pragma omp parallel for
1076 for(
int i = 0; i < neighborNumber_; i++) {
1077 for(
size_t j = 0; j < unmatchedGhosts[i].size(); j++) {
1078 globalVertexId.at(unmatchedGhosts[i][j].ghostVertexLocalId)
1079 = globalIdsToReceive[i][2 * j];
1080 if(globalCellId.at(unmatchedGhosts[i][j].ghostEdgeLocalId) == -1) {
1081 globalCellId.at(unmatchedGhosts[i][j].ghostEdgeLocalId)
1082 = globalIdsToReceive[i][j * 2 + 1];
#define ttkNotUsed(x)
Mark function/method parameters that are not used in the function body at all.
int SimplexId
Identifier type for simplices of any dimension.
#define INTEGRAL_LINE_TABULAR_SIZE
#define TTK_PSORT(NTHREADS,...)
Parallel sort macro.
AbstractTriangulation is an interface class that defines an interface for efficient traversal methods...
virtual int preconditionVertexStars()
virtual int preconditionBoundaryVertices()
virtual int preconditionVertexNeighbors()
This class describes a dynamic size data structure for thread safe computation. It is a linked list o...
std::vector< ttk::SimplexId > * vertexIdentifierScalarField_
ttk::ScalarFieldCriticalPoints scalarFieldCriticalPoints_
void buildScalarFieldCriticalPoints()
void setInputOffsets(const SimplexId *const data)
ttk::SimplexId chunkSize_
const ttk::SimplexId * inputOffsets_
void createTask(const triangulationType *triangulation, std::vector< ttk::intgl::IntegralLine * > &chunkIntegralLine, const ttk::SimplexId *offsets, int nbElement) const
Create an OpenMP task that contains the computation of nbElement integral lines.
void setDirection(int direction)
ttk::SimplexId direction_
void setInputScalarField(void *data)
void setChunkSize(int size)
void computeIntegralLine(const triangulationType *triangulation, ttk::intgl::IntegralLine *integralLine, const ttk::SimplexId *offsets) const
Computes the integral line starting at the vertex of global id seedIdentifier.
void findNextVertex(ttk::SimplexId &vnext, ttk::SimplexId &fnext, std::vector< ttk::SimplexId > &component, const SimplexId *offsets) const
Finds the vertex of highest or lowest offsets (depending on the direction of the integral line) in th...
int preconditionTriangulation(ttk::AbstractTriangulation *triangulation)
void setVertexNumber(const SimplexId &vertexNumber)
ttk::SimplexId seedNumber_
int execute(triangulationType *triangulation)
void setOutputIntegralLines(std::vector< ttk::ArrayLinkedList< ttk::intgl::IntegralLine, INTEGRAL_LINE_TABULAR_SIZE > > *integralLines)
void setVertexIdentifierScalarField(std::vector< SimplexId > *const data)
std::vector< ttk::ArrayLinkedList< ttk::intgl::IntegralLine, INTEGRAL_LINE_TABULAR_SIZE > > * outputIntegralLines_
~IntegralLines() override
ttk::SimplexId vertexNumber_
void prepareForTask(const triangulationType *triangulation, std::vector< ttk::intgl::IntegralLine * > &chunkIntegralLine, int startingIndex, int nbElement, std::vector< SimplexId > *seeds) const
Initializes the three attributes of an integral line: the global id of its seed, its trajectory,...
void setSeedNumber(const SimplexId &seedNumber)
TTK processing package for the computation of critical points in PL scalar fields defined on PL manif...
T distance(const T *p0, const T *p1, const int &dimension=3)
bool operator<(const FiltratedEdge &e1, const FiltratedEdge &e2)
TTK base package defining the standard types.
COMMON_EXPORTS int MPIsize_
int SimplexId
Identifier type for simplices of any dimension.
COMMON_EXPORTS int MPIrank_
T end(std::pair< T, T > &p)
Struct containing the data of an integral line. trajectories: vector of identifiers of each vertex th...
std::vector< ttk::SimplexId > localVertexIdentifier
ttk::SimplexId forkIdentifier
std::vector< double > distanceFromSeed
ttk::SimplexId seedIdentifier
std::vector< ttk::SimplexId > trajectory
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/| (_) |"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)