User wants to reconstruct their Particles-derived type object
from the object storage set.  Let's assume their derived type
is MyParticles, and it inherits from Particles<PTraits>.
Recall that the PTraits template argument is used to export 
the engine tag type for the particle attributes and the particle
layout strategy type.  In Pooma 2.3, the only valid choices for
attribute engine tag type will be MultiPatch<DynamicTag,Dynamic>
and MultiPatch< DynamicTag, Remote<Dynamic> >.  Note that the 
particle attributes themselves are of type DynamicArray<T,EngineTag>.
The only valid options for a particle layout strategy will be
UniformLayout and SpatialLayout. 

The MyParticles object is constructed with a valid ParticleLayout_t
object, so we must build that first.  For a UniformLayout, all we 
need is a single int indicating the total number of patches (on all
contexts) to decompose the particle data into.  So this type of
particle layout should be trivial to serialize and deserialize:

template <class Stream>
int serialize(Stream& s, const UniformLayout& layout)
{
  int nBytes = 0;

  // get and serialize the global number of patches in the layout.
  int numPatches = layout.patchesGlobal();
  nBytes += serialize(s,numPatches);

  return nBytes;
}

template <class Stream>
int deserialize(UniformLayout& layout, Stream& s)
{
  int nBytes = 0;

  // get the global number of patches for the layout.
  int numPatches;
  nBytes += deserialize(numPatches,s);

  // use this data to initialize the layout.
  layout.initialize(numPatches);

  return nBytes;
}

For a SpatialLayout, we need a Geometry object and a FieldLayout
object.  SpatialLayout is actually templated on the Geometry and
FieldLayout types, so the types of objects given to the SpatialLayout
constructor must match the template arguments.  The Geometry will
be one of the DiscreteGeometry types described in the header files
in src/Geometry, either DiscreteGeometry.URM.h or DiscreteGeometry.RM.h.
The FieldLayout will be a UniformGridLayout or a GridLayout.  You 
can examine these class headers to see how these objects would be 
serialized and deserialized.

Let's assume for now that we are using the simpler UniformLayout,
and we have constructed a UniformLayout object and initialized it
using the deserialize function.  Now we can pass that UniformLayout
object to the MyParticles constructor and construct an empty MyParticles
object.  It will have all of the particle attributes specified by
the MyParticles class definition, and each of these attributes will
have the right number of patches, but all of the patches will have 
zero elements.  So the next step would be to fill the attributes with
data.  (There are a few other items inside the Particles data that
I have not discussed here, but these should also be initialized.)

Each attribute is a DynamicArray of some type T and with some engine
type tag EngineTag.  (Recall that there are only two valid types for
this attribute engine type tag.)  So as long as we have serialize()
and deserialize() functions that handle DynamicArray, we are in 
business.  The Particles class stores the attributes of MyParticles 
as an AttributeList, which is essentially an STL vector of Attribute
pointers.  Attribute is a base class for AttributeWrapper<T>, a class
that wraps some arbitrary type T.  This storage mechanism allows 
the Particles base class to store a list of pointers to the heterogeneous
set of particle attribute data types that are members of MyParticles.
The serialize() and deserialize() functions for Particles can just loop
through the list and serialize or deserialize each attribute in turn.
Particles stores the number of attributes in the list as well.

There is one minor problem I noticed with invoking serialize() and
deserialize() on the particle attributes through the Attribute base
class using a virtual function call.  Virtual functions cannot be
templated, but the basic serialize() and deserialize() functions 
are templated on the Stream type.  I will have to provide separate
versions of the serialize() and deserialize() virtual functions in
Attribute for each type of Stream we might want to use.  (Perhaps 
there is a more elegant way to solve this problem, but I have not
come up with one yet.)  This is similar to the problem I noticed 
with the print() virtual function in Attribute.  This print() 
function is provided so that the Particles object can loop over
its attributes and print them out in text form to a stream.  There
is no nice way to make this work for any generic type of stream.
You have to supply instances of print() for each type of stream.
   
