Reconstruct particles from ct images

Asked by Ruidong LI

Hello!

 I have a question about generating clumps from '.gts' files. I have almost 800 gts files. The issue was that the generated clump had a smaller volume than the original GTS file, causing the sample to shrink in the height direction. The inherent contact force chains were also altered as a result of this phenomenon. Can anyone assist me with this problem?

Best Kyle

Question information

Language:
English Edit question
Status:
Solved
For:
Yade Edit question
Assignee:
No assignee Edit question
Solved by:
Ruidong LI
Solved:
Last query:
Last reply:
Revision history for this message
Karol Brzezinski (kbrzezinski) said :
#1

Hi Kyle,

I think that you need to be much more specific. For example, is each of the 800 gts files modelling a single particle? Do you try to reproduce the exact shape of the sample (particles in their corresponding locations from the very beginning)? What code do you use to generate clumps? Can you prepare the MVE[1]? If not possible, please describe your workflow. What do you mean that the sample shrinks in the height direction? Smaller than expected after deposition, or did something actually shrink?

Best wishes,
Karol

[1] https://www.yade-dem.org/wiki/Howtoask

Revision history for this message
Ruidong LI (kyle2000) said :
#2

Karol, Thanks for your reply.

---I think that you need to be much more specific. For example, is each of the 800 gts files modelling a single particle?
---Yes. Each GTS file denotes one single particle.

---Do you try to reproduce the exact shape of the sample (particles in their corresponding locations from the very beginning)? What code do you use to generate clumps?
---Yes. I'd like to reproduce the exact shape of the sample (particles in their corresponding locations from the very beginning). The code I used to generate the clump is presented below:

def createSphere(pred, ndivmin, spacing):
 aabb = pred.aabb()
 dim0 = aabb[1][0] - aabb[0][0]
 bodyList = []
 ndiv = ndivmin
 while len(bodyList)==0:
  radius = dim0 / ndiv # get some characteristic dimension, use it for radius
  bodyList = O.bodies.append(pack.regularHexa(pred, radius = radius, gap = -radius, color = [random.random(),random.random(),random.random()], material = sphereMat))
  ndiv = ndiv + spacing

---Can you prepare the MVE[1]? If not possible, please describe your workflow.
--- I can provide the MVE. But I don't know how to provide my GTS. I just imported gts files into Yade and created the surface, then generate spheres using the 'pack.regularHexa' function.

---What do you mean that the sample shrinks in the height direction? Smaller than expected after deposition, or did something actually shrink?
--- This means that when the unbalanced force ratio tends to be stable, the height of the sample is smaller than the real value. The reason behind this, I guess, is the voids between clumps since clumps are smaller than the gts surface.

Revision history for this message
Karol Brzezinski (kbrzezinski) said :
#3

Are the particles somehow bonded (I mean grains in the sample)? If not, I think that particles rearrange and change the height of the sample.
If you want to adjust the size of the particles, you can expand spheres in the packing a little bit after generation.

Cheers,
Karol

Revision history for this message
Jan Stránský (honzik) said :
#4

or use a different packing method than regularHexa, see e.g. [Kozicki2012] [2]
Cheers
Jan

[2] https://www.yade-dem.org/doc/publications.html

Revision history for this message
Ruidong LI (kyle2000) said :
#5

Thanks for your reply, Karol.

>Are the particles somehow bonded (I mean grains in the sample)?
--What do you mean particles are bonded? The particles in a clump are bonded. But the particle surfaces (gts) are separated from each other with a small gap (one-pixel distance in the ct image).

>If not, I think that particles rearrange and change the height of the sample.
--Yes. I think so. The rearrangement of particle positions and gaps between clumps changed the height of the sample.

>If you want to adjust the size of the particles, you can expand spheres in the packing a little bit after generation.
--Yes, I have tried this method. I have increased the z-coordinates (the direction of height) of each point in the gts file by 1.2 times. But the result indicates that the height of the sample is still not insufficient. My target is 40mm, and the obtained one is 37.5mm (multiple 1.2 times in z coordinates). By doing so, the unbalanced force ratio always stands at a high level (>0.25).

Do you have any other recommendations?

Best
Kyle

Revision history for this message
Ruidong LI (kyle2000) said :
#6

Thanks for your reply, Jan.

