COOLFluiD  Release kernel
COOLFluiD is a Collaborative Simulation Environment (CSE) focused on complex MultiPhysics simulations.
Using Components

Allocating a standalone component

The most basic function to allocate a component is common::allocate_component<T>(). A component is always defined by a name.

boost::shared_ptr<ComponentType> component_ptr = common::allocate_component<ComponentType>("component_name");

The return value is a boost::shared_ptr, a specialized pointer to the allocated component that keeps and shares ownership.
More information on boost::shared_ptr and why it is used can be found on the page Shared pointers and Handles.

Note
It is NOT ALLOWED to create a component the following way:
ComponentType component("component_name"); // will not compile!
This is not allowed because a boost::shared_ptr cannot be created from this object. It is always necessary to use an allocation with a boost::shared_ptr.


Note
Common mistake! Don't use a Handle as the return type for the common::allocate_component<T>() function!!!
Handle<ComponentType> component_handle = common::allocate_component<ComponentType>("component_name"); // wrong! (but will compile)

Handling components

Adding, Renaming, Moving and Removing a component

If we want to nest a allocated component with name "component_name" in another existing component "parent_component", we can use the Component::add_component() function. We want to rename the component first to "my_name";

boost::shared_ptr<ComponentType> component_ptr = common::allocate_component<ComponentType>("component_name");
component_ptr->rename("my_name");
ParentComponentType& parent_component = ... ;
parent_component.add_component(component_ptr);

This will add ownership of the allocated component stored in component_ptr to parent_component, so that when component_ptr gets deleted, the component still exists, nested in parent_component. It is possible to move a nested component from one parent to another:

component_ptr->move_to(other_parent_component);

It is also possible to remove a nested component (the reverse operation of Component::add_component() )

component_ptr = other_parent_component.remove_component("my_name");

Accessing a component

Access through its name

When the name of the desired component is known, use the Component member function Component::get_child()

Handle<Component> child_component = parent_component.get_child("my_component");

When the component needs to be cast to a known derived type (e.g. DerivedComponentType), use the Component member function Component::handle<T>()

Handle<DerivedComponentType> child_component = parent_component.get_child("my_component")->handle<DerivedComponentType>();

Access through its path

When the path of the desired component is known, use the Component member function Component::access_component() Paths can be relative to the component, or global.

Handle<Component> relative_component = current_component.access_component("../../my_component");
Handle<Component> absolute_component = current_component.access_component("/absolute/path/to/my_component");

When the component needs to be cast to a known derived type (e.g. DerivedComponentType), use the Component member function Component::handle<T>() again

Handle<DerivedComponentType> relative_component = current_component.access_component("../../my_component")->handle<DerivedComponentType>();

IMPORTANT: When to access a component? Accessing a component from the tree can be a costly operation and should be avoided in loops that need to be as efficient as possible. It is therefore highly recommended to access the desired component before the loop, and create a reference to it. Several ways are widely used:

Component& myComp_1 = <function that returns the type "Component"> ;
Handle<Component> myComp_2 = <function that returns the type "Component">.handle<Component>() ;
Component& myComp_3 = *<function that returns the type "Handle<Component>" ;
Handle<Component> myComp_4 = <function that returns the type "Handle<Component>"> ;
for (int i=0; i<1000; ++i)
{
// make use of myComp_1, myComp_2, myComp_3, myComp_4, myComp_5
// without calling costly function that looks for a component
}

Notice the "&" sign, indicating that the component will not be copied, but referenced, so that the original, residing in the tree will be accessed. For myComp_2, a shared pointer of the Component type is created by calling the Component member function Component::self(). For myComp_3, a dereferenced version is obtained by using the asterisk to dereference the Component::Ptr. For myComp_5, a direct pointer is obtained from the shared_pointer by calling the boost::shared_ptr member function get(). Care must be taken for myComp_3 and myComp_5, if the shared_ptr is created inside the function that returns it, and if that function doesn't store the component in the component tree, as the shared_ptr will not stay alive. In that case, the component will be invalid.

Say you have a component "myComp" of type Component. To create a subcomponent of type Group with name "group1" inside myComp , use Component::create_component<T>() function:

Handle<common::Group> myGroup = myComp->create_component<common::Group>("group1");

Say you want to create a component of the abstract type mesh::MeshReader, but with concrete implementation from a gmsh reader, use then the Component::build_component() function

Handle<mesh::CMeshReader> mesh_reader =
myComp->build_component("meshreader","cf3.mesh.gmsh.Reader")->handle<mesh::MeshReader>();

