Data Files for “Ribbon Blender” Example

BlenderBalls.p3dat

; fname: BlenderBalls.p3dat

model new
wall resolution full

call '../blender_geometry/ImportBlenderGeometry.p3dat'

model domain extent [lbx-0.05*dx] [ubx+0.05*dx] [lby-0.05*dy] ...
                    [uby+0.05*dy] [lbz-0.05*dz] [ubz+dz] ...
                    condition destroy 

contact cmat default model linear prop dp_nratio 0.8 dp_mode 1

wall import from-geom 'rotor1' clean id 1
wall import from-geom 'rotor2' clean id 2
wall import from-geom 'rotor3' clean id 3
wall import from-geom 'rotor4' clean id 4
wall import from-geom 'rotor5' clean id 5
wall import from-geom 'rotor6' clean id 6
wall import from-geom 'rotor7' clean id 7
wall import from-geom 'rotor8' clean id 8

wall import from-geom 'blade1' clean id 9
wall import from-geom 'blade2' clean id 10
wall import from-geom 'blade3' clean id 11
wall import from-geom 'blade4' clean id 12
wall import from-geom 'blade5' clean id 13
wall import from-geom 'blade6' clean id 14
wall import from-geom 'blade7' clean id 15
wall import from-geom 'blade8' clean id 16
wall group 'facetassembly' facet 
wall group 'wallassembly'

wall import from-geom 'bin' clean id 17
wall active-side top range position-z [ubz-0.05*ubz] [ubz+0.05*ubz]
wall active-side top range name 'facetassembly'

model save 'blender'

fish def setup(rpm)
  global rp_   = vector(0,0,0) ;rotation point
  global spin_ = vector(0,-1,0)*2*math.pi*rpm/60.0 ;axis of rotation
  global knB_  = 1e4 
  global ksB_  = 1e4 
  global knW_  = 1e6
  global ksW_  = 1e6
  global fric_ = 0.18 ;friction
  global dens_ = 2000 ;density
end
@setup(60)

;generate some balls in the box
fish def inBlender(pos,b)
  inBlender = 0
  if type.pointer.id(b) = ball.typeid
    wp = wall.find(17);
    inBlender = wall.inside(wp,ball.pos(b))
  endif
end

fish def inBody(pos,b)
  inBody = 0
  if type.pointer.id(b) = ball.typeid
    local tmp = 0
    loop foreach local wp wall.list
      if wall.id(wp) # 17
        tmp = wall.inside(wp,ball.pos(b))
        if tmp = 1
          inBody = 1
          exit
        endif
      endif
    endloop
  endif
end

ball distribute porosity 0.45     ...
                resolution 1.5e-2 ... 
                bin 1             ...
                radius 1.0 1.5  ...
                volume-fraction 1.0  ...
                box [lbx] [ubx] [lby] [uby] [lbz] [lbz+0.3*dz] ...
                range fish @inBlender ...
                      fish @inBody not
                      
model gravity 0,0,-9.81
ball prop 'kn' @knB_ 'ks' @ksB_ 'fric' @fric_
wall prop 'kn' @knW_ 'ks' @ksW_ 'fric' [fric_*0.5]
ball attr density @dens_ damp 0.7
model cycle 2000 calm 500
model mechanical timestep scale
model solve ratio-average 1e-3
model mechanical timestep auto

wall attribute rotation-center @rp_ range group 'wallassembly'
wall attribute spin @spin_ range group 'wallassembly'

;set up groups for mixing
ball group 'FrontLeft'  range position-x [lbx-0.05*dx]     0.0       ...
                            position-y [lby-0.05*dy]     0.5       
ball group 'FrontRight' range position-x     0.0       [ubx+0.05*dx] ...
                            position-y [lby-0.05*dy]     0.5
ball group 'BackLeft'   range position-x [lbx-0.05*dx]     0.0       ...
                            position-y     0.5       [uby+0.05*dy]
ball group 'BackRight'  range position-x     0.0       [ubx+0.05*dx] ...
                            position-y     0.5       [uby+0.05*dy]

