Summary
Section 7.87 (*MODAL DAMPING) of the 2.23 manual: “The *MODAL DAMPING keyword can be used in any step to redefine damping values defined in a previous step.” Three things go wrong when a deck has more than one such card:
- The ratio table is sized by the last ratio card.
allocation.fsets the size of the table to the highest mode of the last*MODAL DAMPINGcard in the deck, not of all cards. An earlier card for more modes then writes past the end of the table, and its step applies the ratios only up to that size. Indamp_two.inpbelow (ratio 0.05 for modes 1-6 in step 2, 0.02 for mode 1 in a later step 3), step 2 runs with mode 1 damped only: u = 4.555879E-04 at node 3, which is exactly the result with1, 1, 0.05(damp_mode1.inp), instead of 4.291461E-04 (damp_one.inp, the same deck without step 3). A later step changes the result of an earlier one. The run also crashes in almost every run (46 of 48: SIGABRT, exit 134, once SIGSEGV), at a different point each time (before step 1, in step 2 or after step 3), with no message. Under macOS Guard Malloc it stops every time at the out-of-bounds store inmodaldampings_, called fromcalinput_. - RAYLEIGH switches the ratios off for the rest of the run. After a
*MODAL DAMPING, RAYLEIGHcard, a later card with ratios has no effect: the step runs undamped. - RAYLEIGH overwrites
*DAMPING. The α and β of a*MODAL DAMPING, RAYLEIGHcard replace those of the model’s*DAMPINGcard (section 7.33: “It applies to the whole model”), so a later*DYNAMICstep runs with them.
Version
ccx 2.23 (macOS arm64) and current master (537aa81, built on macOS arm64): the same numbers. Source: master 537aa81.
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 (checked 3 Nov 2026). Not fixed: allocation.f, modaldampings.f, dampings.f and dyna.c are unchanged in this respect on master (537aa81).
Minimal input
damp_two.inp: a chain of 10 unit masses and 11 springs, 6 modes, two *MODAL DYNAMIC steps:
** chain of 10 unit masses (nodes 1-10) and 11 springs of stiffness 1000 between
** the held nodes 100 and 101; only x is free
*NODE, NSET=NALL
1, 1.0, 0., 0.
2, 2.0, 0., 0.
3, 3.0, 0., 0.
4, 4.0, 0., 0.
5, 5.0, 0., 0.
6, 6.0, 0., 0.
7, 7.0, 0., 0.
8, 8.0, 0., 0.
9, 9.0, 0., 0.
10, 10.0, 0., 0.
100, 0., 0., 0.
101, 11.0, 0., 0.
*NSET, NSET=MASSES, GENERATE
1, 10, 1
*ELEMENT, TYPE=MASS, ELSET=EM
101, 1
102, 2
103, 3
104, 4
105, 5
106, 6
107, 7
108, 8
109, 9
110, 10
*MASS, ELSET=EM
1.
*ELEMENT, TYPE=SPRINGA, ELSET=ES
200, 100, 1
201, 1, 2
202, 2, 3
203, 3, 4
204, 4, 5
205, 5, 6
206, 6, 7
207, 7, 8
208, 8, 9
209, 9, 10
210, 10, 101
*SPRING, ELSET=ES
1000.
*BOUNDARY
100, 1, 3
101, 1, 3
MASSES, 2, 3
*STEP
*FREQUENCY, STORAGE=YES
6
*CLOAD
1, 1, 0.
*END STEP
*STEP
*MODAL DYNAMIC
0.001, 0.1
*MODAL DAMPING
1, 6, 0.05
*CLOAD
1, 1, 1.
*NODE PRINT, NSET=MASSES, FREQUENCY=20
U
*END STEP
*STEP
*MODAL DYNAMIC
0.001, 0.1
*MODAL DAMPING
1, 1, 0.02
*CLOAD
1, 1, 1.
*NODE PRINT, NSET=MASSES, FREQUENCY=20
U
*END STEP
Variants:
damp_one.inp:damp_two.inpwithout step 3.damp_mode1.inp:damp_one.inpwith1, 1, 0.05instead of1, 6, 0.05.zero_then_direct.inp:damp_one.inpwith1, 6, 0.in step 2, followed by a copy of step 2 with1, 6, 0.05as step 3.ray_then_direct.inp: the same with*MODAL DAMPING, RAYLEIGH/, , 0., 0.in step 2.zero_then_zero.inp:zero_then_direct.inpwith1, 6, 0.in step 3.direct_then_dynamic.inp:damp_one.inpwith1, 6, 0.in step 2;*MATERIAL, NAME=DUMMY/*DAMPING, ALPHA=10./*ELASTIC/1., 0.before*BOUNDARY;*EL PRINT, ELSET=ES, TOTALS=ONLY/ELSEin step 1 (ccx asks for energy requests in the first step when a*DYNAMICstep follows); and a step 3*STEP/*DYNAMIC/0.001, 0.1/*CLOAD, OP=NEW/1, 1, 1./*NODE PRINT, NSET=MASSES, FREQUENCY=20/U/*END STEP.ray_then_dynamic.inp: the same with*MODAL DAMPING, RAYLEIGH/, , 0., 0.in step 2.
How to run
ccx damp_two (and the variants the same way)
Expected
- Step 2 of
damp_twogives the same result asdamp_one: no later card can change it. No crash. - Step 3 of
ray_then_directis damped like step 3 ofzero_then_direct. - Step 3 of
ray_then_dynamicis damped by*DAMPING, ALPHA=10.like step 3 ofdirect_then_dynamic.
Actual
u1 of node 3 at the end of the step (ccx 2.23 and master alike):
| deck | step 2 (t = 0.1) | step 3 (t = 0.2) |
|---|---|---|
damp_one |
4.291461E-04 | |
damp_mode1 |
4.555879E-04 | |
damp_two |
4.555879E-04, then SIGABRT (23 of 24 runs) | |
zero_then_direct |
4.608200E-04 | 9.329898E-04 |
ray_then_direct |
4.608200E-04 | 9.407754E-04 |
zero_then_zero |
4.608200E-04 | 9.407754E-04 |
direct_then_dynamic |
4.608200E-04 | 8.226046E-04 |
ray_then_dynamic |
4.608200E-04 | 9.797238E-04 |
direct_then_dynamic without the *DAMPING line |
4.608200E-04 | 9.797238E-04 |
damp_two: in two series of 12 runs each, 2.23 crashed 12 and 11 times and master 11 and 12 times (SIGABRT, once SIGSEGV; nothing on stdout or stderr). Where step 2 was written completely, it holds 4.555879E-04.
With Guard Malloc (DYLD_INSERT_LIBRARIES=/usr/lib/libgmalloc.dylib), master stops in every run of damp_two (3 of 3) and damp_one runs. Under lldb:
stop reason = EXC_BAD_ACCESS (code=1, address=0x105d80000)
* frame #0: ccx`modaldampings_ + 880 str d31, [x0, #0x48]
frame #1: ccx`calinput_ + 20620
frame #2: ccx`main + 13324
i.e. a store past the end of xmodal while step 2’s *MODAL DAMPING card is read (the *FREQUENCY step has run by then).
Where it goes wrong
-
allocation.f(master lines 1654-1675) restarts the count at every card:elseif(textpart(1)(1:13).eq.'*MODALDAMPING') then if(textpart(2)(1:8).ne.'RAYLEIGH') then nevdamp_=0 do ... nevdamp_ = max(nevdamp_,i)CalculiX.callocatesxmodalwith11+nevdamp_entries and stores the number of damped modes once (lines 383-384:NNEW(xmodal,double,11+nevdamp_);/xmodal[10]=nevdamp_+0.5;).modaldampings.fwritesxmodal(11+k)=zetafor every mode of the card (line 112) without a bound, so a card for more modes than the last one writes past the array, anddyna.capplies ratios only to the first(ITG)xmodal[10]modes (lines 868-882). -
RAYLEIGH sets
xmodal(11)=-0.5d0(modaldampings.fline 58), which makesdyna.cuse Rayleigh damping (line 848:if(xmodal[10]<0)). A later ratio card only writesxmodal(11+k)and never setsxmodal(11)back, so the Rayleigh branch stays active (with α = β = 0 here). -
RAYLEIGH reads α and β into
xmodal(1)andxmodal(2)(modaldampings.flines 69 and 75), the same entries in whichdampings.fstores the model’s*DAMPING, ALPHA=, BETA=(lines 52 and 59) and from whichnonlingeo.ctakes the damping of*DYNAMICsteps (lines 336-337).
Fix
Possible fix (not built or tested):
- In
allocation.f, setnevdamp_=0once before reading the deck (or keep the maximum over all cards) instead of at each card; and letmodaldampings.fstop with an *ERROR for a mode abovenevdamp_. - In
modaldampings.f, setxmodal(11)back to the number of damped modes when a ratio card follows RAYLEIGH (the size from item 1 + 0.5). - Keep the α and β of
*MODAL DAMPING, RAYLEIGHin their own entries ofxmodal, separate from*DAMPING.
Workaround for item 1: let the last ratio card of the deck name the highest mode of all cards. With step 3’s card written as 1, 1, 0.02 / 6, 6, 0.05, damp_two runs without a crash in 3 of 3 runs on both versions and step 2 gives 4.291461E-04, as damp_one does.