Question on Clump Density and Mass

When I generate a clump using a template, it always reports a volume of “1,” regardless of the clump’s size. For example, changing the radius at line 20 (radius = 2 # Radius of the sphere) does not affect the reported volume. As a result, the real mass always equals the density specified in the “clump generate” command.

I have attached an example Python script and a screenshot.

Regards,

get_ipython().magic('reset -sf') #command to initialize iPython environment
get_ipython().magic('clear') #iPython screen clear

import itasca as it
import os

it.command("python-reset-state false")
it.command("""
model new
model large-strain on
model domain extent -10 10 -10 10 -10 10
""")

 
 ####   Define Input variables                      ####
script_directory = r"D:\Clumps"
file_name = "clumps.txt"
path_to_file = os.path.join(script_directory, file_name)

radius = 2         # Radius of the sphere
num_latitude = 5   # Number of latitude bands
num_longitude = 5  # Number of longitude slices
randomness_factor = 0.1  # Factor by which to vary the radius randomly

bpDistance = 100 # 0 ~ 180 The greater, the smoother the pebble distribution
bpRatio = 0.3 # Ratio of the smallest to largest pebble kept in the clump template with 0 < fratio < 1.
bpRadFactor = 1.1 # to control pebble intrusion depth 1.05 is default > 1.0
bprefinenum = 10000 # Increase for dense pebble (Caution)
#######################################################

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D, art3d

def generate_sphere_mesh_with_randomness(radius, num_latitude, num_longitude, randomness_factor):
    vertices = []
    facets = []

    # Top vertex
    vertices.append((0, 0, radius))

    # Generate vertices
    for i in range(1, num_latitude):
        theta = np.pi * i / num_latitude  # Latitude angle
        sin_theta = np.sin(theta)
        cos_theta = np.cos(theta)

        for j in range(num_longitude):
            phi = 2 * np.pi * j / num_longitude  # Longitude angle
            rand_radius = radius * (1 + np.random.uniform(-randomness_factor, randomness_factor))
            x = rand_radius * sin_theta * np.cos(phi)
            y = rand_radius * sin_theta * np.sin(phi)
            z = radius * cos_theta  # z remains the same to maintain the overall shape
            vertices.append((x, y, z))

    # Bottom vertex
    vertices.append((0, 0, -radius))

    # Generate triangular facets for the top cap
    for j in range(num_longitude):
        p1 = 0
        p2 = j + 1
        p3 = (j + 1) % num_longitude + 1
        
        
        facets.append((p1, p2, p3))

    # Generate triangular facets for the middle
    for i in range(1, num_latitude - 1):
        for j in range(num_longitude):
            p1 = (i - 1) * num_longitude + j + 1
            p2 = p1 + num_longitude
            p3 = p2 + 1 if (j + 1) < num_longitude else p2 + 1 - num_longitude
            p4 = p1 + 1 if (j + 1) < num_longitude else p1 + 1 - num_longitude
            facets.append((p1, p2, p3))
            facets.append((p1, p3, p4))

    # Generate triangular facets for the bottom cap
    bottom_vertex_index = len(vertices) - 1
    for j in range(num_longitude):
        p1 = bottom_vertex_index
        p2 = bottom_vertex_index - (j + 1)
        p3 = bottom_vertex_index - ((j + 1) % num_longitude + 1)
        facets.append((p1, p2, p3))

    return vertices, facets

def compute_normal(v0, v1, v2):
    edge1 = np.subtract(v1, v0)
    edge2 = np.subtract(v2, v0)
    normal = np.cross(edge1, edge2)
    normal = normal / np.linalg.norm(normal)  # Normalize the vector
    return normal

def ensure_outward_normals(vertices, facets):
    sphere_center = np.array([0, 0, 0])
    corrected_facets = []
    for facet in facets:
        v0 = np.array(vertices[facet[0]])
        v1 = np.array(vertices[facet[1]])
        v2 = np.array(vertices[facet[2]])
        normal = compute_normal(v0, v1, v2)
        center = np.mean([v0, v1, v2], axis=0)
        dot_product = np.dot(normal, center - sphere_center)
        if dot_product < 0:  # Normal is pointing inward
            corrected_facets.append((facet[0], facet[2], facet[1]))
        else:
            corrected_facets.append(facet)
    return corrected_facets

def verify_normals(vertices, facets):
    normals = []
    for facet in facets:
        v0 = vertices[facet[0]]
        v1 = vertices[facet[1]]
        v2 = vertices[facet[2]]
        normal = compute_normal(np.array(v0), np.array(v1), np.array(v2))
        normals.append(normal)
        center = np.mean([v0, v1, v2], axis=0)
        dot_product = np.dot(normal, center)
        print(f"Facet {facet} normal: {normal}, dot product with center: {dot_product}")

    return normals

def plot_sphere_mesh_with_normals(vertices, facets, normals):
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    
    # Plot vertices
    x = [v[0] for v in vertices]
    y = [v[1] for v in vertices]
    z = [v[2] for v in vertices]
    ax.scatter(x, y, z, c='r', marker='o')

    for i, (xi, yi, zi) in enumerate(vertices):
        ax.text(xi, yi, zi, f'V{i}', color='blue')

    # Plot normals
    for i, normal in enumerate(normals):
        facet = facets[i]
        v0 = vertices[facet[0]]
        v1 = vertices[facet[1]]
        v2 = vertices[facet[2]]
        center = np.mean([v0, v1, v2], axis=0)
        ax.quiver(center[0], center[1], center[2], normal[0], normal[1], normal[2], length=1.0, color='blue')

    # Plot facets
    for facet in facets:
        v0 = vertices[facet[0]]
        v1 = vertices[facet[1]]
        v2 = vertices[facet[2]]
        poly3d = [[v0, v1, v2]]
        ax.add_collection3d(art3d.Poly3DCollection(poly3d, facecolors='cyan', linewidths=1, edgecolors='r', alpha=.25))
    
    plt.show()

def print_facet_vertices(vertices, facets):
    for i, facet in enumerate(facets):
        v0 = vertices[facet[0]]
        v1 = vertices[facet[1]]
        v2 = vertices[facet[2]]
        print(f"Facet {i}:")
        print(f"  Vertex 0: {v0}")
        print(f"  Vertex 1: {v1}")
        print(f"  Vertex 2: {v2}")

# Generate the mesh
vertices, facets = generate_sphere_mesh_with_randomness(radius, num_latitude, num_longitude, randomness_factor)

# Ensure normals point outward
corrected_facets = ensure_outward_normals(vertices, facets)

# Verify the normals
normals = verify_normals(vertices, corrected_facets)

# Print the vertices of each facet
print_facet_vertices(vertices, corrected_facets)

# Plot the vertices, facets, and normals
#plot_sphere_mesh_with_normals(vertices, corrected_facets, normals)

for i, facet in enumerate(facets):
    v0 = vertices[facet[0]]
    v1 = vertices[facet[1]]
    v2 = vertices[facet[2]]
    
    it.command(f"""
        geometry polygon create by-positions {v0} {v1} {v2} set 'whiba'
    """)

it.command(f"""
    clump template create name 'akas' ...
            geometry 'whiba' ...
            bubblepack ratio {bpRatio} distance {bpDistance} radfactor {bpRadFactor} refinenum {bprefinenum} ...
            calculate-surface
            
    clump generate density 2700 number 1 template 1 'akas'
""")


with open(path_to_file, 'w') as f:
    f.write("# Molecule for Clumps\n")
    f.write("# header section:\n")
    f.write(f"{it.clump.pebble.count()} atoms\n")
    
    clump_obj = it.clump.find(1)
    f.write(f"{clump_obj.mass_real()} mass\n")
    f.write(f"{clump_obj.pos_x()} {clump_obj.pos_y()} {clump_obj.pos_z()} com\n")
    moi_real = clump_obj.moi_real()
    f.write(f"{moi_real.xx()} {moi_real.yy()} {moi_real.zz()} {moi_real.xy()} {moi_real.xz()} {moi_real.yz()} inertia\n\n")
    
    f.write(f"# body section:\n")
    f.write(f"Coords\n\n")

    pebble_list = it.clump.pebble.list()  # Returns a list of pebble objects
    for pebble_obj in pebble_list:
        f.write(f"{pebble_obj.id()} {pebble_obj.pos_x()} {pebble_obj.pos_y()} {pebble_obj.pos_z()}\n")

    f.write(f"\nDiameters\n\n")

    pebble_list = it.clump.pebble.list()  # Returns a list of pebble objects You must reset this!!!!
    for pebble_obj in pebble_list:    
        diameter = 2.0 * pebble_obj.radius()
        f.write(f"{pebble_obj.id()} {diameter}\n")