STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
engine.stability_hifi.acoustics.assembly Namespace Reference

Functions

Tuple[float, np.ndarray] _element_geometry (np.ndarray v)
float _triple_integral_factor (int i, int j, int k)
np.ndarray _mass_r_element (float area, np.ndarray r_vertices)
np.ndarray _stiffness_r_element (float area, np.ndarray grad, np.ndarray r_vertices)
np.ndarray _azimuthal_element (float area, np.ndarray r_vertices)
Tuple[sparse.csr_matrix, sparse.csr_matrix, sparse.csr_matrix] assemble_passive (MeridionalMesh mesh, np.ndarray c_sound, int m)
np.ndarray _boundary_mass_r_edge (float r_a, float r_b)
sparse.csr_matrix assemble_boundary_admittance (MeridionalMesh mesh, np.ndarray boundary_nodes, float coefficient)
np.ndarray point_sampling_vector (MeridionalMesh mesh, float x0, float r0)
np.ndarray disk_load_vector (MeridionalMesh mesh, np.ndarray column_nodes)

Variables

 _GAUSS6_BARY
 _GAUSS6_W
 _TRIPLE_FACTOR

Detailed Description

FEM assembly of the passive (no flame, no boundary impedance) axisymmetric
thermoacoustic operator: the ``K``, ``Km``, ``M2`` matrices of Eq. (10)/(11) in
``docs/stability/thermoacoustic_global_stability_paper.md``.

    K   = int_Omega  c^2 (grad p . grad phi)      r  dOmega     stiffness
    Km  = int_Omega  (c^2 / r)  p  phi                 dOmega     azimuthal term (x m^2)
    M2  = int_Omega  p  phi                        r  dOmega     mass

With these, the passive discrete eigenproblem (flame off, rigid walls: C=0, F=0) is

    (K + m^2 Km) p = -lambda^2 M2 p

a *linear* generalized eigenvalue problem in mu = -lambda^2 (real, >= 0 for a lossless
rigid-wall cavity, since K, Km, M2 are all real symmetric positive-(semi)definite) —
see ``eigen/passive.py``.

Why K and M2 have exact closed forms but Km does not
-----------------------------------------------------
On a P1 (linear) triangle, the shape functions N_i are the barycentric coordinates
L_i, and grad(N_i) is *constant* over the element. The radial coordinate r(x) is
itself a linear (degree-1) function of position, since it is a coordinate. So:

  - K's integrand, c^2 * (grad N_i . grad N_j) * r, is (const) * (linear in r) ->
    integrable exactly by the 1-point centroid rule: int_T r dA = Area * r_centroid.

  - M2's integrand, N_i * N_j * r, is a *cubic* polynomial (degree 2 from N_i N_j,
    degree 1 from r). Triangle integrals of monomials in barycentric coordinates have
    a standard closed form (see ``_mass_r_element`` below), so M2 is also exact.

  - Km's integrand, N_i * N_j / r, is *not* polynomial (r appears in the denominator)
    so no closed form exists; it is evaluated with a 6-point Gauss quadrature rule
    (exact for polynomials up to degree 4), which is far more resolution than the
    (non-polynomial) 1/r factor needs for engineering accuracy, and cheap since it
    only touches the m>=1 (transverse) matrix.

Mean-flow fields (``c``, i.e. sound speed) are supplied per-node and averaged over the
three vertices of an element to get a single c^2 per element — piecewise-constant per
element, consistent with the P1 discretization.

Function Documentation

◆ _azimuthal_element()

np.ndarray engine.stability_hifi.acoustics.assembly._azimuthal_element ( float area,
np.ndarray r_vertices )
protected
Element ``int_T N_i N_j / r dA`` via 6-point Gauss quadrature (no closed form).

Definition at line 130 of file assembly.py.

Referenced by assemble_passive().

◆ _boundary_mass_r_edge()

