Spatially varying fields#

There are several different ways how a spatially varying field can be defined. Let us first define a mesh we are going to use to define the fields.

[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)

Using a Python function#

One of the ways how a spatially varying field can be defined is by using a Python function, which can be passed as value argument to discretisedfield.Field. The function should satisfy three main criteria: 1. It takes one argument. discretisedfield.Field is going to pass the coordinates of discertisation cells as tuples to this argument. 2. Function should be able to return a value for any given coordinate in the mesh. 3. The value returned must be of the same dimension as the dimension of the field.

Let us assume we want to have a scalar field which has a value 0 for all points with negative \(x\) coordinate and value 1 otherwise.

\[\begin{split}f(x, y, z)= \begin{cases} 0, & \text{if}\ x<0 \\ 1, & \text{otherwise} \end{cases}\end{split}\]

The Python function is then:

[2]:
def my_value_function(pos):
    x, y, z = pos
    if x < 0:
        return 0
    else:
        return 1

After defining the value function, we can define the field.

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

If we sample the field at a point with negative value of \(x\)

[4]:
field((-10, 5, 5))
[4]:
array([0.])

If the \(x\) coordinate is positive, we get 1.

[5]:
field((25, -3, 14))
[5]:
array([1.])

The array now has different values

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

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


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

        [[1.],
         [1.]]]])

Similar to scalar fields, a Python function can be used to set the value of a vector field. This time, the function returns three-dimensional values for the field.

[7]:
def vector_value_function(pos):
    x, y, z = pos
    vx = x
    vy = x * y
    vz = x * y * z

    return (vx, vy, vz)

This function can now be used at the definition of the field:

[8]:
field = df.Field(mesh, nvdim=3, value=vector_value_function)
[9]:
field.array
[9]:
array([[[[   -25.,    625., -15625.],
         [   -25.,    625.,  15625.]],

        [[   -25.,   -625.,  15625.],
         [   -25.,   -625., -15625.]]],


       [[[    25.,   -625.,  15625.],
         [    25.,   -625., -15625.]],

        [[    25.,    625., -15625.],
         [    25.,    625.,  15625.]]]])

Using mesh regions#

If regions were defined as a part of the mesh, and we want to set the value of the field differently in those regions, we can employ some of the functionality of regions. Let us assume that in the mesh we defined we want to have two regions. Region 1 is going to include all cells with negative \(y\) coordinate and region 2 cells with positive \(y\) coordinate. Our mesh would be:

[10]:
subregions = {
    "region1": df.Region(p1=(-50, -50, -50), p2=(50, 0, 50)),
    "region2": df.Region(p1=(-50, 0, -50), p2=(50, 50, 50)),
}
mesh = df.Mesh(p1=p1, p2=p2, n=n, subregions=subregions)

Python function employing these regions can now be

[11]:
def regions_function(pos):
    if pos in mesh.subregions["region1"]:
        return (1, 0, 0)
    elif pos in mesh.subregions["region2"]:
        return (0, 1, 0)
    else:
        return (0, 0, 0)

We can now pass this function to the discretisedfield.Field class

[12]:
field = df.Field(mesh, nvdim=3, value=regions_function)

For a negative value of \(y\), we get:

[13]:
field((10, -10, 10))
[13]:
array([1., 0., 0.])

And for positive:

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

Another way of setting the field is passing the dictionary as a value to the field. However, there are several points that must be taken care of: 1. Region names must be the same as defined regions in discretisedfield.Mesh. 2. Only those points in the mesh which belong to one of the regions will be set. If there is a point which is not in any of the regions, its value is set to zero.

[15]:
region_values = {"region1": (1, 1, 1), "region2": (2, 2, 2)}
field.update_field_values(region_values)

Now, we can sample points in two regions.

[16]:
field((-10, -10, -10))
[16]:
array([1., 1., 1.])
[17]:
field((10, 10, 10))
[17]:
array([2., 2., 2.])

Initialisation can be simplified if several subregions have the same value or only parts of the region are contained within one of the subregions. It is possible to omit any number of subregion keys and specify the special key default. All points not contained in one of the explicitely given subregions are then set to the value of default.

[18]:
region_values = {"region1": (0, 1, 1), "default": (2, 2, 0)}
field.update_field_values(region_values)
[19]:
field((-10, -10, -10))
[19]:
array([0., 1., 1.])
[20]:
field((10, 10, 10))
[20]:
array([2., 2., 0.])
[21]:
region_values = {"default": (2, 2, 1)}
field.update_field_values(region_values)
[22]:
field((-10, -10, -10))
[22]:
array([2., 2., 1.])
[23]:
field((10, 10, 10))
[23]:
array([2., 2., 1.])

Using another Field object#

Sometimes it is necessary to “resample” the field using a different mesh. Another field can be passed as a value to the new field. If our new mesh is:

[24]:
p1 = (-10, -10, -10)
p2 = (10, 10, 10)
cell = (5, 5, 5)
new_mesh = df.Mesh(p1=p1, p2=p2, cell=cell)

The field we initialised previouly has the value

[25]:
field.array
[25]:
array([[[[2., 2., 1.],
         [2., 2., 1.]],

        [[2., 2., 1.],
         [2., 2., 1.]]],


       [[[2., 2., 1.],
         [2., 2., 1.]],

        [[2., 2., 1.],
         [2., 2., 1.]]]])

We can now resample that field as

[26]:
new_field = df.Field(new_mesh, nvdim=3, value=field)

The values are now

[27]:
new_field.array.shape
[27]:
(4, 4, 4, 3)
[28]:
new_field((-5, -5, -5))
[28]:
array([2., 2., 1.])
[29]:
new_field((5, 5, 5))
[29]:
array([2., 2., 1.])

Creating a field with coordinate values#

There is an additional class method to create a 3d vector field whose values are the coordinates of the cells.

[30]:
mesh = df.Mesh(p1=(-10, -5, 0), p2=(10, 5, 10), n=(10, 10, 10))
[31]:
coord_field = df.Mesh.coordinate_field(mesh)
[32]:
coord_field
[32]:
Field
  • Mesh
    • Region
      • pmin = [-10, -5, 0]
      • pmax = [10, 5, 10]
      • dims = ['x', 'y', 'z']
      • units = ['m', 'm', 'm']
    • n = [10, 10, 10]
  • nvdim = 3
  • vdims:
    • x
    • y
    • z

We can call the field at some point and get the coordinate of the corresponding cell centre.

[33]:
coord_field((1, 1.25, 0))
[33]:
array([1. , 1.5, 0.5])
[34]:
coord_field((8, -3, 4))
[34]:
array([ 9. , -2.5,  4.5])

Other#

Full description of all existing functionality can be found in the API Reference.