From 98fb0537f7c089b0184015696cf58e24c16e2f41 Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@uliege.be>
Date: Mon, 14 Sep 2020 12:53:33 +0200
Subject: [PATCH] basic python implementation of the global mesh size callback

---
 api/GenApi.py          | 10 ++++++++--
 api/gen.py             |  6 +++---
 api/gmsh.h             |  6 ++++--
 api/gmsh.h_cwrap       |  6 ++++--
 api/gmsh.jl            |  2 +-
 api/gmsh.py            | 22 +++++++++++++++++++++-
 api/gmshc.h            |  6 ++++--
 doc/texinfo/api.texi   | 18 +++++++++++-------
 tutorial/c++/t10.cpp   |  5 ++---
 tutorial/python/t10.py |  5 ++---
 10 files changed, 60 insertions(+), 26 deletions(-)

diff --git a/api/GenApi.py b/api/GenApi.py
index 8553639582..5c9b1cbbfb 100644
--- a/api/GenApi.py
+++ b/api/GenApi.py
@@ -738,9 +738,13 @@ def isizefun(name):
     a.cpp = "double (*" + name + ")(int dim, int tag, double x, double y, double z)"
     a.c_arg = name
     a.c = a.cpp
-    a.c_pre = ""
-    a.c_post = ""
     a.cwrap_arg = a.c_arg
+    a.python_pre = ("global api_" + name + "_type_\n" +
+                    "            api_" + name + "_type_ = " +
+                    "CFUNCTYPE(c_double, c_int, c_int, c_double, c_double, c_double)\n" +
+                    "            global api_" + name + "_\n" +
+                    "            api_" + name + "_ = api_" + name + "_type_(" + name + ")")
+    a.python_arg = "api_" + name + "_"
     return a
 
 
@@ -1382,6 +1386,7 @@ class API:
         def write_function(f, fun, c_mpath, py_mpath, indent):
             (rtype, name, args, doc, special) = fun
             if "onlycc++" in special: return
+            if "nopython" in special: return
             iargs = list(a for a in args if not a.out)
             oargs = list(a for a in args if a.out)
             f.write("\n")
@@ -1463,6 +1468,7 @@ class API:
         def write_function(f, fun, c_mpath, jl_mpath):
             (rtype, name, args, doc, special) = fun
             if "onlycc++" in special: return
+            if "nojulia" in special: return
             iargs = list(a for a in args if not a.out)
             oargs = list(a for a in args if a.out)
             f.write('\n"""\n    ')
diff --git a/api/gen.py b/api/gen.py
index f42c862606..6457a98eab 100644
--- a/api/gen.py
+++ b/api/gen.py
@@ -377,10 +377,10 @@ mesh.add('setSize', doc, None, ivectorpair('dimTags'), idouble('size'))
 doc = '''Set mesh size constraints at the given parametric points `parametricCoord' on the model entity of dimension `dim' and tag `tag'. Currently only entities of dimension 1 (lines) are handled.'''
 mesh.add('setSizeAtParametricPoints', doc, None, iint('dim'), iint('tag'), ivectordouble('parametricCoord'), ivectordouble('sizes'))
 
-doc = '''Set global mesh size callback. For C and C++ only.'''
-mesh.add_special('setSizeCallback', doc, ['onlycc++'], None, isizefun('callback'))
+doc = '''Set a global mesh size callback. The callback should take 5 arguments (`dim', `tag', `x', `y' and `z') and return the value of the mesh size at coordinates (`x', `y', `z').'''
+mesh.add_special('setSizeCallback', doc, ['nojulia'], None, isizefun('callback'))
 
-doc = '''Remove global mesh size callback. For C and C++ only.'''
+doc = '''Remove the global mesh size callback.'''
 mesh.add('removeSizeCallback', doc, None)
 
 doc = '''Set a transfinite meshing constraint on the curve `tag', with `numNodes' nodes distributed according to `meshType' and `coef'. Currently supported types are "Progression" (geometrical progression with power `coef') and "Bump" (refinement toward both extremities of the curve).'''
