Using *Model Change ADD or REMOVE Elements for 3D Printing Simulation

Hello,
I’m currently trying to convert Abaqus additive manufacturing simulations into CalculiX. Everything worked out great except for the dynamic addition and removal of elements. I looked in the CCX manual and tried implementing *Model Change but it didn’t work.

To give more details on the problem I’m trying to solve, here is a small example scenario.

*Node
1…
2,
3…

*Element
1…
2…
3…

*Elset, Elset=BottomPlate

*Elset, Elset=PART_LAYER_1

*Elset, Elset=PART_LAYER_2

*Elset, Elset=PART_LAYER_3

Elset, Elset=PART_LAYER_4

Step 1 - Initialize by removing all part layers

*Step
*Static

MODEL CHANGE, TYPE=ELEMENT, REMOVE
PART_LAYER_1
PART_LAYER_2
PART_LAYER_3
PART_LAYER_4
Step 2 - Add Layer 1

*Step
Static

MODEL CHANGE, TYPE=ELEMENT, ADD
PART_LAYER_1
Step 3 - Add Layer 2



Step 4 - Add Layer 3


As outlined in the above example, I want to remove layers (elements) in the first step and subsequently add each layer in the following steps. When I implemented this in CalculiX, element removal in Step 1 functions correctly but the addition of elements does not occur from Step 2 onward.

My question here is, Can *Model Change effectively handle both the addition and removal of elements as described in my example? Any insights or suggestions would be greatly appreciated!"

1 Like

What you describe should work as intended. I don’t see anything wrong with it.

Thank you @jbr for your quick response. The analysis did complete without any issues but when I open the result in PrePoMax, all I can visualize are the results of bottomplate elements that are not removed in step-1. I’m not sure if this is a visualization issue in PrePoMax.

Please advice other ways of visualizing the results and I will verify if this is an issue with solver or visualization.

Thanks again for your response.

Try the cgx directly if in Linux or maybe GitHub - calculix/cae: CalculiX Advanced Environment is designed to guide you through the CalculiX keywords creation process and is aimed to help you reach the correct input file with no mistakes.

1 Like

Also if you could share your input file, it would be nice to see a functional example of this type of analysis.

1 Like

Check this thread on the PrePoMax forum: Display off with *Model Change added elements - Bug Reports - PrePoMax

1 Like

@jbr, Thank you for suggesting CalculiX Advanced Environment GUI. It offers all the keywords I’m looking for. It opens my input file without any issues but can not open the FRD results file. FRD file I’m trying to open is a small file but the GUI doesn’t respond and just loads forever. I will try testing with a smaller model.

1 Like

Here is the input file.

1 Like

Hi Arjun,

you have to do a first, static dummy step, so that all nodes and element information are wrote to
the result file. Then proceed with the removal of the layers.

3 Likes

@febing, This did the trick. Finally, I’m able to visualize the results. Thank you!

One last question, which I have a good understanding of based on the discussion but want to confirm is: can cgx or prepomax be able to show/hide the elements based on steps with ADD and REMOVE Model Change?

For example, in abaqus, if we have add or remove (*model change) elements in a step, abaqus CAE visualization automatically shows or hides elements according to the steps.

The new model with a dummy initial step shows the results in cgx or prepomax but all the removed elements in the second step are still shown in the visualization with no results. This is not a killer, but it would be nice to have this functionality.

Interested to hear all your thoughts on this. Thank you all!

Hi Arjun,
Cgx and Prepomax are not able to disable/enable elements according to model change. I suggested that in the Prepomax discourse group, but sure there are more important topics as new features.

3 Likes

i have added a new option for the result converter in the calculix component for cubit. the missing nodes in the result blocks gets added with zero values and the elements to which them belong will be marked with a cell value 1 for the partial component.

with the treshold filter in paraview you can filter those elements. i’ve have used your dogbone.inp and added the dummy step and a view more steps to add all element layers.

with Treshold

