Type and Class Management in Vish

Types which are used as input/output objects between Vish objects and I/O are required to be equipped with certain meta-information. There are various places for various purposes:

Compile-Time Meta Information

Tensor Operations

Metainformation is for a specific type and describe the algebraic property of a type. This is done via the MetaInfo<> template and TypeIndexingScheme<>. See eagle/MetaInfo.hpp for detailed information. Basically, the MetaInfo template needs to be specialized for a certain type and provide information about atomic type, multiplicity (number of atomic elements), rank (scalar,vector,tensor,…), indexing scheme (covariant, contravariant), grade (vector, bivector, trivector, rotor, …) and coordinate system. There are some related type traits that allow further optional customization.

The indexing scheme is defined through the TypeIndexingScheme<>. It only needs to be defined for tensorial types, i.e. those types that have rank larger than zero. Otherwise, the default implementation will be sufficient. The TypeIndexingScheme<> template needs to provide an integer value for each index number, “+1” for upper indices, “-1” for lower indices. A valid of zero indicates an invalid index and is only allowable for scalar types (zero rank in MetaInfo). For types that are homogeneously contra- or covariant (all indices are either upper or lower), some convenience base class exists that allows to define the indexing scheme for a specific type by deriving from this base class:

template <> struct TypeIndexingScheme<ColorSpace::RGBA::tvector>   : ContravariantIndexingScheme {};

This line defines a type ColorSpace::RGBA::tvector to be a tensor with just contravariant indices (i.e. a tangential vector, in contrast to a co-vector).

Array Operations

Types derived from base classes can be accessed via pointers to the base class. This is true also for C arrays of types made from derived classes, but not for C++ arrays, for instance:

struct A{}; struct B:A{};

Then a std::vector<B> cannot be accessed or even just identified as a std::vector<A>, even though each element of the std::vector<B> is actually an A.

The MemCore::Chunk<> class handles this issue via the META::BaseClass type trait, to be found in meta/BaseClass.hpp . This type trait allows to specify which type is the base class of another type. Thus an Chunk<B> can also be interpreted as a TypedChunk<A>, without any knowledge of type B in some computational algorithm. Any type that is supposed to be used for array operations thus needs to have the BaseClass type trait defined. If not, then this type is considered to be atomic, and no base class information is available, for instance during I/O.

As example, a PhysicalSpace::vector3 is derived from a Vector<double,3>, which is derived from FixedArray<double,3>. Thus, numerical operations that know about a Vector<double,3> can be applied to all kind of vector fields generically (i.e., they must be independent from a coordinate system, which is only available in the derived class). Also, all I/O methods that can handle a FixedArray<double,3> but don’t care about the vector space property can be applied to a PhysicalSpace::vector3.

In contrast, a PhysicalSpace::point3 is not derived from Vector<double,3>, but only from FixedArray<double,3>. Thus, numerical operations requiring vector space property on the type cannot be applied to arrays of PhysicalSpace::point3, but I/O methods for FixedArray<double,3> are still applicable.

Availability of the BaseClass type trait is essential for this mechanism.

Vish Communication

Using Types for communication in the data flow

This requires definition of the VValueTrait<> template for the given type. It needs to be available at the compilation unit when TypedSlot<X> or VOutput<X> is used for the given type X. The template is to be defined in the standard Vish namespace. Member functions can be inline or placed in a .cpp file.

Using Types for communication in the control flow

When a type is used for input notification, which is in particular the case for GUI operations, but also useful for immediate interaction callbacks (synchronization of input values independent from the asynchronous update() function), then the VInputValueTrait<> type trait needs to be defined. It is very similar to the VValueTrait and can borrow functions from there, but for instance needs to define an initialization function for the given type.

Fiber Library

The aforementioned methods are completely inline and compile-time. The FiberLibrary in contrast provides a run-time registry of types, i.e. a list of known types that can be queried at runtime for their properties, without compilation units needing to know anything about the concrete types by itself. This mechanism is provided through the FiberType template and its base class FiberTypeBase as defined in fiber/field/FiberType.hpp . These classes take their information about a specific type from the compile-time meta information and are instantiated automatically. I.e., once a specific type is derived in a compilation unit, such as plugin, then a FiberType is created implicitly, which by construction registers itself in the FiberType registry.

For the FiberType registry to work, the template type traits MetaInfo<> and TypeIndexingScheme<> must be defined. Note that definition of the TypeIndexingScheme<> is optional, but will default to an invalid indexing scheme which is only valid for scalar types. It will lead to errors of the type indexing scheme is not defined for a tensorial type (MetaInfo<>::RANK other than zero).

HDF5 I/O

