Commit 749d5d24 authored by Kirill Terekhov's avatar Kirill Terekhov

Nonlocal ghost enumeration mapping for RowMerger

RowMerger can now remap global indices that do not belong to initial
interval span into local indices and back. This leads to important
memory optimization in Automatizator, which now do not require
RowMerger interval to span over entire range of global indices on each
processor, but require only a small extension.

Added calculation of nonlocal indices of ghost elements in
Automatizator::EnumerateDynamicTags

Fixed a warning in mesh_ecl_file.cpp

Fixed SVG output in OldDrawGrid example that was not sorting correctly
the faces, leaded to visual artifacts.
parent b489eadd
......@@ -4785,6 +4785,8 @@ void svg_draw(std::ostream & file)
//svg_draw_faces(file,temp_boundary,modelview,projection,viewport);
std::vector<face2gl> sorted_clip_boundary(clip_boundary);
for(INMOST_DATA_ENUM_TYPE q = 0; q < sorted_clip_boundary.size() ; q++)
sorted_clip_boundary[q].compute_dist(campos);
face2gl::radix_sort_dist(sorted_clip_boundary);
svg_draw_faces(file,sorted_clip_boundary,modelview,projection,viewport);
......
......@@ -157,6 +157,7 @@ namespace INMOST
}
}
}
std::set<INMOST_DATA_ENUM_TYPE> Pre, Post; //Nonlocal indices
#if defined(USE_MPI)
if (m->GetProcessorsNumber() > 1)
{
......@@ -228,18 +229,99 @@ namespace INMOST
}
}
}
//synchronize indices
last_num += first_num;
{
std::vector<Tag> exch_tags;
for (index_enum::iterator it = index_tags.begin(); it != index_tags.end(); ++it) exch_tags.push_back(it->indices);
m->ExchangeData(exch_tags, exch_mask,0);
}
//compute out-of-bounds indices
for (index_enum::iterator it = index_tags.begin(); it != index_tags.end(); ++it)
{
for (ElementType etype = NODE; etype <= MESH; etype = etype << 1)
{
if (it->indices.isDefined(etype))
{
exch_mask |= etype;
if (it->indices.GetSize() == ENUMUNDEF)
{
if (!it->indices.isSparse(etype))
{
for (Mesh::iteratorStorage jt = m->Begin(etype); jt != m->End(); ++jt)
{
if ((!(etype & paralleltypes) || ((etype & paralleltypes) && jt->getAsElement()->GetStatus() == Element::Ghost)) && (it->d.domain_mask == 0 || jt->GetMarker(it->d.domain_mask)))
{
Storage::integer_array indarr = jt->IntegerArray(it->indices);
for (Storage::integer_array::iterator qt = indarr.begin(); qt != indarr.end(); ++qt)
{
if( *qt < first_num ) Pre.insert(*qt);
else if( *qt >= last_num ) Post.insert(*qt);
}
}
}
}
else
{
for (Mesh::iteratorStorage jt = m->Begin(etype); jt != m->End(); ++jt)
{
if ((!(etype & paralleltypes) || ((etype & paralleltypes) && jt->getAsElement()->GetStatus() == Element::Ghost)) && (jt->HaveData(it->d.t) && (it->d.domain_mask == 0 || jt->GetMarker(it->d.domain_mask))))
{
Storage::integer_array indarr = jt->IntegerArray(it->indices);
indarr.resize(jt->RealArray(it->d.t).size());
for (Storage::integer_array::iterator qt = indarr.begin(); qt != indarr.end(); ++qt)
{
if( *qt < first_num ) Pre.insert(*qt);
else if( *qt >= last_num ) Post.insert(*qt);
}
}
}
}
}
else //getsize
{
if (!it->indices.isSparse(etype))
{
for (Mesh::iteratorStorage jt = m->Begin(etype); jt != m->End(); ++jt)
{
if ((!(etype & paralleltypes) || ((etype & paralleltypes) && jt->getAsElement()->GetStatus() == Element::Ghost)) && (it->d.domain_mask == 0 || jt->GetMarker(it->d.domain_mask)))
{
Storage::integer_array indarr = jt->IntegerArray(it->indices);
for (Storage::integer_array::iterator qt = indarr.begin(); qt != indarr.end(); ++qt)
{
if( *qt < first_num ) Pre.insert(*qt);
else if( *qt >= last_num ) Post.insert(*qt);
}
}
}
}
else
{
for (Mesh::iteratorStorage jt = m->Begin(etype); jt != m->End(); ++jt)
{
if ((!(etype & paralleltypes) || ((etype & paralleltypes) && jt->getAsElement()->GetStatus() == Element::Ghost)) && (jt->HaveData(it->d.t) && (it->d.domain_mask == 0 || jt->GetMarker(it->d.domain_mask))))
{
Storage::integer_array indarr = jt->IntegerArray(it->indices);
for (Storage::integer_array::iterator qt = indarr.begin(); qt != indarr.end(); ++qt)
{
if( *qt < first_num ) Pre.insert(*qt);
else if( *qt >= last_num ) Post.insert(*qt);
}
}
}
}
} //getsize
} //isdefined
} //etype
} //it
// after cycle
}
#endif
// this version will fail in parallel
//merger.Resize(first_num,last_num,false);
// use this version until there is a way to define multiple intervals in RowMerger
INMOST_DATA_INTEGER_TYPE max_unknowns = m->AggregateMax(static_cast<INMOST_DATA_INTEGER_TYPE>(last_num));
//INMOST_DATA_INTEGER_TYPE max_unknowns = m->AggregateMax(static_cast<INMOST_DATA_INTEGER_TYPE>(last_num));
//std::cout << "Proc " << m->GetProcessorRank() << " size " << last_num-first_num << " pre " << Pre.size() << " post " << Post.size() << " max " << max_unknowns << std::endl;
#if defined(USE_OMP)
#pragma omp parallel
{
......@@ -247,10 +329,10 @@ namespace INMOST
{
merger.resize(omp_get_num_procs());
}
merger[omp_get_thread_num()].Resize(0,max_unknowns,false);
merger[omp_get_thread_num()].Resize(first_num,last_num,std::vector<INMOST_DATA_ENUM_TYPE>(Pre.begin(),Pre.end()),std::vector<INMOST_DATA_ENUM_TYPE>(Post.begin(),Post.end()),false);
}
#else
merger.Resize(0,max_unknowns,false);
merger.Resize(first_num,last_num,std::vector<INMOST_DATA_ENUM_TYPE>(Pre.begin(),Pre.end()),std::vector<INMOST_DATA_ENUM_TYPE>(Post.begin(),Post.end()),false);
#endif
}
#endif //USE_MESH
......
......@@ -503,8 +503,7 @@ namespace INMOST
#endif //defined(USE_SOLVER)
#if defined(USE_SOLVER) || defined(USE_AUTODIFF)
/// This class may be used to sum multiple sparse rows.
/// This class may be used to sum multiple sparse rows.
/// \warning
/// In parallel column indices of the matrix may span wider then
/// local row indices, to prevent any problem you are currently
......@@ -550,8 +549,26 @@ namespace INMOST
private:
bool Sorted; ///< Contents of linked list should be sorted.
INMOST_DATA_ENUM_TYPE Nonzeros; ///< Number of nonzero in linked list.
INMOST_DATA_ENUM_TYPE IntervalBeg; ///< Begin of global interval of owned index interval
INMOST_DATA_ENUM_TYPE IntervalEnd; ///< End of global interval of owned index interval
interval< INMOST_DATA_ENUM_TYPE, Row::entry > LinkedList; ///< Storage for linked list.
std::vector< INMOST_DATA_ENUM_TYPE > NonlocalPre; ///< List of global indices, that are to the left of owned index interval
std::vector< INMOST_DATA_ENUM_TYPE > NonlocalPost; ///< List of global indices, that are to the right of owned index interval
public:
/// This function converts global index into local index.
/// @param pos Global index.
/// @return Local index.
INMOST_DATA_ENUM_TYPE MapIndex(INMOST_DATA_ENUM_TYPE pos) const;
/// This function converts local index into global index.
/// @param pos Local index.
/// @return Global index.
INMOST_DATA_ENUM_TYPE UnmapIndex(INMOST_DATA_ENUM_TYPE pos) const;
/// This function provides information about additional non-local indices.
/// \warning
/// All contents of linked list will be lost.
/// @param Pre Non-local indices that go before IntervalBegin.
/// @param Post Non-local indices that follow IntervalEnd.
void SetNonlocal(const std::vector<INMOST_DATA_ENUM_TYPE> & Pre, const std::vector<INMOST_DATA_ENUM_TYPE> & Post);
/// Default constructor without size specified.
RowMerger();
/// Constructor with size specified.
......@@ -559,30 +576,42 @@ namespace INMOST
/// @param interval_end Last index in linked list.
/// @param Sorted Result should be sorted or not.
RowMerger(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, bool Sorted = true);
#if defined(USE_SOLVER)
/// Constructor that gets sizes from the matrix.
/// @param A Matrix to get sizes from.
/// @param Sorted Result should be sorted.
RowMerger(Matrix & A, bool Sorted = true);
#endif //USE_SOLVER
/// Destructor.
/// Constructor with size and non-local mapping specified.
/// @param interval_begin First index in linked list.
/// @param interval_end Last index in linked list.
/// @param Pre Nonlocal indices before First index in linked list.
/// @param Post Nonlocal indices after Last index in linked list.
/// @param Sorted Result should be sorted or not.
RowMerger(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, const std::vector<INMOST_DATA_ENUM_TYPE> & Pre, const std::vector<INMOST_DATA_ENUM_TYPE> & Post, bool Sorted = true);
/// Destructor.
~RowMerger();
/// Resize linked list for new interval.
/// \warning
/// All contents of linked list will be lost after resize.
/// This behavior may be changed in future.
/// @param interval_begin First index in linked list.
/// @param interval_end Last index in linked list.
/// @param Sorted Result should be sorted or not.
void Resize(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, bool Sorted = true);
/// Resize linked list for new interval with non-local mapping.
/// \warning
/// All contents of linked list will be lost after resize.
/// @param interval_begin First index in linked list.
/// @param interval_end Last index in linked list.
/// @param Pre Nonlocal indices before First index in linked list.
/// @param Post Nonlocal indices after Last index in linked list.
/// @param Sorted Result should be sorted or not.
void Resize(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, const std::vector<INMOST_DATA_ENUM_TYPE> & Pre, const std::vector<INMOST_DATA_ENUM_TYPE> & Post, bool Sorted = true);
#if defined(USE_SOLVER)
/// Resize linked list for new matrix.
/// Constructor that gets sizes from the matrix, including non-local mapping.
/// @param A Matrix to get sizes from.
/// @param Sorted Result should be sorted.
RowMerger(const Matrix & A, bool Sorted = true);
/// Resize linked list for new matrix, including non-local mapping.
/// \warning
/// All contents of linked list will be lost after resize.
/// This behavior may be changed in future.
/// @param A Matrix to get sizes from.
/// @param Sorted Result should be sorted or not.
void Resize(Matrix & A, bool Sorted = true);
void Resize(const Matrix & A, bool Sorted = true);
#endif //USE_SOLVER
/// Clear linked list.
void Clear();
......@@ -593,14 +622,26 @@ namespace INMOST
/// @param coef Coefficient to multiply row values.
/// @param r A row to be added.
/// @param PreSortRow Sort values of the row before adding. Will be activated only for sorted linked lists.
void PushRow(INMOST_DATA_REAL_TYPE coef, Row & r, bool PreSortRow = false);
void PushRow(INMOST_DATA_REAL_TYPE coef, Row & r, bool PreSortRow);
/// Add a row with a coefficient into non-empty linked list.
/// Use RowMerger::PushRow for empty linked list.
/// @param coef Coefficient to multiply row values.
/// @param r A row to be added.
/// @param PreSortRow Sort values of the row before adding. Will be activated only for sorted linked lists.
void AddRow(INMOST_DATA_REAL_TYPE coef, Row & r, bool PreSortRow = false);
/// Multiply all entries of linked list by a coefficient.
void AddRow(INMOST_DATA_REAL_TYPE coef, Row & r, bool PreSortRow);
/// Add a row with a coefficient into empty linked list.
/// This routine should be a bit faster then RowMerger::AddRow
/// for empty linked list. It may result in an unexpected behavior
/// for non-empty linked list, asserts will fire in debug mode.
/// @param coef Coefficient to multiply row values.
/// @param r A row to be added.
void PushRow(INMOST_DATA_REAL_TYPE coef, const Row & r);
/// Add a row with a coefficient into non-empty linked list.
/// Use RowMerger::PushRow for empty linked list.
/// @param coef Coefficient to multiply row values.
/// @param r A row to be added.
void AddRow(INMOST_DATA_REAL_TYPE coef, const Row & r);
/// Multiply all entries of linked list by a coefficient.
/// @param coef A coefficient for multiplication.
void Multiply(INMOST_DATA_REAL_TYPE coef);
/// Place entries from linked list into row.
......@@ -629,14 +670,16 @@ namespace INMOST
/// @param a Row a.
/// @param beta Multiplier for row b.
/// @param b Row b.
void Merge(Row & c, INMOST_DATA_REAL_TYPE alpha, Row & a, INMOST_DATA_REAL_TYPE beta, Row & b)
void Merge(Row & c, INMOST_DATA_REAL_TYPE alpha, const Row & a, INMOST_DATA_REAL_TYPE beta, const Row & b)
{
PushRow(alpha,a);
AddRow(beta,b);
RetriveRow(c);
Clear();
}
///Retrive iterator for the first element.
iterator Begin() {return iterator(&LinkedList);}
///Retrive iterator for the position beyond the last element.
iterator End() {iterator ret(&LinkedList); ret.pos = EOL; return ret;}
};
#endif //defined(USE_SOLVER) || defined(USE_AUTODIFF)
......
......@@ -802,7 +802,7 @@ namespace INMOST
entry.j = jj;
entry.k1 = kk1;
entry.k2 = kk2;
if( openshut == "OPEN" )
if( std::string(openshut) == "OPEN" )
entry.open = true;
else
entry.open = false;
......
......@@ -58,10 +58,11 @@ namespace INMOST
////////class RowMerger
RowMerger::RowMerger() : Sorted(true), Nonzeros(0) {}
RowMerger::RowMerger() : Sorted(true), Nonzeros(0), IntervalBeg(0), IntervalEnd(0) {}
INMOST_DATA_REAL_TYPE & RowMerger::operator[] (INMOST_DATA_ENUM_TYPE pos)
{
pos = MapIndex(pos);
if( LinkedList[pos+1].first != UNDEF ) return LinkedList[pos+1].second;
else
{
......@@ -94,46 +95,117 @@ namespace INMOST
INMOST_DATA_REAL_TYPE RowMerger::operator[] (INMOST_DATA_ENUM_TYPE pos) const
{
pos = MapIndex(pos);
if( LinkedList[pos+1].first != UNDEF ) return LinkedList[pos+1].second;
else throw -1;
}
RowMerger::RowMerger(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, bool Sorted)
: Sorted(Sorted), Nonzeros(0), LinkedList(interval_begin,interval_end+1,Row::make_entry(UNDEF,0.0))
: Sorted(Sorted), Nonzeros(0), IntervalBeg(interval_begin), IntervalEnd(interval_end), LinkedList(interval_begin,interval_end+1,Row::make_entry(UNDEF,0.0))
{
LinkedList.begin()->first = EOL;
}
RowMerger::RowMerger(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, const std::vector<INMOST_DATA_ENUM_TYPE> & Pre, const std::vector<INMOST_DATA_ENUM_TYPE> & Post, bool Sorted)
: Sorted(Sorted), Nonzeros(0), IntervalBeg(interval_begin), IntervalEnd(interval_end), NonlocalPre(Pre), NonlocalPost(Post), LinkedList(interval_begin-Pre.size(),interval_end+Post.size()+1,Row::make_entry(UNDEF,0.0))
{
assert(std::is_sorted(Pre.begin(),Pre.end()));
assert(std::is_sorted(Post.begin(),Post.end()));
LinkedList.begin()->first = EOL;
}
void RowMerger::Resize(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, bool _Sorted)
{
LinkedList.set_interval_beg(interval_begin);
LinkedList.set_interval_end(interval_end+1);
LinkedList.set_interval_beg(interval_begin-NonlocalPre.size());
LinkedList.set_interval_end(interval_end+1+NonlocalPost.size());
IntervalBeg = interval_begin;
IntervalEnd = interval_end;
std::fill(LinkedList.begin(),LinkedList.end(),Row::make_entry(UNDEF,0.0));
LinkedList.begin()->first = EOL;
Nonzeros = 0;
Sorted = _Sorted;
}
void RowMerger::Resize(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, const std::vector<INMOST_DATA_ENUM_TYPE> & Pre, const std::vector<INMOST_DATA_ENUM_TYPE> & Post, bool _Sorted)
{
NonlocalPre = Pre;
NonlocalPost = Post;
Resize(interval_begin,interval_end,_Sorted);
}
#if defined(USE_SOLVER)
void RowMerger::Resize(Matrix & A, bool _Sorted)
void RowMerger::Resize(const Matrix & A, bool _Sorted)
{
INMOST_DATA_ENUM_TYPE mbeg, mend;
INMOST_DATA_ENUM_TYPE mbeg, mend, k, l, ind;
//retrive interval of indices
A.GetInterval(mbeg,mend);
//gather non-local mapping from matrix
std::set<INMOST_DATA_ENUM_TYPE> Pre, Post;
for(k = mbeg; k < mend; ++k)
{
for(l = 0; l < A[k].Size(); ++l)
{
ind = A[k].GetIndex(l);
if( ind < mbeg ) Pre.insert(ind);
else if( ind >= mend ) Post.insert(ind);
}
}
//sort to prepare for binary search
NonlocalPre.clear();
NonlocalPre.insert(NonlocalPre.end(),Pre.begin(),Pre.end());
NonlocalPost.clear();
NonlocalPre.insert(NonlocalPost.end(),Post.begin(),Post.end());
Resize(mbeg,mend,_Sorted);
}
RowMerger::RowMerger(Matrix & A, bool Sorted) : Sorted(Sorted), Nonzeros(0)
RowMerger::RowMerger(const Matrix & A, bool Sorted) : Sorted(Sorted), Nonzeros(0)
{
INMOST_DATA_ENUM_TYPE mbeg, mend;
A.GetInterval(mbeg,mend);
LinkedList.set_interval_beg(mbeg);
LinkedList.set_interval_end(mend+1);
std::fill(LinkedList.begin(),LinkedList.end(),Row::make_entry(UNDEF,0.0));
LinkedList.begin()->first = EOL;
Resize(A,Sorted);
}
#endif
RowMerger::~RowMerger() {}
void RowMerger::SetNonlocal(const std::vector<INMOST_DATA_ENUM_TYPE> & Pre, const std::vector<INMOST_DATA_ENUM_TYPE> & Post)
{
assert(std::is_sorted(Pre.begin(),Pre.end()));
assert(std::is_sorted(Post.begin(),Post.end()));
NonlocalPre = Pre;
NonlocalPost = Post;
Resize(IntervalBeg,IntervalEnd,Sorted);
}
INMOST_DATA_ENUM_TYPE RowMerger::MapIndex(INMOST_DATA_ENUM_TYPE pos) const
{
if( pos < IntervalBeg )
{
if(NonlocalPre.empty())
std::cout << "pre " << NonlocalPre.size() << " post " << NonlocalPost.size() << " pos " << pos << " beg " << IntervalBeg << " end " << IntervalEnd << std::endl;
assert(!NonlocalPre.empty()); //there are indices provided
std::vector< INMOST_DATA_ENUM_TYPE >::const_iterator search = std::lower_bound(NonlocalPre.begin(),NonlocalPre.end(),pos);
assert(*search == pos); //is there such index?
return IntervalBeg - NonlocalPre.size() + static_cast<INMOST_DATA_ENUM_TYPE>(search-NonlocalPre.begin());
}
else if( pos >= IntervalEnd )
{
assert(!NonlocalPost.empty()); //there are indices provided
std::vector< INMOST_DATA_ENUM_TYPE >::const_iterator search = std::lower_bound(NonlocalPost.begin(),NonlocalPost.end(),pos);
assert(*search == pos); //is there such index?
return IntervalEnd + static_cast<INMOST_DATA_ENUM_TYPE>(search-NonlocalPost.begin());
}
return pos;
}
INMOST_DATA_ENUM_TYPE RowMerger::UnmapIndex(INMOST_DATA_ENUM_TYPE pos) const
{
if( pos < IntervalBeg )
return NonlocalPre[pos+NonlocalPre.size()-IntervalBeg];
else if( pos >= IntervalEnd )
return NonlocalPost[pos-IntervalEnd];
return pos;
}
void RowMerger::Clear()
{
INMOST_DATA_ENUM_TYPE i = LinkedList.begin()->first, j;
......@@ -148,20 +220,27 @@ namespace INMOST
Nonzeros = 0;
}
void RowMerger::PushRow(INMOST_DATA_REAL_TYPE coef, Row & r, bool PreSortRow)
{
if( Sorted && PreSortRow ) std::sort(r.Begin(),r.End());
//if( Nonzeros != 0 ) printf("nnz %d link %p proc %d\n",Nonzeros,this,omp_get_thread_num());
PushRow(coef,r);
}
void RowMerger::PushRow(INMOST_DATA_REAL_TYPE coef, const Row & r)
{
assert(Nonzeros == 0); //Linked list should be empty
assert(LinkedList.begin()->first == EOL); //again check that list is empty
INMOST_DATA_ENUM_TYPE index = LinkedList.get_interval_beg();
Row::iterator it = r.Begin(), jt;
Row::const_iterator it = r.Begin(), jt;
while( it != r.End() )
{
LinkedList[index].first = it->first+1;
LinkedList[it->first+1].first = EOL;
LinkedList[it->first+1].second = it->second*coef;
index = it->first+1;
INMOST_DATA_ENUM_TYPE pos = it->first;
pos = MapIndex(pos);
LinkedList[index].first = pos+1;
LinkedList[pos+1].first = EOL;
LinkedList[pos+1].second = it->second*coef;
index = pos+1;
++Nonzeros;
jt = it;
++it;
......@@ -172,32 +251,39 @@ namespace INMOST
void RowMerger::AddRow(INMOST_DATA_REAL_TYPE coef, Row & r, bool PreSortRow)
{
if( Sorted && PreSortRow ) std::sort(r.Begin(),r.End());
AddRow(coef,r);
}
void RowMerger::AddRow(INMOST_DATA_REAL_TYPE coef, const Row & r)
{
INMOST_DATA_ENUM_TYPE index = LinkedList.get_interval_beg(), next;
Row::iterator it = r.Begin(), jt;
Row::const_iterator it = r.Begin(), jt;
while( it != r.End() )
{
if( LinkedList[it->first+1].first != UNDEF )
LinkedList[it->first+1].second += coef*it->second;
INMOST_DATA_ENUM_TYPE pos = it->first;
pos = MapIndex(pos);
if( LinkedList[pos+1].first != UNDEF )
LinkedList[pos+1].second += coef*it->second;
else if( Sorted )
{
next = index;
while(next < it->first+1)
while(next < pos+1)
{
index = next;
next = LinkedList[index].first;
}
assert(index < it->first+1);
assert(it->first+1 < next);
LinkedList[index].first = it->first+1;
LinkedList[it->first+1].first = next;
LinkedList[it->first+1].second = coef*it->second;
assert(index < pos+1);
assert(pos+1 < next);
LinkedList[index].first = pos+1;
LinkedList[pos+1].first = next;
LinkedList[pos+1].second = coef*it->second;
++Nonzeros;
}
else
{
LinkedList[it->first+1].first = LinkedList[index].first;
LinkedList[it->first+1].second = coef*it->second;
LinkedList[index].first = it->first+1;
LinkedList[pos+1].first = LinkedList[index].first;
LinkedList[pos+1].second = coef*it->second;
LinkedList[index].first = pos+1;
++Nonzeros;
}
jt = it;
......@@ -206,6 +292,7 @@ namespace INMOST
}
}
void RowMerger::RetriveRow(Row & r)
{
r.Resize(Nonzeros);
......@@ -214,7 +301,7 @@ namespace INMOST
{
if( LinkedList[i].second )
{
r.GetIndex(k) = i-1;
r.GetIndex(k) = UnmapIndex(i-1);
r.GetValue(k) = LinkedList[i].second;
++k;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment