Base Extensions#

Note

The Base-extensions module is a commercially available extension for DAVE.

This module provides a framework for easy making of larger custom nodes. This is useful when modelling many very similar assets such as barges, cranes spreaderbeams, etc.

image-20220430160410392

This module provides means to create the structure of these things in a node and then read the actual properties from a .csv datafile. The different assets can then be loaded from different .csv files. Nodes like this are called super-elements.

Contents

  • Super elements (frame / rigidbody based) base classes

  • Standard nodes in super elements (super-element properties) and their tables in .csv file

  • Attachment types and points

  • Exposed properties of attachments

  • Data tables in .csv files

  • Writing the python backends

Super-Elements#

Super elements are nodes containing a number of other nodes, possibly combined with some custom defined logic.

The properties of some of these nodes are read from a .csv data-file. Other nodes can be set by the user, these are called ‘attachments’.

Assume you have a large number of islands. And you want to have a database of these islands in DAVE. In that case we can make a super-element that can use to model islands. Each island will get its own .csv file containing the particulars of that island.

Islands can have different sizes, shapes and may even be floating.

Besides that many things can optionally be present on the island. These do not really belong to the island, but are often there. So we want to give the user a option to easily add/remove them by (un)ticking a box:

In DAVE this super-element would look as follows:

  • Frame (island)

    • visuals

    • zero or more buoyancy shapes (the first island floats)

    • zero or more weight components

    • zero or more attachment points for swings

This first category are the nodes that are always there. These are called the “super-element properties”. The number of these nodes and their properties are defined in a .csv file. The user only has to select which .csv file to use.

On top of that there are attachments:

  • Trees

  • Lighthouse

  • Swings

  • Monkeys

The attachments are things that are optionally there. They are also defined in the .csv file but the user can choose to enable or disable them. We can also give the user the ability to change some of the properties of these attached nodes (exposed properties).

Super-element properties#

The super-elements properties are just ordinary nodes.

The types of nodes that can be present in a super-element are defined in the class. The number of nodes and their properties are obtained from the .csv file of the island.

Available super-element properties#

The following classes are included in this module:

Class

Fields

SEP_Bollard

Name, x, y, z, capacity,capacity x, capacity y

SEP_Buoyancy

Name, resource, x-offset, y-offset, z-offset, x-rotation, y-rotation, z-rotation, x-scale, y-scale, z-scale, invert-normals

SEP_ContactMesh

Name, resource, x-offset, y-offset, z-offset, x-rotation, y-rotation, z-rotation, x-scale, y-scale, z-scale, invert-normals

SEP_Point

Name, x, y, z

SEP_RoundBarPoint

Name,x, y, z,radius, x-axis, y-axis, z-axis

SEP_Tank

Name, resource, permeability, density, fill-%, x-offset, y-offset, z-offset, x-rotation, y-rotation, z-rotation, x-scale, y-scale, z-scale, invert-normals

SEP_Visual

Name, resource, x-offset, y-offset, z-offset, x-rotation, y-rotation, z-rotation, x-scale, y-scale, z-scale

SEP_Weight

Name, mass [mT], Cog-x, Cog-y, Cog-z, footprint: elevation, footprint: x-min, footprint: x-max, footprint: y-min, footprint: y-max, inertia: rxx, inertia: ryy, inertia: rzz

SEP_WindAreaPoint

Name, x, y, z, A, x-normal, y-normal, z-normal, Cd, Area kind
where area kind is either plane, sphere or cylinder

Attachments#

Attachments are nodes that can be attached to a super-element.

Attachments are a single node (but that node can be an Component)

To attach an attachment you need two things

  1. An attachment point

  2. An attachment-type

Say we want to plant some trees on the island, and all trees are equal.

We can then make a single attachement-type for a Tree. In this case the attachment will be a visual.

To specify where the trees can be planted, we create multiple attachment points. Each of these points gets a name and at each of these points a tree can be spawned by the user.

