*KINEMATIC coupling of one DOF without ORIENTATION: the unused reference rotation stops SPOOLES with exit 255 and no message; an identity ORIENTATION solves

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 SCALING or ITERATIVE CHOLESKY the 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:

  1. Equivalent inputs behave differently. The same coupling with an identity *ORIENTATION (local axes = global axes) on the *COUPLING card, 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.
  2. 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 is rigidbodys.f, not couplings.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 *BOUNDARY block, 100, 6.

  • kin_one_dof_ori.inp: an identity orientation on the coupling, i.e. the *COUPLING line 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 SCALING in 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

  1. Without ORIENTATION, src/couplings.f (lines 473-483) calls rigidmpc for every coupled node with the DOF range of the *KINEMATIC line.

  2. 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.

  3. src/mastruct.c lines 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.

  4. SPOOLES (src/spooles.c lines 224-226) writes “matrix found to be singular” to spooles.out and calls exit(-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 *BOUNDARY above does. Simply not activating zero-coefficient terms in mastruct.c would not do under NLGEOM, where nonlinmpc.f makes 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.

Confirmed using official build of ccx 2.23 on Windows, needs a GitHub ticket.

kin_one_dof:

 STEP            1

 Static analysis was selected

 Decascading the MPC's

 Determining the structure of the matrix:
 Using up to 16 cpu(s) for setting up the structure of the matrix.
 number of equations
 17
 number of nonzero lower triangular matrix elements
 136

 Using up to 1 cpu(s) for the stress calculation.

 Using up to 1 cpu(s) for the symmetric stiffness/mass contributions.

 Factoring the system of equations using the symmetric spooles solver
 Using up to 16 cpu(s) for spooles.

kin_one_dof_iter.dat:

 displacements (vx,vy,vz) for set TOP and time  0.1000000E+01

         5           NaN           NaN           NaN
         6           NaN           NaN           NaN
         7           NaN           NaN           NaN
         8           NaN           NaN           NaN
1 Like

posted: *KINEMATIC coupling of one DOF without ORIENTATION: the unused reference rotation stops SPOOLES with exit 255 and no message; an identity ORIENTATION solves · Issue #290 · Dhondtguido/CalculiX · GitHub

1 Like