running .inp files is quite easy. just a few lines

ccx create job name "Dogbone" filepath "/home/user/Downloads/Dogbone.inp"
ccx run job 1
ccx wait job 1
ccx result load job 1
ccx result convert job 1 partial

if you already have the .frd for the .inp you can skip the run and wait command and directly load them.

2 Likes

@NorbertH Thank you so much for adding the support so quickly!

@NorbertH, Thank you for your time and assistance in adding support to this issue!

1 Like

@Arjun @jbr feedback for bug fixing (can’t catch em all) and other feature requests would be appreciated

1 Like

i am currently creating some examples and now i got one that fits here. the example shows how you can make use of python, custom key words and the partial option for the conversion.

ccx

#!python
from numpy import cos,sin,arccos
import numpy as np
import cubit

cubit.init()
cubit.cmd("reset")
cubit.cmd("undo off")


def check_if_enclosed(vid1,vid2):
 v1 = cubit.volume(vid1)
 v2 = cubit.volume(vid2)
 b_box1 = v1.bounding_box()
 b_box2 = v2.bounding_box()
 for i in range(3):
  if b_box1[i] > b_box2[i]:
   return False
  if b_box1[i+3] < b_box2[i+3]:
   return False
 return True

def split_bodies():
 body_list = cubit.get_entities("body")
 for b in body_list:
  if cubit.is_multi_volume(b): 	 
   cubit.cmd(f"split body {b}")
 return True

def parametric_circle(t,xc,yc,R):
 x = xc + R*cos(t)
 y = yc + R*sin(t)
 return x,y

plate_x = 200
plate_y = 100
plate_z = 5

cube_x = 5
cube_y = 5
cube_z = 5

cubit.cmd("create brick x " + str(plate_x) + " y " + str(plate_y) + " z " + str(plate_z))
cubit.cmd("create brick x " + str(cube_x) + " y " + str(cube_y) + " z " + str(cube_z))
cubit.cmd("move Volume 1 location " + str(plate_x/2) + " " + str(plate_y/2) + " " + str(plate_z/2))
cubit.cmd("move Volume 2 location " + str(cube_x/2 + plate_x/3 - plate_x/8) + " " + str(plate_y/2) + " " + str(plate_z + cube_z/2))

N = 25
R = plate_y*1/3
xc = 0
yc = 0

arc_T = np.linspace(3.14159265359/2, 3.14159265359*3/2, N)
X,Y = parametric_circle(arc_T, xc, yc, R)

vid = 0
for i in range(N):
 cubit.cmd("Volume 2 copy move x " + str(X[i]) + " y " + str(Y[i]) + " z " + str(0))
 vid_last = vid
 vid = cubit.get_last_id( "volume")
 if i!=0:
  cubit.cmd(f"subtract volume {vid} from volume {vid_last} keep_tool")
 
 
cubit.cmd("move Volume 2 location " + str(cube_x/2 + plate_x*2/3 - plate_x/8) + " " + str(plate_y/2) + " " + str(plate_z + cube_z/2))
vid=0
for i in range(N):
 cubit.cmd("Volume 2 copy move x " + str(X[i]) + " y " + str(Y[i]) + " z " + str(0))
 vid_last = vid
 vid = cubit.get_last_id( "volume")
 if i!=0:
  cubit.cmd(f"subtract volume {vid} from volume {vid_last} keep_tool")

vid=0
cubit.cmd("move Volume 2 location " + str(cube_x/2 + plate_x*4/5) + " " + str(plate_y/2) + " " + str(plate_z + cube_z/2))
X = np.linspace(-plate_y*1/5, plate_y*1/5, N)
Y = np.linspace(-plate_y*1/3, plate_y*1/3, N)
for i in range(N):
 cubit.cmd("Volume 2 copy move x " + str(X[i]) + " y " + str(Y[i]) + " z " + str(0))
 vid_last = vid
 vid = cubit.get_last_id( "volume")
 if i!=0:
  cubit.cmd(f"subtract volume {vid} from volume {vid_last} keep_tool")

