    

 ## On this page

  

 

 # Field fundamentals

 Last update: 31.07.2026 

The Field is DPF's fundamental simulation data container. It is a data structure that associates numerical values with entities in a discretized region, along with metadata that makes the data self-describing. Every field contains interconnected components that define what the data represents, where it applies, how it is measured, and how it is structured.

## Field architecture

A field is composed of the following components:

- **Data array**: Holds the actual numerical values
- **Scoping**: Defines which entities have data
- **Support**: Provides the mesh and time/frequency context
- **Unit**: Specifies the measurement system for the values
- **Field definition**: Stores dimensionality information for interpreting the data array

### Data array

The **data** is a flat one-dimensional array of numerical values (stored as double-precision floats). This array contains all the values in the field, organized in a specific ordering structure determined by the field's location and shell layer properties.

The data array ordering follows a nested loop structure from coarse to fine:

1. **Entity** (outermost loop): Iterate through each entity in the scoping
2. **Node** (for ElementalNodal location only): Iterate through nodes of each element
3. **Layer** (for shell elements): Iterate through shell layers (bottom, top, mid)
4. **Component** (innermost loop): Iterate through vector/tensor components

**Example — 2 nodes, 3-component vector (displacement, no shell layers)**:

```
data = [X_node1, Y_node1, Z_node1, X_node2, Y_node2, Z_node2]

```

**Example — 1 shell element, 6-component stress tensor, `topbottom` layers**:

```
data = [
    # Element 1 – bottom layer
    σx_bot, σy_bot, σz_bot, τxy_bot, τyz_bot, τxz_bot,
    # Element 1 – top layer
    σx_top, σy_top, σz_top, τxy_top, τyz_top, τxz_top
]

```

Total length: 1 element × 2 layers × 6 components = 12 values.

**Example — 1 shell element (4 nodes), `ElementalNodal` stress, `topbottom` layers**:

```
data = [
    # Element 1, Node 1
    σx_n1_bot, σy_n1_bot, σz_n1_bot, τxy_n1_bot, τyz_n1_bot, τxz_n1_bot,  # bottom
    σx_n1_top, σy_n1_top, σz_n1_top, τxy_n1_top, τyz_n1_top, τxz_n1_top,  # top
    # Element 1, Node 2
    σx_n2_bot, ..., τxz_n2_bot,
    σx_n2_top, ..., τxz_n2_top,
    # … Node 3, Node 4 (same pattern)
]

```

Total length: 1 element × 4 nodes × 2 layers × 6 components = 48 values.

### Elementary data

**Elementary data** is the set of values associated with a single entity. It represents the complete data for one node, one element, or one time step.

The size of elementary data depends on the field's location:

- **Nodal or Elemental**: components × layers
- **ElementalNodal**: nodes per element × components × layers

**Structure by dimensionality**:

- **Scalar**: 1 value per entity (e.g., temperature: `[21.5]`)
- **Vector**: 3 values per entity (e.g., displacement: `[2.5, -1.2, 0.8]`)
- **Tensor**: 6 or 9 values per entity (e.g., stress: `[σx, σy, σz, τxy, τyz, τxz]`)
- **ElementalNodal**: All nodal values for the element (e.g., 4-node element with 3 components: 12 values)

### ElementalNodal fields and element-type properties

For `ElementalNodal` fields, the size of each elementary data block depends on the **element type** of the entity it describes. Mixed-element meshes therefore produce **variable-size** ElementalNodal fields: each element contributes `n_nodes_per_element × n_components × n_layers` consecutive values, with no padding between elements.

To locate each entity's data block in the flat array without recomputing per-element sizes, every field exposes a **`data_pointer`** array: `data_pointer[i]` is the index in the flat `data` array where entity *i*'s elementary data begins. For fields where every entity has the same number of components (typical `Nodal`, `Elemental`, scalar or 3D-vector cases) the `data_pointer` is **empty** - the simple stride `index × elementary_data_size` is sufficient. For variable-size fields (mixed-element ElementalNodal, connectivity property fields) it is populated and is the canonical way to slice the `data` array per entity without walking element types upfront.

