Fluid–structure interaction
zCFD supports two fluid-structure interaction (FSI) coupling schemes: a built-in modal model driven by mode shapes imported from a Nastran modal analysis, and a generic loosely-coupled scheme for driving an external FEA solver through a user-supplied genericFSI.py script. Both schemes use the same underlying mesh-deformation machinery (RBF, RBF multiscale, or IDW - see Mesh deformation) to propagate the motion of the coupled surface into the volume mesh.
Where FSI settings live
An FSI coupling is attached to the wall boundary condition whose surface is the moving, coupled structural boundary, via that boundary condition's 'fsi' key:
{
"model": {
"<case_name>": {
"boundary_conditions": {
"BC_FSI": {
"zones": [1, 2, 3],
"type": "wall",
"kind": "slip",
"fsi": {
"type": "modal_model", # or "generic_fsi"
...
},
},
...
},
}
},
}
The 'fsi' block is discriminated by its 'type' key: 'modal_model' selects the built-in Nastran modal-model coupling, 'generic_fsi' selects the generic external-solver coupling. For the modal model, the moving zones are not repeated inside the 'fsi' block - they are inherited from the owning boundary condition's own 'zones' list. Generic FSI is the exception: it carries its own 'zone' key (singular, unlike every other zone list in zCFD, which is why it stands out below).
Restarting an FSI simulation
FSI restart behaviour is controlled by 'restart' in the 'initialisation' block - the same flag that governs restarting the flow solution. The FSI coupling resolves this flag through the same model-block-then-solver-block precedence used everywhere else in zCFD: if the model that owns the FSI-coupled boundary condition sets its own 'initialisation' block, the 'restart' value there takes priority; only when the model block does not set 'initialisation' does the coupling fall back to the top-level solver block:
{
"solver": { "initialisation": { "restart": true } },
"model": { "<case_name>": { "initialisation": { "restart": true } } }
}
This determines whether the modal model resumes its structural state (modal displacements and velocities, and, for the RBF transform, the previously built base-point set) from a previous run, so a per-model 'initialisation' block, when present, is always the one that decides FSI restart - not the solver-level one.
Modal model
The modal model reduces the structural response to a small set of Nastran mode shapes, integrates the modal equations of motion (via a Newmark-beta scheme) forced by the aerodynamic pressure load, and maps the resulting modal deformation back onto the fluid surface and, via RBF or IDW, into the volume mesh.
By default the modes are marched and the mesh deformed after every solver cycle. 'update_frequency' spaces the
structural updates out to once every that many cycles; when several walls carry a modal model, the smallest value
applies to all of them.
RBF multiscale transform
Set 'transform_type': 'rbf_multiscale' to deform the volume mesh using the multiscale RBF scheme (see Interpolation method for how RBF and RBF multiscale work).
IDW transform
Set 'transform_type': 'idw' to deform the volume mesh using inverse distance weighting (see Interpolation method for the IDW weighting formulae).
Harmonic initial state
An optional 'harmonic_initial_state' block starts every mode on its steady harmonic response to a point force, instead of at rest. The magnitude of force and the frequency set each mode's initial displacement and velocity at the start of a run that is not a restart; no force is applied while the run marches. The force at location is written to the modal history.
Modal model example
RBF multiscale coupling of a panel to a Nastran modal basis:
{
"boundary_conditions": {
"BC_FSI": {
"zones": [1, 2, 3],
"type": "wall",
"kind": "slip",
"fsi": {
"type": "modal_model",
"file_format": "nastran",
"nastran_casename": "panel_nm_0.04m",
"transform_type": "rbf_multiscale",
"base_point_fraction": 1.0,
"support_radius": 0.02,
"scale": [1.0, 1.0, 1.0],
"mode_mapping_max_distance": 0.01,
"fluid_force_scaling": 0.08 / 0.000166667,
"mode_list": [0, 4, 10, 13],
"modal_damping": [0.0, 0.0, 0.0, 0.0],
},
},
},
}
The same panel coupled with an IDW transform instead, with a set of surrounding zones held fixed:
{
"boundary_conditions": {
"BC_FSI": {
"zones": [1, 2, 3],
"type": "wall",
"kind": "slip",
"fsi": {
"type": "modal_model",
"file_format": "nastran",
"nastran_casename": "panel_nm_0.04m",
"transform_type": "idw",
"power": [3.0, 8.0, 10.0],
"deformation_distance": 0.05,
"blending_stiffness": 0.0,
"n_nearest": 350,
"fixed_zones": [4, 5, 6, 7, 8, 9],
"scale": [1.0, 1.0, 1.0],
"mode_mapping_max_distance": 0.01,
"fluid_force_scaling": 0.08 / 0.000166667,
"mode_list": [0, 4, 10, 13],
"modal_damping": [0.0, 0.0, 0.0, 0.0],
},
},
},
}
Generic FSI
Generic FSI is a loose (explicit) coupling to an external FEA program. By default, genericFSI.py performs no changes - it is a hook to be implemented to exchange pressures, displacements and node coordinates with your own external solver.
Generic FSI example
{
"boundary_conditions": {
"BC_FSI": {
"zones": [1, 2, 3],
"type": "wall",
"kind": "slip",
"fsi": {
"type": "generic_fsi",
"zone": [1, 2, 3],
"fixed_zones": [4, 5, 6],
"transform_type": "idw",
"n_nearest": 100,
"deformation_distance": 1.0,
"blending_stiffness": 0.5,
"power": [3.0, 5.0, 10.0],
"user_variables": {"restart_file": "structure_state.dat"},
},
},
},
}
Mesh deformation
A known deformation of a surface in the mesh is propagated out into the volume using either radial basis function or inverse distance weighted interpolation. Wing deflection and turbine-blade deflection under a static load are two uses of this functionality. Set up as an entry in the model's fluid_zones with 'type': 'rbf transform' or 'type': 'idw transform'.
Two zone types apply a geometric transform to a subset of the mesh rather than a physical source term: the RBF and IDW mesh deformation schemes, typically used to propagate a moving boundary's displacement into the surrounding volume mesh. Both require zones and a func that returns the deformed position of each surface node; the solver takes the displacement as the difference from the original position.
Interpolation method
Selects the interpolation method used to propagate the surface deformation into the volume mesh. Two type values are available:
'rbf transform' with base_point_fraction set to 1.0 uses the 'greedy' method of Allan et al, where the deformation is started with a subset of points and additional points are added until the surface deformation converges.
'rbf transform' with base_point_fraction set below 1.0 instead uses the multiscale RBF method, where a subset of surface points with a fixed support radius are solved implicitly and the remaining points are solved explicitly with a support radius determined as part of the solution process. The base set of points are assigned the support_radius and hence can have a wider sphere of influence than the remaining points which are used in the multiscale process.
'idw transform' uses a simple inverse distance weighted interpolation. The power parameter provides a list of three exponents used in the IDW scheme. The first (p1) is applied to the interpolation weight of points close to the surface, the second (p2) to points which are the deformation distance away from the surface and the third (p3) is used to blend between the two extremes.
Then the weight is:
Examples
RBF Example
import zutil
alpha = 10.0
def my_transform(*, location):
return zutil.rotate_vector(location, alpha, 0.0)
{
"fluid_zones": {
"TR_1": {
"type": "rbf transform",
"zones": [4],
"func": my_transform,
"support_radius": 200.0,
"basis": "c2",
}
}
}
RBF Multiscale Example
import zutil
alpha = 10.0
def my_transform(*, location):
return zutil.rotate_vector(location, alpha, 0.0)
{
"fluid_zones": {
"TR_1": {
"type": "rbf transform",
"zones": [4],
"func": my_transform,
"support_radius": 200.0,
"basis": "c2",
"base_point_fraction": 0.1,
}
}
}
IDW Example
import zutil
alpha = 10.0
def my_transform(*, location):
return zutil.rotate_vector(location, alpha, 0.0)
{
"fluid_zones": {
"TR_1": {
"type": "idw transform",
"zones": [4],
"func": my_transform,
"fixed_zones": [1],
"n_nearest": 500,
"power": [4.0, 8.0, 10.0],
"deformation_distance": 100.0,
"blending_stiffness": 0.5,
}
}
}