In theory, the HDF5 I/O layer should be able to entirely go via the FiberType API and make use of automatically registered types, since all the required metainformation for I/O and type identification is available through the FiberType database. In praxis, there are yet some issues with this automatism, which at the moment requires some hacks to get things working.

The FiberType registry allows to map types to TypeActions, which may add any user-defined information to a registed type. The HDF5 I/O layer utilizes this mechanism to add HDF5 type IDs to registered types and to look them up. When reading a file, the HDF5 I/O layers looks for the meta-information of the type given in a file and tries to find the ‘best match’ of registered types. ‘Best match’ means it will first look for a matching coordinate system, then for the same multiplicity of atomic types and extended information like rank, grade and indexing scheme for tensors. Thus, a dataset stored in an unsupported coordinate system can still be identified as a float[3] (for instance), and possibly also as providing vector space properties, even if the coordinate system by itself is not supported.

Creation of Derived Arrays from Meta-Information

To create an array of a specific type, this type usually needs to be known. The FiberType registry provides a mechanism to avoid this and to allow defining some type in one plugin and to allocate arrays (used for fields) in another plugin, which doesn’t know about the former one. For this mechanism ‘allocator’ objects need to be defined, and registered in the FiberType registry. The type-defining plugin would define this allocator object, knowing the full type information, and provide an abstract base class to be registered with a FiberTypeBase in the registry. This one can then be queried in another plugin, using merely the FiberTypeBase-meta information.

To register an allocator object, a type MemArrayAllocator<X> for some type X must be defined. This is implicitly the case when a MemArray<X> is used anywhere in some code. However, it may be beneficial to enforce the creation and instantiation of such a type, since C++ compilers tend to optimize away global constructors if they think they are not used. This registration can be enforced by calling

    MemArrayAllocator<X>::isRegistered();

somewhere in some plugin’s code that is known to be executed before any attempt of creating arrays of type X. The HDF5 I/O layer is supposed to make use of this MemArrayAllocator mechanism.

Charts

Coordinates Systems

The Eagle library provides a Coordinate<> template class which mere purpose is to provide a set of types that are related to one specific coordinate system, for instance cartesian coordinates, polar coordinates, color space coordinates.
Given a generic description of a coordinate system, it derives all types such as points, vectors, co-vectors, bi-vectors etc.

In theory, all the aforementioned MetaInfo and TypeIndexing scheme should be provided on these types automatically as well. In praxis, this does not work yet due to problems with partial template specialization. So as temporary workaround, these type traits need to be specified explicitly for each of these coordinate types.

Chart Objects in the Fiber Library

The Fiber Library provides chart objects that are ‘anchors’ relative to which data are related. There may be multiple chart objects referring to the same type, of instance multiple charts for cartesian coordinates or an RGBA color space. Data may exist in one chart and requested in another chart. Transformation among chart objects of the same type is supposed to be rather trivial, transformation between chart objects of different type require providing of an explicit transformation routine.

Each chart domain is related to an type (a child class of the Fiber::Chart class), and there is one chart object for each chart domain that is considered to be a ‘standard’ or default chart. Given the type of (derived) Fiber::Chart and a name, a concrete chart object can be retrieved. With no name given and just a type, the standard chart object will be retrieved. Internally, the Fiber::Chart class manages a registry of ChartCreators.

The Fiber::Chart objects are not related to Eagle::Coordinate<>. Any Fiber::Representation can carry any coordinate type as of now.

The HDF5 I/O Layer associates a prefined name in the HDF5 file with a Fiber::Chart object. In a future version, this association may be done based on the type properties found in dataset, but as of now it is based on the name of a skeleton group. Thus, multiple chart objects of the same domain are not yet supported in the HDF5 I/O layer (TBD in function LoadSkeleton() in FiberIO/HDF5/LoadSkeleton.cpp) .

As of now, there is no relationship enforced between Fiber::Chart objects (runtime-information) and Eagle::Coordinate<> types (compile-time information). However, Fiber::Chart objects are associated with F5 ChartDomainID’s (matched by member type name), and members in a ChartDomainID are related to the FiberType registry, and therefore to Eagle::Coordinate<> types.

Add a color type (or any other coordinate type) to FiberF5

A color is considered to be a vector. So an image e.g. is a color field in a color-chart on a Cartesian uniform grid.

Vish-Layer

– Involved adjustments in Eagle (ColorSpace.hpp):

  • Add a struct RGBA to the eagle library defining the number of elements (Dims) and names of the components. This will be used as a template parameter for the Coordinate class.
  • Derive a color type of the coordinate class e.g: class rgba_float_t : public Coordinates<RGBA, float>::vector. This introduces the type as a type in RGBA coordinates using float for numerical representation and being a vector.
  • Provide a template trait for MetaInformation about the type: template <> struct MetaInfo<rgba_float_t> : MetaInfo<rgba_float_t::Base_t::Base_t>. Here one specifies meta information as constants such as collected in fiber/field/FiberTypeBase.hpp. e.g: MULTIPLICITY (number of components), RANK (tensor rank), GRADE.
  • Provide a template trait to have knowledge about the Base class of the color type available (in NameSpace META): template <> struct BaseClass<Eagle::rgba_float_t>.