Let me now attempt to write out the serialize() and deserialize()
functions for Particles.

template <class Stream, class PTraits>
int serialize(Stream& s, const Particles<PTraits>& P)
{
  int nBytes = 0;

  // first serialize the particle layout of P.
  nBytes += serialize(s,P.particleLayout());

  // next, put in our current destroy method code.
  // this is just an int flag indicating which method to use 
  // when filling "holes" in particle attributes that are 
  // created when particles are destroyed
  nBytes += serialize(s,P.destroyMethod());

  // next is our list of deferred particle destroy requests.
  // this is a 1D Array of DynamicArray's (one DynamicArray
  // per local patch) of int's, and it contains lists of local
  // particle indices for particles that are to be destroyed 
  // during the next call to Particles::sync().
  // The easiest way to handle this is to loop over local patches
  // and then serialize the destroy list for each patch.
  // I am assuming here that we have a serialize() function for
  // a DynamicArray<T,Dynamic> object.
  int patchesLocal = P.particleLayout().patchesLocal();
  for (int i=0; i<patchesLocal; ++i)
    nBytes += serialize(s,P.destroyList(i));

  // now let's handle the list of particle attributes.
  // we tell each attribute to write itself out.
  int attributes = P.attributes();
  for (int i=0; i<attributes; ++i)
    nBytes += P.attribute(i)->serialize(s);

  // there is one more item in Particles, the list of
  // particle boundary conditions.  Because the BC's are 
  // templated on the type of the BC subject and object,
  // as well as the type of BC being applied, these are
  // stored via base class pointers in a similar fashion
  // to the attribute list.  So let's use the same approach.

  // Note:  I am not very confident that this is going 
  // to work for particle BC's.  The problem is that BC's
  // store a shallow copy of their subject and object,
  // which may be a particle attribute, an expression 
  // involving particle attributes, or the Particles object
  // itself.  The subject and object that a particle BC
  // referred to are not persistent beyond the lifetime 
  // of the program.  We should probably omit any serialize
  // and deserialize of the particle BC's until we can
  // figure out a way to make these objects persistent.

  /*  
  int boundaryConditions = P.boundaryConditions();
  nBytes += serialize(s,boundaryConditions);
  for (int i=0; i<boundaryConditions; ++i)
    nBytes += P.boundaryCondition(i)->serialize(s);
  */

  // we're done
  return nBytes;
}

template <class Stream, class PTraits>
int deserialize(Particles<PTraits>& P, Stream& s)
{
  int nBytes = 0;

  // first deserialize the particle layout and initialize P.
  typename Particles<PTraits>::ParticleLayout_t playout;
  nBytes += deserialize(playout,s);
  P.initialize(playout);

  // next, set our current destroy method code.
  int destroyMethod;
  nBytes += deserialize(destroyMethod,s);
  P.setDestroyMethod(destroyMethod);

  // next is our list of deferred particle destroy requests.
  int patchesLocal = playout.patchesLocal();
  for (int i=0; i<patchesLocal; ++i)
    nBytes += deserialize(P.destroyList(i),s);

  // now let's handle the list of particle attributes.
  int attributes = P.attributes();
  for (int i=0; i<attributes; ++i)
    nBytes += P.attribute(i)->deserialize(s);

  // finally the list of particle BC's

  /*  
  int boundaryConditions;
  nBytes += deserialize(boundaryConditions,s);
  for (int i=0; i<boundaryConditions; ++i)
    nBytes += P.boundaryCondition(i)->deserialize(s);
  */

  // we're done
  return nBytes;
}

There are still some unsolved problems here, especially with
making particle boundary condition objects truly persistent.
But we ought to at least be able to have the simplest case 
of a Particles-derived object with UniformLayout, single-
context MultiPatch Dynamic Arrays for its attributes, and no
particle boundary conditions working with serialize() and
deserialize() for the Pooma 2.3 release.  This would be a 
nice demonstration of what can be done with the IO system.

   

  
