stuck with own data processor

Hi guys,

I´m trying to write a data processor to count all solid cells in a plane of the lattice. Since I generate the lattice from a simple boolmask, I figured it would be the most easiest way to just count all solid cells in the MultiScalarField3D (for a plane).

Here is my code:


template<typename T, template<typename U> class Descriptor>
class MyFunctional : public BoxProcessingFunctional3D_S<T>  {
  private:
    plint z;
    plint xmax, ymax;
    plint area;
  public:
    MyFunctional (plint z_, plint xmax_, plint ymax_) : z(z_), xmax(xmax_), ymax(ymax_), area(0) {
      }
      
    virtual void process (MultiScalarField3D<bool>& matrix) {
      for (plint i=0; i<xmax; i++) {
	for (plint j=0; j<ymax; j++) {
	  if (matrix.get(i, j, z)) area++;
	}
      }
    }
    
    virtual MyFunctional<T, Descriptor>* clone() const  {
      return new MyFunctional<T, Descriptor> (*this);
    }
};


void CreateMyFunctional (MultiScalarField3D<bool> &matrix, plint z, plint xmax, plint ymax)	{
  Box3D foo(0,xmax,0,ymax,z,z);
  applyProcessingFunctional (new MyFunctional<T, DESCRIPTOR> (z, xmax, ymax), foo, matrix);
  
}

The function CreateMyFunctional is not correct, I think it should look like:

void plb::applyProcessingFunctional(plb::BoxProcessingFunctional3D*, plb::Box3D, std::vector<plb::MultiBlock3D*, std::allocatorplb::MultiBlock3D* >)

but thats where I´m stuck atm, since it´s above my programming skills… I´m reading about std::allocator, but so far it doesn´t make any sense :frowning:

thanks for any input

Hi,

Basically, your code does not compile because there are template statements although the code is not template-dependent. Your code acts on a scalar-field, and you therefore don’t need a Descriptor, which is for block-lattices. You don’t need the type T either, because your code explicitly works for the type “bool”. Therefore, you should simply remove all template statements and template brackets ("< >"), and have your class explicitly inherit from BoxProcessingFunctional3D_S.

This thing about allocators is an advanced C++ concept which is never explicitly used in Palabos.

There is another problem in your code: your class computes a reduction operation (summing over all matrix elements). This cannot spontaneously work in a parallel program, because the value in this case needs to be reduced over all processors as well. Here’s the solution: instead of using BoxProcessingFunctional you should use ReductiveBoxProcessingFunctional, and the reduction is computed for you, properly in serial and parallel. As an example, look at the class BoxSumEnergyFunctional3D in the file dataProcessors/dataAnalysisFunctional3D.h and .hh, which is for computing the average kinetic energy.

Thanks for your answer, I changed the code but still have some errors I can´t solve.
Here´s what the code looks like now:


class MyFunctional : public ReductiveBoxProcessingFunctional3D_S<bool>  {
  private:
    plint z;
    plint xmax, ymax;
    plint area;
  public:
    MyFunctional (plint z_, plint xmax_, plint ymax_) : z(z_), xmax(xmax_), ymax(ymax_), area(0) {
      }
      
    virtual void process (MultiScalarField3D<bool>& matrix) {
      for (plint i=0; i<xmax; i++) {
	for (plint j=0; j<ymax; j++) {
	  if (matrix.get(i, j, z)) area++;
	}
      }
    }
    
    virtual MyFunctional* clone() const  {
      return new MyFunctional (*this);
    }

    plint getArea() {
      return area;
    }
};


plint CreateMyFunctional (MultiScalarField3D<bool> &matrix, plint z, plint xmax, plint ymax)	{
  Box3D foo(0,xmax,0,ymax,z,z);
  MyFunctional* functional;
  functional = new MyFunctional (z, xmax, ymax);
  applyProcessingFunctional (functional, foo, matrix);
  return functional->getArea();
}

and thats the error messages I get:


myfunctional.cpp: In member function ‘virtual MyFunctional* MyFunctional::clone() const’:
myfunctional.cpp:46: error: cannot allocate an object of abstract type ‘MyFunctional’
myfunctional.cpp:28: note:   because the following virtual functions are pure within ‘MyFunctional’:
/home/work/dev/palabos-v0.7r1/src/atomicBlock/reductiveDataProcessingFunctional3D.h:125: note: 	void plb::ReductiveBoxProcessingFunctional3D_S<T>::process(plb::Box3D, plb::ScalarField3D<T>&) [with T = bool]
myfunctional.cpp: In function ‘plb::plint CreateMyFunctional(plb::MultiScalarField3D<bool>&, plb::plint, plb::plint, plb::plint)’:
myfunctional.cpp:58: error: cannot allocate an object of abstract type ‘MyFunctional’
myfunctional.cpp:28: note:   since type ‘MyFunctional’ has pure virtual functions
myfunctional.cpp:62: error: no matching function for call to ‘applyProcessingFunctional(MyFunctional*&, plb::Box3D&, plb::MultiScalarField3D<bool>&)’


So error46/28 is because my virtual function doesn´t look like any of those which MyFunctional inherits from, right ?
But how should it look like - all versions (again make it a template class)I tried so far end up with the same error.

