Skip to content

Commit 5a7fb30

Browse files
Merge branch 'master' into updating_tools_dir
# Conflicts: # parcels/codegenerator.py # parcels/field.py
2 parents 0e5355d + 526af07 commit 5a7fb30

7 files changed

Lines changed: 145 additions & 104 deletions

File tree

parcels/codegenerator.py

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
from parcels.field import Field, VectorField
2-
from parcels.fieldset import FieldList, VectorFieldList
1+
from parcels.field import Field, VectorField, SummedField, SummedVectorField
32
from parcels.tools.loggers import logger
43
import ast
54
import cgen as c
@@ -20,12 +19,12 @@ def __getattr__(self, attr):
2019
if isinstance(getattr(self.obj, attr), Field):
2120
return FieldNode(getattr(self.obj, attr),
2221
ccode="%s->%s" % (self.ccode, attr))
23-
elif isinstance(getattr(self.obj, attr), VectorFieldList):
24-
return VectorFieldListNode(getattr(self.obj, attr),
25-
ccode="%s->%s" % (self.ccode, attr))
26-
elif isinstance(getattr(self.obj, attr), FieldList) or isinstance(getattr(self.obj, attr), list):
27-
return FieldListNode(getattr(self.obj, attr),
28-
ccode="%s->%s" % (self.ccode, attr))
22+
elif isinstance(getattr(self.obj, attr), SummedVectorField):
23+
return SummedVectorFieldNode(getattr(self.obj, attr),
24+
ccode="%s->%s" % (self.ccode, attr))
25+
elif isinstance(getattr(self.obj, attr), SummedField) or isinstance(getattr(self.obj, attr), list):
26+
return SummedFieldNode(getattr(self.obj, attr),
27+
ccode="%s->%s" % (self.ccode, attr))
2928
elif isinstance(getattr(self.obj, attr), VectorField):
3029
return VectorFieldNode(getattr(self.obj, attr),
3130
ccode="%s->%s" % (self.ccode, attr))
@@ -60,24 +59,24 @@ def __init__(self, field, args, var, var2, var3):
6059
self.var3 = var3 # third variable for UVW interpolation
6160

6261

63-
class FieldListNode(IntrinsicNode):
62+
class SummedFieldNode(IntrinsicNode):
6463
def __getitem__(self, attr):
65-
return FieldListEvalNode(self.obj, attr)
64+
return SummedFieldEvalNode(self.obj, attr)
6665

6766

68-
class FieldListEvalNode(IntrinsicNode):
67+
class SummedFieldEvalNode(IntrinsicNode):
6968
def __init__(self, fields, args, var):
7069
self.fields = fields
7170
self.args = args
7271
self.var = var # the variable in which the interpolated field is written
7372

7473

75-
class VectorFieldListNode(IntrinsicNode):
74+
class SummedVectorFieldNode(IntrinsicNode):
7675
def __getitem__(self, attr):
77-
return VectorFieldListEvalNode(self.obj, attr)
76+
return SummedVectorFieldEvalNode(self.obj, attr)
7877

7978

80-
class VectorFieldListEvalNode(IntrinsicNode):
79+
class SummedVectorFieldEvalNode(IntrinsicNode):
8180
def __init__(self, field, args, var, var2, var3):
8281
self.field = field
8382
self.args = args
@@ -210,18 +209,18 @@ def visit_Subscript(self, node):
210209

211210
# If we encounter field evaluation we replace it with a
212211
# temporary variable and put the evaluation call on the stack.
213-
if isinstance(node.value, FieldListNode):
212+
if isinstance(node.value, SummedFieldNode):
214213
tmp = [self.get_tmp() for _ in node.value.obj]
215214
# Insert placeholder node for field eval ...
216-
self.stmt_stack += [FieldListEvalNode(node.value, node.slice, tmp)]
215+
self.stmt_stack += [SummedFieldEvalNode(node.value, node.slice, tmp)]
217216
# .. and return the name of the temporary that will be populated
218217
return ast.Name(id='+'.join(tmp))
219-
elif isinstance(node.value, VectorFieldListNode):
218+
elif isinstance(node.value, SummedVectorFieldNode):
220219
tmp = [self.get_tmp() for _ in node.value.obj.U]
221220
tmp2 = [self.get_tmp() for _ in node.value.obj.U]
222221
tmp3 = [self.get_tmp() if node.value.obj.W else None for _ in node.value.obj.U]
223222
# Insert placeholder node for field eval ...
224-
self.stmt_stack += [VectorFieldListEvalNode(node.value, node.slice, tmp, tmp2, tmp3)]
223+
self.stmt_stack += [SummedVectorFieldEvalNode(node.value, node.slice, tmp, tmp2, tmp3)]
225224
# .. and return the name of the temporary that will be populated
226225
if all(tmp3):
227226
return ast.Tuple([ast.Name(id='+'.join(tmp)), ast.Name(id='+'.join(tmp2)), ast.Name(id='+'.join(tmp3))], ast.Load())
@@ -621,7 +620,7 @@ def visit_FieldNode(self, node):
621620
"""Record intrinsic fields used in kernel"""
622621
self.field_args[node.obj.name] = node.obj
623622

