laristra / flecsi

A mirror of FleCSI's internal gitlab repository.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`mesh_definition_u` interface

cmsquared opened this issue · comments

This is a follow-on to our discussions @charest & @tuxfan ;

tl;dr - I think CRTP might be a nice way to extend the interface, but there are other options.

The problem

mesh_definition_u is an abstract base class that defines the API for a mesh definition as seen by FleCSI. Specializations define an implementation of this API (e.g. the exodus definition). The Burton specialization's specialization_tlt_init method calls other methods in FleCSI-SP, which require methods of a mesh definition that are in addition to the API defined by FleCSI. In particular, the method mesh_def.entities(int, int) called here is not defined in mesh_definition_u but rather in flecsi_sp::io::exodus_definition__.

This is currently not a problem because the Burton specialization currently explicitly instantiates an Exodus definition (from flecsi_sp::burton::partition_mesh):

  using exodus_definition_t = flecsi_sp::io::exodus_definition__<num_dims, real_t>;
  ...
  // load the mesh
  auto filename_string = filename.str();
  exodus_definition_t mesh_def( filename_string );

This mesh_def then gets passed around and is used as an exodus_definition__ or a mesh_definition_u type where needed.

The problem arises now that we want to relax the requirement that the mesh file be of an Exodus format. In particular, I have implemented (but not committed) an X3D format reader that provides a mesh definition similar to that of Exodus:

template<typename T>
class x3d_definition__<2, T> : public flecsi::topology::mesh_definition_u<2> {
...
};

The corresponding code changes to partition_mesh look like:

  // mesh definitions
  using mesh_definition_t = flecsi::topology::mesh_definition_u<num_dims>;
  using exodus_definition_t = flecsi_sp::io::exodus_definition__<num_dims, real_t>;
  using x3d_definition_t = flecsi_sp::io::x3d_definition__<num_dims, real_t>;

  // load the mesh
  auto filename_string = filename.str();
  std::unique_ptr<mesh_definition_t> mesh_def;
  if (filename_string.find(".exo") != std::string::npos)
    mesh_def = std::make_unique<exodus_definition_t>(filename_string);
  else if (filename_string.find(".x3d") != std::string::npos)
    mesh_def = std::make_unique<x3d_definition_t>(filename_string);
  else
    clog_fatal("Burton specialization doesn't know how to read " <<
               filename_string);

The problem is that now mesh_def is a pointer to the base class type, it doesn't know about the extension to the API (the previously mentioned mesh_def.entities(int, int)).

Proposed Solutions

What we discussed (doesn't work)

We talked about just adding the entities(int, int) method to the ABC mesh_definition_u as a virtual method with some no-op default implementation. This doesn't work directly because the actual return type of this method depends on the connectivity type. For example, although it is auto deduced in exodus_definition__, the return type is of type exodus_definition__::connectivity_t, which eventually resolves to std::vector<std::vector<std::size_t>>.

We could enforce this type for the return within mesh_definition_u, but that seems to disregard the notion that mesh_definition_u should not need to know details of a specialization. If everyone is fine with this, then this is the cleanest solution. Note that because this is a virtual function, we can't just template the return type.

CRTP

The other idea I have been toying with is using CRTP to handle the polymorphism required between mesh_definition_u and any specializations on top of this. Formally, this would require redefining the API for the derived classes, which would be non-backwards compatible. To be concrete, we'd have something like this (I'm open to better naming for the methods):

template<size_t DIMENSION, typename Specialization>
class mesh_definition_u {
...
  const auto & entities(size_t from_dim, size_t to_dim) const {
    return static_cast<Specialization const &>(*this).entities_imp(from_dim, to_dim);
  }
};

and then the derived classes would be something like

template<typename T>
class exodus_definition__<1, T> : public flecsi::topology::mesh_definition_u<1, template exodus_definition__<1,T>> {
  ...
    const auto & entities_imp(size_t from_dim, size_t to_dim) const {
      return entities_.at(from_dim).at(to_dim);
  }
  ...
};

If the return type couldn't be auto resolved like this, we could use traits to define the return type.

I think this is the most flexible and allows for expanding the API without needing to know details of the specialization.

Some sort of wrapper

We might be able to put another class in the way between mesh_definition_u and the "final" specialized mesh definition (like exodus_definition__) that would declare the functions needed by the specialization that are not needed by the FleCSI API. Something like

// in FleCSI - this stays as is
class mesh_definition_u ... {...};

// in FleCSI-SP
class intermediate... : mesh_definition_u ... {
  virtual std::vector<std::vector<std::size_t>> entities(int, int) = 0;
};
class exodus_definition__... : intermediate... {
  virtual std::vector<std::vector<std::size_t>> entities(int, int) override {...}
};

The unique_ptr above then points to an intermediate type, but we static_cast to a mesh_definition_u when needed?

Other options?

I think entities(int,int) should just be moved into the abstract class interface. Because the other entities() member functions force a return type anyway. And the code calling the entities() member functions requires a specific return type as well. This is the easiest solution to me.

Sorry I hadn't updated this Issue.

I basically came to the same conclusion as @charest I should commit this soon, but had gotten side tracked.

I'll keep this issue open until I get that committed.

@cmsquared how close are you to committing this?

@ipdemes Apologies, I was strongly side-tracked on another project. I will get to this in the next couple of days?

Finally getting around to working on this today.

@cmsquared :Thank you for working on this! Could you, please, close an issue now?

@ipdemes Sure, but I'd like #553 to be merged first.