X = np.linspace(-plate_y*1/5, plate_y*1/5, N)
Y = np.linspace(plate_y*1/3, -plate_y*1/3, N)
for i in range(N):
 cubit.cmd("Volume 2 copy move x " + str(X[i]) + " y " + str(Y[i]) + " z " + str(0))
 vid = cubit.get_last_id("volume")
 if i!=0:
  overlapping_volumes = cubit.get_overlapping_volumes_at_volume(vid,cubit.get_entities("volume"))
  for ov in overlapping_volumes:
   if ov!=1 and ov!=2:
    split_bodies()
    if check_if_enclosed(vid,ov):
     cubit.cmd(f"delete volume {ov}")
    else:
     cubit.cmd(f"subtract volume {vid} from volume {ov} keep_tool")

cubit.cmd("delete vol 2")

cubit.cmd("imprint vol all")
cubit.cmd("merge vol all")
cubit.cmd("vol all except 1 size 2.5")
cubit.cmd("vol 1 size 10")
cubit.cmd("surface all with z_coord = "+str(plate_z + cube_z)+" scheme pave")
cubit.cmd("mesh surface all with z_coord = "+ str(plate_z + cube_z))
cubit.cmd("mesh vol all except 1")
cubit.cmd("surface all in volume 1 with z_coord >= " + str(plate_z) +" scheme pave")
cubit.cmd("mesh surface all with z_coord >= "+str(plate_z))
cubit.cmd("mesh vol 1")
cubit.cmd("compress")

volume_list = cubit.get_entities("volume")
for v in volume_list:
 cubit.cmd(f"block {v} add vol {v}")
 cubit.cmd(f"nodeset {v} add vol {v}")
 cubit.cmd(f"sideset {v} add surface all in vol 1 to {v} with is_merged = 0")


#material
cubit.cmd('create material "solid" property_group "CalculiX-FEA"')
cubit.cmd('modify material "solid" scalar_properties "CCX_ELASTIC_ISO_USE_CARD" 1')
cubit.cmd('modify material "solid" scalar_properties "CCX_PLASTIC_ISO_USE_CARD" 1')
cubit.cmd('modify material "solid" scalar_properties "CCX_DENSITY_USE_CARD" 1')
cubit.cmd('modify material "solid" matrix_property "CCX_DENSITY_DENSITY" 8 0 ')
cubit.cmd('modify material "solid" scalar_properties "CCX_SPECIFIC_HEAT_USE_CARD" 1')
cubit.cmd('modify material "solid" matrix_property "CCX_SPECIFIC_HEAT_SPECIFIC_HEAT" 420 0 ')
cubit.cmd('modify material "solid" scalar_properties "CCX_EXPANSION_ISO_USE_CARD" 1')
cubit.cmd('modify material "solid" scalar_properties "CCX_CONDUCTIVITY_USE_CARD" 1')
cubit.cmd('modify material "solid" scalar_properties "CCX_CONDUCTIVITY_ISO_USE_CARD" 1')
cubit.cmd('modify material "solid" matrix_property "CCX_CONDUCTIVITY_ISO_K_TEMPERATURE" 50 0 ')
cubit.cmd('modify material "solid" scalar_properties "CCX_ELASTIC_USE_CARD" 1')
cubit.cmd('modify material "solid" matrix_property "CCX_ELASTIC_ISO_MODULUS_VS_POISSON_VS_TEMPERATURE" 210000 0.3 0') 


#section
cubit.cmd('ccx create section solid block all material 1')

#heatflux and initial temperature
volume_list = cubit.get_entities("volume")
for v in volume_list:
 cubit.cmd(f"create heatflux  on sideset {v} value -7.5")
 #cubit.cmd(f"ccx modify heatflux {v} op new")
 cubit.cmd(f"create temperature on volume {v} value 2000")
 cubit.cmd(f"ccx modify temperature {v} op new")

