Skip to content

Commit 82d40ff

Browse files
authored
Merge pull request #403 from jcapriot/TreeMesh_modify_lock
Updates for python free threading
2 parents 267d976 + dff9c40 commit 82d40ff

File tree

13 files changed

+209
-108
lines changed

13 files changed

+209
-108
lines changed

.ci/azure/setup_env.sh

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ set -ex #echo on and exit if any line fails
44
# TF_BUILD is set to True on azure pipelines.
55
is_azure=$(echo "${TF_BUILD:-false}" | tr '[:upper:]' '[:lower:]')
66
do_doc=$(echo "${DOC_BUILD:-false}" | tr '[:upper:]' '[:lower:]')
7+
is_free_threaded=$(echo "${PYTHON_FREETHREADING:-false}" | tr '[:upper:]' '[:lower:]')
78

89
if ${is_azure}
910
then
@@ -13,8 +14,14 @@ then
1314
fi
1415
fi
1516

16-
cp .ci/environment_test.yml environment_test_with_pyversion.yml
17-
echo " - python="$PYTHON_VERSION >> environment_test_with_pyversion.yml
17+
if ${is_free_threaded}
18+
then
19+
cp .ci/environment_test_bare.yml environment_test_with_pyversion.yml
20+
echo " - python-freethreading="$PYTHON_VERSION >> environment_test_with_pyversion.yml
21+
else
22+
cp .ci/environment_test.yml environment_test_with_pyversion.yml
23+
echo " - python="$PYTHON_VERSION >> environment_test_with_pyversion.yml
24+
fi
1825

1926
conda env create --file environment_test_with_pyversion.yml
2027
rm environment_test_with_pyversion.yml

.ci/azure/test.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,21 @@ jobs:
55
linux-Python310:
66
image: ubuntu-latest
77
python.version: '3.10'
8-
coverage: True
98
linux-Python311:
109
image: ubuntu-latest
1110
python.version: '3.11'
11+
coverage: True
1212
linux-Python312:
1313
image: ubuntu-latest
1414
python.version: '3.12'
1515
linux-Python313:
1616
image: ubuntu-latest
1717
python.version: '3.13'
18+
linux-Python313t:
19+
image: ubuntu-latest
20+
python.version: '3.13'
21+
python.freethreading: True
22+
coverage: True
1823
osx-Python310:
1924
image: macOS-latest
2025
python.version: '3.10'
@@ -27,6 +32,10 @@ jobs:
2732
osx-Python313:
2833
image: macOS-latest
2934
python.version: '3.13'
35+
osx-Python313t:
36+
image: macOS-latest
37+
python.version: '3.13'
38+
python.freethreading: True
3039
win-Python310:
3140
image: windows-latest
3241
python.version: '3.10'
@@ -39,6 +48,10 @@ jobs:
3948
win-Python313:
4049
image: windows-latest
4150
python.version: '3.13'
51+
win-Python313t:
52+
image: windows-latest
53+
python.version: '3.13'
54+
python.freethreading: True
4255
displayName: "${{ variables.image }} ${{ variables.python.version }}"
4356
pool:
4457
vmImage: $(image)

.ci/environment_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@ dependencies:
3131
- meson-python>=0.14.0
3232
- meson
3333
- ninja
34-
- cython>=0.29.35
34+
- cython>=3.1.0
3535
- setuptools_scm

.ci/environment_test_bare.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: discretize-test
2+
channels:
3+
- conda-forge
4+
dependencies:
5+
- numpy>=1.22.4
6+
- scipy>=1.8
7+
8+
# testing
9+
- sympy
10+
- pytest
11+
- pytest-cov
12+
13+
# Building
14+
- pip
15+
- meson-python>=0.14.0
16+
- meson
17+
- ninja
18+
- cython>=3.1.0
19+
- setuptools_scm

discretize/_extensions/interputils_cython.pyx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# cython: embedsignature=True, language_level=3
22
# cython: linetrace=True
3+
# cython: freethreading_compatible = True
34
import numpy as np
45
import cython
56
cimport numpy as np