model save 'ball_model'
model solve time 1.0

model save 'end_blenderball'

;=============================================================================
; eof: BlenderBalls.p3dat

importBlenderGeometry.p3dat

; fname: ImportBlenderGeometry.p3dat

geom import 'rotor1.stl'
geom import 'rotor2.stl'
geom import 'rotor3.stl'
geom import 'rotor4.stl'
geom import 'rotor5.stl'
geom import 'rotor6.stl'
geom import 'rotor7.stl'
geom import 'rotor8.stl'

geom import 'blade1.stl'
geom import 'blade2.stl'
geom import 'blade3.stl'
geom import 'blade4.stl'
geom import 'blade5.stl'
geom import 'blade6.stl'
geom import 'blade7.stl'
geom import 'blade8.stl'

geom import 'bin.stl'

;calculate the dimensions of the box needed
fish def cdim
  lbx = 1e5
  lby = 1e5
  lbz = 1e5
  ubx = -1e5
  uby = -1e5
  ubz = -1e5
  local gs = geom.set.find(17)
  loop foreach local gn geom.node.list(gs)
    local pos = geom.node.pos(gn)
    if lbx > comp.x(pos)
      lbx = comp.x(pos)
    endif
    if lby > comp.y(pos)
      lby = comp.y(pos)
    endif
    if lbz > comp.z(pos)
      lbz = comp.z(pos)
    endif

    if ubx < comp.x(pos)
      ubx = comp.x(pos)
    endif
    if uby < comp.y(pos)
      uby = comp.y(pos)
    endif
    if ubz < comp.z(pos)
      ubz = comp.z(pos)
    endif

    endloop
    dx = ubx - lbx
    dy = uby - lby
    dz = ubz - lbz
end
@cdim

;=============================================================================
; eof: ImportBlenderGeometry.p3dat

BlenderClumps.p3dat

; fname: BlenderClumps.p3dat

model new
wall resolution full

call '../blender_geometry/ImportBlenderGeometry.p3dat'

model domain extent [lbx-0.05*dx] [ubx+0.05*dx] [lby-0.05*dy] ...
                    [uby+0.05*dy] [lbz-0.05*dz] [ubz+dz]      ...
                    condition destroy 

wall import from-geom 'rotor1' clean id 1
wall import from-geom 'rotor2' clean id 2
wall import from-geom 'rotor3' clean id 3
wall import from-geom 'rotor4' clean id 4
wall import from-geom 'rotor5' clean id 5
wall import from-geom 'rotor6' clean id 6
wall import from-geom 'rotor7' clean id 7
wall import from-geom 'rotor8' clean id 8

wall import from-geom 'blade1' clean id 9
wall import from-geom 'blade2' clean id 10
wall import from-geom 'blade3' clean id 11
wall import from-geom 'blade4' clean id 12
wall import from-geom 'blade5' clean id 13
wall import from-geom 'blade6' clean id 14
wall import from-geom 'blade7' clean id 15
wall import from-geom 'blade8' clean id 16
wall group 'facetassembly' facet 
wall group 'wallassembly'

wall import from-geom 'bin' clean id 17
wall active-side top range position-z [ubz-0.05*ubz] [ubz+0.05*ubz]
wall active-side top range name 'facetassembly'

model save 'blender'

fish def setup(rpm)
  rp_ = vector(0,0,0)
  spin_ = vector(0,-1,0)*2*math.pi*rpm/60.0
  knB_ = 1e4 
  ksB_ = 1e4 
  knW_ = 1e8
  ksW_ = 1e8
  fric_ = 0.18
  dens_ = 2000
end
@setup(60)

geometry import 'ellipsoid.stl'

clump template create name 'pill' geometry 'ellipsoid' ...
                      surfcalc bubblepack ratio 0.5 distance 150

contact cmat default type Pebble-Pebble model Linear ...
        property kn 10e4 inh off ks 10e4 inh off fric 0.18
contact cmat default type Pebble-Facet model Linear  ...
        property kn 10e5 inh off ks 10e5 inh off fric 0.09