cubit.cmd("modify temperature 1 value 100")

# field outputs
cubit.cmd('ccx create fieldoutput name "fo_node" node')
cubit.cmd('ccx modify fieldoutput 1 node frequency 1')
cubit.cmd('ccx modify fieldoutput 1 node key_on NT')
cubit.cmd('ccx modify fieldoutput 1 node key_off CP U DEPF DEPT DTF HCRI KEQ MACH MAXU MF PNT POT PRF PS PSF PT PTF PU RF RFL SEN TS TSF TT TTF TURB V VF ')
cubit.cmd('ccx create fieldoutput name "fo_element" element')
cubit.cmd('ccx modify fieldoutput 2 element frequency 1')
cubit.cmd('ccx modify fieldoutput 2 element key_on HFL')
cubit.cmd('ccx modify fieldoutput 2 element key_off CEEQ S E ECD EMFB EMFE ENER ERR HER HFLF MAXE MAXS ME PEEQ PHS SF SMID SNEG SPOS SVF SDV THE ZZS ')

# initial conditions
cubit.cmd("ccx create initialcondition temperature")
cubit.cmd(f"ccx modify initialcondition {1} temperature bc_id {1}")

cubit.cmd("ccx create step name 'dummy_step' static")
cubit.cmd("ccx step 1 add fieldoutput 1 2")
cubit.cmd("ccx create step name 'deacivate_step' static")
cubit.cmd("ccx step 2 add fieldoutput 1 2")
cubit.cmd("ccx modify step 2 parameter nlgeom_yes")
cubit.cmd("ccx create customline name 'step_2_top' after step_begin item_id 2 cline '*MODEL CHANGE,TYPE=ELEMENT,REMOVE'")
volume_list = cubit.get_entities("volume")
for v in volume_list:
 if v >1:
  cubit.cmd(f"ccx create customline name '{v}' after step_begin item_id 2 cline 'Block_{v}'")

cs = 2
volume_list = cubit.get_entities("volume")
for v in volume_list:
# if v > 1 and v < 3:
 if v > 1:
  cs = cs + 1
  cubit.cmd(f"ccx create step name 'heat_{cs}' heattransfer")
  cubit.cmd(f"ccx modify step {cs} heattransfer initialtimeincrement 10 timeperiodofstep 10 minimumtimeincrement 1e-5 maximumtimeincrement 10")
  cubit.cmd(f"ccx step {cs} add load heatflux {v}")
  cubit.cmd(f"ccx step {cs} add bc temperature {v}")
  cubit.cmd(f"ccx step {cs} add fieldoutput 1 2")
  cubit.cmd(f"ccx create customline name 'step_{cs}_top' after step_begin item_id {cs} cline '*MODEL CHANGE,TYPE=ELEMENT,ADD'")
  cubit.cmd(f"ccx create customline name '{v}' after step_begin item_id {cs} cline 'Block_{v}'")
  cs = cs + 1
  cubit.cmd(f"ccx create step name 'heat_{cs}' heattransfer")
  cubit.cmd(f"ccx modify step {cs} heattransfer initialtimeincrement 10 timeperiodofstep 20 minimumtimeincrement 1e-5 maximumtimeincrement 10")
  cubit.cmd(f"ccx step {cs} add load heatflux {v}")
  cubit.cmd(f"ccx step {cs} add fieldoutput 1 2")

cubit.cmd("ccx create job name 'model_change'")

print("ccx run job 1 no_conversion")
command = ("ccx result convert job 1 block ")
for v in volume_list:
 command = command + str(v) + " "
command = command + " partial"
print(command)

PS: it is also now possible to run a job without an auto conversion after the run. and also to convert only defined blocks, nodesets and sidesets.

4 Likes

Hello Arjun,
I’m also stuck in similar problem. can you share the final “.inp” file for the reference.

thanks in advance.