Attachment points can accept more than a single attachment type. In the GUI the user gets a drop-down box for each attachment point in which one of the possible attachment types can be activated.

image-20220430141303877

Attachment-points#

These define what can be attached where. They are defined in the .csv file as follows (Using the Table (*) definition):

*Attachments
#
# Name,                  Target, posx, posy, posz, rotx, roty, rotz, attachments
Monkey 1,                self  ,  3  ,  3   ,  3,   , 0   , 0  , 0  , Small monkey, Big monkey
Monkey 2,                self  ,  2  , -3.5 ,  5,   , 0   ,10  , 0  , Small monkey, Big monkey
Palm tree center,        self  ,  0  ,  0   ,  2    , 0   , 0  , 0  , Palmtree
Another point,           self  ,  2  ,  0   ,  2    , 0   , 0  , 0  , Palmtree, Small monkey, Big monkey
  • name: name of the point [unique].
    The name shall be unique and shall also not be the name of an already existing node.

  • target: the node that the attachment needs to be attached to.

    • Use “self” to attach to the main node

    • Use “none” to attach to nothing

    • Use the relative name of the node for SE-properties created in the super-element. For example crane/boom.

  • posx, posy, posx, rotx, roty, rotz: define the location and rotation under which the attachment should be attached. For target=none these are global coordinate, otherwise they are local.

  • attachments: This is a comma-separated list of all possible attachment types (names) that can be attached to this point. These names need to match one of the defined attachment type names (next).

Attachment types#

These define WHAT can be attached. These are also defined in the .csv file

The definition of an attachment type is as follows (using the Section (@) definition):

@ATT: Small monkey      # This is the NAME of an attachment type,
                        # @ATT: signals that this section is an attachment
class, Visual           # This is the node-class. In this case the attachment is a "Visual"
path, res: monkey.obj   # These are properties of the node. All properties are supported.
scale, 0.5,0.5,0.5      # this evaluates to node.scale = (0.5, 0.5, 0.5)

The first line is @ATT: <name> where <name> is the name of the attachment type. This name is unique.

Below that are a number of “settings”. They are in the following form: <setting> , <value>

The only mandatory setting is class which defines the type of node that this attachment is made of.

The other settings are the properties that need to be applied to that node. In this example path, res: monkey.obj sets the .path property of the created Visual to res: monkey.obj.

scale, 0.5,0.5,0.5 sets the .scale property to (0.5, 0.5, 0.5)

All together this means that the following is done if this attachment-type is attached:

  • create a Visual

  • Attach it to the attachment point by setting its parent property

  • set its path property to res: monkey.obj

  • set its scale property to (0.5, 0.5, 0.5)

API - Attaching an attachment#

Now that we have attachment-types and attachment-points the hard work is done and we can attach something. Attaching something is easy:

node.attach(WHAT, WHERE)

For example:

s['Monkey Island'].attach(what = 'Palmtree', where = 'Palm tree center')

This creates

  1. a dedicated Frame at the position of the attachment-point.

  2. the prescribed Visual node on that frame

and then sets the properties of the created visual as defined in the attachment-type.

Exposing properties of attached items#

The use may need to adjust one or more of the properties of an attached node. Because the attached nodes are managed by the super-element the user can not do that directly.

So we need to expose those properties through the super-element.

Exposing properties is done by including a line “exposed” in the Attachment type:

@ATT: Palmtree          # This is the NAME of an attachement type,
class, Visual           # This is the node-class. In this case
path, res: palmtree.obj # These are properties of the node
expose, scale, offset
expose, scale, offset     # Expose scale and offset properties to user

image-20220430144307346

API#

Once exposed, the exposed properties of the attachment can be get/set using:

a = b.attachment_points['Stability pontoon on ps']

x = a.get_exposed('x')
a.set_exposed('x',150)
assert a.get_exposed('x') == 150