clump distribute porosity 0.45            ...
                  resolution 5e-2         ...
                  number-bins    1        ...
                  diameter                ...
                  bin 1 size 1.0 1.20     ...
                        volume-fraction 1 ...
                        template 'pill'   ...
                            az 90 90      ...
                            el 0 0        ...
                            tilt 0 0      ...  
                  box [lbx] [ubx] [lby] [uby] [lbz] [lbz+0.3*dz]
 
fish def inBlender(pos,c)
  inBlender = 1
  if type.pointer.id(c) = clump.typeid
    loop foreach local pb clump.pebblelist(c)
      local tmp = 0
      wp = wall.find(17);
      tmp = wall.inside(wp,clump.pebble.pos(pb))
      if tmp = 0
        inBlender = 0
        exit
      endif
    endloop
  endif
end
clump delete range fish @inBlender not

fish def inBody(pos,c)
  inBody = 0
  if type.pointer.id(c) = clump.typeid
    loop foreach local pb clump.pebblelist(c)
      local tmp = 0
      loop foreach local wp wall.list
        if wall.id(wp) # 17
          tmp = wall.inside(wp,clump.pebble.pos(pb))
          if tmp = 1
            inBody = 1
            exit
          endif
        endif
      endloop
    endloop
  endif
end
clump delete range fish @inBody

model gravity 0,0,-9.81
clump attr density @dens_ damp 0.7
model cycle 2000 calm 500
model mechanical timestep scale
model solve ratio-average 1e-1
model mechanical timestep auto
clump attr damp 0.0

wall attr rotation-center @rp_ range group 'wallassembly'
wall attr spin @spin_ range group 'wallassembly'

;set up groups for mixing
clump group 'FrontLeft'  range position-x [lbx-0.05*dx] 0.0 ...
                               position-y [lby-0.05*dy] 0.5
clump group 'FrontRight' range position-x 0.0 [ubx+0.05*dx] ...
                               position-y [lby-0.05*dy] 0.5
clump group 'BackLeft'   range position-x [lbx-0.05*dx] 0.0 ...
                               position-y 0.5  [uby+0.05*dy]
clump group 'BackRight'  range position-x 0.0 [ubx+0.05*dx] ...
                               position-y 0.5 [uby+0.05*dy]

model save 'clump_model'
model solve time 0.5

model save 'end_blenderclump'

;=============================================================================
; eof: BlenderClumps.p3dat

BlenderClusters.p3dat

; fname: BlenderClusters.p3dat

model new
wall resolution full

call 'ClusterMakeCluster.p3dat'
call 'ClusterSizeDistribution.p3dat'
call '../blender_geometry/ImportBlenderGeometry.p3dat'

geometry import 'ellipsoid.stl'

model domain extent [lbx-0.05*dx] [ubx+0.05*dx] [lby-0.05*dy] ...
                    [uby+0.05*dy] [lbz-0.05*dz] [ubz+dz] ...
                    condition destroy 

wall import from-geom 'rotor1' clean id 1
wall import from-geom 'rotor2' clean id 2
wall import from-geom 'rotor3' clean id 3
wall import from-geom 'rotor4' clean id 4
wall import from-geom 'rotor5' clean id 5
wall import from-geom 'rotor6' clean id 6
wall import from-geom 'rotor7' clean id 7
wall import from-geom 'rotor8' clean id 8

wall import from-geom 'blade1' clean id 9
wall import from-geom 'blade2' clean id 10
wall import from-geom 'blade3' clean id 11
wall import from-geom 'blade4' clean id 12
wall import from-geom 'blade5' clean id 13
wall import from-geom 'blade6' clean id 14
wall import from-geom 'blade7' clean id 15
wall import from-geom 'blade8' clean id 16
wall group 'facetassembly' facet
wall group 'wallassembly'

wall import from-geom 'bin' clean id 17
wall active-side top range position-z [ubz-0.05*ubz] [ubz+0.05*ubz]
wall active-side top range name 'facetassembly'

