4. Parametric model generation

Parametric model generation is the mechanism that turns a CAE automation platform from a batch script runner into a reusable engineering asset. Instead of rebuilding a model for every design variant, you write a Python script that reads a parameter set, regenerates geometry, re-meshes, applies boundary conditions, and submits the solve — all without manual GUI interaction. This chapter focuses on the geometry and mesh update layer, which is the part most sensitive to solver-specific API behavior.

Why Parametric Regeneration Beats Model Editing

There are two ways to handle a design change: edit an existing model, or regenerate it from parameters. Editing is fragile. A fillet that referenced an edge ID may fail when an upstream dimension changes, and mesh controls tied to face IDs silently detach. Regeneration from a parameter dictionary is deterministic: given the same inputs, you get the same topology and the same mesh, which makes regression comparison meaningful.

The practical rule is to treat the CAD model as a derived artifact, not a source of truth. The source of truth is the parameter set plus the generation script. Store both in version control, and let the platform rebuild the model on demand.

Design principle: Every dimension that a design engineer might vary should be a named parameter, not a hard-coded literal. Every mesh control that depends on geometry should be expressed relative to a named parameter (for example, global_size = thickness / 3), never as an absolute value that only works for one configuration.

Geometry Scripting Across Solver APIs

The three common targets differ mainly in how they expose geometry construction. ANSYS SpaceClaim and Discovery expose a Python API through the SpaceClaim.Api.V20xx namespace, or you can drive geometry through PyMAPDL's MapdlGeometry primitives. Abaqus uses the Part and Sketch objects inside abaqusConstants. Open-source solvers such as CalculiX or Code_Aster typically rely on an external CAD kernel — OpenCASCADE via pythonocc, or Gmsh's built-in geometry engine — with the mesh exported as INP or MED.

Despite the API differences, the workflow shape is consistent. A parametric generator does four things in order:

  1. Read parameters from a dictionary, JSON file, or the platform database.
  2. Build or update the geometry using those parameters.
  3. Assign named selections (faces, edges, bodies) so downstream steps do not depend on volatile IDs.
  4. Generate the mesh with size fields derived from the parameters.

The following example uses Gmsh because it is open-source, scriptable, and representative of the pattern. The same structure maps directly onto SpaceClaim or Abaqus with different calls.

import gmsh

def build_plate(params):
    L, W, T = params["length"], params["width"], params["thickness"]
    hole_r = params["hole_radius"]
    mesh_size = T / 3.0

    gmsh.initialize()
    gmsh.model.add("plate")

    # Base block: origin at lower-left-front corner
    box = gmsh.model.occ.addBox(0, 0, 0, L, W, T)

    # Through-hole centered on the plate
    cyl = gmsh.model.occ.addCylinder(L/2, W/2, -T, 0, 0, 2*T, hole_r)
    gmsh.model.occ.cut([(3, box)], [(3, cyl)])

    gmsh.model.occ.synchronize()

    # Named selection for the loaded face (top face, z = T)
    top = gmsh.model.getEntitiesInBoundingBox(
        -1e-6, -1e-6, T-1e-6, L+1e-6, W+1e-6, T+1e-6, dim=2)
    gmsh.model.addPhysicalGroup(2, [t[1] for t in top], name="TOP")

    gmsh.option.setNumber("Mesh.MeshSizeMax", mesh_size)
    gmsh.model.mesh.generate(3)
    gmsh.write(f"plate_{L}x{W}x{T}.inp")
    gmsh.finalize()

Two details matter here. First, the hole is cut with a cylinder that extends beyond both faces (-T to +T), which avoids coincident-face failures when the thickness changes. Second, the top face is captured by a bounding-box query rather than a stored tag, so the named selection survives regeneration.

Mesh Update Strategies

When geometry changes, you have three mesh update options, and choosing the wrong one is the most common source of wasted compute.

StrategyWhen to useCostRisk
Full remeshTopology changes (holes added/removed, splits)HighLow — always valid
Parametric remeshDimensions change, topology fixedMediumLow if size fields are parameter-relative
Mesh morphingSmall shape perturbations, same node countLowHigh — element quality degrades silently

For most parametric studies, parametric remesh is the right default. It regenerates the mesh from the same recipe, so element counts stay comparable across variants, which is essential when you compare stress or frequency results. Morphing is attractive for optimization loops because it preserves node correspondence, but you must monitor minimum Jacobian and aspect ratio after every morph step; a morph that pushes a tet into negative volume will produce a solver error that looks unrelated to the geometry change.

Parameter Flow and Failure Points

The diagram below shows how a parameter set flows through geometry, named selections, and meshing, and where the common failure modes appear.

Parameter Set length, width, thickness, hole_r Geometry Build OCC / Sketch / Part primitives Named Selections TOP, FIXED, LOAD_FACE Mesh Generation size = T/3, export INP/MED Failure: boolean cut coincident faces when T changes Failure: stale IDs face tag changes after regeneration Failure: bad elements negative Jacobian after morph Post-Regeneration Checks element count within ±5% of baseline · min Jacobian > 0.3 · named selections resolve · mass within tolerance any check fails → abort before solve, log parameter set

Verification Before Solve

Regeneration is cheap compared to a solve, so always validate the model before submitting. Four checks catch nearly every parametric failure:

  • Element count drift. Compare against a baseline. A jump from 48,000 to 61,000 elements for a 2% thickness change usually means a size field is no longer parameter-relative.
  • Minimum Jacobian. Query the mesh quality metric from the API. Values below roughly 0.3 indicate distorted elements that will slow convergence or produce wrong stress.
  • Named selection resolution. Assert that every expected group exists and contains at least one entity. An empty group means the bounding-box query missed after a geometry change.
  • Mass or volume check. Compute the model volume and compare to the analytic value. For the plate example, V = L·W·T − π·r²·T. A mismatch beyond 0.5% signals a failed boolean.
Common trap: Do not assume that a successful mesh generation means a valid model. Gmsh, Abaqus, and ANSYS will all happily mesh a geometry with a sliver face or a self-intersection. The mesh will look fine in a screenshot and fail in the solver with a singular matrix. Always run the four checks above as a gate.

Making It Reproducible

Wrap the generator in a function that takes a parameter dictionary and returns a result dictionary containing the mesh file path, element count, and check outcomes. Log the full parameter set alongside the mesh hash. When a result looks wrong three weeks later, you can rerun the exact configuration and get the same mesh — which is the entire point of parametric generation.

def generate(params):
    build_plate(params)
    report = {
        "mesh_file": f"plate_{params['length']}x{params['width']}x{params['thickness']}.inp",
        "elements": gmsh.model.mesh.getNumElements(),
        "params": params,
    }
    assert report["elements"] > 0, "empty mesh"
    return report

With geometry and mesh regeneration under control, the next layer — boundary conditions, solver settings, and result extraction — can be scripted with the same discipline. The key habit to carry forward is that every downstream step references named selections and parameter-derived values, never raw entity IDs or hard-coded numbers.