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.
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)
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:
discretisedfield.Field
is going to pass the coordinates of discertisation cells as tuples to this argument.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.
$$ f(x, y, z)= \begin{cases} 0, & \text{if}\ x<0 \\ 1, & \text{otherwise} \end{cases} $$The Python function is then:
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.
field = df.Field(mesh, nvdim=1, value=my_value_function)
If we sample the field at a point with negative value of $x$
field((-10, 5, 5))
array([0.])
If the $x$ coordinate is positive, we get 1.
field((25, -3, 14))
array([1.])
The array now has different values
field.array
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.
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:
field = df.Field(mesh, nvdim=3, value=vector_value_function)
field.array
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.]]]])
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:
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
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
field = df.Field(mesh, nvdim=3, value=regions_function)
For a negative value of $y$, we get:
field((10, -10, 10))
array([1., 0., 0.])
And for positive:
field((10, 30, 10))
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:
discretisedfield.Mesh
.region_values = {"region1": (1, 1, 1), "region2": (2, 2, 2)}
field.update_field_values(region_values)
Now, we can sample points in two regions.
field((-10, -10, -10))
array([1., 1., 1.])
field((10, 10, 10))
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
.
region_values = {"region1": (0, 1, 1), "default": (2, 2, 0)}
field.update_field_values(region_values)
field((-10, -10, -10))
array([0., 1., 1.])
field((10, 10, 10))
array([2., 2., 0.])
region_values = {"default": (2, 2, 1)}
field.update_field_values(region_values)
field((-10, -10, -10))
array([2., 2., 1.])
field((10, 10, 10))
array([2., 2., 1.])
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:
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
field.array
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
new_field = df.Field(new_mesh, nvdim=3, value=field)
The values are now
new_field.array.shape
(4, 4, 4, 3)
new_field((-5, -5, -5))
array([2., 2., 1.])
new_field((5, 5, 5))
array([2., 2., 1.])
There is an additional class method to create a 3d vector field whose values are the coordinates of the cells.
mesh = df.Mesh(p1=(-10, -5, 0), p2=(10, 5, 10), n=(10, 10, 10))
coord_field = df.Mesh.coordinate_field(mesh)
coord_field
We can call the field at some point and get the coordinate of the corresponding cell centre.
coord_field((1, 1.25, 0))
array([1. , 1.5, 0.5])
coord_field((8, -3, 4))
array([ 9. , -2.5, 4.5])
Full description of all existing functionality can be found in the API Reference.