Skip to content
cad-mesh-generation logo

CAD and Mesh Generation Skill

cad-mesh-generation

CAD and Mesh Generation Skill

SKILL.md

Full skill instructions

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

GMSH

Mesh Generation

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.

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View