# Assigning properties to geometry

As of version 0.7.1, the way to define properties for elements of the geometry has changed thoroughly. As a result, the property system has become much more flexibel and powerful, and can be used for Formex data structures as well as for TriSurfaces and Finite Element models.

With properties we mean any data connected with some part of the geometry other than the coordinates of its points or the structure of points into elements. Also, values that can be calculated purely from the coordinates of the points and the structure of the elements are usually not considerer properties.

Properties can e.g. define material characteristics, external loading and boundary conditions to be used in numerical simulations of the mechanics of a structure. The properties module includes some specific functions to facilitate assigning such properties. But the system is general enough to used it for any properties that you can think of.

Properties are collected in a `PropertyDB` object. Before you can store anything in this database, you need to create it. Usually, you will start with an empty database.

    P = PropertyDB()

## General properties

Now you can start entering property records into the database. A property record is a lot like a Python dict object, and thus it can contain nearly anything. It is implemented however as a `CascadingDict` object, which means that the key values are strings and can also be used as attributes to address the value. Thus, if P is a property record, then a field named key can either be addressed as P[‘key’] or as P.key. This implementation was choosen for the convenience of the user, but has no further advantages over a normal dict object. You should not use any of the methods of Python’s dict class as key in a property record: it would override this method for the object.

The property record has four more reserved (forbidden) keys: kind, tag, set, setname and nr. The kind and nr should never be set nor changed by the user. kind is used internally to distinguish among different kind of property records (see Node properties). It should only be used to extend the `PropertyDB` class with new kinds of properties, e.g. in subclasses. nr will be set automatically to a unique record number. Some application modules use this number for identification and to create automatic names for property sets.

The tag, set and setname keys are optional fields and can be set by the user. They should however only be used for the intended purposes explained hereafter, because they have a special meaning for the database methods and application modules.

The tag field can be used to attach an identification string to the property record. This string can be as complex as the user wants and its interpretation is completely left to the user. The `PropertyDB` class just provides an easy way to select the records by their tag name or by a set of tag names. The set and setname fields are treated further in Using the set and setname fields.

So let’s create a property record in our database. The Prop() method does just that. It also returns the property record, so you can directly use it further in your code.

    >>> Stick = P.Prop(color='green',name='Stick',weight=25,\
            comment='This could be anything: a gum, a frog, a usb-stick,...'})
    >>> print Stick

      color = green
      comment = This could be anything: a gum, a frog, a usb-stick,...
      nr = 0
      name = Stick
      weight = 25

Notice the auto-generated nr field. Here’s another example, with a tag:

    >>> author = P.Prop(tag='author',name='Alfred E Neuman',\
            address=CascadingDict({'street':'Krijgslaan',\
            'city':'Gent','country':'Belgium'}))
    >>> print author

      nr = 1
      tag = author
      name = Alfred E Neuman
      address =
        city = Gent
        street = Krijgslaan
        country = Belgium

This example shows that record values can be complex structured objects. Notice how the `CascadingDict` object is by default printed in a very readible layout, offsetting each lower level dictionary two more postions to the right.

The `CascadingDict` has yet another fine characteristic: if an attribute is not found in the toplevel, all values that are instances of `CascadingDict` or `Dict` (but not the normal Python dict) will be searched for the attribute. If needed, this searching is even repeated in the values of the next levels, and further on, thus cascading though all levels of `CascadingDict` structures until the attribute can eventually be found. The cascading does not proceed through values in a `Dict`. An attribute that is not found in any of the lower level dictionaries, will return a None value.

If you set an attribute of a `CascadingDict`, it is always set in the toplevel. If you want to change lower level attributes, you need to use the full path to it.

    >>> print author.st
      Krijgslaan
    >>> author.street = 'Voskenslaan'
    >>> print author.street
      Voskenslaan
    >>> print author.address.street
      Krijgslaan
    >>> author.address.street = 'Wiemersdreef'
    >>> print author.address.street
      Wiemersdreef
    >>> author = P.Prop(tag='author',alias='John Doe',\
            address={'city': 'London', 'street': 'Downing Street 10',\
                     'country': 'United Kingdom'})
    >>> print author

      nr = 2
      tag = author
      alias = John Doe
      address = {'city': 'London', 'street': 'Downing Street 10',\
                 'country': 'United Kingdom'}

In the examples above, we have given a name to the created property records, so that we could address them in the subsequent print and field assigment statements. In most cases however, it will be impractical and unnecessary to give your records a name. They all are recorded in the `PropertyDB` database, and will exist as long as the database variable lives. There should be a way though to request selected data from that database. The `getProp()` method returns a list of records satisfying some conditions. The examples below show how it can be used.

    >>> for p in P.getProp(rec=[0,2]):
            print p.name
    Stick
    John Doe
    >>>  for p in P.getProp(tag=['author']):
            print p.name
    None
    John Doe
    >>>  for p in P.getProp(attr=['name']):
            print p.nr
    0
    2
    >>>  for p in P.getProp(tag=['author'],attr=['name']):
            print p.name
    John Doe