fish define setup(rpm)
  rp_ = vector(0,0,0)
  spin_ = vector(0,-1,0)*2*math.pi*rpm/60.0
  knB_ = 1e4 
  ksB_ = 1e4 
  knW_ = 1e8
  ksW_ = 1e8
  fric_ = 0.18
  dens_ = 2000
end
@setup(60)

clump template create name 'pill' geometry 'ellipsoid' ...
                      surfcalc bubblepack ratio 0.5 distance 150
    
contact cmat default type pebble-pebble model linear ...
        property kn 10e4 inh off ks 10e4 inh off fric 0.18
contact cmat default type pebble-facet model linear  ...
        property kn 10e5 inh off ks 10e5 inh off fric 0.09

clump distribute porosity 0.45                      ...
                        number-bins    1            ...
                        diameter                    ...
                        resolution 1.0              ...
                        bin 1 size 0.05 0.075       ...
                               volume-fraction  1   ...
                               template 'pill'      ...
                               az 90 90             ...
                               el 0 0               ...
                               tilt 0 0             ...  
                        box [lbx] [ubx] [lby] [uby] [lbz] [lbz+0.3*dz]

fish def inBlender(pos,c)
  inBlender = 1
  if type.pointer.id(c) = clump.typeid
    loop foreach local pb clump.pebblelist(c)
      local tmp = 0
      wp = wall.find(17)
      tmp = wall.inside(wp,clump.pebble.pos(pb))
      if tmp = 0
        inBlender = 0
        exit
      endif
    endloop
  endif
end
clump delete range fish @inBlender not

fish def inBody(pos,c)
  inBody = 0
  if type.pointer.id(c) = clump.typeid
    loop foreach local pb clump.pebblelist(c)
      local tmp = 0
      loop foreach local wp wall.list
        if wall.id(wp) # 17
          tmp = wall.inside(wp,clump.pebble.pos(pb))
          if tmp = 1
            inBody = 1
            exit
          endif
        endif
      endloop
    endloop
  endif
end
clump delete range fish @inBody

model gravity 0,0,-9.81
clump attr density @dens_ damp 0.7
model cycle 2000 calm 500

@ini_clusters
@replace
@order_clusters
@list_clusters

fish define inclump(extra, ct)
  inclump = 0
  if type.pointer.id(ct) = contact.typeid("ball-ball")
    if ball.extra(contact.end1(ct),1) == ball.extra(contact.end2(ct),1) then
      inclump = 1
      exit
    endif
  endif
end

contact cmat 	default type ball-ball model linear ... 
        property kn 10e4 inh off ks 10e4 inh off fric 0.18 inh off
contact cmat 	default type ball-facet   model Linear ... 
        property kn 10e5 inh off ks 10e5 inh off fric 0.09 inh off
contact cmat	add 1 model linearPBond ... 
        property fric 0.0 kn 0.0 inh off ks 0.0 inh off dp_nratio 0.2 ...
        range fish @inclump 

model clean
contact cmat apply

@applybonds