diff --git a/api/gmsh.h b/api/gmsh.h
index b88666ea7a..5530e7c58a 100644
--- a/api/gmsh.h
+++ b/api/gmsh.h
@@ -1171,12 +1171,14 @@ namespace gmsh { // Top-level functions
 
       // gmsh::model::mesh::setSizeCallback
       //
-      // Set global mesh size callback. For C and C++ only.
+      // Set a global mesh size callback. The callback should take 5 arguments
+      // (`dim', `tag', `x', `y' and `z') and return the value of the mesh size at
+      // coordinates (`x', `y', `z').
       GMSH_API void setSizeCallback(double (*callback)(int dim, int tag, double x, double y, double z));
 
       // gmsh::model::mesh::removeSizeCallback
       //
-      // Remove global mesh size callback. For C and C++ only.
+      // Remove the global mesh size callback.
       GMSH_API void removeSizeCallback();
 
       // gmsh::model::mesh::setTransfiniteCurve
diff --git a/api/gmsh.h_cwrap b/api/gmsh.h_cwrap
index ed864d4843..8963f0e0f0 100644
--- a/api/gmsh.h_cwrap
+++ b/api/gmsh.h_cwrap
@@ -1785,7 +1785,9 @@ namespace gmsh { // Top-level functions
         gmshFree(api_sizes_);
       }
 
-      // Set global mesh size callback. For C and C++ only.
+      // Set a global mesh size callback. The callback should take 5 arguments
+      // (`dim', `tag', `x', `y' and `z') and return the value of the mesh size at
+      // coordinates (`x', `y', `z').
       inline void setSizeCallback(double (*callback)(int dim, int tag, double x, double y, double z))
       {
         int ierr = 0;
@@ -1793,7 +1795,7 @@ namespace gmsh { // Top-level functions
         if(ierr) throwLastError();
       }
 
-      // Remove global mesh size callback. For C and C++ only.
+      // Remove the global mesh size callback.
       inline void removeSizeCallback()
       {
         int ierr = 0;
diff --git a/api/gmsh.jl b/api/gmsh.jl
index cebd116ae1..7277bef96f 100644
--- a/api/gmsh.jl
+++ b/api/gmsh.jl
@@ -2200,7 +2200,7 @@ end
 """
     gmsh.model.mesh.removeSizeCallback()
 
-Remove global mesh size callback. For C and C++ only.
+Remove the global mesh size callback.
 """
 function removeSizeCallback()
     ierr = Ref{Cint}()
diff --git a/api/gmsh.py b/api/gmsh.py
index daa213274e..d2666a771a 100644
--- a/api/gmsh.py
+++ b/api/gmsh.py
@@ -2602,12 +2602,32 @@ class model:
             if ierr.value != 0:
                 raise Exception(logger.getLastError())
 
+        @staticmethod
+        def setSizeCallback(callback):
+            """
+            gmsh.model.mesh.setSizeCallback(callback)
+
+            Set a global mesh size callback. The callback should take 5 arguments
+            (`dim', `tag', `x', `y' and `z') and return the value of the mesh size at
+            coordinates (`x', `y', `z').
+            """
+            global api_callback_type_
+            api_callback_type_ = CFUNCTYPE(c_double, c_int, c_int, c_double, c_double, c_double)
+            global api_callback_
+            api_callback_ = api_callback_type_(callback)
+            ierr = c_int()
+            lib.gmshModelMeshSetSizeCallback(
+                api_callback_,
+                byref(ierr))
+            if ierr.value != 0:
+                raise Exception(logger.getLastError())
+
         @staticmethod
         def removeSizeCallback():
             """
             gmsh.model.mesh.removeSizeCallback()
 
-            Remove global mesh size callback. For C and C++ only.
+            Remove the global mesh size callback.
             """
             ierr = c_int()
             lib.gmshModelMeshRemoveSizeCallback(
diff --git a/api/gmshc.h b/api/gmshc.h
index 2513e032cb..c2830ee620 100644
--- a/api/gmshc.h
+++ b/api/gmshc.h
@@ -1030,11 +1030,13 @@ GMSH_API void gmshModelMeshSetSizeAtParametricPoints(const int dim,
                                                      double * sizes, size_t sizes_n,
                                                      int * ierr);
 
-/* Set global mesh size callback. For C and C++ only. */
+/* Set a global mesh size callback. The callback should take 5 arguments
+ * (`dim', `tag', `x', `y' and `z') and return the value of the mesh size at
+ * coordinates (`x', `y', `z'). */
 GMSH_API void gmshModelMeshSetSizeCallback(double (*callback)(int dim, int tag, double x, double y, double z),
                                            int * ierr);
 
-/* Remove global mesh size callback. For C and C++ only. */
+/* Remove the global mesh size callback. */
 GMSH_API void gmshModelMeshRemoveSizeCallback(int * ierr);
 
 /* Set a transfinite meshing constraint on the curve `tag', with `numNodes'
diff --git a/doc/texinfo/api.texi b/doc/texinfo/api.texi
index 019eadb5cd..e4502e2c0f 100644
--- a/doc/texinfo/api.texi
+++ b/doc/texinfo/api.texi
@@ -1774,7 +1774,9 @@ entities of dimension 1 (lines) are handled.
 @end table
 
 @item gmsh/model/mesh/setSizeCallback
-Set global mesh size callback. For C and C++ only.
+Set a global mesh size callback. The callback should take 5 arguments
+(@code{dim}, @code{tag}, @code{x}, @code{y} and @code{z}) and return the value
+of the mesh size at coordinates (@code{x}, @code{y}, @code{z}).
 
 @table @asis
 @item Input:
@@ -1783,10 +1785,12 @@ Set global mesh size callback. For C and C++ only.
 -
 @item Return:
 -
+@item Examples:
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t10.cpp#L117,t10.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t10.py#L108,t10.py})
 @end table
 
 @item gmsh/model/mesh/removeSizeCallback
-Remove global mesh size callback. For C and C++ only.
+Remove the global mesh size callback.
 
 @table @asis
 @item Input:
@@ -2243,7 +2247,7 @@ the field tag.
 @item Return:
 integer value
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t7.cpp#L49,t7.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t10.cpp#L46,t10.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t11.cpp#L38,t11.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t13.cpp#L68,t13.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t17.cpp#L41,t17.cpp}, ...), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t7.py#L44,t7.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t10.py#L44,t10.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t13.py#L57,t13.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t17.py#L35,t17.py}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.py#L113,adapt_mesh.py})
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t7.cpp#L49,t7.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t10.cpp#L51,t10.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t11.cpp#L38,t11.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t13.cpp#L68,t13.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t17.cpp#L41,t17.cpp}, ...), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t7.py#L44,t7.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t10.py#L44,t10.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t13.py#L57,t13.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t17.py#L35,t17.py}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.py#L113,adapt_mesh.py})
 @end table
 
 @item gmsh/model/mesh/field/remove
@@ -2270,7 +2274,7 @@ Set the numerical option @code{option} to value @code{value} for field
 @item Return:
 -
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t10.cpp#L48,t10.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.cpp#L271,adapt_mesh.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t10.py#L46,t10.py}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.py#L114,adapt_mesh.py})
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t10.cpp#L53,t10.cpp}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.cpp#L271,adapt_mesh.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t10.py#L46,t10.py}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.py#L114,adapt_mesh.py})
 @end table
 
 @item gmsh/model/mesh/field/setString
@@ -2284,7 +2288,7 @@ Set the string option @code{option} to value @code{value} for field @code{tag}.
 @item Return:
 -
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t10.cpp#L73,t10.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t11.cpp#L39,t11.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t13.cpp#L70,t13.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t10.py#L70,t10.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t13.py#L59,t13.py})
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t10.cpp#L78,t10.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t11.cpp#L39,t11.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t13.cpp#L70,t13.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t10.py#L70,t10.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t13.py#L59,t13.py})
 @end table
 
 @item gmsh/model/mesh/field/setNumbers
@@ -2299,7 +2303,7 @@ Set the numerical list option @code{option} to value @code{value} for field
 @item Return:
 -
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t10.cpp#L47,t10.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t10.py#L45,t10.py})
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t10.cpp#L52,t10.cpp}), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t10.py#L45,t10.py})
 @end table
 
 @item gmsh/model/mesh/field/setAsBackgroundMesh
@@ -2313,7 +2317,7 @@ Set the field @code{tag} as the background mesh size field.
 @item Return:
 -
 @item Examples:
-C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t7.cpp#L52,t7.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t10.cpp#L108,t10.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t11.cpp#L41,t11.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t13.cpp#L73,t13.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t17.cpp#L42,t17.cpp}, ...), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t7.py#L47,t7.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t10.py#L102,t10.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t13.py#L62,t13.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t17.py#L36,t17.py}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.py#L115,adapt_mesh.py})
+C++ (@url{@value{GITLAB-PREFIX}/tutorial/c++/t7.cpp#L52,t7.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t10.cpp#L113,t10.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t11.cpp#L41,t11.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t13.cpp#L73,t13.cpp}, @url{@value{GITLAB-PREFIX}/tutorial/c++/t17.cpp#L42,t17.cpp}, ...), Python (@url{@value{GITLAB-PREFIX}/tutorial/python/t7.py#L47,t7.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t10.py#L102,t10.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t13.py#L62,t13.py}, @url{@value{GITLAB-PREFIX}/tutorial/python/t17.py#L36,t17.py}, @url{@value{GITLAB-PREFIX}/demos/api/adapt_mesh.py#L115,adapt_mesh.py})
 @end table
 
 @item gmsh/model/mesh/field/setAsBoundaryLayer
diff --git a/tutorial/c++/t10.cpp b/tutorial/c++/t10.cpp
index 86c1148c2a..59fcc2619d 100644
--- a/tutorial/c++/t10.cpp
+++ b/tutorial/c++/t10.cpp
@@ -15,8 +15,7 @@
 
 double meshSizeCallback(int dim, int tag, double x, double y, double z)
 {
-  double val = 0.02 * x + 0.01;
-  return val;
+  return 0.02 * x + 0.01;
 }
 
 int main(int argc, char **argv)
@@ -113,7 +112,7 @@ int main(int argc, char **argv)
 
   gmsh::model::mesh::field::setAsBackgroundMesh(7);
 
-  // In the API we can also set a global mesh size callback, which is called
+  // The API also allows to set a global mesh size callback, which is called
   // each time the mesh size is queried
   gmsh::model::mesh::setSizeCallback(meshSizeCallback);
 
diff --git a/tutorial/python/t10.py b/tutorial/python/t10.py
index 38924eb679..e542747ea3 100644
--- a/tutorial/python/t10.py
+++ b/tutorial/python/t10.py
@@ -101,11 +101,10 @@ gmsh.model.mesh.field.setNumbers(7, "FieldsList", [2, 3, 5, 6])
 
 gmsh.model.mesh.field.setAsBackgroundMesh(7)
 
-# In the API we can also set a global mesh size callback, which is called each
+# The API also allows to set a global mesh size callback, which is called each
 # time the mesh size is queried
 def meshSizeCallback(dim, tag, x, y, z):
-    val = 0.02 * x + 0.01
-    return val
+    return 0.02 * x + 0.01
 gmsh.model.mesh.setSizeCallback(meshSizeCallback)
 
 # To determine the size of mesh elements, Gmsh locally computes the minimum of
-- 
GitLab