>or use a different packing method than regularHexa, see e.g. [Kozicki2012] [2]
-- I have looked through your recommended paper. But in my opinion, maybe the method mentioned in this paper is more suitable for regular shapes. What I have are irregular-shaped particles with interior holes.

Do you have any other recommendations?

Best
Kyle

Revision history for this message
Karol Brzezinski (kbrzezinski) said :
#7

I can also recommend Matlab code for clumo generation developed by Angelidakis et al. It is described in [3].

[3] Angelidakis et al. 2021 CLUMP: A Code Library to generate Universal Multi-sphere Particles, Softwarex

Revision history for this message
Jan Stránský (honzik) said :
#8

Yes, [Kozicki2012] uses "regular" (symmetric) shapes.
Just an idea to use this concept of "suitably" overlapping spheres, which would represent more realistically the actual shape then cropped regular hexa packing.
Of course, it would need some development, with uncertain result :-)
Cheers
Jan

Revision history for this message
Ruidong LI (kyle2000) said :
#9

Thanks for your reply, Karol.

>I can also recommend Matlab code for clumo generation developed by Angelidakis et al. It is described in [3].
[3] Angelidakis et al. 2021 CLUMP: A Code Library to generate Universal Multi-sphere Particles, Softwarex
--I will try and respond to you ASAP. Many thanks.

Best
Kyle

Revision history for this message
Ruidong LI (kyle2000) said :
#10

Thanks for your reply, Jan.

>Just an idea to use this concept of "suitably" overlapping spheres, which would represent more realistically the actual shape then cropped regular hexa packing. Of course, it would need some development, with uncertain result :-)
-- yes. I will try and respond to you ASAP. Many thanks.

Best
Kyle

Revision history for this message
Ruidong LI (kyle2000) said :
#11

Thanks for your reply, Karol.

>I can also recommend Matlab code for clumo generation developed by Angelidakis et al. It is described in [3].
[3] Angelidakis et al. 2021 CLUMP: A Code Library to generate Universal Multi-sphere Particles, Softwarex
--By the way, I'd like to ask how can I import the position and radius of generated spheres into Yade. I mean once I obtain the positions and radius of spheres forming a clump externally such as your recommendation [1]. I can use the function 'O.bodies.appendClumped([])', right? But I have more than 800 particles. Should I put those lines into the main py file together with other definitions such as material, or create a new py file and called by the main py file?

[1] Angelidakis et al. 2021 CLUMP: A Code Library to generate Universal Multi-sphere Particles, Softwarex

Best
Kyle

Revision history for this message
Vasileios Angelidakis (vsangelidakis) said :
#12

Hi Kyle,

You can use something like:

c1 = ymport.text('yourClump.txt', shift=Vector3(0,0,0.5), scale=1.0, material='yourMaterial')
O.bodies.appendClumped(c1)

where yourClump.txt file should be in the format: x y z r, i.e. center and radius of each sphere, like here (for a three-sphere rod):
0 0 0 1
1 0 0 1
2 0 0 1

If you have all of your clump files in a the same directory, it should be easy to iterate through them in a loop.

Hope this helps,
Vasileios

Revision history for this message
Ruidong LI (kyle2000) said :
#13

Dear Vasileios,

Thanks for your answer. I have tried your code.

The error is below:

unning script /home/kyle/Yade/install/bin/clump_test1.py
Traceback (most recent call last):
  File "./yade-Unknown", line 343, in runScript
    execfile(script,globals())
  File "/usr/lib/python3/dist-packages/past/builtins/misc.py", line 87, in execfile
    exec_(code, myglobals, mylocals)
  File "/home/kyle/Yade/install/bin/clump_test1.py", line 69, in <module>
    O.bodies.appendClumped(spheres)
TypeError: No registered converter was able to produce a C++ rvalue of type boost::shared_ptr<yade::Body> from this Python object of type tuple

My code is:

inputfile = '/home/kyle/Downloads/yadetxtClumps.txt'
spheres = ymport.textClumps(inputfile,material=sphereMat,discretization=0)
O.bodies.appendClumped(spheres)

