Defining a uniform field#

Scalar field#

In order to define a finite difference field, discretisedfield.Field class is used. The main object, a field is built on is the mesh.

[1]:
import discretisedfield as df

p1 = (-50, -50, -50)
p2 = (50, 50, 50)
n = (2, 2, 2)
mesh = df.Mesh(p1=p1, p2=p2, n=n)

discretisedfield.Field assigns a value to every discretisation cell of the mesh. There are two main parameters of the field that define its value: nvdim and value. nvdim is a positive integer which defines the dimension of the value field. For instance, if nvdim=1, one number is assigned to each discretisation cell and that field can be considered as a scalar field. On the other hand, if nvdim=3, the value is a three dimensional vector field. In this way, it is possible to define an n dimensional field. Let us start with a scalar field.

[2]:
field = df.Field(mesh, nvdim=1)

We only defined the dimension of the value, but we did not provide any information about what that value actually is. By default, if value is not provided, a field is initialised as a “zero-field”. By calling the field with a point coordinates, we can sample the value at that point.

[3]:
point = (2, 3, 5)
field(point)
[3]:
array([0.])

Sampling the value of the field always returns a numpy array, whose length equals the dimension of the field. Let us now provide the same value for the field at all its points. This can be achieved by simply passing a number.

[4]:
value = 1.23
field.update_field_values(value)

If we sample the field now, we can see that we do not have a “zero-field” anymore.

[5]:
field(point)
[5]:
array([1.23])

The underlying attribute that keeps the values of all points in the field is discretisedfield.Field.array.

[6]:
field.array
[6]:
array([[[[1.23],
         [1.23]],

        [[1.23],
         [1.23]]],


       [[[1.23],
         [1.23]],

        [[1.23],
         [1.23]]]])
[7]:
(field.array == 1.23).all()
[7]:
True

Its shape is

[8]:
field.array.shape
[8]:
(2, 2, 2, 1)

The first three numbers are the number of discretisation cells in all three dimensions

[9]:
field.mesh.n
[9]:
array([2, 2, 2])

and the last number is the dimension of the value in the field

[10]:
field.nvdim
[10]:
1

Vector field#

So far, we investigated a basic definition of a scalar field. Now, we are going to define a vector field with nvdim=3, so that the vector at each point is pointing in the positive \(y\) direction (value=(0, 1, 0)).

[11]:
field = df.Field(mesh, nvdim=3, value=(0, 1, 0))

If we check the shape of the underlying array, we can see that the last number is now 3.

[12]:
field.array.shape
[12]:
(2, 2, 2, 3)

The values are now three-dimensional

[13]:
field.array
[13]:
array([[[[0., 1., 0.],
         [0., 1., 0.]],

        [[0., 1., 0.],
         [0., 1., 0.]]],


       [[[0., 1., 0.],
         [0., 1., 0.]],

        [[0., 1., 0.],
         [0., 1., 0.]]]])
[14]:
field(point)
[14]:
array([0., 1., 0.])

If we want to change the value of the field, we can do it via discretisedfield.Field.update_field_values.

[15]:
field.update_field_values([1, 0, -3])

The values are now changed

[16]:
field.array
[16]:
array([[[[ 1.,  0., -3.],
         [ 1.,  0., -3.]],

        [[ 1.,  0., -3.],
         [ 1.,  0., -3.]]],


       [[[ 1.,  0., -3.],
         [ 1.,  0., -3.]],

        [[ 1.,  0., -3.],
         [ 1.,  0., -3.]]]])

Note that we have used a list instead of a tuple to set a new value. Both give the same result and it is true for any iterable of length 3.

Norm#

It is possible to set the norm of each vector in a field using norm.

[17]:
field = df.Field(mesh, nvdim=3, value=(1, 1, 0), norm=1)
field(point)
[17]:
array([0.70710678, 0.70710678, 0.        ])

Naming components#

Up to three components, the names of each vector component defaults to x, y, and z. For more than three components, it defaults to x1, x2, x3, x4, etc.

It is possible to create a field with customs names for each vector component using vdims.

[18]:
field = df.Field(mesh, nvdim=3, value=(0, 1, 0), vdims=["a", "b", "c"])
field
[18]:
Field
  • Mesh
    • Region
      • pmin = [-50, -50, -50]
      • pmax = [50, 50, 50]
      • dims = ['x', 'y', 'z']
      • units = ['m', 'm', 'm']
    • n = [2, 2, 2]
  • nvdim = 3
  • vdims:
    • a
    • b
    • c

We can then access the components by using their names.

[19]:
field.a
[19]:
Field
  • Mesh
    • Region
      • pmin = [-50, -50, -50]
      • pmax = [50, 50, 50]
      • dims = ['x', 'y', 'z']
      • units = ['m', 'm', 'm']
    • n = [2, 2, 2]
  • nvdim = 1

We can check how our named components map to the spatial dimensions by looking at vdim_mapping.

[20]:
field.vdim_mapping
[20]:
{'a': 'x', 'b': 'y', 'c': 'z'}

By default, the components are directly mapped to the spatial dimensions if the ndim and nvdim are the same. Otherwise, there is no default mapping. We can initialise a field with a custom mapping as follows:

[21]:
field = df.Field(
    mesh,
    nvdim=3,
    value=(0, 1, 0),
    vdims=["a", "b", "c"],
    vdim_mapping={"a": "y", "b": "z", "c": "x"},
)
field.vdim_mapping
[21]:
{'a': 'y', 'b': 'z', 'c': 'x'}

Field units#

Scalar and vector fields can have a unit (string). Only a single unit is supported for vector fields. Please note, that the unit does not affect any mathematical operations and is lost during typical operations. Its main use is in plots in combination with xarray. It can be specified as follows:

[22]:
field = df.Field(mesh, nvdim=3, value=(0, 0, 1), unit="A/m")
[23]:
field.unit
[23]:
'A/m'