Closed bent-cylinder scattering

Consider a rigid cylinder with 5 mm radius, 20 mm cylindrical arc length, 20 mm curvature radius and 5 mm hemispherical ends. Its bend lies in the xy plane, with midpoint tangent along +x. Incidence has polar angle 60° and azimuth 0.4 radians. The full surface includes both ends, so observations need not be near broadside.

Solve and inspect the actual surface

using AcousticScattering
using CairoMakie: Figure, Axis, Colorbar, lines!, scatter!, plot, save
const AS = AcousticScattering

body = Cylinder(0.005, 0.02; radius_curvature = 0.02, endcap_depth = 0.005)
surface = mesh(body; method = :full, resolution = 0.0032, mesh_order = 3, qorder = 4)
options = (compression = (method = :none,),
    gmres_kwargs = (reltol = 1e-9, restart = 150, maxiter = 1200))
beta, alpha = pi / 3, 0.4
solution = bem(surface, Rigid(), 100.0; incidence_angle = beta, incidence_azimuth = alpha,
    options...)
@assert diagnostics(solution).converged
field_figure = plot(solution; kind = :surface_field, field = :pressure_phase,
    colorrange = (-pi, pi), colormap = :twilight,
    figure = (size = (760, 480), figure_padding = (70,20,20,20)),
    axis = (xlabel = "x (m)", ylabel = "y (m)", zlabel = "z (m)"))
Colorbar(field_figure.figure[1, 2]; limits = (-pi, pi), colormap = :twilight,
    label = "Scattered pressure phase (rad)")
save("bent_surface.png", field_figure)

Scattered pressure phase on the closed bent cylinder where coordinates are meters.

Pressure is normalized to a unit incident plane wave. The mesh and field plot use the same curved triangles as the solve. For nonuniform bending, supply a closed triangular surface through mesh(nodes, triangles) or mesh(path).

Check convergence

Refine triangle size and quadrature separately. Compare backscatter, forward scatter and a side observation in both target strength and complex amplitude:

d = [cos(beta), sin(beta) * cos(alpha), sin(beta) * sin(alpha)]
directions = (-d, d, [0.0, 0.0, 1.0])
base = [scattering_amplitude(solution; direction) for direction in directions]
changes = map(((0.0027, 4), (0.0032, 5))) do (h, q)
    refined = bem(body, Rigid(), 100.0; method = :full, meshsize = h,
        mesh_order = 3, qorder = q, incidence_angle = beta, incidence_azimuth = alpha, options...)
    @assert diagnostics(refined).converged
    amplitudes = [scattering_amplitude(refined; direction) for direction in directions]
    db = maximum(abs.(target_strength.(amplitudes) .- target_strength.(base)))
    relative = maximum(abs.(amplitudes .- base) ./ abs.(amplitudes))
    @assert db < 0.1 && relative < 0.01
    (; edge_length_m = h, quadrature_order = q, max_change_db = db,
        max_relative_amplitude_change = relative)
end
changes
((edge_length_m = 0.0027, quadrature_order = 4, max_change_db = 0.0043650046913654705, max_relative_amplitude_change = 0.0005044338988796517), (edge_length_m = 0.0032, quadrature_order = 5, max_change_db = 0.00628726992619022, max_relative_amplitude_change = 0.0007965917515954766))

Refine across the requested frequency and angular range, especially near resonances or scattering nulls. For a separate MFS comparison, pass the closed mesh to mfs and vary source_mesh and source offsets. Fluid transmission uses separate offset_ext and offset_int controls.

The bent modal and lateral MFS approximations omit end scattering. Restrict their use near broadside and compare with a converged closed-surface solution. See Jech et al. (2015) for finite-cylinder model limitations.

Frequency, incidence and bistatic patterns

Frequency sweeps reuse the geometry. At fixed frequency, mesh-based incidence sweeps also reuse the assembled boundary operators and their compression. Each incidence gets a fresh GMRES solve. A bistatic map reuses an existing solution and changes only the observation direction.

sound_speed = 1500.0
frequencies = collect(range(18000.0, 30000.0; length = 5))
spectrum = AS.frequency_sweep(k -> bem(surface, Rigid(), k;
    incidence_angle = beta, incidence_azimuth = alpha, options...), frequencies, sound_speed)
angles = deg2rad.([30.0, 45.0, 60.0, 75.0, 90.0])
aspect = AS.incidence_angle_sweep(surface, Rigid(), 100.0, angles;
    incidence_azimuth = alpha, options...)
figure = Figure(; size = (1000, 400))
frequency_axis = Axis(figure[1, 1]; xlabel = "Frequency (kHz)", ylabel = "TS (dB re 1 m²)")
lines!(frequency_axis, spectrum.frequencies ./ 1000, spectrum.target_strength)
scatter!(frequency_axis, spectrum.frequencies ./ 1000, spectrum.target_strength)
angle_axis = Axis(figure[1, 2]; xlabel = "Incidence polar angle (degrees)", ylabel = "TS (dB re 1 m²)")
lines!(angle_axis, rad2deg.(aspect.angles), aspect.target_strength)
scatter!(angle_axis, rad2deg.(aspect.angles), aspect.target_strength)
save("bent_sweeps.png", figure)
save("bent_bistatic.png", plot(solution; kind = :bistatic_map,
    thetas = range(0, pi; length = 61), phis = range(0, 2pi; length = 121),
    figure = (size = (760, 480),)))

Frequency and incidence-angle samples for the rigid bent cylinder.

Bistatic target strength at exterior wavenumber 100 inverse meters.

Replace Rigid() with PressureRelease(), FluidFilled(g, h) or GasFilled(g, h) to solve those boundary conditions on the same surface. Density and sound-speed ratios are interior/exterior values. Gas resonances require their own frequency and mesh refinement.