I think it´s not much missing now to get it right (since it pretty much looks like the BoxSumEnergyFunctional and the computeAverageEnergy in dataAnalysisWrapper3D.hh), but I don´t know how to fix it.

Hello,

The function “process” takes an additional argument which is of type Box3D. There are many more mistakes in the code, though. You should really take one of the existing examples (like, as I suggested, BoxSumEnergyFunctional3D), and adapt it to your case, because this stuff cannot be improvised.

Hi,

you were right, taking the existing code of a functional and changing it is the best way, I finally got it to work :slight_smile:

Although, two problems still remain (were one of them I´m sure I´ll solve on my own), about the other one I´m not so sure:

The thing is, the ReductiveBoxProcessingFunctional3D_S´s virtual process function requires a ScalarField, and not a MultiScalarField. And I didnt find any other ProcessingFunctional which has a MultiScalarField in its process function.

So if I use a ScalarField, the code compiles and works - but not in parallel mode. Which is the point of the DataProcessor in the first place, to work in mpi-mode :frowning:

Thats my Functional:


class MyFunctional :
    public ReductiveBoxProcessingFunctional3D_S<bool>
{
public:
    MyFunctional();
    virtual void process(Box3D domain, ScalarField3D<bool>& matrix);
    virtual MyFunctional* clone() const;
    plint getArea() const;
private:
    plint sumEnergyId;
    plint area;
};

MyFunctional::MyFunctional() { 
area = 0;
}

void MyFunctional::process (Box3D domain, ScalarField3D<bool>& matrix )

{
    for (plint iX=domain.x0; iX<=domain.x1; ++iX) {
        for (plint iY=domain.y0; iY<=domain.y1; ++iY) {
            for (plint iZ=domain.z0; iZ<=domain.z1; ++iZ) {
                if (matrix.get(iX, iY, iZ)) area++;
                pcout << matrix.get(iX, iY, iZ);
            }
	}
    pcout << endl;
    }
pcout << area << endl;
}

MyFunctional* MyFunctional::clone() const
{
    return new MyFunctional(*this);
}

plint MyFunctional::getArea() const {
  return area;
}

plint computeArea(MultiScalarField3D<bool>& matrix, Box3D& domain)  {
  MyFunctional  functional;
  applyProcessingFunctional(functional, domain, matrix);
  return functional.getArea();
}

(note: those pcouts are just for testing)

the ReductiveBoxProcessingFunctional3D_S in reductiveDataProcessingFunctional3D.h looks like:

/


// Easy instantiation of boxed data processor for a single scalar field
template<typename T>
struct ReductiveBoxProcessingFunctional3D_S : public PlainReductiveBoxProcessingFunctional3D {
    virtual void process(Box3D domain, ScalarField3D<T>& field) =0;
    /// Invoke parent-method "processGenericBlocks" through a type-cast
    virtual void processGenericBlocks(Box3D domain, std::vector<AtomicBlock3D*> atomicBlocks);
};

Can I just make another one, with a MultiScalarField (and does it work with an AtomicBlock or shouldn´t it be a MultiBlock). Well thats the question, and I´ll try it, but I think it´s not going to be that simple ?

Hi,

The philosophy of Palabos is that, while the end-user works with MultiBlocks, the data-processors are applied individually to each AtomicBlock inside the MultiBlock. That’s how parallelism works in Palabos, and it is explained in the user’s guide. There is even a tutorial that investigates this point in detail.

Yes I´ve started with that tutorial to write my functional. And I think the functional i wrote is OK, the only problem that remains is to get my data :slight_smile:

That parallel programming is still something I´ve to get used to:
In the example with the average energy, you use some BlockStatistics class to calculate the energy (for each process) and then somehow sum it up…
and since i only use one simple variable inside my class to store the value i want, of course it only works for a single process, since it gets split up in parallel mode…

edit:
So, I cant say that I completely understand how that BlockStatistics class is working, but it´s doing what I want right now.
Here´s the final code:


class MyFunctional :
    public ReductiveBoxProcessingFunctional3D_S<bool>
{
public:
    MyFunctional();
    virtual void process(Box3D domain, ScalarField3D<bool>& matrix);
    virtual MyFunctional* clone() const;
    plint getArea() const;
private:
    plint sumEnergyId;
    plint area;
};

MyFunctional::MyFunctional() : area(this->getStatistics().subscribeSum()) { 

}

void MyFunctional::process (Box3D domain, ScalarField3D<bool>& matrix )

{
  plint subarea=0;
  BlockStatistics& statistics = this->getStatistics();
    for (plint iX=domain.x0; iX<=domain.x1; ++iX) {
        for (plint iY=domain.y0; iY<=domain.y1; ++iY) {
            for (plint iZ=domain.z0; iZ<=domain.z1; ++iZ) {
                if (matrix.get(iX, iY, iZ)) subarea++;
            }
	}
    }
statistics.gatherSum(area, subarea);
}

MyFunctional* MyFunctional::clone() const
{
    return new MyFunctional(*this);
}

plint MyFunctional::getArea() const {
  return this->getStatistics().getSum(area);
  
}


plint computeArea(MultiScalarField3D<bool>& matrix, Box3D& domain)  {
  MyFunctional*  functional;
  functional = new MyFunctional();
  applyProcessingFunctional(*functional, domain, matrix);
  return functional->getArea();
}

1 Like