Repository: OceanParcels/parcels
Pull request #577 (2019-04-17 14:23:17)

Uv and uvw jit
This commit enables to call `fieldset.UV` and `fieldset.UVW` in a same kernel.
Before, vectorFields containing the same components were conflicting in JIT mode


Patch:
diff --git a/.travis.yml b/.travis.yml
index 6253cebe..40f36620 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,6 +6,7 @@ notifications:
 matrix:
   include:
     - os: linux
+      python: 2.7
       env: PY_VERSION=2 MINICONDA_NAME=Miniconda2-latest-Linux-x86_64.sh
     - os: linux
       env: PY_VERSION=3 MINICONDA_NAME=Miniconda3-latest-Linux-x86_64.sh
diff --git a/parcels/codegenerator.py b/parcels/codegenerator.py
index 5dbb21fc..27a5cffa 100644
--- a/parcels/codegenerator.py
+++ b/parcels/codegenerator.py
@@ -416,10 +416,14 @@ class KernelGenerator(ast.NodeVisitor):
         for field in self.field_args.values():
             args += [c.Pointer(c.Value("CField", "%s" % field.ccode_name))]
         for field in self.vector_field_args.values():
-            Wname = field.W.ccode_name if field.W else 'not_defined'
-            for fname in [field.U.ccode_name, field.V.ccode_name, Wname]:
-                if fname not in self.field_args and fname != 'not_defined':
-                    args += [c.Pointer(c.Value("CField", "%s" % fname))]
+            for fcomponent in ['U', 'V', 'W']:
+                try:
+                    f = getattr(field, fcomponent)
+                    if f.ccode_name not in self.field_args:
+                        args += [c.Pointer(c.Value("CField", "%s" % f.ccode_name))]
+                        self.field_args[f.ccode_name] = f
+                except:
+                    pass  # field.W does not always exist
         for const, _ in self.const_args.items():
             args += [c.Value("float", const)]
 


Test patch:
diff --git a/parcels/examples/tutorial_nemo_3D.ipynb b/parcels/examples/tutorial_nemo_3D.ipynb
old mode 100755
new mode 100644
diff --git a/tests/test_fieldset.py b/tests/test_fieldset.py
index 6fa5a81d..8b32b7ec 100644
--- a/tests/test_fieldset.py
+++ b/tests/test_fieldset.py
@@ -324,6 +324,7 @@ def test_periodic(mode, time_periodic, dt_sign):
 
     U = np.zeros((2, 2, 2, tsize), dtype=np.float32)
     V = np.zeros((2, 2, 2, tsize), dtype=np.float32)
+    V[0, 0, 0, :] = 1e-5
     W = np.zeros((2, 2, 2, tsize), dtype=np.float32)
     temp = np.zeros((2, 2, 2, tsize), dtype=np.float32)
     temp[:, :, :, :] = temp_vec
@@ -337,9 +338,16 @@ def test_periodic(mode, time_periodic, dt_sign):
         # Indeed, sampleTemp is called at time=time, but the result is written
         # at time=time+dt, after the Kernel update
         particle.temp = fieldset.temp[time+particle.dt, particle.depth, particle.lat, particle.lon]
+        # test if we can interpolate UV and UVW together
+        (particle.u1, particle.v1) = fieldset.UV[time+particle.dt, particle.depth, particle.lat, particle.lon]
+        (particle.u2, particle.v2, w_) = fieldset.UVW[time+particle.dt, particle.depth, particle.lat, particle.lon]
 
     class MyParticle(ptype[mode]):
         temp = Variable('temp', dtype=np.float32, initial=20.)
+        u1 = Variable('u1', dtype=np.float32, initial=0.)
+        u2 = Variable('u2', dtype=np.float32, initial=0.)
+        v1 = Variable('v1', dtype=np.float32, initial=0.)
+        v2 = Variable('v2', dtype=np.float32, initial=0.)
 
     dt_sign = -1
     pset = ParticleSet.from_list(fieldset, pclass=MyParticle,
@@ -355,6 +363,8 @@ def test_periodic(mode, time_periodic, dt_sign):
     elif dt_sign == -1:
         temp_theo = temp_vec[0]
     assert np.allclose(temp_theo, pset.particles[0].temp, atol=1e-5)
+    assert np.allclose(pset.particles[0].u1, pset.particles[0].u2)
+    assert np.allclose(pset.particles[0].v1, pset.particles[0].v2)
 
 
 @pytest.mark.parametrize('fail', [False, pytest.param(True, marks=pytest.mark.xfail(strict=True))])