The file in the 'inputfile' is like this (contain many clumps):
0.068037 0.02416 0.0017637 0.0011423 1
0.068615 0.022601 0.0014278 0.00077244 1
0.069542 0.023528 0.0017906 0.00071539 1
0.066801 0.025141 0.0019788 0.0006786 1
0.049731 0.03499 0.0017112 0.00070436 10
0.048747 0.034309 0.0013637 0.00063928 10
0.047719 0.034628 0.0014651 0.00056027 10
0.047299 0.035323 0.0020008 0.00052311 10

Sorry to trouble you. But do you know what's wrong with this?

Best,
Kyle

Revision history for this message
Vasileios Angelidakis (vsangelidakis) said :
#14

Hi Kyle,

In your code, you do not need the "O.bodies.appendClumped(spheres)" statement, this is already included in the ymport.textClumps statement. Look into [1]

Best,
Vasileios

[1] https://gitlab.com/yade-dev/trunk/-/blob/master/py/ymport.py#L147

Revision history for this message
Ruidong LI (kyle2000) said :
#15

Dear Vasileios,

Thanks for your answer. I have tried your code. So I just need to create a text for each clump and import them iteratively, right? Just using the 'ymport.textClumps statement'.

Best,
Kyle

Revision history for this message
Vasileios Angelidakis (vsangelidakis) said :
#16

Dear Kyle,

If you used my suggestion from answer #12 of this discussion you would need iterations (not good).
You actually have a better solution in answer #13.

Can you please run your script:

spheres = ymport.textClumps(inputfile,material=sphereMat,discretization=0)
O.bodies.appendClumped(spheres)

In your inputfile you have 2 clumps in one file, so your solution does not require iterations. Is something still unclear?

Best,
Vasileios

Revision history for this message
Ruidong LI (kyle2000) said :
#17

Dear Vasileios,

Thanks for your answer.

I am so confused. In my opinion, the statement 'ymport.textClumps' is enough. is Adding the statement 'O.bodies.appendClumped' unnecessary?

Moreover, What I want to do is import multiple grains with different colour (same colour for spheres in the same clump). This can be done with the statement 'ymport.textClumps' but requires an iteration of loading text.

If I put all clump information into one single text like this answer #13. Can I still distinguish clumps by assigning different colours?

Best,
Kyle

Revision history for this message
Karol Brzezinski (kbrzezinski) said :
#18

Hi Kyle,

If you use ymport.text() function, you don't have to append bodies (it is included in the function).

If you want to differentiate between clump properties, you can do it in two ways (at least):
1) put each clump in a separate file and load it with different keyword arguments passed to ymport.text() function.
2) put all the clumps in one file, and load them at once. Next, iterate over bodies in the simulation and apply required properties.

Please see example of using the second approach below:

####### input file with clumps "clumps.txt"
0.068037 0.02416 0.0017637 0.0011423 1
0.068615 0.022601 0.0014278 0.00077244 1
0.069542 0.023528 0.0017906 0.00071539 1
0.066801 0.025141 0.0019788 0.0006786 1
0.049731 0.03499 0.0017112 0.00070436 10
0.048747 0.034309 0.0013637 0.00063928 10
0.047719 0.034628 0.0014651 0.00056027 10
0.047299 0.035323 0.0020008 0.00052311 10

##### Yade script
from yade import ymport

spheres = ymport.textClumps('clumps.txt')

currentClumpId = O.bodies[0].clumpId
color = randomColor()
for b in O.bodies:
    if b.clumpId != currentClumpId:#change color each time clumpId changes
        currentClumpId = b.clumpId
        color = randomColor()

    if isinstance(b.shape,Sphere):#colorize spheres
        b.shape.color = color

###

Cheers,
Karol

Revision history for this message
Ruidong LI (kyle2000) said :
#19

Dear Karol,

Many thanks. Your suggestion sovled my problem.

Cheers,
Kyle

Revision history for this message
William (qfxx-123) said :
#20

Hi, Karol.
I wonder that what the '1' and '10' mean in the 'clumps.txt'

####### input file with clumps "clumps.txt"
0.068037 0.02416 0.0017637 0.0011423 1
0.068615 0.022601 0.0014278 0.00077244 1
0.069542 0.023528 0.0017906 0.00071539 1
0.066801 0.025141 0.0019788 0.0006786 1
0.049731 0.03499 0.0017112 0.00070436 10
0.048747 0.034309 0.0013637 0.00063928 10
0.047719 0.034628 0.0014651 0.00056027 10
0.047299 0.035323 0.0020008 0.00052311 10