The number of rows per element (`n_nodes_per_element` for ElementalNodal, `1` for Elemental) is derivable from the element type alone, independently of any specific field. This information lives on the [`meshed_region`](./dpf-types.md#meshed-region), not on the field. Two element-type properties are central to interpreting the data layout:

- **Shape** (`0D`, `1D`, `2D`, `3D` -&gt; point, beam, shell, solid), exposed on the meshed region as the `elshape` property. It governs the geometric role of the element and which nodes carry physically meaningful results. (Note that `dimensionality` in DPF is a distinct, field-level concept - scalar, vector, tensor - and does not describe element geometry.)
- **Order**. Second-order (quadratic) element types have **mid-side nodes** in addition to their corner nodes. This is a topology fact of the element type itself.

Shape and order are properties of the **element type**; they are not field-level concepts. The field's `location` only states *where* the data sits (`Nodal`, `Elemental`, `ElementalNodal`); it does not encode the element shape, order, or node count.

Whether an ElementalNodal field defined on a mesh with second-order elements actually contains rows for those mid-side nodes is a separate, **field-level**property. It is recorded as a boolean flag on the [field definition](#field-definition)(`has_midnode_data` in the C++ API). Two ElementalNodal fields can share the same support and still differ in how much data they store per second-order element: one may carry only corner-node rows, another the full corner + mid-node layout. See [Mid-node values and `extend_to_mid_nodes`](#mid-node-values-and-extend_to_mid_nodes)below for how to move between the two.

#### Entity ID uniqueness across element types

Element IDs used in the `scoping` of an `Elemental` or `ElementalNodal` field are **globally unique within the `MeshedRegion`**, across all element types. A given integer ID identifies exactly one element regardless of whether it is a tetrahedron, hexahedron, shell, beam, or point. The same uniqueness guarantee applies to node IDs in `Nodal` scopings. The scoping's ID list is therefore sufficient on its own to locate each entity in the mesh - no element-type lookup is needed to disambiguate IDs.

#### Mid-node values and `extend_to_mid_nodes`

When an ElementalNodal field is read from a solver file that produced results at both corner and mid-side nodes, the field already contains one row per node per element - corner and mid-node rows interleaved per element in the order declared by the element's connectivity.

Solver files that only store corner-node values for second-order elements produce an ElementalNodal field with corner-node rows only. To obtain a field that also carries mid-node rows for downstream consumers that expect them, use the [`extend_to_mid_nodes`](../operator-specifications/averaging/extend_to_mid_nodes.md)operator (or [`extend_to_mid_nodes_fc`](../operator-specifications/averaging/extend_to_mid_nodes_fc.md)for a fields container). It interpolates corner-node values onto the mid-side nodes, producing a field whose elementary data sizes match the full corner + mid-node layout.

#### Homogeneous sub-fields per element type

When downstream code needs to process the data array as a regular block - for example reshaping it as `(n_entities, rows_per_entity * components)` - the variable-size layout of a mixed-element ElementalNodal field becomes an obstacle. The recommended strategy is to **split by element type**: use the [`split_on_property_type`](../operator-specifications/scoping/split_on_property_type.md)operator with the `eltype` property to produce one scoping per element type (triangles, quads, tets, hexes, ...). Re-evaluating (or rescoping) the result for each scoping yields homogeneous-size sub-fields, one per element type, that can each be processed as a regular block without per-element bookkeeping. Splitting on the coarser `elshape` property (solid, shell, beam, point) is not sufficient on its own: a single shape class can still mix element types with different node counts (for example `tet4` and `hex8` are both solids).

### Unit

The **unit** defines the measurement system for all values in the field. All data in a field shares the same unit.

**Common units**: `m` (meters), `Pa` (Pascals), `°C` (Celsius), `s` (seconds), `kg` (kilograms)

### Location

The **location** specifies what kind of entities the field's data is associated with within the support.

**Standard locations**:

LocationDescriptionTypical use`Nodal`Data at mesh nodes (vertices)Displacement, temperature`Elemental`Data at mesh element centersElement volumes, material IDs`ElementalNodal`Data at each node of each elementStress, strain (extrapolated to nodes)`TimeFreq`Data at time steps or frequenciesGlobal quantities over time`Overall`Data for the entire model (single value)Total mass, global energy### Scoping

The **scoping** of the field defines which specific entities have data in the field by listing their IDs. It acts as an index mapping between the data array position and the actual entity identifiers.

**Structure**: An ordered list of entity IDs (e.g., `[10, 25, 100, 533]` for node IDs)

**Key properties**:

- **Length**: Number of entities with data (`scoping.size`)
- **IDs**: The actual entity identifiers from the mesh/support (potentially non-sequential)
- **Index mapping**: Position in the scoping (0-based) vs. entity ID (1-based or non-sequential)

**Why scoping is essential**: It allows fields to store data for subsets of a model rather than requiring data for all entities. For example, a boundary condition field might only contain data for nodes on a specific surface.

### Support

The **support** defines the region where the field's data applies. It provides the spatial, temporal, or structural context for interpreting the entities.

**Support types**:

Support typePurposeExample`MeshedRegion`Physical space (3D geometry)Finite element mesh`TimeFreqSupport`Temporal/frequency domainTime steps 0s to 10s`CyclicSupport`Cyclic symmetry structuresTurbine with repeated sectorsThe support is referenced by the field but not owned by it - multiple fields can share the same support.

### Field definition

The **field definition** aggregates metadata properties about the field: its dimensionality (number of components), unit, location, and shell layer configuration. It is accessed as a single object containing these interconnected properties.

### Shell layers

Shell elements model thin-walled structures (plates, sheet metal, composite panels) by collapsing physical thickness into a 2D surface mesh. Because stresses and strains vary through the thickness, results can be stored at several positions — called **layers** — through the wall. The `shell_layers` property on a field records which of those positions are present.

**Shell layer options**:

ValueDescriptionUse case`layerindependent`No layer informationSolid elements, or results that do not vary through thickness (e.g. displacement)`bottom`Bottom surface onlySingle-sided result`top`Top surface onlySingle-sided result`topbottom`Bottom and top surfacesStandard shell stress/strain`mid`Mid-surface onlyMembrane behaviour`topbottommid`Bottom, top, and mid surfacesDetailed through-thickness analysis#### Effect on the data array

When layers are present, the flat data array gains an extra nesting level. The ordering rules are:

- **Nodal / Elemental location**: `entity → layer → component`
- **ElementalNodal location**: `entity (element) → node → layer → component`

For a field with `layerindependent` (or for solid-element results), the layer loop collapses to a single pass, so the ordering reduces to the standard `entity → component` (or `entity → node → component` for ElementalNodal).

**Elementary data size**:

- Nodal / Elemental with layers: `components × number_of_layers`
- ElementalNodal with layers: `nodes_per_element × components × number_of_layers`

#### Return shape from entity-level access

When layers are present, `get_entity_data()` and `get_entity_data_by_id()` return a **2D array** shaped `(number_of_layers, components)` rather than a 1D array. The layer order in the returned array matches the order declared by `shell_layers` (typically bottom → top → mid for `topbottommid`).

**Example — 6-component stress tensor, `topbottommid` layers, entity ID 100**:

```
# Returned shape: (3, 6)
[
  [σx_bot, σy_bot, σz_bot, τxy_bot, τyz_bot, τxz_bot],   # row 0: bottom
  [σx_top, σy_top, σz_top, τxy_top, τyz_top, τxz_top],   # row 1: top
  [σx_mid, σy_mid, σz_mid, τxy_mid, τyz_mid, τxz_mid]    # row 2: mid
]

```

For a scalar field (`layerindependent`), the return is always a 1D array with a single value `[value]`, unchanged from the no-layer case.

### Name

The **name** is an optional text identifier for the field, used for documentation and debugging purposes. Examples: `"displacement"`, `"stress_von_mises"`, `"temperature_field"`.

## Field variants

While the standard Field stores double-precision floating-point data, DPF provides specialized field types for different data types:

### PropertyField

Stores **integer values** instead of doubles. Used for:

- Material IDs or element type IDs
- Boolean flags (represented as 0/1)
- Element or node property indices
- Mesh connectivity information

### StringField

Stores **text values**. Used for:

- Material names or labels
- Result descriptions
- Custom annotations

### CustomTypeField

Stores **custom data types** beyond standard primitives. Used for:

- Complex numbers (electromagnetic analysis)
- Custom types defined by specialized plugins
- Any data type compatible with `numpy.dtype`

All field variants follow the same organizational structure (data, scoping, support, location) but differ in the data type stored in the data array.

## Data access patterns

How you read field data efficiently depends on two factors: what scope of data you need (a handful of entities vs. the whole field) and where the DPF server is running relative to your client code.

### Entity-by-entity access

Two methods let you query data for a single entity at a time:

**`get_entity_data(index)`**: Retrieve data by position (0-based index) in the scoping.

- The *index* is the ordinal position in the scoping list, not the entity ID.
- Use when iterating sequentially through all entities — though see the loop warning below.

**`get_entity_data_by_id(id)`**: Retrieve data by entity ID (the actual node or element ID from the mesh).

- The *id* is the entity identifier from the support (1-based, potentially non-sequential).
- Use when you need data for a small set of known entities.

**Index vs. ID**: Because scoping IDs are not necessarily sequential (e.g., scoping `[10, 25, 100]` has index 0 → ID 10, index 1 → ID 25), the two methods serve distinct needs. Conversions are available through the scoping object (`scoping.id(index)` and `scoping.index(id)`).

**Return shape**: The returned array is automatically shaped based on the field's component count and shell layers:

- Scalar field (1 component): `[value]`
- Vector field (3 components): `[X, Y, Z]`
- Tensor field with layers (6 components, 3 layers): `[[σx_bot, ...], [σx_top, ...], [σx_mid, ...]]`

**Loop anti-pattern**: Do not call entity-by-entity methods inside a loop over many entities. Each call is a round-trip to the DPF server. With thousands of entities this becomes prohibitively slow, regardless of whether the server is local or remote. Use bulk access instead (see below).

### Bulk data access

**`field.data`**: Returns the complete flat data array.

- On a **local server**, this returns a direct pointer to server memory — no data is copied, making it the fastest option for any field size.
- On a **remote server**, this transfers the entire array over the network in a single operation.

**`as_local_field()`**: Downloads the field from a remote server into local memory and wraps it in a context manager. All subsequent `get_entity_data()` calls within the context block operate on the local copy (local array indexing, no further network traffic). This approach also ensures proper resource cleanup when the context exits.

### Choosing the right approach

The table below summarises which pattern to use based on server topology and field size:

ScenarioRecommended approachRationaleLocal server, any field size`field.data` + manual index arithmeticDirect memory pointer, zero copy overheadRemote server, small-to-medium field`field.data` or `as_local_field()`Single network transfer, then local operationsRemote server, large field (&gt; ~100 MB)`as_local_field()`Controlled download with explicit cleanupRemote server, very large field (&gt; ~1 GB)Filter server-side first, then download the subsetAvoids transferring data that will not be usedOnly a few specific entities needed`get_entity_data()` or `get_entity_data_by_id()`Sufficient for small query counts; avoid in tight loops### Filtering server-side before downloading

When only a subset of entities is needed from a large field on a remote server, the most efficient pattern is to reduce the data on the server first — before any network transfer occurs. Two common mechanisms are:

- **Mesh scoping**: Provide a scoping to the result operator (or to a rescope operator) so that only data for the relevant nodes or elements is evaluated and returned.
- **Time/frequency scoping**: Request only the time sets or frequencies you need rather than all sets.

This server-side filtering can reduce a gigabyte-scale transfer to a megabyte-scale one, after which the normal `field.data` or `as_local_field()` patterns apply to the smaller result.

## Properties summary

Key field properties for introspection:

PropertyDescriptionType`location`Type of entities (Nodal, Elemental, etc.)`str``component_count`Number of components per elementary data`int``elementary_data_count`Number of elementary data (entities)`int``size`Total data array length (entities × layers × components)`int``scoping`Entity IDs with data[`scoping`](./dpf-types.md#scoping)`unit`Measurement unit`str``shell_layers`Shell layer configuration`enum``field_definition`Aggregated metadata[`field_definition`](./dpf-types.md#field-definition)`data`Raw data array`array of double`## Common pitfalls

### Replacing the scoping does not resize the data buffer

Assigning a new scoping to a field updates only the metadata - it does **not** filter or resize the underlying data array. After reassignment, the scoping length and the data length may be inconsistent, which leads to silent incorrect results or errors when the field is consumed by a downstream operator.

**Do not** use the scoping setter to extract a spatial subset. Instead:

- Use the `rescope` operator to produce a correctly-sized field for a chosen subset.
- Call `field.deep_copy()` to obtain an independent copy before modifying the scoping.

```python
import ansys.dpf.core as dpf

model = dpf.Model("file.rst")
result_field = model.results.displacement()[0]
my_scoping = dpf.Scoping(ids=[1, 2, 3], location=dpf.locations.nodal)

# Correct - use rescope to filter any field to a scoping
rescope_op = dpf.operators.scoping.rescope()
rescope_op.inputs.fields.connect(result_field)
rescope_op.inputs.mesh_scoping.connect(my_scoping)
filtered_field = rescope_op.outputs.fields_as_field()

# Correct - deep-copy a field before modifying its scoping
my_field = result_field.deep_copy()

```

### `coordinates_field` returns a live reference

The `MeshedRegion.nodes.coordinates_field` property (Python `nodes.coordinates_field`, C# or IronPython MechDPF `MeshedRegion.CoordinatesField`) returns a **live reference** to the internal mesh node coordinate array. Any modification to the returned field is reflected immediately in the mesh, and subsequent calls return the same underlying data.

To obtain an independent snapshot that is safe to modify without affecting the mesh:

```python
# Python - deep copy
coords_copy = model.mesh.nodes.coordinates_field.deep_copy()

```

In C# or IronPython (MechDPF), call `GetHardCopy()` to obtain an independent copy:

```csharp
// C# - independent copy
Field snapshot = mesh.CoordinatesField.GetHardCopy();

```

## Related types

Fields are often used in combination with:

- [`fields_container`](./dpf-types.md#fields-container): Collection of fields indexed by labels (time, frequency, etc.)
- [`meshed_region`](./dpf-types.md#meshed-region): Support for spatial fields
- [`time_freq_support`](./dpf-types.md#time-freq-support): Support for temporal/frequency fields
- [`scoping`](./dpf-types.md#scoping): Entity selection mechanism