624-
def visit_FieldListNode(self, node):
623+
def visit_SummedFieldNode(self, node):
625624
"""Record intrinsic fields used in kernel"""
626625
for fld in node.obj:
627626
self.field_args[fld.name] = fld
@@ -630,7 +629,7 @@ def visit_VectorFieldNode(self, node):
630629
"""Record intrinsic fields used in kernel"""
631630
self.vector_field_args[node.obj.name] = node.obj
632631

633-
def visit_VectorFieldListNode(self, node):
632+
def visit_SummedVectorFieldNode(self, node):
634633
"""Record intrinsic fields used in kernel"""
635634
for fld in node.obj.U:
636635
self.field_args[fld.name] = fld
@@ -672,7 +671,7 @@ def visit_VectorFieldEvalNode(self, node):
672671
node.ccode = c.Block([c.Assign("err", ccode_eval),
673672
conv_stat, c.Statement("CHECKERROR(err)")])
674673

675-
def visit_FieldListEvalNode(self, node):
674+
def visit_SummedFieldEvalNode(self, node):
676675
self.visit(node.fields)
677676
self.visit(node.args)
678677
cstat = []
@@ -683,7 +682,7 @@ def visit_FieldListEvalNode(self, node):
683682
cstat += [c.Assign("err", ccode_eval), conv_stat, c.Statement("CHECKERROR(err)")]
684683
node.ccode = c.Block(cstat)
685684

686-
def visit_VectorFieldListEvalNode(self, node):
685+
def visit_SummedVectorFieldEvalNode(self, node):
687686
self.visit(node.field)
688687
self.visit(node.args)
689688
cstat = []

parcels/examples/tutorial_FieldLists.ipynb renamed to parcels/examples/tutorial_SummedFields.ipynb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"cell_type": "markdown",
55
"metadata": {},
66
"source": [
7-
"## Tutorial on how to combine different Fields for advection into a `FieldList` object"
7+
"## Tutorial on how to combine different Fields for advection into a `SummedField` object"
88
]
99
},
1010
{
@@ -13,9 +13,9 @@
1313
"source": [
1414
"In some oceanographic applications, you may want to advect particles using a combination of different velocity data sets. For example, particles at the surface are transported by a combination of geostrophic, Ekman and Stokes flow. And often, these flows are not even on the same grid.\n",
1515
"\n",
16-
"One option would be to write a `Kernel` that computes the movement of particles due to each of these flows. However, in Parcels it is possible to directly combine different flows (without interpolation) and feed them into the built-in `AdvectionRK4` kernel. For that, we use so-called `FieldList` objects.\n",
16+
"One option would be to write a `Kernel` that computes the movement of particles due to each of these flows. However, in Parcels it is possible to directly combine different flows (without interpolation) and feed them into the built-in `AdvectionRK4` kernel. For that, we use so-called `SummedField` objects.\n",
1717
"\n",
18-
"This tutorial shows how to use these `FieldLists` with a very idealised example. We start by importing the relevant modules."
18+
"This tutorial shows how to use these `SummedField` with a very idealised example. We start by importing the relevant modules."
1919
]
2020
},
2121
{
@@ -123,7 +123,7 @@
123123
"cell_type": "markdown",
124124
"metadata": {},
125125
"source": [
126-
"Now comes the trick of the `FieldLists`. We can simply define a new `FieldSet` with a list of different `Fields`, as in `U=[U, Ustokes]`."
126+
"Now comes the trick of the `SummedFields`. We can simply define a new `FieldSet` with a summation of different `Fields`, as in `U=U+Ustokes`."
127127
]
128128
},
129129
{
@@ -132,7 +132,7 @@
132132
"metadata": {},
133133
"outputs": [],
134134
"source": [
135-
"fieldset = FieldSet(U=[U, Ustokes], V=[V, Vstokes])"
135+
"fieldset = FieldSet(U=U+Ustokes, V=V+Vstokes)"
136136
]
137137
},
138138
{
@@ -177,9 +177,9 @@
177177
"cell_type": "markdown",
178178
"metadata": {},
179179
"source": [
180-
"What happens under the hood is that each `Field` in the `FieldList` is interpolated separately to the particle location, and that the different velocities are added in each step of the RK4 advection. So `FieldLists` are effortless to users\n",
180+
"What happens under the hood is that each `Field` in the `SummedField` is interpolated separately to the particle location, and that the different velocities are added in each step of the RK4 advection. So `SummedFields` are effortless to users\n",
181181
"\n",
182-
"Note that `FieldLists` work for any type of `Field`, not only for velocities. Any call to a `Field` interpolation (`fieldset.fld[time, lon, lat, depth]`) will return the sum of all `Fields` in the list if `fld` is a `FieldList`."
182+
"Note that `SummedFields` work for any type of `Field`, not only for velocities. Any call to a `Field` interpolation (`fieldset.fld[time, lon, lat, depth]`) will return the sum of all `Fields` in the list if `fld` is a `SummedField`."
183183
]
184184
},
185185
{
@@ -206,7 +206,7 @@
206206
"name": "python",
207207
"nbconvert_exporter": "python",
208208
"pygments_lexer": "ipython2",
209-
"version": "2.7.15"
209+
"version": "2.7.14"
210210
}
211211
},
212212
"nbformat": 4,