discretize/_extensions/simplex_helpers.pyx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# distutils: language=c++
22
# cython: embedsignature=True, language_level=3
33
# cython: linetrace=True
4+
# cython: freethreading_compatible = True
45

56
from libcpp.pair cimport pair
67
from libcpp.unordered_map cimport unordered_map

discretize/_extensions/tree_ext.pyx

Lines changed: 75 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# distutils: language=c++
22
# cython: embedsignature=True, language_level=3
33
# cython: linetrace=True
4+
# cython: freethreading_compatible=True
45
cimport cython
56
cimport numpy as np
67
from libc.stdlib cimport malloc, free
@@ -359,6 +360,7 @@ cdef class _TreeMesh:
359360
cdef int_t[3] ls
360361
cdef int _finalized
361362
cdef bool _diagonal_balance
363+
cdef cython.pymutex _tree_modify_lock
362364

363365
cdef double[:] _xs, _ys, _zs
364366
cdef double[:] _origin
@@ -546,9 +548,12 @@ cdef class _TreeMesh:
546548

547549
#Wrapping function so it can be called in c++
548550
cdef void * func_ptr = <void *> function
549-
self.wrapper.set(func_ptr, _evaluate_func)
550-
#Then tell c++ to build the tree
551-
self.tree.refine_function(self.wrapper, diag_balance)
551+
552+
553+
with self._tree_modify_lock:
554+
self.wrapper.set(func_ptr, _evaluate_func)
555+
#Then tell c++ to build the tree
556+
self.tree.refine_function(self.wrapper, diag_balance)
552557
if finalize:
553558
self.finalize()
554559

@@ -631,7 +636,9 @@ cdef class _TreeMesh:
631636
for i in range(n_balls):
632637
ball = geom.Ball(self._dim, &cs[i_c, 0], rs[i_r])
633638
l = _wrap_levels(ls[i_l], max_level)
634-
self.tree.refine_geom(ball, l, diag_balance)
639+
640+
with self._tree_modify_lock:
641+
self.tree.refine_geom(ball, l, diag_balance)
635642

636643
i_c += cs_step
637644
i_r += rs_step
@@ -715,7 +722,8 @@ cdef class _TreeMesh:
715722
for i in range(n_boxes):
716723
box = geom.Box(self._dim, &x0[i_x0, 0], &x1[i_x1, 0])
717724
l = _wrap_levels(ls[i_l], max_level)
718-
self.tree.refine_geom(box, l, diag_balance)
725+
with self._tree_modify_lock:
726+
self.tree.refine_geom(box, l, diag_balance)
719727

720728
i_x0 += x0_step
721729
i_x1 += x1_step
@@ -792,7 +800,8 @@ cdef class _TreeMesh:
792800
for i in range(n_segments):
793801
line = geom.Line(self._dim, &line_nodes[i_line, 0], &line_nodes[i_line+1, 0])
794802
l = _wrap_levels(ls[i_l], max_level)
795-
self.tree.refine_geom(line, l, diag_balance)
803+
with self._tree_modify_lock:
804+
self.tree.refine_geom(line, l, diag_balance)
796805

797806
i_line += line_step
798807
i_l += l_step
@@ -875,7 +884,8 @@ cdef class _TreeMesh:
875884
for i in range(n_planes):
876885
plane = geom.Plane(self._dim, &x_0s[i_o, 0], &norms[i_n, 0])
877886
l = _wrap_levels(ls[i_l], max_level)
878-
self.tree.refine_geom(plane, l, diag_balance)
887+
with self._tree_modify_lock:
888+
self.tree.refine_geom(plane, l, diag_balance)
879889

880890
i_o += origin_step
881891
i_n += normal_step
@@ -953,7 +963,8 @@ cdef class _TreeMesh:
953963
for i in range(n_triangles):
954964
triang = geom.Triangle(self._dim, &tris[i_tri, 0, 0], &tris[i_tri, 1, 0], &tris[i_tri, 2, 0])
955965
l = _wrap_levels(ls[i_l], max_level)
956-
self.tree.refine_geom(triang, l, diag_balance)
966+
with self._tree_modify_lock:
967+
self.tree.refine_geom(triang, l, diag_balance)
957968