The exposed properties are managed by the super-element and thus written to python when generating the model code.

To get a list of exposed properties do: a.active_type.exposed_properties

Attachment classes#

The line

class, Visual

defines that the attachment is a “Visual”-node.

All standard DAVE nodes are supported as well as any node registered in the DAVE_Additional_Runtime_Modules dictionary.

The fact that Components are supported makes its possible to use other DAVE models as attachment.

Example of a component:

@ATT: Stability pontoon 15x3x4   # This is the NAME of an attachement type, @ATT: signals that this section is an attachment
class, Component                # This is the node-class. In this case the attachement is a "Component"
path, res: SP/pontoon_15x3x4.dave   , # These are properties of the node. All properties are supported.
z, -2.5                       , # place the component at z=-2 below the connection point

Passing additional arguments to the Node constructor#

Some node classes may need additional arguments to be passed to the constructor. For example a Suspended4PLoad requires information about the prongs (points) before it can be created.

When attachments are activated, DAVE scans for properties that can be passed directly to the constructor.

In this case the constructor (__init__) is as follow:

def __init__(self, scene: Scene, name: str, prong1, prong2, prong3, prong4):

this means that the properties prong1, prong2, prong3 and prong4 are passed directly to init, meaning that the node can be made.

@ATT: Swing
class, Suspended4PLoad   # Suspended load takes four prongs are required input
prong1, point1,          #
prong2, point2,          # these four attributes
prong3, point3,          # are mandatory
prong4, point4,          #
rigging_length, 2       # these are optional
length, 3.5
width, 4
height, 0.2
mass, 0.1
expose, mass, rigging_length, EA, length, width, height, cogx, cogy, cogz

The attachment activation function also checks if point1, point2, point3 and point4 happen to be names of super-element properties. If that is the case (which it is here) then they are replaced with references to the corresponding nodes:

*Points
# Name, x, y, z
point1, 4, -4 , 4
point2, 8, -4 , 4
point3, 8, -8 , 4
point4, 4, -8 , 4

the result is a configurable swing.

image-20220818064857954

image-20220818085452471

Tags#

Tags can be applied to any created node. Tags can be specified using a tags-table.

Because tags typically need to be applied to more than a single node it is possible to describe the nodes by name and or type. Wildcards can be used in the name. Example:

*Tags
# Name        # Type          # Tag
monkey,         *         ,  danger
*cat*,         Point      ,  cute
  *  ,         Circle     ,  round
beam ,         Frame      ,  heavy     

the first line will tag any created node with a name ending with “monkey” of any type with “danger”.

The second line will tag any created node with a name containing “cat” AND of type “Point” with tag “cute”.

The third line will tag any circle (this includes roundbars) with “round”.

The last line will match all all nodes ending with “beam” of type Frame or any class derived from it.

The rules are:

  • * matches any number of characters, including zero

  • ? matches exactly one character

  • names are matches against *name (meaning anything can be added in front)

  • nothing matches all

  • types are matched using isinstance meaning that derived classes are matched.

Other data#

Other data can be read from the .csv file as well.

Tabular data can be read as “data-tables” while single settings can be read as “settings”.

Data-tables#

Data-tables can be defined in the .csv file like:

*Bananas
Banana1,sweet, 1, in the tree
Banana2,baked, 1, hidden
And, now, for something, completely, different

The table is identified by the * in front of the name. The table ends when an empty line is encountered or when something else starts.

It needs to be defined which data-tables are to be expected. This is done in the class by setting the class-variable data_table_names:

class DEMO_Island(SuperElementFrameBased):

    data_table_names = ('Bananas',)  # note the notation for a tuple with one entry

All the data in the table is read into nested list which is stored in a dictionary data-tables.

print(monkey_island.data_tables['Bananas']) 

gives:

[['Banana1', 'sweet', 1.0, 'in the tree'],
 ['Banana2', 'baked', 1.0, 'hidden'],
 ['And', 'now', 'for something', 'completely', 'different']]

Settings#

Any value defined in the .csv outside a table, attachment or attachment point is a setting. Settings are defined as key, value:

# General settings
#  key, value (s)
population, 0
location, imaginary space
best_visited_between, April, November

They are read into a dictionary with name data_settings:

monkey_island.data_settings :

{'population': 0.0,
 'location': 'imaginary space',
 'best_visited_between': ['April', 'November']}

hence all keys need to be unique.

It needs to be defined which settings are to be expected. This is done in the class by setting the class-variable expected_settings.

The keys of this dictionary are the expected settings. The values can be strings or tuples. If a tuple then the first entry is the documentation and the second value is the default for the setting.

class DEMO_Island(SuperElementFrameBased):

    expected_settings = {'population' : 'the number of monkies living on the island',
                         'location' : ('description of the location of the island',0),
                         'best_visited_between' : ('Suggestion of best months to visit',['April','November'])}

ValueErrors are thrown if settings are missing or unexpected settings are provided.

Optional settings#

Besides expected settings, there is also to provide optional settings. This is done by defining a optional_settings dictionary next to the expected settings. The only difference is that optional settings that are not defined will simply not be added to the dictionary. In that case no error will be raised.

Doing something with the loaded data#

Override the method

def onCSVloaded(self):
    """Function is called after csv is loaded (setting .path triggers that)"""
    pass

to do something with the data obtained from the .csv if needed.

Future work and ideas#

Managed or not-managed:#

In the current master branch the attachments are managed by the super-element. This means that the attachments themselves are read-only.

It is possible to make them not-managed, there is a PR for that:

The Frame that is created by the attachment point is managed. The attached node itself is not managed meaning that is it freely editable and can even be deleted.

The .node property of AttachmentPoint checks if the created node still exists in the scene before returning it. So it returns None if the node no longer exists.

This means that the attachment-point can not create the node. That would override its possible user changes. So AttachmentPoint.create() returns False for the node, but True for the Frame.

This means that exporting the AttachmentPoint needs to create the Frame but not the node.

But in the end the attachment point does need a reference to the node (if it exists). This is set at AFTER creating all nodes because otherwise we would run into circular references.

Dissolving attachments#

It should be straight-forward to dissolve an attachment. Just unmanage it

Attachments are nodes that can be attached to a super-element.

Attachments are a single node (but that node can be an Component)

To attach an attachment you need two things

  1. An attachment point

  2. An attachment-type

Creating a super-element in Python#

A super-element can be based on a frame or a rigid-body. The basics are similar.

For the island we will use a Frame as basis. This means we derive our class from SuperElementFrameBased

We want the super-element to contain visuals, weights and buoyancy nodes. This is easily done using SEProperty objects. The properties and number of those nodes can then be defined in the .csv file.

The only thing that needs to be done for this is to set the class-variable su_properties_tables dictionary. The keys of the dictionary correspond to the tables names in the .csv file while the value is the SEProperty class. So in the following example the .csv file is expected to have a table named “Visuals” where each row supplies the input for a single SEP_Visual object.

from DAVE_BaseExtensions.super_elements_common import SuperElementFrameBased
from DAVE_BaseExtensions.super_elements_properties import SEP_Visual, SEP_Weight,SEP_Buoyancy

class DEMO_Island(SuperElementFrameBased):

	su_property_tables = dict()
	su_property_tables['Visuals'] = SEP_Visual
    su_property_tables['Weights'] = SEP_Weight
    su_property_tables['Buoyancy'] = SEP_Buoyancy
    su_property_tables['Points'] = SEP_Point
    
    csv_extension = 'island.csv'

A dictionary su_property['key'] is created containing lists of SU_properties using the same keys. In the example above this means that any DEMO_Island node will contain a list of its visual nodes as node.su_property['Visuals']

