Skip to content

Commit 51801d9

Browse files
authored
Merge pull request #9 from Baraa-Elmoussa/main
Remove MatrixConstraint function and fix dependencies
2 parents c6a704f + 8a47556 commit 51801d9

5 files changed

Lines changed: 64 additions & 31 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Changed
1515

16+
* Replaced kernel-layer `MatrixConstraint` with standard `pyo.Constraint` rules in `static_equilibrium_constraints` for compatibility with recent Pyomo versions.
17+
* Pinned Python to `< 3.14` for viewer compatibility.
18+
1619
### Removed
1720

1821

environment.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ name: cra-dev
22
channels:
33
- conda-forge
44
dependencies:
5-
- python
5+
- python < 3.14
66
- pip
77
- compas >=2.4
8-
- ipopt ==3.14.9
9-
- pyomo ==6.4.2
8+
- ipopt
9+
- pyomo
1010
- pip:
1111
- compas_assembly >=0.7.0
12+
- matplotlib
1213
- -e .[dev]

scripts/tutorial_cubes.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
"""This is the complete code for the tutorial"""
22

3-
from compas.geometry import Box, Frame, Translation, Rotation
3+
from compas.geometry import Box
4+
from compas.geometry import Frame
5+
from compas.geometry import Rotation
6+
from compas.geometry import Translation
47
from compas_assembly.datastructures import Block
5-
from compas_cra.datastructures import CRA_Assembly
8+
69
from compas_cra.algorithms import assembly_interfaces_numpy
10+
from compas_cra.datastructures import CRA_Assembly
711
from compas_cra.equilibrium import cra_solve
812
from compas_cra.viewers import cra_view
913

10-
11-
support = Box(Frame.worldXY(), 4, 2, 1) # supporting block
14+
support = Box(frame=Frame.worldXY(), xsize=4, ysize=2, zsize=1) # supporting block
1215
free1 = Box(
13-
Frame.worldXY().transformed(
14-
Translation.from_vector([0, 0, 1])
15-
* Rotation.from_axis_and_angle([0, 0, 1], 0.2)
16+
frame=Frame.worldXY().transformed(
17+
Translation.from_vector([0, 0, 1]) * Rotation.from_axis_and_angle([0, 0, 1], 0.2)
1618
),
17-
1,
18-
3,
19-
1,
19+
xsize=1,
20+
ysize=3,
21+
zsize=1,
2022
) # block to analyse
2123

2224
assembly = CRA_Assembly() # create empty assembly
@@ -28,6 +30,6 @@
2830

2931
assembly_interfaces_numpy(assembly) # identify interface between two blocks
3032

31-
cra_solve(assembly, verbose=True, timer=True) # solve equilibrium using cra_solve
33+
cra_solve(assembly, verbose=False, timer=True) # solve equilibrium using cra_solve
3234

33-
cra_view(assembly, resultant=False, nodal=True, grid=True) # visiualisation
35+
# cra_view(assembly, resultant=False, nodal=True, grid=True) # visiualisation

src/compas_cra/algorithms/interfaces_numpy.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from math import fabs
44

5+
import compas.geometry as cg
56
from compas.geometry import Frame
67
from compas.geometry import local_to_world_coordinates_numpy
78
from numpy import array
@@ -127,8 +128,8 @@ def assembly_interfaces_numpy(assembly, nmax=10, tmax=1e-6, amin=1e-1):
127128

128129
if area >= amin:
129130
coords = [[x, y, 0.0] for x, y, z in intersection.exterior.coords]
130-
131-
coords = local_to_world_coordinates_numpy(Frame(o, A[0], A[1]), coords)
131+
origin = cg.Point(*o.flatten())
132+
coords = local_to_world_coordinates_numpy(Frame(origin, A[0], A[1]), coords)
132133

133134
assembly.add_to_interfaces(
134135
node,

src/compas_cra/equilibrium/pyomo_helper.py

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
from typing import Literal
55

66
import pyomo.environ as pyo
7-
from numpy import zeros
8-
from pyomo.core.base.matrix_constraint import MatrixConstraint
97

108

119
def initialisations(
@@ -244,18 +242,46 @@ def static_equilibrium_constraints(model, aeq, afr, p) -> Callable:
244242
245243
"""
246244

247-
equilibrium_constraints = MatrixConstraint(
248-
aeq.data, aeq.indices, aeq.indptr, -p.flatten(), -p.flatten(), model.array_f
249-
)
250-
251-
friction_constraint = MatrixConstraint(
252-
afr.data,
253-
afr.indices,
254-
afr.indptr,
255-
[None for i in range(afr.shape[0])],
256-
zeros(afr.shape[0]),
257-
model.array_f,
258-
)
245+
# The previous MatrixConstraint function was causing problems,
246+
# it's now replaced by
247+
248+
# aeq = aeq.tocsr() # tocsr gives data, indicies and indptr instead of decomposing them one by one
249+
# afr = afr.tocsr()
250+
rhs = (-p).flatten()
251+
f_var = model.array_f
252+
253+
# Deprecated code for reference:
254+
# equilibrium_constraints = matrix_constraint(
255+
# aeq.data, aeq.indices, aeq.indptr, -p.flatten(),
256+
# -p.flatten(), model.array_f )
257+
# A_eq is the tangent matrix, equal to -p,
258+
# A container for constraints of the form lb <= Ax <= ub.
259+
# here, lp and ub are both equal to -p,
260+
# so the constraint is Ax = -p, which is the equilibrium equation.
261+
# https://pyomo.readthedocs.io/en/6.9.1/api/pyomo.core.kernel.matrix_constraint.matrix_constraint.html
262+
263+
def eq_rule(m, i):
264+
s, e = aeq.indptr[i], aeq.indptr[i + 1]
265+
if s == e:
266+
return pyo.Constraint.Skip
267+
expr = pyo.quicksum(float(aeq.data[k]) * f_var[int(aeq.indices[k])] for k in range(s, e))
268+
return expr == float(rhs[i])
269+
270+
# Expression equivalent to:
271+
# -p <= A_eq * f <= -p (Friction)
272+
273+
def fr_rule(m, j):
274+
s, e = afr.indptr[j], afr.indptr[j + 1]
275+
if s == e:
276+
return pyo.Constraint.Skip
277+
expr = pyo.quicksum(float(afr.data[k]) * f_var[int(afr.indices[k])] for k in range(s, e))
278+
return expr <= 0.0
279+
280+
# Expression equivalent to:
281+
# A_fr * f <= 0 (Unilateral constraint)
282+
283+
equilibrium_constraints = pyo.Constraint(range(aeq.shape[0]), rule=eq_rule)
284+
friction_constraint = pyo.Constraint(range(afr.shape[0]), rule=fr_rule)
259285
return equilibrium_constraints, friction_constraint
260286

261287

0 commit comments

Comments
 (0)