Just noticed after upgrading to the most recent version of Quantlib Python that the class ql.SabrSwaptionVolCube is now available. This is a very useful class in that it behaves in very much the same way as the now deprecated ql.SwaptionVolCube1 class and takes the same inputs (swaption ATM vol matrix, strike spreads, vol spreads ...etc) along with $\alpha,\beta,\nu,\rho$ vectors to return a SABR vol cube.
Now, to calibrate the ATM vols $\sigma_{N,ATM}$, we can take method 2 of this approach: i.e. fix $\alpha$ and set $\beta=0$ (assuming a Bachelier distribution of the forwards) and recursively perturb the $\alpha$ parameter by find the (smallest positive) root of the cubic polynomial $$\frac{β(β−2)T}{ 24F^{(2−2β)}}α^3 +\frac{ρβνT}{4F^{(1−β)}}α^2+(1+\frac{2−3ρ^2}{24}ν^2T)α−σ_{N,ATM}F^{−β}=0$$ via calibrating the $\nu,\rho$. However, this obviously works for the option expiries and swap tenors specified in the skew matrix (what the class refers to as "sparse parameters"). It does not calibrate $\sigma_{N,ATM}$ for expires and tenors not given in the skew matrix (what the class refers to as "dense parameters"). So, for example, let's say I input skew data for the subset of expiries 1m,3m,1y,5y,10y,30y on the subset of swap tenors 2y,5y,10y,30y then the above approach will return the correctly calibrated $\sigma_{N,ATM}$ for, say, 3m10y but not 3m15y (even though the ATM swaption vol matrix being supplied has the 15y tail).
My question is how does one achieve the ATM calibration for those tenors not in the skew matrix? One approach is to include every tail and expiry in the skew matrix from the whole ATM vol matrix but this is impractical as skew data is sparsely available in the market (that's the whole point of using SABR!). Ideally, what is it to be achieved is for the skew to be derived from the sparse parameters while the ATM vol coming from the ATM vol matrix.
In any case, one approach that does work is to supply the full set of expiries and tenors and fill the strike spreads with the "appropriate values" from sparse data (i.e. if the strike ATM+1% entry for the 3m10y is say +0.15% normals from the market then the same value should be entered for 3m15Y). If any Quantlib experts out there have a more intelligent solution to the above, or if I'm missing something in my approach, please do respond.