parcels/field.py

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
CurvilinearSGrid, CGrid, GridCode)
1414

1515

16-
__all__ = ['Field', 'VectorField']
16+
__all__ = ['Field', 'VectorField', 'SummedField', 'SummedVectorField']
1717

1818

1919
class Field(object):
@@ -39,7 +39,7 @@ class Field(object):
3939
2. flat: No conversion, lat/lon are assumed to be in m.
4040
:param grid: :class:`parcels.grid.Grid` object containing all the lon, lat depth, time
4141
mesh and time_origin information. Can be constructed from any of the Grid objects
42-
:param fieldtype: Type of Field to be used for UnitConverter when using FieldLists
42+
:param fieldtype: Type of Field to be used for UnitConverter when using SummedFields
4343
(either 'U', 'V', 'Kh_zonal', 'Kh_Meridional' or None)
4444
:param transpose: Transpose data to required (lon, lat) layout
4545
:param vmin: Minimum allowed value on the field. Data below this value are set to zero
@@ -851,6 +851,42 @@ def computeTimeChunk(self, data, tindex):
851851

852852
return data
853853

854+
def __add__(self, field):
855+
if isinstance(self, Field) and isinstance(field, Field):
856+
return SummedField([self, field])
857+
elif isinstance(field, SummedField):
858+
field.insert(0, self)
859+
return field
860+
861+
862+
class SummedField(list):
863+
"""Class SummedField is a list of Fields over which Field interpolation
864+
is summed. This can e.g. be used when combining multiple flow fields,
865+
where the total flow is the sum of all the individual flows.
866+
Note that the individual Fields can be on different Grids.
867+
Also note that, since SummedFields are lists, the individual Fields can
868+
still be queried through their list index (e.g. SummedField[1]).
869+
"""
870+
def eval(self, time, x, y, z, applyConversion=True):
871+
tmp = 0
872+
for fld in self:
873+
tmp += fld.eval(time, x, y, z, applyConversion)
874+
return tmp
875+
876+
def __getitem__(self, key):
877+
if isinstance(key, int):
878+
return list.__getitem__(self, key)
879+
else:
880+
return self.eval(*key)
881+
882+
def __add__(self, field):
883+
if isinstance(field, Field):
884+
self.append(field)
885+
elif isinstance(field, SummedField):
886+
for fld in field:
887+
self.append(fld)
888+
return self
889+
854890

855891
class VectorField(object):
856892
"""Class VectorField stores 2 or 3 fields which defines together a vector field.
@@ -1025,6 +1061,49 @@ def ccode_eval(self, varU, varV, varW, U, V, W, t, x, y, z):
10251061
% (varU, varV, U.interp_method.upper())
10261062

10271063

1064+
class SummedVectorField(list):
1065+
"""Class SummedVectorField stores 2 or 3 SummedFields which defines together a vector field.
1066+
This enables to interpolate them as one single vector SummedField in the kernels.
1067+
1068+
:param name: Name of the vector field
1069+
:param U: SummedField defining the zonal component
1070+
:param V: SummedField defining the meridional component
1071+
:param W: SummedField defining the vertical component (default: None)
1072+
"""
1073+
1074+
def __init__(self, name, U, V, W=None):
1075+
self.name = name
1076+
self.U = U
1077+
self.V = V
1078+
self.W = W
1079+
1080+
def eval(self, time, x, y, z):
1081+
zonal = meridional = vertical = 0
1082+
if self.W is not None:
1083+
for (U, V, W) in zip(self.U, self.V, self.W):
1084+
vfld = VectorField(self.name, U, V, W)
1085+
vfld.fieldset = self.fieldset
1086+
(tmp1, tmp2, tmp3) = vfld.eval(time, x, y, z)
1087+
zonal += tmp1
1088+
meridional += tmp2
1089+
vertical += tmp3
1090+
return (zonal, meridional, vertical)
1091+
else:
1092+
for (U, V) in zip(self.U, self.V):
1093+
vfld = VectorField(self.name, U, V)
1094+
vfld.fieldset = self.fieldset
1095+
(tmp1, tmp2) = vfld.eval(time, x, y, z)
1096+
zonal += tmp1
1097+
meridional += tmp2
1098+
return (zonal, meridional)
1099+
1100+
def __getitem__(self, key):
1101+
if isinstance(key, int):
1102+
return list.__getitem__(self, key)
1103+
else:
1104+
return self.eval(*key)
1105+
1106+
10281107
class NetcdfFileBuffer(object):
10291108
""" Class that encapsulates and manages deferred access to file data. """
10301109

0 commit comments

Comments
 (0)