fish def restorefriction(arr)
    ct=arr(1)
    if ball.extra(contact.end1(ct),1) == ball.extra(contact.end2(ct),1)
      io.out(string.build("Contact broken! %1",contact.prop(ct,'pb_state')))
      contact.model(ct) = 'Linear'
      contact.prop(ct,'kn') = 10e4
      contact.prop(ct,'ks') = 10e4
      contact.prop(ct,'lin_mode') = 1
      
      idc = idc + 1     
      blist = array.create(1)
      blist(1) = contact.end1(ct)
      size = array.size(blist,1)
      ind = 0
      stop = 0
      loop while stop = 0
      
          newbonds = 0
          newvisited = 0
          loop local i (1,size+ind)
              if ball.extra(blist(i),2) = 0 ;not visited
                  newvisited = newvisited + 1
                  loop foreach local con ball.contactmap(blist(i))
                    if contact.model(con) # "Linear"
                      if contact.prop(con,'pb_state')=3
                          if ball.extra(contact.end1(con),2) = 0 
                              ;not visited
                              if ball.extra(contact.end2(con),2) = 0 
                                  ;not visited
                                  newbonds = newbonds + 1
                              endif
                          endif
                      endif
                    endif
                 endloop
                  ball.extra(blist(i),2) = 1 ;isvisited
                  ball.extra(blist(i),1) = idc
              endif
          endloop
          
        if newbonds#0
          oldblist = blist
          blist = array.create(size+ind+newbonds)
          loop local k (1,size+ind)
            blist(k) = oldblist(k)
          endloop
          
          loop local ii (size+ind+1-newvisited,size+ind)
            clist = ball.contactmap(blist(ii))
            loop foreach local jj clist
             if contact.model(jj) # "linear"
              if contact.prop(jj,'pb_state')=3
                if ball.extra(contact.end1(jj),2) = 0 ;not visited
                  ind = ind + 1
                  blist(size+ind) = contact.end1(jj)
                else if ball.extra(contact.end2(jj),2) = 0 ;not visited
                  ind = ind + 1
                  blist(size+ind) = contact.end2(jj)
                endif
              endif
             endif
            endloop
          endloop
        else
          stop = 1
        endif
      endloop
    endif
    
    loop foreach local bp ball.list
      ball.extra(bp,2) = 0 ;not visited
    endloop
end

ball attr density 2000 damp 0.7

fish callback add @restorefriction event bond_break

model mechanical timestep auto
model cycle 2000 calm 500
ball attr damp 0.0

wall attribute rotation-center @rp_ range group 'wallassembly'
wall attribute spin @spin_ range group 'wallassembly'

fish def initializeTime
  mp_age_c = mech.time.total
  global tinc = 0.01
  global tnext = tinc
end
@initializeTime

fish def buildPSD
  whilestepping
  global temps = mech.time.total-mp_age_c
  if temps > tnext then
    sizedistribution(20)
    tnext = tnext + tinc
  endif
end

@sizedistribution(20)

model save 'cluster_model'
model solve time 0.1

model save 'end_blendercluster'

;=============================================================================
; eof: BlenderClusters.p3dat

ClusterMakeCluster.p3dat

; fname: ClusterMakeCluster.p3dat

fish define ini_clusters
  global cluster_head = null
  global bnumber=0
  clt = clump.template.find('pill')
  loop foreach p clump.template.pebblelist(clt)
    bnumber = bnumber + 1
  endloop
  io.out(string.build("number of pebbles = %1", bnumber))
end

fish define make_cluster(clump)
  ; replace a clump with a set of balls
  newclus = memory.create(bnumber+3)
  memory(newclus) = cluster_head
  cluster_head = newclus
  global idc = clump.id(clump)
  local num=1
  memory(newclus+num) = idc
  num=num+1
  loop foreach local p clump.pebblelist(clump)
    global prad = clump.pebble.radius(p)
    global ppos = clump.pebble.pos(p)
    global idp = clump.pebble.id(p)
    command
      ball create id @idp rad @prad pos @ppos
    endcommand
    num = num + 1
    local bp = ball.find(idp)
    if num>3
      if comp.x(ball.pos(bp)) < comp.x(ball.pos(bfirst))
        bfirst = bp
      endif
    else 
      bfirst = bp
    endif
    ball.extra(bp,1) = idc
    memory(newclus+num) = bp
  endloop
  memory(newclus+2) = bfirst
  clump.delete(clump)
end

fish define replace
  loop foreach clump clump.list
    make_cluster(clump)
  endloop
end

fish define order_clusters
  cluster = cluster_head
  loop while cluster # null
    newcluster = array.create(9)
    newcluster(1) = memory(cluster+2)
    loop j(2,9)
        referenceball = newcluster(j-1)
        dist=1000
        loop i(1,bnumber)
          thisball = memory(cluster+2+i)
          newdist = comp.x(ball.pos(thisball))-comp.x(ball.pos(referenceball))
          io.out(string.build("newdist = %1",newdist))
          if newdist > 0
            if newdist < dist
              nearestball = thisball
              dist = newdist
            endif
          endif
        endloop
        newcluster(j) = nearestball
    endloop
    loop i(1,bnumber)
      memory(cluster+i+2) = newcluster(i)
    endloop
    cluster = memory(cluster)
  endloop