958969
i_tri += tri_step
959970
i_l += l_step
@@ -1048,7 +1059,8 @@ cdef class _TreeMesh:
10481059
for i in range(n_triangles):
10491060
vert_prism = geom.VerticalTriangularPrism(self._dim, &tris[i_tri, 0, 0], &tris[i_tri, 1, 0], &tris[i_tri, 2, 0], hs[i_h])
10501061
l = _wrap_levels(ls[i_l], max_level)
1051-
self.tree.refine_geom(vert_prism, l, diag_balance)
1062+
with self._tree_modify_lock:
1063+
self.tree.refine_geom(vert_prism, l, diag_balance)
10521064

10531065
i_tri += tri_step
10541066
i_h += h_step
@@ -1133,7 +1145,9 @@ cdef class _TreeMesh:
11331145
for i in range(n_triangles):
11341146
l = _wrap_levels(ls[i_l], max_level)
11351147
tet = geom.Tetrahedron(self._dim, &tris[i_tri, 0, 0], &tris[i_tri, 1, 0], &tris[i_tri, 2, 0], &tris[i_tri, 3, 0])
1136-
self.tree.refine_geom(tet, l, diag_balance)
1148+
1149+
with self._tree_modify_lock:
1150+
self.tree.refine_geom(tet, l, diag_balance)
11371151

11381152
i_tri += tri_step
11391153
i_l += l_step
@@ -1192,7 +1206,8 @@ cdef class _TreeMesh:
11921206

11931207
for i in range(ls.shape[0]):
11941208
l = _wrap_levels(ls[i_l], max_level)
1195-
self.tree.insert_cell(&cs[i_p, 0], l, diagonal_balance)
1209+
with self._tree_modify_lock:
1210+
self.tree.insert_cell(&cs[i_p, 0], l, diagonal_balance)
11961211

11971212
i_l += l_step
11981213
i_p += p_step
@@ -1208,10 +1223,11 @@ cdef class _TreeMesh:
12081223
operators. When finalized, mesh refinement is no longer enabled.
12091224
12101225
"""
1211-
if not self._finalized:
1212-
self.tree.finalize_lists()
1213-
self.tree.number()
1214-
self._finalized=True
1226+
with self._tree_modify_lock:
1227+
if not self._finalized:
1228+
self.tree.finalize_lists()
1229+
self.tree.number()
1230+
self._finalized=True
12151231

12161232
@property
12171233
def finalized(self):
@@ -1228,7 +1244,9 @@ cdef class _TreeMesh:
12281244
bool
12291245
Returns *True* if finalized, *False* otherwise
12301246
"""
1231-
return self._finalized
1247+
with self._tree_modify_lock:
1248+
val = self._finalized
1249+
return val
12321250

12331251
@property
12341252
@cython.boundscheck(False)
@@ -1248,7 +1266,9 @@ cdef class _TreeMesh:
12481266

12491267
def number(self):
12501268
"""Number the cells, nodes, faces, and edges of the TreeMesh."""
1251-
self.tree.number()
1269+
1270+
with self._tree_modify_lock:
1271+
self.tree.number()
12521272

