# Model and integral operators

The class `tamaas::Model`

(and its counterpart `Model`

) is both a central class in Tamaas and one of the simplest. It mostly serves as holder for material properties, fields and integral operators, and apart from a linear elastic behavior does not perform any computation on its own.

## Units in Tamaas

All quantities in Tamaas are unitless. To put real units onto them, one can use the following equation, which must have a consistent set of units:

It relates the surface displacement \(u\) to the pressure \(p\), with
the Young’s modulus \(E\), the Poisson coefficient \(\nu\) and the
*horizontal* physical size of the system \(L\) (i.e. the horizontal
period of the system). The wavenumber \(k\) is adimensional. This means
that \(L\) is the natural unit for lengths (which can be specified when
creating the `Model`

object), and \(E^* := E / (1 - \nu^2)\) is the
natural unit for pressures (which can be accessed with `model.E_star`

). All
other units (areas, forces, energies, etc.) are derived from these two units.

In general, it is a good idea to work with a unit set that gives numerical values of the same order of magnitude to minimize floating point errors in algebraic operations. While this is not always possible, because in elastic contact we have \(u \sim h_\mathrm{rms}\) and \(p \sim h'_\mathrm{rms} E^*\), which are dependent, one should avoid using meters if expected lengths are nanometers for example.

## Model types

`tamaas::Model`

has a concrete subclass `tamaas::ModelTemplate`

which implements the model function for a given `tamaas::model_type`

:

`tamaas::basic_2d`

Model type used in normal frictionless contact: traction and displacement are 2D fields with only one component.

`tamaas::surface_2d`

Model type used in frictional contact: traction and displacement are 2D fields with three components.

`tamaas::volume_2d`

Model type used in elastoplastic contact: tractions are the same as with

`tamaas::surface_2d`

but the displacement is a 3D field.

The enumeration values suffixed with `_1d`

are the one dimensional (line contact) counterparts of the above model types. The domain physical dimension and number of components are encoded in the class `tamaas::model_type_traits`

.

## Model creation and basic functionality

The instanciation of a `Model`

requires the
model type, the physical size of the system, and the number of points in each direction:

```
physical_size = [1., 1.]
discretization = [512, 512]
model = tm.Model(tm.model_type.basic_2d, physical_size, discretization)
```

Warning

For models of type `volume_*d`

, the first component of the
`physical_size`

and `discretization`

arrays corresponds to the depth
dimension (\(z\) in most cases). For example:

```
tm.Model(tm.model_type.basic_2d, [0.3, 1, 1], [64, 81, 81])
```

creates a model of depth 0.3 and surface size 1^{2}, while the
number of points is 64 in depth and 81^{2} on the surface. This is done
for data contiguity reasons, as we do discrete Fourier transforms in the
horizontal plane.

Note

If ran in an MPI context, the constructor to `Model`

expects the *global* system sizes
and discretization of the model.

Tip

Deep copies of model objects can be done with the `copy.deepcopy()`

function:

```
import copy
model_copy = copy.deepcopy(model)
```

Note that it only copies fields, not operators or dumpers.

### Model properties

The properties `E`

and `nu`

can be used to set the Young’s modulus and Poisson ratio respectively:

```
model.E = 1
model.nu = 0.3
```

Tip

The Greenwood–Tripp equivalence, sometimes called Johnson equivalence, allows one to model a normal, frictionless contact between two homogeneous linear, isotropic, elastic materials with rough surfaces \(h_\alpha\) and \(h_\beta\) as the contact of a rigid-rough surface \(h_\beta - h_\alpha\) and a flat elastic solid with an equivalent contact modulus \(E^*\):

Since a model only defines a single set of elastic properties \((E,
\nu)\), one can model contact between two different elastic materials by
computing \(E^*\) *a priori*, and setting \((E, \nu) = (E^*, 0)\).
The total displacement \(u\) computed by, for example, a contact solver
can then be redistributed in proportion according to:

Note that is equivalence breaks down with friction, plasticity, heterogeneities, anisotropy, etc.

### Fields

Fields can be easlily accessed with the `[]`

operator, similar to Python’s dictionaries:

```
surface_traction = model['traction']
# surface_traction is a numpy array
```

To know what fields are available, you can call the `list`

function on
a model (`list(model)`

). You can add new fields to a model object with the
`[]`

operator:`model['new_field'] = new_field`

, which is convenient for
dumping fields that are computed outside of Tamaas.

Note

The fields `traction`

and `displacement`

are always registered in models,
and are accessible via `model.traction`

and `model.displacement`

.

A model can also be used to compute stresses from a strain field:

```
import numpy
strain = numpy.zeros(model.shape + [6]) # Mandel--Voigt notation
stress = numpy.zeros_like(strain)
model.applyElasticity(stress, strain)
```

Tip

`print(model)`

gives a lot of information about the model: the model type,
shape, registered fields, and more!

#### Manipulating fields

Fields are stored in C-contiguous order. The last dimension of any field is
always the vector/tensor components, if the number of components is greater than
one. The other dimensions are the space dimensions, in `z, x, (y)`

order for
volumetric quantities and `x, (y)`

for boundary quantities.

Tip

To select normal component of the surface displacement of a `volume_2d`

model, one can write:

```
model.displacement[0, ..., 2]
```

Symmetric tensors are stored in Mandel–Voigt notation as vectors in the form:

## Integral operators

Integral operators are a central part of Tamaas: they are carefully designed for
performance in periodic system. When a `Model`

object is used with contact solvers or with a residual object (for plasticty),
the objects using the model register integral operators with the model, so the
user typically does not have to worry about creating integral operators.

Integral operators are accessed through the `operators`

property of a model object. The `[]`

operator gives access to the operators, and `list(model.operators)`

gives the
list of registered operators:

```
# Accessing operator
elasticity = model.operators['hooke']
# Applying operator
elasticity(strain, stress)
# Print all registered operators
print(list(model.operators))
```

Note

At model creation, these operators are automatically registered:

`hooke`

: Hooke’s elasticity law`von_mises`

: computes Von Mises stresses`deviatoric`

: computes the deviatoric part of a stress tensor`eigenvalues`

: computes the eigenvalues of a symetric tensor field

`Westergaard`

operators are automatically
registered when `solveNeumann`

or `solveDirichlet`

are called.

## Model dumpers

The submodule tamaas.dumpers contains a number of classes to save model data into different formats:

`UVWDumper`

Dumps a model to VTK format. Requires the UVW python package which you can install with pip:

pip install uvw

This dumper is made for visualization with VTK based software like Paraview.

`NumpyDumper`

Dumps a model to a compressed Numpy file.

`H5Dumper`

Dumps a model to a compressed HDF5 file. Requires the h5py package. Saves separate files for each dump of a model.

`NetCDFDumper`

Dumps a model to a compressed NetCDF file. Requires the netCDF4 package. Saves sequential dumps of a model into a single file, with the

`frame`

dimension containing the model dumps.

The dumpers are initialzed with a basename and the fields that you wish to write to file (optionally you can set `all_fields`

to `True`

to dump all fields in the model). By default, each write operation creates a new file in a separate directory (e.g. `UVWDumper`

creates a `paraview`

directory). To write to a specific file you can use the dump_to_file method. Here is a usage example:

```
from tamaas.dumpers import UVWDumper, H5Dumper
# Create dumper
uvw_dumper = UVWDumper('rough_contact_example', 'stress', 'plastic_strain')
# Dump model
uvw_dumper << model
# Or alternatively
model.addDumper(H5Dumper('rough_contact_archive', all_fields=True))
model.addDumper(uvw_dumper)
model.dump()
```

The last `model.dump()`

call will trigger all dumpers. The resulting files will have the following hierachy:

```
./paraview/rough_contact_example_0000.vtr
./paraview/rough_contact_example_0001.vtr
./hdf5/rough_contact_archive_0000.h5
```

Important

Currently, only `H5Dumper`

and
`NetCDFDumper`

support
parallel output with MPI.