end

fish define list_clusters
  cluster = cluster_head
  loop while cluster # null
    local idc = memory(cluster+1)
    local bleft = memory(cluster+2)
    io.out(string.build("--cluster with ID %1 : ",idc))
    io.out(string.build("--left most ball id : %1",ball.id(bleft)))
    loop i(1,bnumber)
       bp = memory(cluster+i+2)
       io.out(string.build("    - ball id %1",ball.id(bp)))
    endloop
    cluster = memory(cluster)
  endloop
end

fish def applybonds
    cluster = cluster_head
    loop while cluster # null
        loop i(1,bnumber-1)
            bp = memory(cluster+i+2)
            bp_next=memory(cluster+i+3)
            local clist = ball.contactmap(bp)
            section
                loop foreach local con clist
                    ok=0
                    if contact.end1(con) = bp_next
                        ok=1
                    else if contact.end2(con) = bp_next
                        ok=1
                    endif
                    if ok=1
                        local arg = array.create(1,2)
                        arg(1,1) = 'gap'
                        arg(1,2) = 0.0
                        contact.method(con,"bond",arg)
                        contact.prop(con,"pb_kn")=1e12
                        contact.prop(con,"pb_ks")=1e12
                        contact.prop(con,"pb_ten")=1e8
                        contact.prop(con,"pb_coh")=1e6
                        contact.prop(con,"pb_rmul")=0.8
                        exit section
                    endif
                endloop
            endsection
        endloop
        cluster = memory(cluster)
    endloop
end

;=============================================================================
; eof: ClusterMakeCluster.p3dat

ClusterSizeDistribution.p3dat

; fname: ClusterSizeDistribution.p3dat

fish def clumpvolumes
  global volumes = array.create(idc)
  loop local i (1,array.size(volumes,1))
    volumes(i)=0.0
  endloop
  loop foreach bp ball.list
    volumes(ball.extra(bp,1)) = volumes(ball.extra(bp,1)) ...
                                + 1.3333*math.pi*ball.radius(bp)^3
  endloop
end

fish def sizedistribution(intervals)
  command
    @clumpvolumes
  endcommand
  io.out(string.build("size volumes = %1", array.size(volumes,1)))
  nentries = array.size(volumes,1)
  radius = array.create(nentries)
  minD = 1000
  maxD = -1
  local nonzeroentries = 0
  loop local i (1,array.size(volumes,1))
    radius(i) = ((3*volumes(i))/(4*math.pi))^(0.33333)
    if radius(i)#0
      nonzeroentries = nonzeroentries + 1
      minD = math.min(radius(i)*2,minD)
      maxD = math.max(radius(i)*2,maxD)
    endif
  endloop
  io.out(string.build("minD = %1 , maxD = %2", minD, maxD))
  increment = (maxD-minD)/intervals
  passing = array.create(intervals+1)
  aveD = array.create(intervals+1)
  t = string.build("sizedist%1",nentries)
  dist = table.get(t)
  loop local j (1,array.size(aveD,1))
    aveD(j) = minD + increment*(j-1)
    passing(j) = 0.0
  endloop
  table(t,aveD(1))=passing(1)
  loop local p (2,array.size(passing,1))
    pass = 0
    loop local r (1,array.size(radius,1))
      d = radius(r)*2
      if d#0
        if d<=aveD(p)
          pass = pass+1
        endif
      endif
    endloop
    passing(p) = 100*(float(pass)/nonzeroentries)
    table(t,aveD(p))=passing(p)
  endloop
  loop local tt (1,array.size(aveD,1))   
    io.out(string.build("passing[%1] = %2", tt, passing(tt)))
  endloop
end

;=============================================================================
; eof: SizeDistribution.p3dat