12531273
def get_containing_cells(self, points):
12541274
"""Return the cells containing the given points.
@@ -1453,52 +1473,53 @@ cdef class _TreeMesh:
14531473
if dim == 3:
14541474
shift[2] = self._origin[2] - self._zs[0]
14551475

1456-
for i in range(self._xs.shape[0]):
1457-
self._xs[i] += shift[0]
1458-
for i in range(self._ys.shape[0]):
1459-
self._ys[i] += shift[1]
1460-
if dim == 3:
1461-
for i in range(self._zs.shape[0]):
1462-
self._zs[i] += shift[2]
1463-
1464-
#update the locations of all of the items
1465-
self.tree.shift_cell_centers(&shift[0])
1466-
1467-
for itN in self.tree.nodes:
1468-
node = itN.second
1469-
for i in range(dim):
1470-
node.location[i] += shift[i]
1476+
with self._tree_modify_lock:
1477+
for i in range(self._xs.shape[0]):
1478+
self._xs[i] += shift[0]
1479+
for i in range(self._ys.shape[0]):
1480+
self._ys[i] += shift[1]
1481+
if dim == 3:
1482+
for i in range(self._zs.shape[0]):
1483+
self._zs[i] += shift[2]
14711484

1472-
for itE in self.tree.edges_x:
1473-
edge = itE.second
1474-
for i in range(dim):
1475-
edge.location[i] += shift[i]
1485+
#update the locations of all of the items
1486+
self.tree.shift_cell_centers(&shift[0])
14761487

1477-
for itE in self.tree.edges_y:
1478-
edge = itE.second
1479-
for i in range(dim):
1480-
edge.location[i] += shift[i]
1488+
for itN in self.tree.nodes:
1489+
node = itN.second
1490+
for i in range(dim):
1491+
node.location[i] += shift[i]
14811492

1482-
if dim == 3:
1483-
for itE in self.tree.edges_z:
1493+
for itE in self.tree.edges_x:
14841494
edge = itE.second
14851495
for i in range(dim):
14861496
edge.location[i] += shift[i]
14871497

1488-
for itF in self.tree.faces_x:
1489-
face = itF.second
1490-
for i in range(dim):
1491-
face.location[i] += shift[i]
1492-
1493-
for itF in self.tree.faces_y:
1494-
face = itF.second
1498+
for itE in self.tree.edges_y:
1499+
edge = itE.second
14951500
for i in range(dim):
1496-
face.location[i] += shift[i]
1501+
edge.location[i] += shift[i]
14971502

1498-
for itF in self.tree.faces_z:
1499-
face = itF.second
1500-
for i in range(dim):
1501-
face.location[i] += shift[i]
1503+
if dim == 3:
1504+
for itE in self.tree.edges_z:
1505+
edge = itE.second
1506+
for i in range(dim):
1507+
edge.location[i] += shift[i]
1508+
1509+
for itF in self.tree.faces_x:
1510+
face = itF.second
1511+
for i in range(dim):
1512+
face.location[i] += shift[i]
1513+
1514+
for itF in self.tree.faces_y:
1515+
face = itF.second
1516+
for i in range(dim):
1517+
face.location[i] += shift[i]
1518+
1519+
for itF in self.tree.faces_z:
1520+
face = itF.second
1521+
for i in range(dim):
1522+
face.location[i] += shift[i]
15021523
#clear out all cached grids
15031524
self._cell_centers = None
15041525
self._nodes = None
@@ -7046,7 +7067,6 @@ cdef class _TreeMesh:
70467067
)
70477068
return np.require(arr, dtype=dtype, requirements=requirements)
70487069

7049-
70507070
def _check_first_dim_broadcast(**kwargs):
70517071
"""Perform a check to make sure that the first dimensions of the inputs will broadcast."""
70527072
n_items = 1

pyproject.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
build-backend = 'mesonpy'
44
requires = [
55
"meson-python>=0.15.0",
6-
"Cython>=3.0.8",
6+
"Cython>=3.1.0",
77
"setuptools_scm[toml]>=6.2",
88

99
# numpy requirement for wheel builds for distribution on PyPI - building
@@ -93,7 +93,7 @@ build = [
9393
"meson",
9494
"ninja",
9595
"numpy>=1.22.4",
96-
"cython>=0.29.35",
96+
"cython>=3.1.0",
9797
"setuptools_scm",
9898
]
9999

@@ -124,9 +124,9 @@ setup-args = [
124124
[tool.coverage.run]
125125
branch = true
126126
source = ["discretize", "tests", "examples", "tutorials"]
127-
plugins = [
128-
"Cython.Coverage",
129-
]
127+
# plugins = [
128+
# "Cython.Coverage",
129+
# ]
130130

131131
[tool.coverage.report]
132132
ignore_errors = false

0 commit comments

Comments
 (0)