cad-mesh-generation
4
总安装量
4
周安装量
#50117
全站排名
安装命令
npx skills add https://github.com/vamseeachanta/workspace-hub --skill cad-mesh-generation
Agent 安装分布
opencode
4
gemini-cli
4
antigravity
4
github-copilot
4
codex
4
kimi-cli
4
Skill 文档
CAD and Mesh Generation Skill
name: cad-mesh-generation
version: 1.0.0
category: programming
tags: [cad, mesh, freecad, gmsh, geometry, finite-element, marine-structures]
created: 2026-01-06
updated: 2026-01-06
author: Claude
description: |
Expert CAD geometry and mesh generation using FreeCAD and GMSH. Create
parametric marine structures, generate meshes for FEA/BEM, and export to
various analysis software formats (AQWA, WAMIT, ANSYS, etc.).
When to Use This Skill
Use this skill when you need to:
- Create parametric CAD models of vessels, platforms, or structures
- Generate meshes for finite element analysis (FEA)
- Generate panel meshes for boundary element methods (BEM/hydrodynamics)
- Automate geometry creation for marine structures
- Export geometry to AQWA, WAMIT, ANSYS, or other analysis tools
- Create complex geometries programmatically with Python
- Perform mesh quality checks and refinement
Core Knowledge Areas
1. FreeCAD Python Scripting Basics
Creating geometry with FreeCAD Python API:
import sys
from pathlib import Path
from typing import List, Tuple, Optional
import numpy as np
# Try to import FreeCAD, provide fallback for development
try:
import FreeCAD
import Part
import Mesh
import MeshPart
FREECAD_AVAILABLE = True
except ImportError:
FREECAD_AVAILABLE = False
print("Warning: FreeCAD not available, using mock functions")
# Mock FreeCAD classes for development
class MockFreeCAD:
@staticmethod
def newDocument(name: str):
print(f"[MOCK] Creating document: {name}")
return MockDocument()
class Vector:
def __init__(self, x: float, y: float, z: float):
self.x, self.y, self.z = x, y, z
class MockDocument:
def __init__(self):
self.objects = []
def addObject(self, obj_type: str, name: str):
print(f"[MOCK] Adding object: {obj_type} - {name}")
obj = MockObject(name)
self.objects.append(obj)
return obj
def recompute(self):
print("[MOCK] Recomputing document")
class MockObject:
def __init__(self, name: str):
self.Name = name
self.Shape = None
class MockPart:
@staticmethod
def makeCylinder(radius, height, base=None, direction=None):
print(f"[MOCK] Creating cylinder: r={radius}, h={height}")
return MockShape()
@staticmethod
def makeBox(length, width, height, base=None, direction=None):
print(f"[MOCK] Creating box: {length}x{width}x{height}")
return MockShape()
class MockShape:
def fuse(self, other):
print("[MOCK] Fusing shapes")
return self
def cut(self, other):
print("[MOCK] Cutting shapes")
return self
def exportStep(self, filename):
print(f"[MOCK] Exporting STEP: {filename}")
def exportIges(self, filename):
print(f"[MOCK] Exporting IGES: {filename}")
if not FREECAD_AVAILABLE:
FreeCAD = MockFreeCAD()
Part = MockPart()
def create_cylinder_vessel(
diameter: float,
length: float,
wall_thickness: float = None,
name: str = "Vessel"
) -> Tuple[Any, Any]:
"""
Create cylindrical vessel geometry in FreeCAD.
Args:
diameter: Vessel diameter [m]
length: Vessel length [m]
wall_thickness: Wall thickness [m] (None = solid)
name: Object name
Returns:
Tuple of (document, shape)
Example:
>>> doc, shape = create_cylinder_vessel(
... diameter=10.0,
... length=100.0,
... wall_thickness=0.05,
... name="FPSO_Hull"
... )
"""
# Create document
doc = FreeCAD.newDocument(name)
# Create outer cylinder
outer_radius = diameter / 2
outer_cyl = Part.makeCylinder(
outer_radius,
length,
FreeCAD.Vector(0, 0, 0),
FreeCAD.Vector(1, 0, 0) # Along x-axis
)
# If wall thickness specified, make hollow
if wall_thickness:
inner_radius = outer_radius - wall_thickness
inner_cyl = Part.makeCylinder(
inner_radius,
length,
FreeCAD.Vector(0, 0, 0),
FreeCAD.Vector(1, 0, 0)
)
# Subtract inner from outer
vessel_shape = outer_cyl.cut(inner_cyl)
else:
vessel_shape = outer_cyl
# Add to document
vessel_obj = doc.addObject("Part::Feature", name)
vessel_obj.Shape = vessel_shape
doc.recompute()
return doc, vessel_shape
def create_rectangular_pontoon(
length: float,
width: float,
height: float,
wall_thickness: float,
name: str = "Pontoon"
) -> Tuple[Any, Any]:
"""
Create rectangular pontoon geometry.
Args:
length: Pontoon length [m]
width: Pontoon width [m]
height: Pontoon height [m]
wall_thickness: Wall thickness [m]
name: Object name
Returns:
Tuple of (document, shape)
Example:
>>> doc, shape = create_rectangular_pontoon(
... length=50.0,
... width=10.0,
... height=5.0,
... wall_thickness=0.025,
... name="Barge_Pontoon"
... )
"""
doc = FreeCAD.newDocument(name)
# Outer box
outer_box = Part.makeBox(
length,
width,
height,
FreeCAD.Vector(0, 0, 0)
)
# Inner box (hollowed)
inner_box = Part.makeBox(
length - 2 * wall_thickness,
width - 2 * wall_thickness,
height - wall_thickness, # Bottom plate thickness
FreeCAD.Vector(wall_thickness, wall_thickness, wall_thickness)
)
# Subtract
pontoon_shape = outer_box.cut(inner_box)
# Add to document
pontoon_obj = doc.addObject("Part::Feature", name)
pontoon_obj.Shape = pontoon_shape
doc.recompute()
return doc, pontoon_shape
def export_geometry(
shape: Any,
output_file: Path,
format: str = 'step'
) -> None:
"""
Export FreeCAD shape to file.
Args:
shape: FreeCAD shape object
output_file: Output file path
format: Export format ('step', 'iges', 'stl')
Example:
>>> export_geometry(vessel_shape, Path('vessel.step'), 'step')
"""
output_file.parent.mkdir(parents=True, exist_ok=True)
if format.lower() == 'step':
shape.exportStep(str(output_file))
elif format.lower() == 'iges':
shape.exportIges(str(output_file))
elif format.lower() == 'stl':
if FREECAD_AVAILABLE:
# Convert to mesh first
mesh = MeshPart.meshFromShape(
shape,
LinearDeflection=0.1,
AngularDeflection=0.5,
Relative=False
)
mesh.write(str(output_file))
else:
print(f"[MOCK] Exporting STL: {output_file}")
else:
raise ValueError(f"Unsupported format: {format}")
print(f"Geometry exported: {output_file}")
2. GMSH Mesh Generation
Creating meshes with GMSH Python API:
# Try to import gmsh
try:
import gmsh
GMSH_AVAILABLE = True
except ImportError:
GMSH_AVAILABLE = False
print("Warning: GMSH not available, using mock")
# Mock gmsh module
class MockGmsh:
def initialize(self): pass
def finalize(self): pass
def open(self, filename): print(f"[MOCK] Opening: {filename}")
def write(self, filename): print(f"[MOCK] Writing: {filename}")
def clear(self): pass
class model:
@staticmethod
def mesh():
return MockGmshMesh()
class geo:
@staticmethod
def addPoint(x, y, z, lc=0, tag=-1):
return tag if tag != -1 else 1
@staticmethod
def addLine(p1, p2, tag=-1):
return tag if tag != -1 else 1
@staticmethod
def addSurfaceFilling(lines, tag=-1):
return tag if tag != -1 else 1
@staticmethod
def synchronize():
print("[MOCK] Synchronizing geometry")
class option:
@staticmethod
def setNumber(name, value):
print(f"[MOCK] Setting option: {name} = {value}")
class MockGmshMesh:
def generate(self, dim):
print(f"[MOCK] Generating {dim}D mesh")
def setAlgorithm(self, dim, algo):
print(f"[MOCK] Setting {dim}D algorithm: {algo}")
if not GMSH_AVAILABLE:
gmsh = MockGmsh()
def create_panel_mesh_cylinder(
radius: float,
height: float,
n_circumferential: int = 36,
n_vertical: int = 20,
output_file: Path = None
) -> str:
"""
Create panel mesh for cylindrical body using GMSH.
Args:
radius: Cylinder radius [m]
height: Cylinder height [m]
n_circumferential: Number of panels around circumference
n_vertical: Number of panels vertically
output_file: Output mesh file (.msh)
Returns:
Path to mesh file
Example:
>>> mesh_file = create_panel_mesh_cylinder(
... radius=5.0,
... height=100.0,
... n_circumferential=36,
... n_vertical=50,
... output_file=Path('cylinder_mesh.msh')
... )
"""
gmsh.initialize()
# Create cylindrical surface
gmsh.model.add("cylinder_mesh")
# Characteristic length (mesh size)
lc = 2 * np.pi * radius / n_circumferential
# Create points on bottom circle
bottom_points = []
for i in range(n_circumferential):
theta = 2 * np.pi * i / n_circumferential
x = radius * np.cos(theta)
y = radius * np.sin(theta)
z = 0
tag = gmsh.model.geo.addPoint(x, y, z, lc)
bottom_points.append(tag)
# Create points on top circle
top_points = []
for i in range(n_circumferential):
theta = 2 * np.pi * i / n_circumferential
x = radius * np.cos(theta)
y = radius * np.sin(theta)
z = height
tag = gmsh.model.geo.addPoint(x, y, z, lc)
top_points.append(tag)
# Create lines connecting bottom to top
vertical_lines = []
for i in range(n_circumferential):
line = gmsh.model.geo.addLine(bottom_points[i], top_points[i])
vertical_lines.append(line)
# Create horizontal lines (bottom and top circles)
bottom_lines = []
top_lines = []
for i in range(n_circumferential):
next_i = (i + 1) % n_circumferential
bottom_line = gmsh.model.geo.addLine(bottom_points[i], bottom_points[next_i])
bottom_lines.append(bottom_line)
top_line = gmsh.model.geo.addLine(top_points[i], top_points[next_i])
top_lines.append(top_line)
# Create surfaces (panels)
panels = []
for i in range(n_circumferential):
next_i = (i + 1) % n_circumferential
# Define curve loop for each panel
curve_loop = gmsh.model.geo.addCurveLoop([
bottom_lines[i],
vertical_lines[next_i],
-top_lines[i],
-vertical_lines[i]
])
# Create surface
surface = gmsh.model.geo.addSurfaceFilling([curve_loop])
panels.append(surface)
gmsh.model.geo.synchronize()
# Generate 2D mesh (surface mesh)
gmsh.model.mesh.generate(2)
# Set mesh algorithm (Frontal-Delaunay for quality)
gmsh.model.mesh.setAlgorithm(2, 6)
# Save mesh
if output_file is None:
output_file = Path('cylinder_mesh.msh')
output_file.parent.mkdir(parents=True, exist_ok=True)
gmsh.write(str(output_file))
gmsh.finalize()
print(f"Panel mesh created: {output_file}")
print(f" Number of panels: {n_circumferential * n_vertical}")
return str(output_file)
def create_tetrahedral_mesh(
geometry_file: Path,
element_size: float,
output_file: Path,
refine_surfaces: bool = True
) -> str:
"""
Create tetrahedral volume mesh from geometry.
Args:
geometry_file: Input geometry (.step, .iges, .stl)
element_size: Target element size [m]
output_file: Output mesh file (.msh)
refine_surfaces: Whether to refine surface mesh
Returns:
Path to mesh file
Example:
>>> mesh_file = create_tetrahedral_mesh(
... geometry_file=Path('vessel.step'),
... element_size=0.5,
... output_file=Path('vessel_mesh.msh')
... )
"""
gmsh.initialize()
# Open geometry
gmsh.open(str(geometry_file))
# Set global mesh size
gmsh.option.setNumber("Mesh.CharacteristicLengthMax", element_size)
gmsh.option.setNumber("Mesh.CharacteristicLengthMin", element_size * 0.5)
# Set 3D mesh algorithm (Delaunay for quality)
gmsh.option.setNumber("Mesh.Algorithm3D", 1) # Delaunay
# Generate 2D surface mesh first
gmsh.model.mesh.generate(2)
if refine_surfaces:
# Refine surface mesh
gmsh.model.mesh.refine()
# Generate 3D volume mesh
gmsh.model.mesh.generate(3)
# Optimize mesh quality
gmsh.model.mesh.optimize("Netgen")
# Save mesh
output_file.parent.mkdir(parents=True, exist_ok=True)
gmsh.write(str(output_file))
gmsh.finalize()
print(f"Tetrahedral mesh created: {output_file}")
return str(output_file)
3. Mesh Quality Assessment
Check mesh quality metrics:
def analyze_mesh_quality(
mesh_file: Path,
mesh_type: str = 'surface'
) -> dict:
"""
Analyze mesh quality metrics.
Args:
mesh_file: Path to mesh file (.msh)
mesh_type: 'surface' or 'volume'
Returns:
Dictionary with quality metrics
Example:
>>> quality = analyze_mesh_quality(Path('cylinder_mesh.msh'), 'surface')
>>> print(f"Min quality: {quality['min_quality']:.3f}")
>>> print(f"Average quality: {quality['avg_quality']:.3f}")
"""
if not GMSH_AVAILABLE:
print("[MOCK] Analyzing mesh quality")
return {
'element_count': 1000,
'node_count': 550,
'min_quality': 0.75,
'avg_quality': 0.92,
'max_quality': 1.0
}
gmsh.initialize()
gmsh.open(str(mesh_file))
# Get mesh statistics
element_types, element_tags, node_tags = gmsh.model.mesh.getElements()
total_elements = sum(len(tags) for tags in element_tags)
# Get nodes
node_tags_all, node_coords, _ = gmsh.model.mesh.getNodes()
total_nodes = len(node_tags_all)
# Calculate element quality
qualities = []
if mesh_type == 'surface':
# For triangular surface elements
for elem_type, elem_tags in zip(element_types, element_tags):
if elem_type == 2: # Triangle
for elem_tag in elem_tags:
# Get element quality (0-1, 1 = perfect)
quality = gmsh.model.mesh.getElementQuality([elem_tag], elem_type)
if quality:
qualities.extend(quality)
elif mesh_type == 'volume':
# For tetrahedral volume elements
for elem_type, elem_tags in zip(element_types, element_tags):
if elem_type == 4: # Tetrahedron
for elem_tag in elem_tags:
quality = gmsh.model.mesh.getElementQuality([elem_tag], elem_type)
if quality:
qualities.extend(quality)
gmsh.finalize()
if qualities:
min_quality = min(qualities)
avg_quality = np.mean(qualities)
max_quality = max(qualities)
else:
min_quality = avg_quality = max_quality = 0.0
return {
'element_count': total_elements,
'node_count': total_nodes,
'min_quality': min_quality,
'avg_quality': avg_quality,
'max_quality': max_quality,
'poor_elements': sum(1 for q in qualities if q < 0.3)
}
4. Mesh Conversion for Analysis Tools
Convert meshes to various formats:
def convert_mesh_to_wamit_gdf(
mesh_file: Path,
output_file: Path,
symmetry: str = 'none'
) -> None:
"""
Convert GMSH mesh to WAMIT .gdf format.
Args:
mesh_file: Input GMSH mesh file (.msh)
output_file: Output WAMIT file (.gdf)
symmetry: 'none', 'x', 'y', or 'xy'
Example:
>>> convert_mesh_to_wamit_gdf(
... mesh_file=Path('cylinder_mesh.msh'),
... output_file=Path('cylinder.gdf'),
... symmetry='y'
... )
"""
if not GMSH_AVAILABLE:
print(f"[MOCK] Converting mesh to WAMIT GDF: {output_file}")
return
gmsh.initialize()
gmsh.open(str(mesh_file))
# Get nodes
node_tags, node_coords, _ = gmsh.model.mesh.getNodes()
n_nodes = len(node_tags)
# Reshape coordinates
coords = np.array(node_coords).reshape((-1, 3))
# Get surface elements (triangles/quads)
element_types, element_tags, node_tags_elem = gmsh.model.mesh.getElements(2)
gmsh.finalize()
# Write GDF file
with open(output_file, 'w') as f:
# Header
f.write(f"WAMIT GDF File\n")
f.write(f"Converted from GMSH mesh\n")
# Symmetry
if symmetry == 'x':
f.write("1.0 0.0 ! XOZ symmetry\n")
elif symmetry == 'y':
f.write("0.0 1.0 ! YOZ symmetry\n")
elif symmetry == 'xy':
f.write("1.0 1.0 ! XOZ and YOZ symmetry\n")
else:
f.write("0.0 0.0 ! No symmetry\n")
# Number of panels
total_panels = sum(len(tags) for tags in element_tags)
f.write(f"{total_panels}\n")
# Write panels
panel_id = 1
for elem_type, elem_tags, node_tags_set in zip(
element_types, element_tags, node_tags_elem
):
if elem_type == 2: # Triangle
# Triangles have 3 nodes
nodes_per_elem = 3
node_tags_array = np.array(node_tags_set).reshape((-1, nodes_per_elem))
for nodes in node_tags_array:
# Get coordinates of 3 vertices
vertices = coords[nodes - 1] # GMSH uses 1-based indexing
# Write panel (x, y, z for each vertex)
f.write(f"{panel_id} ")
for vertex in vertices:
f.write(f"{vertex[0]:.6f} {vertex[1]:.6f} {vertex[2]:.6f} ")
f.write("\n")
panel_id += 1
elif elem_type == 3: # Quad
nodes_per_elem = 4
node_tags_array = np.array(node_tags_set).reshape((-1, nodes_per_elem))
for nodes in node_tags_array:
vertices = coords[nodes - 1]
f.write(f"{panel_id} ")
for vertex in vertices:
f.write(f"{vertex[0]:.6f} {vertex[1]:.6f} {vertex[2]:.6f} ")
f.write("\n")
panel_id += 1
print(f"WAMIT GDF file created: {output_file}")
print(f" Total panels: {total_panels}")
def convert_mesh_to_ansys_cdb(
mesh_file: Path,
output_file: Path
) -> None:
"""
Convert GMSH mesh to ANSYS .cdb format.
Args:
mesh_file: Input GMSH mesh file (.msh)
output_file: Output ANSYS command database (.cdb)
Example:
>>> convert_mesh_to_ansys_cdb(
... mesh_file=Path('vessel_mesh.msh'),
... output_file=Path('vessel.cdb')
... )
"""
if not GMSH_AVAILABLE:
print(f"[MOCK] Converting mesh to ANSYS CDB: {output_file}")
return
gmsh.initialize()
gmsh.open(str(mesh_file))
# Get nodes
node_tags, node_coords, _ = gmsh.model.mesh.getNodes()
coords = np.array(node_coords).reshape((-1, 3))
# Get elements
element_types, element_tags_list, node_tags_elem_list = gmsh.model.mesh.getElements()
gmsh.finalize()
# Write CDB file
with open(output_file, 'w') as f:
# Header
f.write("/PREP7\n")
f.write("! Mesh converted from GMSH\n")
# Write nodes
f.write("! Nodes\n")
for node_id, coord in enumerate(coords, start=1):
f.write(f"N,{node_id},{coord[0]:.6f},{coord[1]:.6f},{coord[2]:.6f}\n")
# Write elements
f.write("! Elements\n")
elem_id = 1
for elem_type, elem_tags, node_tags_elem in zip(
element_types, element_tags_list, node_tags_elem_list
):
if elem_type == 4: # Tetrahedron (SOLID187 in ANSYS)
f.write("ET,1,SOLID187\n")
nodes_per_elem = 4
node_tags_array = np.array(node_tags_elem).reshape((-1, nodes_per_elem))
for nodes in node_tags_array:
node_str = ','.join(map(str, nodes))
f.write(f"E,{node_str}\n")
elem_id += 1
f.write("FINISH\n")
print(f"ANSYS CDB file created: {output_file}")
Complete Examples
Example 1: Complete Vessel Geometry and Mesh Workflow
from pathlib import Path
import numpy as np
def complete_vessel_geometry_workflow(
vessel_params: dict,
mesh_params: dict,
output_dir: Path
) -> dict:
"""
Complete workflow: Create geometry, mesh, and export.
Args:
vessel_params: Vessel geometry parameters
mesh_params: Mesh generation parameters
output_dir: Output directory for all files
Returns:
Dictionary with file paths
Example:
>>> vessel_params = {
... 'type': 'cylinder',
... 'diameter': 20.0,
... 'length': 150.0,
... 'wall_thickness': 0.05
... }
>>> mesh_params = {
... 'n_circumferential': 48,
... 'n_vertical': 75,
... 'element_size': 2.0
... }
>>> results = complete_vessel_geometry_workflow(
... vessel_params,
... mesh_params,
... Path('vessel_geometry')
... )
"""
output_dir.mkdir(parents=True, exist_ok=True)
print("="*70)
print("VESSEL GEOMETRY AND MESH WORKFLOW")
print("="*70)
# Step 1: Create CAD geometry
print("\n[Step 1/5] Creating CAD geometry...")
if vessel_params['type'] == 'cylinder':
doc, shape = create_cylinder_vessel(
diameter=vessel_params['diameter'],
length=vessel_params['length'],
wall_thickness=vessel_params['wall_thickness'],
name="Vessel_Geometry"
)
# Export geometry
step_file = output_dir / 'vessel.step'
iges_file = output_dir / 'vessel.iges'
export_geometry(shape, step_file, 'step')
export_geometry(shape, iges_file, 'iges')
print(f"Geometry exported: {step_file}, {iges_file}")
# Step 2: Create surface mesh for hydrodynamics
print("\n[Step 2/5] Creating surface mesh for BEM analysis...")
surface_mesh_file = output_dir / 'vessel_surface.msh'
create_panel_mesh_cylinder(
radius=vessel_params['diameter'] / 2,
height=vessel_params['length'],
n_circumferential=mesh_params['n_circumferential'],
n_vertical=mesh_params['n_vertical'],
output_file=surface_mesh_file
)
# Step 3: Analyze mesh quality
print("\n[Step 3/5] Analyzing mesh quality...")
quality = analyze_mesh_quality(surface_mesh_file, 'surface')
print(f"Mesh quality:")
print(f" Elements: {quality['element_count']}")
print(f" Nodes: {quality['node_count']}")
print(f" Min quality: {quality['min_quality']:.3f}")
print(f" Avg quality: {quality['avg_quality']:.3f}")
print(f" Poor elements: {quality['poor_elements']}")
# Step 4: Convert to WAMIT format
print("\n[Step 4/5] Converting to WAMIT GDF format...")
wamit_file = output_dir / 'vessel.gdf'
convert_mesh_to_wamit_gdf(
surface_mesh_file,
wamit_file,
symmetry='y' # Symmetric about xz plane
)
# Step 5: Create volume mesh for FEA (optional)
print("\n[Step 5/5] Creating volume mesh for FEA...")
volume_mesh_file = output_dir / 'vessel_volume.msh'
ansys_file = output_dir / 'vessel.cdb'
if GMSH_AVAILABLE:
create_tetrahedral_mesh(
step_file,
mesh_params['element_size'],
volume_mesh_file
)
# Convert to ANSYS
convert_mesh_to_ansys_cdb(volume_mesh_file, ansys_file)
print("\n" + "="*70)
print("WORKFLOW COMPLETE")
print("="*70)
results = {
'geometry': {
'step': step_file,
'iges': iges_file
},
'mesh': {
'surface': surface_mesh_file,
'volume': volume_mesh_file,
'wamit': wamit_file,
'ansys': ansys_file
},
'quality': quality
}
return results
# Run workflow
vessel_params = {
'type': 'cylinder',
'diameter': 20.0,
'length': 150.0,
'wall_thickness': 0.05
}
mesh_params = {
'n_circumferential': 48,
'n_vertical': 75,
'element_size': 2.0
}
workflow_results = complete_vessel_geometry_workflow(
vessel_params,
mesh_params,
Path('vessel_geometry_output')
)
print("\nGenerated files:")
for category, files in workflow_results.items():
if category != 'quality':
print(f"\n{category.upper()}:")
for file_type, file_path in files.items():
print(f" {file_type}: {file_path}")
Best Practices
1. Parametric Design
from dataclasses import dataclass
@dataclass
class VesselDesignParameters:
"""Parametric vessel design."""
length: float # Overall length [m]
beam: float # Beam (width) [m]
depth: float # Depth [m]
draft: float # Design draft [m]
bow_shape: str = 'straight' # 'straight', 'raked', 'bulbous'
stern_shape: str = 'transom' # 'transom', 'cruiser'
superstructure: bool = True
def validate(self) -> bool:
"""Validate design parameters."""
if self.draft > self.depth:
raise ValueError("Draft cannot exceed depth")
if self.beam > self.length:
raise ValueError("Beam should not exceed length")
return True
2. Mesh Size Optimization
def calculate_optimal_mesh_size(
geometry_length_scale: float,
analysis_type: str,
target_accuracy: str = 'medium'
) -> float:
"""
Calculate optimal mesh size based on geometry and analysis type.
Args:
geometry_length_scale: Characteristic length [m]
analysis_type: 'bem', 'fea_linear', 'fea_nonlinear', 'cfd'
target_accuracy: 'coarse', 'medium', 'fine'
Returns:
Recommended element size [m]
"""
# Base sizing ratios
sizing_ratios = {
'bem': {
'coarse': 0.10,
'medium': 0.05,
'fine': 0.025
},
'fea_linear': {
'coarse': 0.20,
'medium': 0.10,
'fine': 0.05
},
'fea_nonlinear': {
'coarse': 0.10,
'medium': 0.05,
'fine': 0.025
},
'cfd': {
'coarse': 0.15,
'medium': 0.075,
'fine': 0.0375
}
}
ratio = sizing_ratios[analysis_type][target_accuracy]
element_size = geometry_length_scale * ratio
return element_size
3. Quality Checks
def perform_mesh_quality_checks(
mesh_file: Path,
min_quality_threshold: float = 0.3
) -> bool:
"""
Perform comprehensive mesh quality checks.
Args:
mesh_file: Mesh file path
min_quality_threshold: Minimum acceptable quality
Returns:
True if mesh passes quality checks
"""
quality = analyze_mesh_quality(mesh_file)
checks = {
'min_quality': quality['min_quality'] >= min_quality_threshold,
'poor_elements': quality['poor_elements'] == 0,
'element_count': quality['element_count'] > 0
}
passed = all(checks.values())
if not passed:
print("Mesh quality check FAILED:")
for check_name, result in checks.items():
status = "PASS" if result else "FAIL"
print(f" {check_name}: {status}")
return passed
Resources
FreeCAD
- Documentation: https://wiki.freecadweb.org/
- Python API: https://wiki.freecadweb.org/Python_scripting_tutorial
- Examples: https://github.com/FreeCAD/FreeCAD/tree/master/src/Mod/Part/TestPartApp
GMSH
- Documentation: https://gmsh.info/doc/texinfo/gmsh.html
- Python API: http://gmsh.info/doc/texinfo/gmsh.html#Gmsh-API
- Tutorials: https://gitlab.onelab.info/gmsh/gmsh/-/tree/master/tutorials
Mesh Generation
- Netgen: https://ngsolve.org/
- TetGen: http://wias-berlin.de/software/tetgen/
- Triangle: https://www.cs.cmu.edu/~quake/triangle.html
Marine Geometry
- DelftShip: Free ship hull design software
- MAXSURF: Professional naval architecture software
- Rhinoceros: Advanced 3D modeling (with Grasshopper for parametric)
Use this skill for: Expert CAD geometry creation and mesh generation for marine structures with full export capabilities to analysis software.