np.ndarray engine.stability_hifi.acoustics.assembly._boundary_mass_r_edge ( float r_a,
float r_b )
protected
Exact 2x2 edge matrix ``int_edge N_i N_j r dr`` for a radial edge from r_a to r_b.

Derived the same way as ``_mass_r_element`` but for a 1-D edge (2 nodes) instead of
a 2-D triangle (3 nodes): expand r(zeta) = r_a*(1-zeta) + r_b*zeta linearly in the
edge's local coordinate and integrate exactly (integrals of monomials in 1-D
barycentric coordinates have the same style of closed form as the 2-D case).

Definition at line 231 of file assembly.py.

Referenced by assemble_boundary_admittance().

◆ _element_geometry()

Tuple[float, np.ndarray] engine.stability_hifi.acoustics.assembly._element_geometry ( np.ndarray v)
protected
Triangle area and constant P1 shape-function gradients.

``v`` is (3, 2): vertex (x, r) coordinates. Returns ``(area, grad)`` with
``grad[i] = (dN_i/dx, dN_i/dr)``, constant over the element (standard P1 formula).

Definition at line 74 of file assembly.py.

Referenced by assemble_passive().

◆ _mass_r_element()

np.ndarray engine.stability_hifi.acoustics.assembly._mass_r_element ( float area,
np.ndarray r_vertices )
protected
Exact element mass matrix ``int_T N_i N_j r dA`` (r linear -> cubic integrand).

Expand ``r = sum_k N_k r_k`` and use the exact triple-product triangle integral.

Definition at line 111 of file assembly.py.

Referenced by assemble_passive().

◆ _stiffness_r_element()

np.ndarray engine.stability_hifi.acoustics.assembly._stiffness_r_element ( float area,
np.ndarray grad,
np.ndarray r_vertices )
protected
Exact element stiffness ``int_T (grad N_i . grad N_j) r dA`` (centroid rule).

``grad`` is constant over the element, and ``r`` is linear, so the 1-point
centroid rule (`int_T r dA = Area * mean(r_vertices)`) is exact.

Definition at line 120 of file assembly.py.

Referenced by assemble_passive().

◆ _triple_integral_factor()

float engine.stability_hifi.acoustics.assembly._triple_integral_factor ( int i,
int j,
int k )
protected

Definition at line 98 of file assembly.py.

◆ assemble_boundary_admittance()

sparse.csr_matrix engine.stability_hifi.acoustics.assembly.assemble_boundary_admittance ( MeridionalMesh mesh,
np.ndarray boundary_nodes,
float coefficient )
Assemble ``C`` for a compact (frequency-independent) admittance boundary patch.

``boundary_nodes`` must be the node indices along the boundary, SORTED BY RADIUS
(exactly what ``MeridionalMesh.nodes_at_x`` returns) — consecutive pairs are treated
as the mesh's boundary edges there. ``coefficient`` is ``c_bar * y`` (real, for the
compact admittance case); the caller multiplies the returned matrix into ``lambda*C``
when assembling the full operator.

Definition at line 246 of file assembly.py.

References _boundary_mass_r_edge().

◆ assemble_passive()

Tuple[sparse.csr_matrix, sparse.csr_matrix, sparse.csr_matrix] engine.stability_hifi.acoustics.assembly.assemble_passive ( MeridionalMesh mesh,
np.ndarray c_sound,
int m )
Assemble the passive (flame-off, rigid-wall) K, Km, M2 matrices (Eq. 10, 11).

``c_sound`` is the sound-speed field [m/s], given EITHER per-node (shape
``(n_nodes,)`` — the mean flow varies smoothly, e.g. V1's uniform field or a real
CFD/parametric temperature profile; each element's c^2 is the mean of its 3 vertex
values, i.e. the P1 linear interpolant evaluated at the centroid) OR per-element
(shape ``(n_tri,)`` — the material is piecewise-constant across a genuine interface
that a shared node cannot represent, e.g. V2's temperature-jump duct; each element's
c^2 is used directly, with no averaging across the interface). See
``_element_c_squared`` above for why this distinction matters physically.