Note: Reserved names (Tags, Attachments) should not be used here

and register is in the usual way:

# Register the super-elements for runtime

from DAVE.settings import DAVE_ADDITIONAL_RUNTIME_MODULES
DAVE_ADDITIONAL_RUNTIME_MODULES["DEMO_Island"] = DEMO_Island

We now have a super-element that can read a number of visuals, weight, buoyancy shapes and points from a comma-separated-values file ending with island.csv

An example of such a file is:

# This is example data for an island in .csv format (comma separated values)
#
# General settings
#  key, value (s)
population, 0
location, imaginary space
best_visited_between, April, November
#
*Visuals
#
# Name , resource, off-x, off-y, off-z, rot-x, rot-y, rot-z, scale-x, scale-y, scale-z,,,
Visual1, res: island.obj,0,0,0,0,0,0,1,1,1,
#
*Buoyancy,,,,,,,,,,,,,
# Name, resource, off-x, off-y, off-z, rot-x, rot-y, rot-z, scale-x, scale-y, scale-z,invert_normals,,
Hull,res: island.obj,0,0,0,0,0,0,1,1,1,false
#
*Weights
# 'Name', 'mass [mT]', 'Cog-x', 'Cog-y', 'Cog-z', 'footprint: elevation', 'footprint: aft', 'footprint: front', 'footprint: sb', 'footprint: ps', 'inertia: rxx', 'inertia: ryy', 'inertia: rzz'
Main chunk, 230,     0       ,  0     ,  0     ,  0                    , -5              ,  5                ,   -5           , 5              , 1             , 1             , 1
Second chunk, 3,     2       ,  0     ,  0     ,  0                    , -5              ,  5                ,   -5           , 5              , 1             , 1             , 1

which looks awful. Luckily it can be edited in a spreadsheet program like excel or libreoffice which makes it look much better:

image-20220307204520301

It follows a few simple rules:

  1. anything after a # is ignored

  2. A star (*) signals the start of a “table”.

  3. A table ends when an other table starts or an empty row is encountered or the file ends. A row starting with a # does not count as an empty row.

In the class we instruct the table ‘Visuals’ (the key of the dict) to be read and transformed into SEP_Visual-objects.

