Summary
A *COUPLING + *KINEMATIC without ORIENTATION whose DOF lines couple only DOF 1, only DOF 2 or only DOF 3 leaves the reference node’s rotation about that axis out of every equation. Nothing defines that rotation, and ccx 2.23 does not say so:
- with SPOOLES (the default solver of the build used) ccx exits with status 255 while factoring. Nothing on stdout or stderr says why; the only message is “matrix found to be singular” in
spooles.out; - with
SOLVER=ITERATIVE SCALINGorITERATIVE CHOLESKYthe job finishes with exit 0 and every displacement is NaN.
The answer may be that the user has to hold that rotation. Section 7.113 (*RIGID BODY) of the 2.23 manual says so for its own case: “If the participating nodes in a rigid body definition lie on a straight line, the rigid body rotation about the line is not defined and an error will occur. To remove the rotational degree of freedom, specify that the rotation about the axis is zero.” The test deck test/coupling1.inp (*KINEMATIC / 3,3, no ORIENTATION) holds the reference rotations with 262,4,6. Section 7.80 (*KINEMATIC) does not state such a rule. It allows a single DOF per line (“if left blank the last degree of freedom coincides with the first degree of freedom”), and says only that the reference node’s DOFs 4 to 6 “can be constrained by a *BOUNDARY card”.
Even with that rule, two problems remain:
- Equivalent inputs behave differently. The same coupling with an identity
*ORIENTATION(local axes = global axes) on the*COUPLINGcard, without any hold, solves. It agrees to all printed digits with the held model and with the closed form. Only the path without ORIENTATION fails. - The failure gives no usable message: exit 255 with the reason only in
spooles.out, or NaN with exit 0. For the rigid-body case the manual promises that “an error will occur”.
So this asks for an *ERROR or *WARNING that names the free reference rotation (or for the path without ORIENTATION to handle it as the ORIENTATION path does), and for a sentence in section 7.80 that rotations which no coupled DOF reaches must be held.
The same happens for 1, 2 when the coupled nodes lie in the plane z = z(ref): then rotations 4 and 5 enter no equation. It also happens with *STEP, NLGEOM, and with the reference node above the face.
Version
ccx 2.23 (macOS arm64). Source: master 9ba5d76 (16 Oct 2026).
Already reported or fixed?
Not reported: I found no matching GitHub issue (open or closed) or pull request in Dhondtguido/CalculiX, and no calculix.discourse.group topic (searched 21 Oct 2026). Not fixed: rigidmpc.f and mastruct.c are unchanged on master (9ba5d76).
Related:
- The same missing check for
*RIGID BODY(reported separately): when the nodes of a rigid body lie on a straight line, the manual says “an error will occur”, but ccx gives no error or warning, never detects the undefined rotation and exits 0. Its code path isrigidbodys.f, notcouplings.f. - Issues · Dhondtguido/CalculiX · GitHub the same SPOOLES reaction to DOFs without stiffness (exit 255, message only in
spooles.out), from a different cause (nodes left over after element deletion).
Minimal input
kin_one_dof.inp:
** C3D8 unit cube (E=1000, nu=0) on rollers at z=0; its top face is coupled
** in z only to node 100 at the face centre, which carries a force of 1 in z
*NODE
1, 0., 0., 0.
2, 1., 0., 0.
3, 1., 1., 0.
4, 0., 1., 0.
5, 0., 0., 1.
6, 1., 0., 1.
7, 1., 1., 1.
8, 0., 1., 1.
100, 0.5, 0.5, 1.
*ELEMENT, TYPE=C3D8, ELSET=EALL
1, 1, 2, 3, 4, 5, 6, 7, 8
*NSET, NSET=TOP
5, 6, 7, 8
*SURFACE, NAME=STOP
1, S2
*MATERIAL, NAME=M
*ELASTIC
1000., 0.
*SOLID SECTION, ELSET=EALL, MATERIAL=M
*COUPLING, REF NODE=100, SURFACE=STOP, CONSTRAINT NAME=C1
*KINEMATIC
3
*BOUNDARY
1, 1, 3
2, 2, 3
3, 3
4, 3
*STEP
*STATIC
*CLOAD
100, 3, 1.
*NODE PRINT, NSET=TOP
U
*END STEP
Twins:
-
kin_one_dof_hold6.inp: one more line in the*BOUNDARYblock,100, 6. -
kin_one_dof_ori.inp: an identity orientation on the coupling, i.e. the*COUPLINGline replaced by these three lines:*ORIENTATION, NAME=OR1 1., 0., 0., 0., 1., 0. *COUPLING, REF NODE=100, SURFACE=STOP, CONSTRAINT NAME=C1, ORIENTATION=OR1 -
kin_one_dof_iter.inp:*STATIC, SOLVER=ITERATIVE SCALINGin place of*STATIC.
How to run
ccx kin_one_dof; echo $?
cat spooles.out
ccx kin_one_dof_hold6
ccx kin_one_dof_ori
ccx kin_one_dof_iter
Expected
The top face moves rigidly in z by F L / (E A) = 1 × 1 / (1000 × 1) = 1.0E-03 (uniaxial stress, nu = 0). Both twins give exactly that, with identical output: the held model (the remedy of section 7.113, as in test/coupling1.inp) and the identity ORIENTATION without a hold.
5 7.105427E-21 -1.048051E-19 1.000000E-03
6 7.105427E-21 -1.083578E-19 1.000000E-03
7 1.776357E-21 -8.526513E-20 1.000000E-03
8 1.776357E-21 -1.136868E-19 1.000000E-03
For kin_one_dof.inp itself: either the same result as the ORIENTATION twin, or an *ERROR (or a *WARNING plus an automatic hold) that names node 100, DOF 6. Not exit 255 with the reason only in spooles.out, and not NaN with exit 0.
Actual
kin_one_dof: exit status 255. The last lines on the terminal are
Factoring the system of equations using the symmetric spooles solver
Using 1 cpu for spooles.
and the .dat file has no results. spooles.out holds:
matrix found to be singular
kin_one_dof_iter: exit 0, “Job finished”, and
5 NaN NaN NaN
6 NaN NaN NaN
7 NaN NaN NaN
8 NaN NaN NaN
ITERATIVE CHOLESKY gives the same NaN rows. With SPOOLES the same stop (exit 255, same spooles.out line) also comes from *STEP, NLGEOM, and with node 100 at z = 3 instead of 1. It comes from *KINEMATIC / 1, 2 or 1, 2 with the force in a coupled direction too; there the face is level with node 100, so rotations 4 and 5 are both unused, and adding 100, 4, 5 to the *BOUNDARY block makes all three run. 2, 3 solves.
Where it goes wrong
-
Without ORIENTATION,
src/couplings.f(lines 473-483) callsrigidmpcfor every coupled node with the DOF range of the*KINEMATICline. -
src/rigidmpc.f(lines 85-103) writes all three rotation terms (DOFs 1-3 of the internal rotation node) into the MPC of direction j, with coefficients e(j,k,l)·(x_ref,l − x_node,l). For k = j the permutation symbol is zero, so the rotation about the coupled axis appears in its own equation with coefficient exactly 0:nodempc(1,mpcfree)=irotnode nodempc(2,mpcfree)=3 coefmpc(mpcfree)=e(j,3,1)*(co(1,irefnode)-co(1,node))+With only one direction coupled, no other equation contains that rotation.
-
src/mastruct.clines 241-275: when one DOF of an MPC is active, every term of that MPC is made active (nactdof[idof]=1), whatever its coefficient. So the rotation node’s DOF 3 becomes an unknown although nothing gives it stiffness: its row and column in the stiffness matrix are empty. -
SPOOLES (
src/spooles.clines 224-226) writes “matrix found to be singular” tospooles.outand callsexit(-1). How the iterative solvers turn the empty row into NaN was not traced.
With an ORIENTATION, couplings.f takes another path. It duplicates every coupled node and ties each duplicate to the reference node with rigid-body MPCs for all three directions (lines 392-418, ibounstart=1, ibounend=3). Then it connects each original node to its duplicate in the coupled local directions only (mpcadd, lines 489-497). That model solves. Why the rotation about the coupled axis does not end up as an empty row there was not traced.
Workaround / fix
Workaround: hold the unused reference rotations with *BOUNDARY (for *KINEMATIC / 3: ref, 6; for 1, 2 or 1, 2 with the members level with the reference node: ref, 4, 5), as test/coupling1.inp does, or put an identity *ORIENTATION on the *COUPLING card.
Possible fix (not built or tested):
couplings.f, path without ORIENTATION: after the DOF lines are read, check which rotation-node DOFs have a nonzero coefficient in none of the generated MPCs. For each, either stop with an *ERROR that names the reference node and the DOF, or print a *WARNING and hold it, as the*BOUNDARYabove does. Simply not activating zero-coefficient terms inmastruct.cwould not do under NLGEOM, wherenonlinmpc.fmakes that coefficient nonzero once the other rotations are finite.- Section 7.80 (*KINEMATIC): state that reference rotations which no coupled DOF reaches must be held by the user, as section 7.113 says for a rigid body on a straight line.