``m`` is the azimuthal wavenumber; ``Km`` is returned *without* the m^2 factor
(caller multiplies when assembling ``K + m^2 * Km``), so the m=0 matrix (which has
no 1/r term at all) can be skipped cheaply by callers that only need
longitudinal/radial modes.

Returns ``(K, Km, M2)`` as real symmetric sparse CSR matrices of size n_nodes^2.
Axis-regularity Dirichlet elimination for m>=1 is the caller's job (``eigen/passive.py``),
since it depends on how the reduced system is solved, not on the assembly.

Definition at line 145 of file assembly.py.

References _azimuthal_element(), _element_geometry(), _mass_r_element(), and _stiffness_r_element().

◆ disk_load_vector()

np.ndarray engine.stability_hifi.acoustics.assembly.disk_load_vector ( MeridionalMesh mesh,
np.ndarray column_nodes )
The g_k vector for a compact-in-x, UNIFORM-across-the-cross-section flame at the
axial column given by ``column_nodes`` (node indices there, SORTED BY RADIUS —
exactly what ``MeridionalMesh.nodes_at_x`` returns; the flame location must
therefore fall on an exact mesh column, e.g. by building the mesh with
``two_zone_duct_mesh`` even when there is no real material jump, purely to get an
exact shared column at the flame's axial location).

``g_i = int_r N_i(x_f, r) * r dr`` along that column: same r dOmega measure as K,
M2, C (see the module-level note above for why that consistency matters), computed
per edge via the same barycentric-monomial exact-integral trick as
``_boundary_mass_r_edge``, but for a LOAD VECTOR (int N_i * 1 * r dr) rather than a
mass MATRIX (int N_i N_j r dr): for an edge from r_a to r_b, the exact two-node
contribution is ``L*[r_a/3+r_b/6, r_a/6+r_b/3]`` (L = r_b - r_a).

Sanity check used while developing this: summed over the whole column, ``g``'s
total is exactly ``int_0^R r dr = R^2/2`` — reproduced below to machine precision,
confirming the exact-integral bookkeeping is right.

Definition at line 309 of file assembly.py.

◆ point_sampling_vector()

np.ndarray engine.stability_hifi.acoustics.assembly.point_sampling_vector ( MeridionalMesh mesh,
float x0,
float r0 )
The b_k vector: nodal weights such that ``g @ p`` = the P1-interpolated value of
field ``p`` at the point ``(x0, r0)`` — a plain point sample, Eq. 7a's x_ref.

Nonzero only at the 3 nodes of the triangle containing the point (its barycentric
weights there — see ``MeridionalMesh.locate_point``).

Definition at line 296 of file assembly.py.

Variable Documentation

◆ _GAUSS6_BARY

engine.stability_hifi.acoustics.assembly._GAUSS6_BARY
protected
Initial value:
1= np.array([
2 [0.108103018168070, 0.445948490915965, 0.445948490915965],
3 [0.445948490915965, 0.108103018168070, 0.445948490915965],
4 [0.445948490915965, 0.445948490915965, 0.108103018168070],
5 [0.816847572980459, 0.091576213509771, 0.091576213509771],
6 [0.091576213509771, 0.816847572980459, 0.091576213509771],
7 [0.091576213509771, 0.091576213509771, 0.816847572980459],
8])

Definition at line 60 of file assembly.py.

◆ _GAUSS6_W

engine.stability_hifi.acoustics.assembly._GAUSS6_W
protected
Initial value:
1= np.array([
2 0.223381589678011, 0.223381589678011, 0.223381589678011,
3 0.109951743655322, 0.109951743655322, 0.109951743655322,
4])

Definition at line 68 of file assembly.py.

◆ _TRIPLE_FACTOR

engine.stability_hifi.acoustics.assembly._TRIPLE_FACTOR
protected
Initial value:
1= np.array(
2 [[[_triple_integral_factor(i, j, k) for k in range(3)] for j in range(3)] for i in range(3)]
3)

Definition at line 106 of file assembly.py.