– Involved adjustments in Shrimp (enabling the colortype to be a valid module intput/output)

  • Add a trait: template <> class SHRIMP_API VValueTrait<rgba_float_t>. Implement the to and from text conversion function.
  • Add a trait: template <> struct VInputValueTrait<rgba_float_t>. Implement the init and text conversion function.

– Involved adjustments in Fiber (fish/fiber)

  • Add a chart object for RGBA charts in fiber/grid: class GRID_API RGBAChart : public Chart. Set the name of the chart by defining a creator object. Note that the name must should the F5 name, but the code here shall be independent of F5. Specify component names.
  • Register the rgba_float_t in fiber/field/ChartRegistry.hpp/cpp as a chart for fields? In RegisterTypes(): nTypes += MemArrayAllocator<typename ChartType::rgba_float_t>::isRegistered() and in RegisterStandardCharts(): nt += MemArrayAllocator<Eagle::rgba_float_t>::isRegistered(); (Did not really understand why)

– Involved adjustments in FiberIO (fish/fiber/FiberIO/HDF5)

  • HDF5init.c: Register the Eagle::RGBA coordinate type and associate it to the F5 name. Make a possible domain type of rgba_float_t as TypeAction.
  • HDF5init.c: Register a HDF5 hid for the rgba_float_t as a component type and setting the component names. (Not sure if this is really required. How to tell, that it is a vector type?
  • Add an iterator for the new color representation in LoadSkeleton.cpp for the F5iterate_topology_fields in LoadSkeleton(): rgba_field_iterator, that will hanlde a representation with the F5 color name FIBER_HDF5_DEFAULT_COLOR_CHART. Required for reading only.

F5-Layer

  • Provide a data type for a float color in F5 types: typedef struct { F5_float_t r,g,b,a; } F5_rgba_real_t;
  • Create a rgba color chart in F5coordinates.h/c: F5coordinates.h:#define F5T_COLOR_RGBA_32F F5B_new_global_color_chart_rgba_real()->SinglePrecision.Vector_hid_t. Here the color becomes a vector type.
  • Define names for the chart groups in the hdf5 file in F5defs.h: #define FIBER_HDF5_DEFAULT_COLOR_CHART FIBER_HDF5_RGBA_CHART; define FIBER_HDF5_RGBA_CHART “StandardRGBA”
  • Provide a check of the fieldtype for a color chart in F5Bchart.c in F5file_type() and use the F5Tmake_tensor function to save the global tensor type? Sets the covariance to 1, uses the defined name and also makes it a Vector. Not 100% sure… Here different colortypes require different handling (integer type etc. )
  • In a converter following code will create an image (uniform) grid with colors in the colorspace (will be replaced by a convenience function, if stabalized):
        // create uniform grid
        fpath = F5Rcreate_uniform_cartesian3D(FileID, Time, gridname, &Origin, &Delta, dims, NULL );
    
        // create color representation 
        fpath = F5LTcreate(FileID, &Time,
                   gridname,
                   F5B_new_global_color_chart_rgba_real(), // chart domain
                   F5B_new_global_color_chart_rgba_real,   // coordinate creator
                   FIBER_HDF5_DEFAULT_COLOR_CHART,         // name of coordinate system (StandardRGBA)
                   FIBER_HDF5_POINTS,                      // topologyname (point position field)
                   0,                                      // index depth
                   0,                                      // skeleton dimensionality
                           3,                                      // dataspace dimensionality (3D uniform grid)
                   0 );                                    // refinement
    
        // write a color field 
        F5Fwrite( fpath,                   // representation to write to
                  "color",                 // name of the field
                  3,                       // number of dims
                  dims,                    // size in each dimension (an 512x512 image would have dims[3]={512,512,1}
              F5T_COLOR_RGBA_32F,      // memtype
              F5T_COLOR_RGBA_32F,      // datatype
              result_rgb,              // data pointer of F5_rgba_real_t*
              F5P_DEFAULT );           // additional store properties

    SinglePrecision.Vector_hid_t. Here the color becomes a vector type.

  • Define names for the chart groups in the hdf5 file in F5defs.h: #define FIBER_HDF5_DEFAULT_COLOR_CHART FIBER_HDF5_RGBA_CHART; define FIBER_HDF5_RGBA_CHART