Troubleshooting Common Issues#
This page covers common problems and their solutions when creating StructureGene from custom mesh data.
Connectivity Issues#
Error: “Element connectivity out of bounds”#
Symptoms:
IndexError: index 100 is out of bounds for axis 0 with size 5
Cause:
Connectivity array uses original node IDs instead of 0-based array indices.
Solution:
Create a mapping from original node IDs to array indices:
# ✗ WRONG: Using original IDs directly
points = np.array([
[0, 0, 0], # This is index 0
[1, 0, 0], # This is index 1
])
cells = [CellBlock(type='line', data=np.array([[100, 200]]))] # Wrong!
# ✓ CORRECT: Map original IDs to indices
original_node_ids = [100, 200, 300, 400]
node_id_to_index = {nid: idx for idx, nid in enumerate(original_node_ids)}
# Apply mapping
original_connectivity = [100, 200]
correct_connectivity = [node_id_to_index[nid] for nid in original_connectivity]
# Result: [0, 1]
cells = [CellBlock(type='line', data=np.array([correct_connectivity]))]
Best Practice:
Always build the mapping at the start of your conversion:
# Sort node IDs to get consistent ordering
node_ids = sorted(mesh_data['nodes'].keys())
node_id_to_index = {nid: idx for idx, nid in enumerate(node_ids)}
# Create points array in the same order
points = np.array([mesh_data['nodes'][nid] for nid in node_ids])
# Apply to all connectivity
for elem in mesh_data['elements']:
conn_indices = [node_id_to_index[nid] for nid in elem['connectivity']]
# Use conn_indices in CellBlock
Error: “Node ID not found in mapping”#
Symptoms:
KeyError: 999
Cause:
An element references a node ID that doesn’t exist in your nodes dictionary.
Solution:
Add validation and helpful error messages:
for elem in mesh_data['elements']:
try:
conn = [node_id_to_index[nid] for nid in elem['connectivity']]
except KeyError as e:
missing_node_id = int(str(e))
raise ValueError(
f"Element {elem['id']} references undefined node ID {missing_node_id}. "
f"Available node IDs: {min(node_ids)}-{max(node_ids)}"
)
Property and Material Issues#
Error: “Property ID not found in mocombos”#
Symptoms:
KeyError: 5
Cause:
Property IDs in cell_data['property_id'] don’t match keys in sg.mocombos.
Solution:
Ensure all property IDs used by elements are defined in mocombos:
# Check which property IDs are actually used
used_prop_ids = set()
for prop_array in sg.mesh.cell_data['property_id']:
used_prop_ids.update(prop_array)
print(f"Property IDs used: {sorted(used_prop_ids)}")
print(f"Property IDs defined: {sorted(sg.mocombos.keys())}")
# Find missing definitions
for prop_id in used_prop_ids:
if prop_id not in sg.mocombos:
print(f"ERROR: Missing mocombo for property_id {prop_id}")
Prevention:
When building mocombos, ensure every unique (material, angle) combination gets an entry:
mocombo_map = {}
next_property_id = 1
for elem in mesh_data['elements']:
key = (elem['material'], elem['angle'])
if key not in mocombo_map:
mocombo_map[key] = next_property_id
sg.mocombos[next_property_id] = key
next_property_id += 1
Error: “Material name not found”#
Symptoms:
KeyError: 'steel'
Cause:
Material name in mocombos doesn’t exactly match a key in sg.materials. Often due to:
- Case sensitivity ('Steel' vs 'steel')
- Whitespace ('Steel' vs 'Steel ')
- Typos
Solution:
Verify material names match exactly:
# Check what's defined
print("Defined materials:", list(sg.materials.keys()))
# Check what's referenced
for prop_id, (mat_name, angle) in sg.mocombos.items():
if mat_name not in sg.materials:
print(f"ERROR: Property {prop_id} references undefined material '{mat_name}'")
# Show similar names
from difflib import get_close_matches
similar = get_close_matches(mat_name, sg.materials.keys())
if similar:
print(f" Did you mean: {similar[0]}?")
Prevention:
Normalize material names during conversion:
def normalize_name(name):
"""Normalize material name (strip whitespace, consistent case)."""
return name.strip()
# Use when adding materials
for mat_name, props in mesh_data['materials'].items():
normalized_name = normalize_name(mat_name)
material = CauchyContinuumModel(name=normalized_name, ...)
sg.materials[normalized_name] = material
# Use when creating mocombos
key = (normalize_name(elem['material']), elem['angle'])
Mesh Structure Issues#
Error: “Mixed element types in one cell block”#
Symptoms:
Elements with different numbers of nodes in the same CellBlock, or validation errors.
Cause:
Trying to put different element types (e.g., triangles and quads) in one CellBlock.
Solution:
Group elements by type before creating CellBlocks:
# ✗ WRONG: Mixed types
cells = [CellBlock(type='triangle', data=np.array([
[0, 1, 2], # 3 nodes (triangle)
[3, 4, 5, 6], # 4 nodes (quad) - ERROR!
]))]
# ✓ CORRECT: Separate by type
elements_by_type = {}
for elem in mesh_data['elements']:
elem_type = elem['type']
if elem_type not in elements_by_type:
elements_by_type[elem_type] = []
elements_by_type[elem_type].append(elem)
cells = []
for elem_type, elems in elements_by_type.items():
connectivity = [...] # Build connectivity for this type
cells.append(CellBlock(type=elem_type, data=np.array(connectivity)))
Error: “Unsupported element type”#
Symptoms:
ValueError: Unsupported element type: TRIA6
Cause:
Your custom format uses element type names that need mapping to meshio names.
Solution:
Create a type mapping dictionary:
TYPE_MAP = {
# Custom format -> meshio format
'TRIA3': 'triangle',
'TRIA6': 'triangle6',
'QUAD4': 'quad',
'QUAD8': 'quad8',
'QUAD9': 'quad9',
'TETRA4': 'tetra',
'TETRA10': 'tetra10',
'HEXA8': 'hexahedron',
'HEXA20': 'hexahedron20',
'HEXA27': 'hexahedron27',
'WEDGE6': 'wedge',
'PYRA5': 'pyramid',
}
for elem in mesh_data['elements']:
if elem['type'] not in TYPE_MAP:
raise ValueError(
f"Unsupported element type: {elem['type']}. "
f"Supported types: {list(TYPE_MAP.keys())}"
)
meshio_type = TYPE_MAP[elem['type']]
Dimension and Configuration Issues#
Error: “Invalid model type for dimension”#
Symptoms:
ValueError: Model type 'PL1' requires sgdim=2, but got sgdim=3
Cause:
Mismatch between StructureGene dimension and model type.
Solution:
Match model type to dimension:
# Dimension 1 (Beam)
sg = StructureGene(sgdim=1, smdim=1)
sgio.write(..., model_type='BM1') # or BM2, BM3, BM4
# Dimension 2 (Plate/Shell)
sg = StructureGene(sgdim=2, smdim=2)
sgio.write(..., model_type='PL1') # or PL2
# Dimension 3 (Solid)
sg = StructureGene(sgdim=3, smdim=3)
sgio.write(..., model_type='SD1') # or SD2
Auto-select model type:
MODEL_TYPES = {
1: 'BM1', # Default for beams
2: 'PL1', # Default for plates
3: 'SD1', # Default for solids
}
model_type = MODEL_TYPES[sg.sgdim]
sgio.write(sg=sg, fn='output.sc', model_type=model_type, ...)
Validation and Debugging#
Empty or Missing Mesh#
Symptoms:
AttributeError: 'NoneType' object has no attribute 'points'
Solution:
Check mesh exists before accessing:
if sg.mesh is None:
raise ValueError("Mesh not created. Did you assign sg.mesh?")
if sg.nnodes == 0:
raise ValueError("Mesh has no nodes")
if sg.nelems == 0:
raise ValueError("Mesh has no elements")
Pre-write Validation Checklist#
Use this validation function before writing:
def validate_sg(sg):
"""Comprehensive validation with helpful messages."""
errors = []
# Configuration
if sg.sgdim not in [1, 2, 3]:
errors.append(f"Invalid sgdim: {sg.sgdim}")
if not sg.name:
errors.append("Name is empty")
# Mesh
if sg.mesh is None:
errors.append("Mesh is None")
elif sg.nnodes == 0:
errors.append("Mesh has no nodes")
elif sg.nelems == 0:
errors.append("Mesh has no elements")
# Materials
if not sg.materials:
errors.append("No materials defined")
# Mocombos
if not sg.mocombos:
errors.append("No mocombos defined")
# Property IDs
if sg.mesh is not None:
for i, prop_array in enumerate(sg.mesh.cell_data.get('property_id', [])):
for prop_id in np.unique(prop_array):
if prop_id not in sg.mocombos:
errors.append(
f"Block {i}: property_id {prop_id} not in mocombos"
)
# Material references
for prop_id, (mat_name, angle) in sg.mocombos.items():
if mat_name not in sg.materials:
errors.append(
f"Mocombo {prop_id} references undefined material '{mat_name}'"
)
if errors:
print("Validation errors:")
for error in errors:
print(f" ✗ {error}")
return False
else:
print("✓ Validation passed")
return True
Performance Issues#
Slow conversion for large meshes#
Problem: Converting millions of nodes/elements is slow.
Solutions:
Pre-allocate arrays:
# Slow: Appending to lists
connectivity = []
for elem in elems:
connectivity.append([...])
# Fast: Pre-allocate numpy array
n_elems = len(elems)
nodes_per_elem = 4
connectivity = np.zeros((n_elems, nodes_per_elem), dtype=int)
for i, elem in enumerate(elems):
connectivity[i] = [...]
Vectorize operations:
# Slow: Loop over mapping
conn_indices = [node_id_to_index[nid] for nid in elem['connectivity']]
# Fast: Use numpy vectorized operations
conn_array = np.array(elem['connectivity'])
conn_indices = np.vectorize(node_id_to_index.get)(conn_array)
Process in chunks:
CHUNK_SIZE = 10000
for i in range(0, len(elements), CHUNK_SIZE):
chunk = elements[i:i+CHUNK_SIZE]
# Process chunk
Getting More Help#
If you’re still having issues:
Check data structure documentation: Data Structure Reference
Review working examples: Quick Start: Simple Triangle Mesh and Converting from Custom Format
Verify with minimal example: Strip down to smallest failing case
Check sgio version: Ensure you’re using the latest version
Debug output template:
print("Debug info:")
print(f" sgio version: {sgio.__version__}")
print(f" sgdim: {sg.sgdim}")
print(f" Nodes: {sg.nnodes if sg.mesh else 'No mesh'}")
print(f" Elements: {sg.nelems if sg.mesh else 'No mesh'}")
print(f" Materials: {list(sg.materials.keys())}")
print(f" Mocombos: {dict(sg.mocombos)}")
if sg.mesh and 'property_id' in sg.mesh.cell_data:
all_props = []
for pa in sg.mesh.cell_data['property_id']:
all_props.extend(pa)
print(f" Property IDs used: {sorted(set(all_props))}")