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.
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:
- Read parameters from a dictionary, JSON file, or the platform database.
- Build or update the geometry using those parameters.
- Assign named selections (faces, edges, bodies) so downstream steps do not depend on volatile IDs.
- 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.
| Strategy | When to use | Cost | Risk |
|---|---|---|---|
| Full remesh | Topology changes (holes added/removed, splits) | High | Low — always valid |
| Parametric remesh | Dimensions change, topology fixed | Medium | Low if size fields are parameter-relative |
| Mesh morphing | Small shape perturbations, same node count | Low | High — 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.
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.
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.