The header of the table is only a comment (it starts with a #). The required columns are defined in SEP_Visual.tuple_fields() but are also listed in the overview in this document.

Warning:

Exporting .csv files from a spreadsheet program like excel when using a language where a comma (,) is used in numbers will very likely result in files that DAVE can not read

Using the above class and .csv file in the GUI results in the following:

image-20220307205834146

the single visual, single buoyancy shape and two weight are read from the .csv file and created as managed nodes of the super-element.

The node-editor automatically gets a “File” entry where we can select a .csv file.

What not yet works is saving. For saving to work we need to override the give_python_code of the class to yield the correct python code. In this case:

    def give_python_code(self) -> str:
        """Returns the python code needed to re-create this object"""

        code = list()
        code.append(f"# Demo island {self.name}")
        code.append(f'island = DEMO_Island(s, name = "{self.name}")')
        code.extend(super().give_python_code_common("island"))

        return "\n".join(code)

The call to super() adds the properties of the frame and the .csv file.

Adding attachments#

Now the island needs some trees where “some” is flexible. For that we need

  • Attachment-type: tree

  • Attachment-points: various points on the island where we can plant a tree

Both of these can be defined in the .csv file. No python code required.

Defining the tree attachment type#

The tree attachment is just a visual with a fixed visual file.

@ATT: Palmtree          , # This is the NAME of an attachement type, 
class, Visual           , # This is the node-class. In this case a Visual
path, res: palmtree.obj , # Set the path of the visual to res: palmtree.obj

If we want the user to be able to manually edit some of the properties of the attachment, then use the EXPOSE functionality:

expose, scale, rotation # expose these

Defining the attachment locations#

Next (and final) step is to define the locations where an attachment can be added.

*Attachments
#
# Name,                  Target, posx, posy, posz, rotx, roty, rotz, attachments
Palm tree center,        self  ,  0  ,  0   ,  2    , 0   , 0  , 0  , Palmtree
Another point,           self  ,  2  ,  0   ,  2    , 0   , 0  , 0  , Palmtree

More fun with attachments#

#
#
*Attachments
#
# Name,                  Target, posx, posy, posz, rotx, roty, rotz, attachments
Monkey 1,                self  ,  3  ,  3   ,  3,   , 0   , 0  , 0  , Small monkey, Big monkey
Monkey 2,                self  ,  2  , -3.5 ,  5,   , 0   ,10  , 0  , Small monkey, Big monkey
Palm tree center,        self  ,  0  ,  0   ,  2    , 0   , 0  , 0  , Palmtree
Another point,           self  ,  2  ,  0   ,  2    , 0   , 0  , 0  , Palmtree, Small monkey, Big monkey
#
# Attachments-types
#
@ATT: Small monkey    # This is the NAME of an attachment type, @ATT: signals that this section is an attachment
class, Visual         # This is the node-class. In this case the attachment is a "Visual"
path, res: monkey.obj # These are properties of the node. All properties are supported.
scale, 0.5,0.5,0.5,   # this evaluates to node.scale = (0.5, 0.5, 0.5)
#
@ATT: Big monkey      # This is the NAME of an attachment type, @ATT: signals that this section is an attachment
class, Visual         # This is the node-class. In this case the attachment is a "Visual"
path, res: monkey.obj # These are properties of the node. All properties are supported.
scale, 1,1,4
#
#
@ATT: Palmtree        # This is the NAME of an attachement type,
class, Visual         # This is the node-class. In this case
path, res: palmtree.obj   , # These are properties of the node
expose, scale, rotation # expose these
#
# DATA tables
#
*Bananas
Banana1,sweet, 1, in the tree
Banana2,baked, 1, hidden
And, now, for something, completely, different

Creating super-element properties in Python#

Super element properties are just normal nodes wrapped by a SEProperty class.

This class takes care of creating and maintaining a node from a row obtained from a table in the .csv file.

Specifically:

  • creating the node from a tuple and throwing sensible errors for incorrect input (the tuple is read from a user-defined .csv file)

  • maintaining the name, keeping it in sync with the super-element that it is on

  • creating new properties where needed, for example:

    • .scalez for trimesh-based nodes

    • adding a “capacity” property which get/sets on of the nodes limits.

Super-Element property implementation details:#

The SEProperty is an abstract class (ABC).

The SEProperty contains a Node which is managed by the super-element. This is self.node.

The properties of this node are controlled through this SEProperty object as follows:

  • creating an instance of this class will create the corresponding node in the scene of the super-element (parent)

  • the name of the node will be prefixed by the name of the Super-Element as well as an identifier of the node type. For example Barge/Bollard/PS5

  • attributes with their name defined in ‘linked_properties’ are synced from this element to the node: if ‘x’ is in linked_properties then setting this.x will trigger setting this.node.x as well. This is done by overriding setattr

  • To easily work with tables the class adds to_tuple and from_tuple methods

  • The delete_node is added to remove the managed node from the scene. By default this just deletes the node.

SEP_Bollard is a good example of how to derive from this class:

class SEP_Bollard(SEProperty):
    """A bollard is a Point on with a location and a capacity.

    the capacity is the total force capacity of the bollard.
    capacity_x and capacity_y are the capacities in local x and y directions.

    The parent manager needs to derive from Frame"""

    def _init_properties(self):

        self.linked_props = ("x", "y", "z")
        # Linked properties are automatically initialized to their default values

    def _create_node(self):
        # self.parent._scene is the scene where this node needs to be created
        self.node = self.parent._scene.new_point(
            name=self.node_name, parent=self.parent
        )
        self.node.manager = self.parent

    @staticmethod
    def tuple_fields() -> tuple:
        """These are human names describing the fields, they are used in (error) messages"""
        return ("Name", "x", "y", "z", "capacity","capacity x","capacity y")

    @staticmethod
    def tuple_field_types() -> tuple:
        return (str, float,float,float, float)

    def _from_tuple(self, values: tuple):
        self.name = values[0]
        self.x = values[1]
        self.y = values[2]
        self.z = values[3]
        self.capacity = values[4]
        self.capacity_x = values[5]
        self.capacity_y = values[6]

    def to_tuple(self):
        return (self.name, self.x, self.y, self.z, self.capacity, self.capacity_x, self.capacity_y)

    @property
    def node_name(self):
        """Returns the name of the node"""
        return self.parent.name + "/Bol/" + self.name

    @property
    def capacity(self):
        """Capacity [kN]"""
        return self.node.limits['force'] 

    @capacity.setter
    def capacity(self, value):
        self.node.limits['force'] = value 

    @property
    def capacity_x(self):
        """Capacity in local x direction [kN]"""
        return self.node.limits['fx'] 

    @capacity_x.setter
    def capacity_x(self, value):
        self.node.limits['fx'] = value 

    @property
    def capacity_y(self):
        """Capacity in local y direction [kN]"""
        return self.node.limits['fy'] 

    @capacity_y.setter
    def capacity_y(self, value):
        self.node.limits['fy'] = value 

It is very well possible to wrap and control more than a single node, this however requires overriding more of the base-class methods.

Appendix B - Example Super Element#

from DAVE_BaseExtensions import SuperElementFrameBased
from DAVE_BaseExtensions.super_elements_properties import SEP_Visual, SEP_Weight, SEP_Frame


class MyNode(SuperElementFrameBased):

    # su_property_tables = dict()  # class variable with
    # key : name (str) of the property-table in the .csv file
    # value : class of the SU_properties that have to be crated based on this
    su_property_tables = dict()
    su_property_tables['Visuals'] = SEP_Visual
    su_property_tables['Weights'] = SEP_Weight
    su_property_tables['Frames'] = SEP_Frame


    # data_table_names = tuple()  # data-tables to be read from the .csv file
    data_table_names = ('Bar','Foo','Candy',)

    expected_settings = dict()  # key: name of setting , value: human documentation
    expected_settings['Mandatory_setting'] = "Something needs to be provided here in the .csv file"

    optional_settings = dict()  # key: name of setting , value: human documentation
    optional_settings['Optional_setting'] = "Define this if you need it"

    # csv_extension = 'csv'  # expected extension for the datafiles
    csv_extension = 'mynode.csv'

    def __init__(self, scene, name):
        super().__init__(scene, name)

        # other things that needs to be done.
        # good idea to init the data_tables to empty
        self.data_tables = dict()
        for key in self.data_table_names:
            self.data_tables[key] = []

        self.block = None

    def onCSVloaded(self):
        # this executes when a .csv has loaded
        body = self._scene.new_rigidbody('Demo')
        self.nodes = [body]


    # --- from manager ---

    def _created_nodes(self):

        return tuple(self.nodes)  # iterable of nodes that are created by this node

    # --- from node ---

    def give_python_code(self):
        code = list()
        code.append(f'my_node = MyNode(s, name = "{self.name}")')
        code.extend(super().give_python_code_common("my_node"))
        return "\n".join(code)

# Register the super-elements for runtime

from DAVE.settings import DAVE_ADDITIONAL_RUNTIME_MODULES
DAVE_ADDITIONAL_RUNTIME_MODULES["MyNode"] = MyNode

# just a quick test
if __name__ == '__main__':
    from DAVE import *
    s = Scene()
    my_node = MyNode(s,'my node')
    print(s.give_python_code())