Here "cf3.mesh.gmsh.Reader" is the name of the concrete class cf3::mesh::gmsh::Reader The possibility of this last approach is that component creation can be configurable, as the concrete type can be passed as a string.

How to configure a component

Using c++ code

Say the component "myComp" has a configuration option "My Integer Number" which is of the type "integer", then the value of option "My Integer Number" can be modified or configured using the code:

myComp.options().configure_option( "my_integer_number" , int(2) );

It is important to statically cast the value to the type int. Similarly for another option "My Real Number" with value of type "real" , the code would have to be:

myComp.options().configure_option( "my_real_number" , Real(2) );

Accessing these properties occurs using the function property().

int my_integer = myComp.options().value<int>("my_integer_number");
Real my_real = myComp.options().value<Real>("my_real_number");

Notice the value<int>() part in this code.

Access through a search

It is also possible to perform a search for components using predicates. What is returned from a search is a component iterator range. This is helpful as the iterator range can easily be used in a "boost_foreach" macro:

boost_foreach (Component& component, <search function returning iterator range>)
{
// do stuff with component
}

A search function can look in a component going only one level down, or recursively traverse the whole tree of child components and its child components etc. Say we have a component tree as follows with names:

 root                      ( type = Root               ,  tags = )
     component1            ( type = Group              ,  tags = )
         subcomponent1     ( type = MyType             ,  tags = )
         subcomponent2     ( type = MyType             ,  tags = )
     component2            ( type = Group              ,  tags = special_tag )
         subcomponent1     ( type = MySpecializedType  ,  tags = )
         subcomponent2     ( type = MyType             ,  tags = special_tag )

In the case we want to visit only the first level child components of root, use find_components() :

boost_foreach ( Component& component , find_components(root_component) )
{
// component will be in order: component1, component2
}

Note the & since component is non-copyable, and accessing it will access the one in the tree. In the case we want to visit every component in the tree under root, use find_components_recursively() :

boost_foreach ( Component& component , find_components_recursively(root_component) )
{
// component will be in order: component1, subcomponent1, subcomponent2, component2, subcomponent1, subcomponent2
}

There are ways to filter this range, by appending "_with_filter" , such as find_components_recursively_with_filter(). The function then takes 2 arguments: first the component, and secondly a predicate. A predicate is a mini class with the operator () defined and returning true or false. Developers can create their own predicates to do advanced searches. Some built-in predicates exist such as "IsComponentName or IsComponentTag".

All components with name "subcomponent1" :

boost_foreach ( Component& component , find_components_with_filter(root_component , IsComponentName("subcomponent1") )
{
// component will be in order: //component1/subcomponent1 , //component2/subcomponent1
}

All components with tag "special_tag" :

boost_foreach ( Component& component , find_components_recursively_with_filter(root_component , IsComponentTag("special_tag") )
{
// component will be in order: //component2 , //component2/subcomponent2
}

All components with tag "MyType" :

boost_foreach ( Component& component , find_components_recursively_with_filter(root_component , IsComponentTag("MyType") )
{
// component will be in order:
// component1/subcomponent1 , component1/subcomponent2 , component2/subcomponent1 , component2/subcomponent2
}

This last search is interesting as it allows to search by the type of the component. We would then of course like to use specialized functionality of that component, and it would have to be cast to the type we look for. To avoid this code, there is a templated-by-type version of every previously shown search function that automatically casts the component to the type. All you have to do is add <TYPE> after the find function, where "TYPE" is the type of the component you look for.

The last example could be rewritten to give all components of type MyType using find_components_recursively<MyType>() :

boost_foreach ( MyType& component , find_components_recursively<MyType>(root_component) )
{
// component is now of type MyType and will be in order:
// component1/subcomponent1 , component1/subcomponent2 , component2/subcomponent1 , component2/subcomponent2
}

Note that it is no longer necessary to use the "filtered" version, as this happens under the hood. It is still possible to filter on tags or names or other predicates.

To get the components of type MyType with extra tag "special_tag", use find_components_recursively_with_tag<MyType>() :

boost_foreach ( MyType& component ,
find_components_recursively_with_tag<MyType>( root_component , IsComponentTag("special_tag") ) )
{
// component is now of type MyType , and will have the tag "special_tag", and will be in order:
// component2/subcomponent2 and NOT component2 as it is not of the type MyType
// although it also meets the IsComponentTag("special_tag") predicate
}

Send comments to:
COOLFluiD Web Admin