The first call selects records by number: either a single record number or a list of numbers can be specified. The second method selects records based on the value of their tag field. Again a single tag value or a list of values can be specified. Only those records having a ‘tag’ filed matching any of the values in the list will be returned. The third selection method is based on the existence of some attribute names in the record. Here, always a list of attribute names is required. Records are returned that posess all the attributes in the list, independent from the value of those attributes. If needed, the user can add a further filtering based on the attribute values. Finally, as is shown in the last example, all methods of record selection can be combined. Each extra condition will narrow the selection further down.

## Using the set and setname fields

In the examples above, the property records contained general data, not related to any geometrical object. When working with large geometrical objects (whether `Formex` or other type), one often needs to specify properties that only hold for some of the elements of the object.

The set can be used to specify a list of integer numbers identifying a collection of elements of the geometrical object for which the current property is valid. Absence of the set usually means that the property is assigned to all elements; however, the property module itself does not enforce this behavior: it is up to the application to implement it.

Any record that has a set field, will also have a setname field, whose value is a string. If the user did not specify one, a set name will be auto-generated by the system. The setname field can be used in other records to refer to the same set of elements without having to specify them again. The following examples will make this clear.

    >>> P.Prop(set=[0,1,3],setname='green_elements',color='green')
        P.Prop(setname='green_elements',transparent=True)

    >>> a = P.Prop(set=[0,2,4,6],thickness=3.2)
        P.Prop(setname=a.setname,material='steel')

    >>> for p in P.getProp(attr=['setname']):
            print p

    color = green
    nr = 3
    set = [0 1 3]
    setname = green_elements

    nr = 4
    transparent = True
    setname = green_elements

    nr = 5
    set = [0 2 4 6]
    setname = Set_5
    thickness = 3.2

    nr = 6
    material = steel
    setname = Set_5

In the first case, the user specifies a setname himself. In the second case, the auto-generated name is used. As a convenience, the user is allowed to write set=name instead of setname=name when referring to an already defined set.

    >>> P.Prop(set='green_elements',transparent=False)
        for p in P.getProp(attr=['setname']):
            if p.setname == 'green_elements':
                print p.nr,p.transparent

    3 None
    4 True
    7 False

Record 3 does not have the transparent attribute, so a value None is printed.

## Specialized property records

The property system presented above allows for recording any kind of values. In many situations however we will want to work with a specialised and limited set of attributes. The main developers of e.g. often use the program to create geometrical models of structures of which they want to analyse the mechanical behavior. These numerical simulations (FEA, CFD) require specific data that support the introduction of specialised property records. Currently there are two such property record types: node properties (see Node properties), which are attributed to a single point in space, and element properties (Element properties), which are attributed to a structured collection of points.

Special purpose properties are distincted by their kind field. General property records have kind=”, node properties haven kind=’n’ and kind=’e’ is set for element properties. Users can create their own specialised property records by using other value for the kind parameter.

## Node properties

Node properties are created with the `nodeProp()` method, rather than the general `Prop()`. The kind field does not need to be set: it will be done automatically. When selecting records using the `getProp()` method, add a kind=’n’ argument to select only node properties.

Node properties will recognize some special field names and check the values for consistency. Application plugins such as the Abaqus input file generator depend on these property structure, so the user should not mess with them. Currently, the following attributes are in use:

cload
  A concentrated load at the node. This is a list of 6 items: three force components in axis directions and three force moments around the axes: [F_0, F_1, F_2, M_0, M_1, M_2].
bound
  A boundary condition for the nodal displacement components. This can be defined in 2 ways:

- as a list of 6 items [ u_0, u_1, u_2, r_0, r_1, r_2 ]. These items have 2 possible values:

  > 0

  >   The degree of freedom is not restrained.

  > 1

  >   The degree of freedom is restrained.

- as a string. This string is a standard boundary type. Abaqus will recognize the following strings:

- PINNED

- ENCASTRE

- XSYMM

- YSYMM

- ZSYMM

- XASYMM

- YASYMM

- ZASYMM

displacement
  Prescribed displacements. This is a list of tuples (i,v), where i is a DOF number (1..6) and v is the prescribed value for that DOF.
coords
  The coordinate system which is used for the definition of cload, bound and displ fields. It should be a `CoordSys` object.

Some simple examples:

    P.nodeProp(cload=[5,0,-75,0,0,0])
    P.nodeProp(set=[2,3],bound='pinned')
    P.nodeProp(5,displ=[(1,0.7)])

The first line sets a concentrated load all the nodes, the second line sets a boundary condition ‘pinned’ on nodes 2 and 3. The third line sets a prescribed displacement on node 5 with value 0.7 along the first direction. The first positional argument indeed corresponds to the ‘set’ attribute.

