{ "cells": [ { "cell_type": "markdown", "id": "c247a198", "metadata": {}, "source": [ "# FFT\n", "This tutorial illustrates the Fast Fourier Transforms interface in ROOT.\n", "FFT transform types provided in ROOT:\n", "\n", " - \"C2CFORWARD\" - a complex input/output discrete Fourier transform (DFT)\n", "in one or more dimensions, -1 in the exponent\n", " - \"C2CBACKWARD\"- a complex input/output discrete Fourier transform (DFT)\n", " in one or more dimensions, +1 in the exponent\n", " - \"R2C\" - a real-input/complex-output discrete Fourier transform (DFT)\n", " in one or more dimensions,\n", " - \"C2R\" - inverse transforms to \"R2C\", taking complex input\n", " (storing the non-redundant half of a logically Hermitian array)\n", " to real output\n", " - \"R2HC\" - a real-input DFT with output in \"halfcomplex\" format,\n", " i.e. real and imaginary parts for a transform of size n stored as\n", " r0, r1, r2, ..., rn/2, i(n+1)/2-1, ..., i2, i1\n", " - \"HC2R\" - computes the reverse of FFTW_R2HC, above\n", " - \"DHT\" - computes a discrete Hartley transform\n", "\n", "\n", "\n", "Sine/cosine transforms:\n", "\n", " - DCT-I (REDFT00 in FFTW3 notation)\n", " - DCT-II (REDFT10 in FFTW3 notation)\n", " - DCT-III(REDFT01 in FFTW3 notation)\n", " - DCT-IV (REDFT11 in FFTW3 notation)\n", " - DST-I (RODFT00 in FFTW3 notation)\n", " - DST-II (RODFT10 in FFTW3 notation)\n", " - DST-III(RODFT01 in FFTW3 notation)\n", " - DST-IV (RODFT11 in FFTW3 notation)\n", "\n", "First part of the tutorial shows how to transform the histograms\n", "Second part shows how to transform the data arrays directly\n", "\n", "\n", "\n", "\n", "**Author:** Anna Kreshuk, Jens Hoffmann \n", "This notebook tutorial was automatically generated with ROOTBOOK-izer from the macro found in the ROOT repository on Tuesday, May 19, 2026 at 08:24 PM." ] }, { "cell_type": "markdown", "id": "2cec6e6d", "metadata": {}, "source": [ "Histograms\n", "=========\n", "prepare the canvas for drawing" ] }, { "cell_type": "code", "execution_count": 1, "id": "d6f2ad82", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:30.919115Z", "iopub.status.busy": "2026-05-19T20:24:30.918988Z", "iopub.status.idle": "2026-05-19T20:24:31.267983Z", "shell.execute_reply": "2026-05-19T20:24:31.267303Z" } }, "outputs": [], "source": [ "TCanvas *myc = new TCanvas(\"myc\", \"Fast Fourier Transform\", 800, 600);\n", "myc->SetFillColor(45);\n", "TPad *c1_1 = new TPad(\"c1_1\", \"c1_1\",0.01,0.67,0.49,0.99);\n", "TPad *c1_2 = new TPad(\"c1_2\", \"c1_2\",0.51,0.67,0.99,0.99);\n", "TPad *c1_3 = new TPad(\"c1_3\", \"c1_3\",0.01,0.34,0.49,0.65);\n", "TPad *c1_4 = new TPad(\"c1_4\", \"c1_4\",0.51,0.34,0.99,0.65);\n", "TPad *c1_5 = new TPad(\"c1_5\", \"c1_5\",0.01,0.01,0.49,0.32);\n", "TPad *c1_6 = new TPad(\"c1_6\", \"c1_6\",0.51,0.01,0.99,0.32);\n", "c1_1->Draw();\n", "c1_2->Draw();\n", "c1_3->Draw();\n", "c1_4->Draw();\n", "c1_5->Draw();\n", "c1_6->Draw();\n", "c1_1->SetFillColor(30);\n", "c1_1->SetFrameFillColor(42);\n", "c1_2->SetFillColor(30);\n", "c1_2->SetFrameFillColor(42);\n", "c1_3->SetFillColor(30);\n", "c1_3->SetFrameFillColor(42);\n", "c1_4->SetFillColor(30);\n", "c1_4->SetFrameFillColor(42);\n", "c1_5->SetFillColor(30);\n", "c1_5->SetFrameFillColor(42);\n", "c1_6->SetFillColor(30);\n", "c1_6->SetFrameFillColor(42);\n", "\n", "c1_1->cd();\n", "TDirectory::TContext ctx{nullptr}; // Don't register histograms to the current directory" ] }, { "cell_type": "markdown", "id": "30363861", "metadata": {}, "source": [ "A function to sample" ] }, { "cell_type": "code", "execution_count": 2, "id": "1c957286", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:31.269814Z", "iopub.status.busy": "2026-05-19T20:24:31.269662Z", "iopub.status.idle": "2026-05-19T20:24:31.475947Z", "shell.execute_reply": "2026-05-19T20:24:31.475258Z" } }, "outputs": [], "source": [ "TF1 *fsin = new TF1(\"fsin\", \"sin(x)+sin(2*x)+sin(0.5*x)+1\", 0, 4*TMath::Pi());\n", "fsin->Draw();\n", "\n", "Int_t n=25;\n", "TH1D *hsin = new TH1D(\"hsin\", \"hsin\", n+1, 0, 4*TMath::Pi());\n", "Double_t x;" ] }, { "cell_type": "markdown", "id": "a1527b75", "metadata": {}, "source": [ "Fill the histogram with function values" ] }, { "cell_type": "code", "execution_count": 3, "id": "c00de51f", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:31.477724Z", "iopub.status.busy": "2026-05-19T20:24:31.477571Z", "iopub.status.idle": "2026-05-19T20:24:31.700837Z", "shell.execute_reply": "2026-05-19T20:24:31.691425Z" } }, "outputs": [], "source": [ "for (Int_t i=0; i<=n; i++){\n", " x = (Double_t(i)/n)*(4*TMath::Pi());\n", " hsin->SetBinContent(i+1, fsin->Eval(x));\n", "}\n", "hsin->Draw(\"same\");\n", "fsin->GetXaxis()->SetLabelSize(0.05);\n", "fsin->GetYaxis()->SetLabelSize(0.05);\n", "\n", "c1_2->cd();" ] }, { "cell_type": "markdown", "id": "e253472f", "metadata": {}, "source": [ "Compute the transform and look at the magnitude of the output" ] }, { "cell_type": "code", "execution_count": 4, "id": "49aa6fd2", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:31.702537Z", "iopub.status.busy": "2026-05-19T20:24:31.702386Z", "iopub.status.idle": "2026-05-19T20:24:31.908887Z", "shell.execute_reply": "2026-05-19T20:24:31.907999Z" } }, "outputs": [], "source": [ "TH1 *hm =nullptr;\n", "TVirtualFFT::SetTransform(nullptr);\n", "hm = hsin->FFT(hm, \"MAG\");\n", "hm->SetTitle(\"Magnitude of the 1st transform\");\n", "hm->Draw();" ] }, { "cell_type": "markdown", "id": "a34e2faf", "metadata": {}, "source": [ "NOTE: for \"real\" frequencies you have to divide the x-axes range with the range of your function\n", "(in this case 4*Pi); y-axes has to be rescaled by a factor of 1/SQRT(n) to be right: this is not done automatically!" ] }, { "cell_type": "code", "execution_count": 5, "id": "e7271c46", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:31.910436Z", "iopub.status.busy": "2026-05-19T20:24:31.910303Z", "iopub.status.idle": "2026-05-19T20:24:32.146918Z", "shell.execute_reply": "2026-05-19T20:24:32.130393Z" } }, "outputs": [], "source": [ "hm->SetStats(kFALSE);\n", "hm->GetXaxis()->SetLabelSize(0.05);\n", "hm->GetYaxis()->SetLabelSize(0.05);\n", "c1_3->cd();" ] }, { "cell_type": "markdown", "id": "3f3ddbf2", "metadata": {}, "source": [ "Look at the phase of the output" ] }, { "cell_type": "code", "execution_count": 6, "id": "ee93baa5", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:32.152319Z", "iopub.status.busy": "2026-05-19T20:24:32.152184Z", "iopub.status.idle": "2026-05-19T20:24:32.369289Z", "shell.execute_reply": "2026-05-19T20:24:32.361480Z" } }, "outputs": [], "source": [ "TH1 *hp = nullptr;\n", "hp = hsin->FFT(hp, \"PH\");\n", "hp->SetTitle(\"Phase of the 1st transform\");\n", "hp->Draw();\n", "hp->SetStats(kFALSE);\n", "hp->GetXaxis()->SetLabelSize(0.05);\n", "hp->GetYaxis()->SetLabelSize(0.05);" ] }, { "cell_type": "markdown", "id": "4a01d8b5", "metadata": {}, "source": [ "Look at the DC component and the Nyquist harmonic:" ] }, { "cell_type": "code", "execution_count": 7, "id": "0ad82ecb", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:32.371055Z", "iopub.status.busy": "2026-05-19T20:24:32.370886Z", "iopub.status.idle": "2026-05-19T20:24:32.577417Z", "shell.execute_reply": "2026-05-19T20:24:32.576700Z" } }, "outputs": [], "source": [ "Double_t re, im;" ] }, { "cell_type": "markdown", "id": "323d41d6", "metadata": {}, "source": [ "That's the way to get the current transform object:" ] }, { "cell_type": "code", "execution_count": 8, "id": "bcca01aa", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:32.579396Z", "iopub.status.busy": "2026-05-19T20:24:32.579255Z", "iopub.status.idle": "2026-05-19T20:24:32.785663Z", "shell.execute_reply": "2026-05-19T20:24:32.785034Z" } }, "outputs": [], "source": [ "TVirtualFFT *fft = TVirtualFFT::GetCurrentTransform();\n", "c1_4->cd();" ] }, { "cell_type": "markdown", "id": "a0f32f90", "metadata": {}, "source": [ "Use the following method to get just one point of the output" ] }, { "cell_type": "code", "execution_count": 9, "id": "c3147209", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:32.788130Z", "iopub.status.busy": "2026-05-19T20:24:32.787984Z", "iopub.status.idle": "2026-05-19T20:24:33.017625Z", "shell.execute_reply": "2026-05-19T20:24:33.017224Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1st transform: DC component: 26.000000\n", "1st transform: Nyquist harmonic: -0.932840\n" ] } ], "source": [ "fft->GetPointComplex(0, re, im);\n", "printf(\"1st transform: DC component: %f\\n\", re);\n", "fft->GetPointComplex(n/2+1, re, im);\n", "printf(\"1st transform: Nyquist harmonic: %f\\n\", re);" ] }, { "cell_type": "markdown", "id": "f788077f", "metadata": {}, "source": [ "Use the following method to get the full output:" ] }, { "cell_type": "code", "execution_count": 10, "id": "1a6ef4e8", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:33.042317Z", "iopub.status.busy": "2026-05-19T20:24:33.042173Z", "iopub.status.idle": "2026-05-19T20:24:33.250095Z", "shell.execute_reply": "2026-05-19T20:24:33.248839Z" } }, "outputs": [], "source": [ "Double_t *re_full = new Double_t[n];\n", "Double_t *im_full = new Double_t[n];\n", "fft->GetPointsComplex(re_full,im_full);" ] }, { "cell_type": "markdown", "id": "8316c160", "metadata": {}, "source": [ "Now let's make a backward transform:" ] }, { "cell_type": "code", "execution_count": 11, "id": "393d75fe", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:33.251804Z", "iopub.status.busy": "2026-05-19T20:24:33.251657Z", "iopub.status.idle": "2026-05-19T20:24:33.478003Z", "shell.execute_reply": "2026-05-19T20:24:33.477280Z" } }, "outputs": [], "source": [ "TVirtualFFT *fft_back = TVirtualFFT::FFT(1, &n, \"C2R M K\");\n", "fft_back->SetPointsComplex(re_full,im_full);\n", "fft_back->Transform();\n", "TH1 *hb = nullptr;" ] }, { "cell_type": "markdown", "id": "58d2ed2c", "metadata": {}, "source": [ "Let's look at the output" ] }, { "cell_type": "code", "execution_count": 12, "id": "d6fdc0e4", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:33.486158Z", "iopub.status.busy": "2026-05-19T20:24:33.486035Z", "iopub.status.idle": "2026-05-19T20:24:33.692274Z", "shell.execute_reply": "2026-05-19T20:24:33.691687Z" } }, "outputs": [], "source": [ "hb = TH1::TransformHisto(fft_back,hb,\"Re\");\n", "hb->SetTitle(\"The backward transform result\");\n", "hb->Draw();" ] }, { "cell_type": "markdown", "id": "80f48c4e", "metadata": {}, "source": [ "NOTE: here you get at the x-axes number of bins and not real values\n", "(in this case 25 bins has to be rescaled to a range between 0 and 4*Pi;\n", "also here the y-axes has to be rescaled (factor 1/bins)" ] }, { "cell_type": "code", "execution_count": 13, "id": "b1d4969f", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:33.693998Z", "iopub.status.busy": "2026-05-19T20:24:33.693886Z", "iopub.status.idle": "2026-05-19T20:24:33.929596Z", "shell.execute_reply": "2026-05-19T20:24:33.929067Z" } }, "outputs": [], "source": [ "hb->SetStats(kFALSE);\n", "hb->GetXaxis()->SetLabelSize(0.05);\n", "hb->GetYaxis()->SetLabelSize(0.05);\n", "delete fft_back;\n", "fft_back=nullptr;" ] }, { "cell_type": "markdown", "id": "bbfbe053", "metadata": {}, "source": [ "Data array - same transform\n", "===========================" ] }, { "cell_type": "markdown", "id": "28a690b8", "metadata": {}, "source": [ "Allocate an array big enough to hold the transform output\n", "Transform output in 1d contains, for a transform of size N,\n", "N/2+1 complex numbers, i.e. 2*(N/2+1) real numbers\n", "our transform is of size n+1, because the histogram has n+1 bins" ] }, { "cell_type": "code", "execution_count": 14, "id": "96da6c56", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:33.959522Z", "iopub.status.busy": "2026-05-19T20:24:33.959373Z", "iopub.status.idle": "2026-05-19T20:24:34.161668Z", "shell.execute_reply": "2026-05-19T20:24:34.160998Z" } }, "outputs": [], "source": [ "Double_t *in = new Double_t[2*((n+1)/2+1)];\n", "Double_t re_2,im_2;\n", "for (Int_t i=0; i<=n; i++){\n", " x = (Double_t(i)/n)*(4*TMath::Pi());\n", " in[i] = fsin->Eval(x);\n", "}" ] }, { "cell_type": "markdown", "id": "c15713af", "metadata": {}, "source": [ "Make our own TVirtualFFT object (using option \"K\")\n", "Third parameter (option) consists of 3 parts:\n", "- transform type:\n", "real input/complex output in our case\n", "- transform flag:\n", "the amount of time spent in planning\n", "the transform (see TVirtualFFT class description)\n", "- to create a new TVirtualFFT object (option \"K\") or use the global (default)" ] }, { "cell_type": "code", "execution_count": 15, "id": "94fe9bb1", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:34.163647Z", "iopub.status.busy": "2026-05-19T20:24:34.163506Z", "iopub.status.idle": "2026-05-19T20:24:34.369904Z", "shell.execute_reply": "2026-05-19T20:24:34.369288Z" } }, "outputs": [], "source": [ "Int_t n_size = n+1;\n", "TVirtualFFT *fft_own = TVirtualFFT::FFT(1, &n_size, \"R2C ES K\");\n", "if (!fft_own) return;\n", "fft_own->SetPoints(in);\n", "fft_own->Transform();" ] }, { "cell_type": "markdown", "id": "ed7d59b1", "metadata": {}, "source": [ "Copy all the output points:" ] }, { "cell_type": "code", "execution_count": 16, "id": "6b8e1f7a", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:34.371899Z", "iopub.status.busy": "2026-05-19T20:24:34.371780Z", "iopub.status.idle": "2026-05-19T20:24:34.591040Z", "shell.execute_reply": "2026-05-19T20:24:34.590299Z" } }, "outputs": [], "source": [ "fft_own->GetPoints(in);" ] }, { "cell_type": "markdown", "id": "ed5dab48", "metadata": {}, "source": [ "Draw the real part of the output" ] }, { "cell_type": "code", "execution_count": 17, "id": "52fa8a71", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:34.592898Z", "iopub.status.busy": "2026-05-19T20:24:34.592783Z", "iopub.status.idle": "2026-05-19T20:24:34.799038Z", "shell.execute_reply": "2026-05-19T20:24:34.798392Z" } }, "outputs": [], "source": [ "c1_5->cd();\n", "TH1 *hr = nullptr;\n", "hr = TH1::TransformHisto(fft_own, hr, \"RE\");\n", "hr->SetTitle(\"Real part of the 3rd (array) transform\");\n", "hr->Draw();\n", "hr->SetStats(kFALSE);\n", "hr->GetXaxis()->SetLabelSize(0.05);\n", "hr->GetYaxis()->SetLabelSize(0.05);\n", "c1_6->cd();\n", "TH1 *him = nullptr;\n", "him = TH1::TransformHisto(fft_own, him, \"IM\");\n", "him->SetTitle(\"Im. part of the 3rd (array) transform\");\n", "him->Draw();\n", "him->SetStats(kFALSE);\n", "him->GetXaxis()->SetLabelSize(0.05);\n", "him->GetYaxis()->SetLabelSize(0.05);\n", "\n", "myc->cd();" ] }, { "cell_type": "markdown", "id": "ab034446", "metadata": {}, "source": [ "Now let's make another transform of the same size\n", "The same transform object can be used, as the size and the type of the transform\n", "haven't changed" ] }, { "cell_type": "code", "execution_count": 18, "id": "f4b0678c", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:34.800891Z", "iopub.status.busy": "2026-05-19T20:24:34.800778Z", "iopub.status.idle": "2026-05-19T20:24:35.007616Z", "shell.execute_reply": "2026-05-19T20:24:35.007304Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2nd transform: DC component: 29.000000\n", "2nd transform: Nyquist harmonic: -0.000000\n" ] } ], "source": [ "TF1 *fcos = new TF1(\"fcos\", \"cos(x)+cos(0.5*x)+cos(2*x)+1\", 0, 4*TMath::Pi());\n", "for (Int_t i=0; i<=n; i++){\n", " x = (Double_t(i)/n)*(4*TMath::Pi());\n", " in[i] = fcos->Eval(x);\n", "}\n", "fft_own->SetPoints(in);\n", "fft_own->Transform();\n", "fft_own->GetPointComplex(0, re_2, im_2);\n", "printf(\"2nd transform: DC component: %f\\n\", re_2);\n", "fft_own->GetPointComplex(n/2+1, re_2, im_2);\n", "printf(\"2nd transform: Nyquist harmonic: %f\\n\", re_2);\n", "delete fft_own;\n", "delete [] in;\n", "delete [] re_full;\n", "delete [] im_full;" ] }, { "cell_type": "markdown", "id": "2023bbd6", "metadata": {}, "source": [ "Draw all canvases " ] }, { "cell_type": "code", "execution_count": 19, "id": "e60dc06e", "metadata": { "collapsed": false, "execution": { "iopub.execute_input": "2026-05-19T20:24:35.026431Z", "iopub.status.busy": "2026-05-19T20:24:35.026302Z", "iopub.status.idle": "2026-05-19T20:24:35.253802Z", "shell.execute_reply": "2026-05-19T20:24:35.253433Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "
\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%jsroot on\n", "gROOT->GetListOfCanvases()->Draw()" ] } ], "metadata": { "kernelspec": { "display_name": "ROOT C++", "language": "c++", "name": "root" }, "language_info": { "codemirror_mode": "text/x-c++src", "file_extension": ".C", "mimetype": " text/x-c++src", "name": "c++" } }, "nbformat": 4, "nbformat_minor": 5 }