Often the properties are computed and stored in variables rather than entered directly.

    P1 = [ 1.0,1.0,1.0, 0.0,0.0,0.0 ]
    P2 = [ 0.0 ] * 3 + [ 1.0 ] * 3
    B1 = [ 1 ] + [ 0 ] * 5
    CYL = CoordSystem('cylindrical',[0,0,0,0,0,1])
    P.nodeProp(bound=B1,csys=CYL)

The first two lines define two concentrated loads: `P1` consists of three point loads in each of the coordinate directions; P2 contains three force moments around the axes. The third line specifies a boundary condition where the first DOF (usually displacement in -direction) is constrained, while the remaining 5 DOF’s are free. The next line defines a local coordinate system, in this case a cylindrical coordinate system with axis pointing from point `[0.,0.,0.]` to point `[0.,0.,1.]`. The last line

To facilitate property selection, a tag can be added.

    nset1 = P.nodeProp(tag='loadcase 1',set=[2,3,4],cload=P1).nr
    P.nodeProp(tag='loadcase 2',set=Nset(nset1),cload=P2)

The last two lines show how you can avoid duplication of sets in mulitple records. The same set of nodes should receive different concentrated load values for different load cases. The load case is stored in a tag, but duplicating the set definition could become wasteful if the sets are large. Instead of specifying the node numbers of the set directly, we can pass a string setting a set name. Of course, the application will need to know how to interprete the set names. Therefore the property module provides a unified way to attach a unique set name to each set defined in a property record. The name of a node property record set can be obtained with the function Nset(nr), where nr is the record number. In the example above, that value is first recorded in nset1 and then used in the last line to guarantee the use of the same set as in the property above.

## Element properties

The `elemProp()` method creates element properties, which will have their `kind` attribute set to ‘e’. When selecting records using the `getProp()` method, add the kind=’e’ argument to get element properties.

Like node properties, element property records have a number of specialize fields. Currently, the following ones are recognized by the Abaqus input file generator.

eltype
  This is the single most important element property. It sets the element type that will be used during the analysis. Notice that a Formex object also may have an `eltype` attribute; that one however is only used to describe the type of the geometric elements involved. The element type discussed here however may also define some other characteristics of the element, like the number and type of degrees of freedom to be used in the analysis or the integration rules to be used. What element types are available is dependent on the analysis package to be used. Currently, does not do any checks on the element type, so the simulation program’s own element designation may be used.
section
  The section properties of the element. This should be an `ElemSection` instance, grouping material properties (like Young’s modulus) and geometrical properties (like plate thickness or beam section).
dload
  A distributed load acting on the element. The value is an `ElemLoad` instance. Currently, this can include a label specifying the type of distributed loading, a value for the loading, and an optional amplitude curve for specifying the variation of a time dependent loading.

## Property data classes

The data collected in property records can be very diverse. At times it can become quite difficult to keep these data consistent and compatible with other modules for further processing. The property module contains some data classes to help you in constructing appropriate data records for Finite Element models. The FeAbq module can currently interprete the following data types.

`CoordSystem` defines a local coordinate system for a node. Its constructor takes two arguments:

- a string defining the type of coordinate system, either ‘Rectangular’, ‘Cylindrical’ or ‘Spherical’ (the first character suffices), and
- a list of 6 coordinates, specifying two points A and B. With ‘R’, A is on the new -axis and B is on the new ‘ axis. With ‘C’ and ‘S’, AB is the axis of the cylindrical/spherical coordinates.

Thus, `CoordSystem('C',[0.,0.,0.,0.,0.,1.])` defines a cylindrical coordinate system with the global as axis.

`ElemLoad` is a distributed load on an element. Its constructor takes two arguments:

- a label defining the type of loading,
- a value for the loading,
- optionally, the name of an amplitude curve.

E.g., ElemLoad(‘PZ’,2.5) defines a distributed load of value 2.5 in the direction of the -axis.

`ElemSection` can be used to set the material and section properties on the elements. It can hold:

- a section,
- a material,
- an optional orientation,
- an optional connector behavior,
- a sectiontype (deprecated). The sectiontype should preferably be set togehter with the other section parameters.

An example:

    >>> steel = {
        'name': 'steel',
        'young_modulus': 207000,
        'poisson_ratio': 0.3,
        'density': 0.1,
        }
    >>> thin_plate = {
        'name': 'thin_plate',
        'sectiontype': 'solid',
        'thickness': 0.01,
        'material': 'steel',
        }
    >>> P.elemProp(eltype='CPS3',section=ElemSection(section=thin_plate,material=steel))

First, a material is defined. Then a thin plate section is created, referring to that material. The last line creates a property record that will attribute this element section and an element type ‘CPS3’ to all elements.

### Exporting to finite element programs