funcnts(1)

okay bestie, sit down, hydrate, because we're about to count some photons ✨ This is funcnts(1), straight from the SAORD Documentation shelf, and honestly? It eats. Every time. πŸ’…

✨ NAME ✨

funcnts β€” count photons in specified regions, with bkgd subtraction


πŸ¦‹ SYNOPSIS πŸ¦‹

funcnts [switches] <source_file> [source_region] [bkgd_file]
[bkgd_regionβŽͺbkgd_value]

πŸ’… OPTIONS πŸ’…

Every single one, no skips, no cap:

  • -e "source_exposure[;bkgd_exposure]" β€” source (bkgd) FITS exposure image using matching files
  • -w "source_exposure[;bkgd_exposure]" β€” source (bkgd) FITS exposure image using WCS transform
  • -t "source_timecorr[;bkgd_timecorr]" β€” source (bkgd) time correction value or header parameter name
  • -g β€” output using nice g format
  • -G β€” output using %.14g format (maximum precision)
  • -i "[column;]int1;int2..." β€” column-based intervals
  • -m β€” match individual source and bkgd regions
  • -p β€” output in pixels, even if wcs is present
  • -r β€” output inner/outer radii (and angles) for annuli (and pandas)
  • -s β€” output summed values
  • -v "scol[;bcol]" β€” src and bkgd value columns for tables
  • -T β€” output in starbase/rdb format
  • -z β€” output regions with zero area

🌸 DESCRIPTION 🌸

funcnts counts photons inside the source regions you give it, and then reports the results per region. Regions are specified using the Spatial Region Filtering mechanism, iykyk. Photons also get counted in whatever bkgd regions you hand over β€” applied to the same data file, or to a whole different data file. (Or, if you're feeling lazy, you can just drop a constant background value in counts/pixel**2 instead. πŸ’…)

The bkgd regions are either paired one-to-one with the source regions, or pooled and normalized by area, and then subtracted from the source counts in each region. What comes out includes:

  • the bkgd-subtracted counts in each region
  • the error on the counts
  • the area in each region
  • the surface brightness (cnts/area**2) calculated for each region

The first argument to the program specifies the FITS input image, array, or raw event file to process. If "stdin" is specified, data are read from the standard input. Use Funtools Bracket Notation to specify FITS extensions, image sections, and filters. πŸŽ€

The optional second argument is the source region descriptor. If no region is specified, the entire field is used.

The background arguments can take one of two forms, depending on whether a separate background file is specified. If the source file is to be used for background as well, the third argument can be either the background region, or a constant value denoting background cnts/pixel. Alternatively, the third argument can be a background data file, in which case the fourth argument is the background region. If no third argument is specified, a constant value of 0 is used (i.e., no background).

In summary, the following command arguments are valid:

[sh] funcnts sfile                        # counts in source file
[sh] funcnts sfile sregion                # counts in source region
[sh] funcnts sfile sregion bregion        # bkgd reg. is from source file
[sh] funcnts sfile sregion bvalue         # bkgd reg. is constant
[sh] funcnts sfile sregion bfile bregion  # bkgd reg. is from separate file
NB: unlike other Funtools programs, source and background regions are specified as separate arguments on the command line, rather than being placed inside brackets as part of the source and background filenames. This is because regions in funcnts are not simply used as data filters, but also are used to calculate areas, exposure, etc. If you put the source region inside the brackets (i.e. use it simply as a filter) rather than specifying it as argument two, the program still will only count photons that pass the region filter. However, the area calculation will be performed on the whole field, since field() is the default source region. This rarely is the desired behavior. On the other hand, with FITS binary tables, it often is useful to put a column filter in the filename brackets, so that only events matching the column filter are counted inside the region.

fr fr, read that NB twice. πŸ™ˆ

For example, to extract the counts within a radius of 22 pixels from the center of the FITS binary table snr.ev and subtract the background determined from the same image within an annulus of radii 50-100 pixels:

[sh] funcnts snr.ev "circle(502,512,22)" "annulus(502,512,50,100)"
# source
#   data file:        snr.ev
#   degrees/pix:      0.00222222
# background
#   data file:        snr.ev
# column units
#   area:             arcsec**2
#   surf_bri:         cnts/arcsec**2
#   surf_err:         cnts/arcsec**2

# background-subtracted results
 reg   net_counts     error   background    berror      area  surf_bri  surf_err
---- ------------ --------- ------------ --------- --------- --------- ---------
   1     3826.403    66.465      555.597     5.972  96831.98     0.040     0.001

# the following source and background components were used:
source region(s)
----------------
circle(502,512,22)

 reg       counts    pixels
---- ------------ ---------
   1     4382.000      1513

background region(s)
--------------------
annulus(502,512,50,100)

 reg       counts    pixels
---- ------------ ---------
all      8656.000     23572

look at that table. immaculate. πŸ’–

The area units for the output columns labeled "area", "surf_bri" (surface brightness) and "surf_err" will be given either in arc-seconds (if appropriate WCS information is in the data file header(s)) or in pixels. If the data file has WCS info, but you do not want arc-second units, use the -p switch to force output in pixels. Also, regions having zero area are not normally included in the primary (background-subtracted) table, but are included in the secondary source and bkgd tables. If you want these regions to be included in the primary table, use the -z switch. 🫢

Note that a simple sed command will extract the background-subtracted results for further analysis:

[sh] cat funcnts.sed
1,/---- .*/d
/^$/,$d

[sh] sed -f funcnts.sed funcnts.out
1     3826.403    66.465      555.597     5.972  96831.98     0.040     0.001

If separate source and background files are specified, funcnts will attempt to normalize the the background area so that the background pixel size is the same as the source pixel size. This normalization can only take place if the appropriate WCS information is contained in both files (e.g. degrees/pixel values in CDELT). If either file does not contain the requisite size information, the normalization is not performed. In this case, it is the user's responsibility to ensure that the pixel sizes are the same for the two files. 😭 (no pressure bestie)

Normally, if more than one background region is specified, funcnts will combine them all into a single region and use this background region to produce the background-subtracted results for each source region. The -m (match multiple backgrounds) switch tells funcnts to make a one to one correspondence between background and source regions, instead of using a single combined background region. For example, the default case is to combine 2 background regions into a single region and then apply that region to each of the source regions:

[sh] funcnts snr.ev "annulus(502,512,0,22,n=2)" "annulus(502,512,50,100,n=2)"
# source
#   data file:        snr.ev
#   degrees/pix:      0.00222222
# background
#   data file:        snr.ev
# column units
#   area:             arcsec**2
#   surf_bri:         cnts/arcsec**2
#   surf_err:         cnts/arcsec**2

# background-subtracted results
 reg   net_counts     error   background    berror      area  surf_bri  surf_err
---- ------------ --------- ------------ --------- --------- --------- ---------
   1     3101.029    56.922      136.971     1.472  23872.00     0.130     0.002
   2      725.375    34.121      418.625     4.500  72959.99     0.010     0.000

# the following source and background components were used:
source region(s)
----------------
annulus(502,512,0,22,n=2)

 reg       counts    pixels
---- ------------ ---------
   1     3238.000       373
   2     1144.000      1140

background region(s)
--------------------
annulus(502,512,50,100,n=2)

 reg       counts    pixels
---- ------------ ---------
all      8656.000     23572

Note that the basic region filter rule "each photon is counted once and no photon is counted more than once" still applies when using The -m to match background regions. That is, if two background regions overlap, the overlapping pixels will be counted in only one of them. In a worst-case scenario, if two background regions are the same region, the first will get all the counts and area and the second will get none. πŸ’€

Using the -m switch causes funcnts to use each of the two background regions independently with each of the two source regions:

[sh] funcnts -m snr.ev "annulus(502,512,0,22,n=2)" "ann(502,512,50,100,n=2)"
# source
#   data file:        snr.ev
#   degrees/pix:      0.00222222
# background
#   data file:        snr.ev
# column units
#   area:             arcsec**2
#   surf_bri:         cnts/arcsec**2
#   surf_err:         cnts/arcsec**2

# background-subtracted results
 reg   net_counts     error   background    berror      area  surf_bri  surf_err
---- ------------ --------- ------------ --------- --------- --------- ---------
   1     3087.015    56.954      150.985     2.395  23872.00     0.129     0.002
   2      755.959    34.295      388.041     5.672  72959.99     0.010     0.000

# the following source and background components were used:
source region(s)
----------------
annulus(502,512,0,22,n=2)

 reg       counts    pixels
---- ------------ ---------
   1     3238.000       373
   2     1144.000      1140

background region(s)
--------------------
ann(502,512,50,100,n=2)

 reg       counts    pixels
---- ------------ ---------
   1     3975.000      9820
   2     4681.000     13752

Note that most floating point quantities are displayed using "f" format. You can change this to "g" format using the -g switch. This can be useful when the counts in each pixel is very small or very large. If you want maximum precision and don't care about the columns lining up nicely, use -G, which outputs all floating values as %.14g. ⭐

When counting photons using the annulus and panda (pie and annuli) shapes, it often is useful to have access to the radii (and panda angles) for each separate region. The -r switch will add radii and angle columns to the output table:

Radii are given in units of pixels or arc-seconds (depending on whether there's WCS info hanging around), and the angle values (when they show up) are in degrees. These columns are literally made for plotting radial profiles. For example, the script funcnts.plot in the funtools distribution will plot a radial profile using gnuplot (version 3.7 or above). A simplified version of this script is shown below:

#!/bin/sh

if [ x"$1" = xgnuplot ]; then
  if [ x`which gnuplot 2>/dev/null` = x ]; then
    echo "ERROR: gnuplot not available"
    exit 1
  fi
  awk '
  BEGIN{HEADER=1; DATA=0; FILES=""; XLABEL="unknown"; YLABEL="unknown"}
  HEADER==1{
    if( $1 == "#" && $2 == "data" && $3 == "file:" ){
      if( FILES != "" ) FILES = FILES ","
      FILES = FILES $4
    }
    else if( $1 == "#" && $2 == "radii:" ){
      XLABEL = $3
    }
    else if( $1 == "#" && $2 == "surf_bri:" ){
      YLABEL = $3
    }
    else if( $1 == "----" ){
      printf "set nokey; set title \"funcnts(%s)\"\n", FILES
      printf "set xlabel \" radius(%s)\"\n", XLABEL
      printf "set ylabel \"surf_bri(%s)\"\n", YLABEL
      print  "plot \"-\" using 3:4:6:7:8 with boxerrorbars"
      HEADER = 0
      DATA = 1
      next
    }
  }
  DATA==1{
    if( NF == 12 ){
      print $9, $10, ($9+$10)/2, $7, $8, $7-$8, $7+$8, $10-$9
    }
    else{
      exit
    }
  }
  ' βŽͺ gnuplot -persist - 1>/dev/null 2>&1

elif [ x"$1" = xds9 ]; then
  awk '
  BEGIN{HEADER=1; DATA=0; XLABEL="unknown"; YLABEL="unknown"}
  HEADER==1{
    if( $1 == "#" && $2 == "data" && $3 == "file:" ){
      if( FILES != "" ) FILES = FILES ","
      FILES = FILES $4
    }
    else if( $1 == "#" && $2 == "radii:" ){
      XLABEL = $3
    }
    else if( $1 == "#" && $2 == "surf_bri:" ){
      YLABEL = $3
    }
    else if( $1 == "----" ){
      printf "funcnts(%s) radius(%s) surf_bri(%s) 3\n", FILES, XLABEL, YLABEL
      HEADER = 0
      DATA = 1
      next
    }
  }
  DATA==1{
    if( NF == 12 ){
      print $9, $7, $8
    }
    else{
      exit
    }
  }
  '
else
  echo "funcnts -r ... βŽͺ funcnts.plot [ds9βŽͺgnuplot]"
  exit 1
fi

So bestie, to run funcnts and then plot the results using gnuplot (version 3.7 or above), you'd do:

funcnts -r snr.ev "annulus(502,512,0,50,n=5)" ...  βŽͺ funcnts.plot gnuplot

Okay so the -s (sum) switch? That one makes funcnts spit out an extra table of summed (integrated) background-subtracted values, right on top of the default table of individual values:

[sh] funcnts -s snr.ev "annulus(502,512,0,50,n=5)" "annulus(502,512,50,100)"
# source
#   data file:        snr.ev
#   degrees/pix:      0.00222222
# background
#   data file:        snr.ev
# column units
#   area:             arcsec**2
#   surf_bri:         cnts/arcsec**2
#   surf_err:         cnts/arcsec**2

# summed background-subtracted results
upto   net_counts     error   background    berror      area  surf_bri  surf_err
---- ------------ --------- ------------ --------- --------- --------- ---------
   1     2880.999    54.722      112.001     1.204  19520.00     0.148     0.003
   2     3776.817    65.254      457.183     4.914  79679.98     0.047     0.001
   3     4025.492    71.972     1031.508    11.087 179775.96     0.022     0.000
   4     4185.149    80.109     1840.851    19.786 320831.94     0.013     0.000
   5     4415.540    90.790     2873.460    30.885 500799.90     0.009     0.000

# background-subtracted results
 reg       counts     error   background    berror      area  surf_bri  surf_err
---- ------------ --------- ------------ --------- --------- --------- ---------
   1     2880.999    54.722      112.001     1.204  19520.00     0.148     0.003
   2      895.818    35.423      345.182     3.710  60159.99     0.015     0.001
   3      248.675    29.345      574.325     6.173 100095.98     0.002     0.000
   4      159.657    32.321      809.343     8.699 141055.97     0.001     0.000
   5      230.390    37.231     1032.610    11.099 179967.96     0.001     0.000

# the following source and background components were used:
source region(s)
----------------
annulus(502,512,0,50,n=5)

 reg       counts    pixels      sumcnts    sumpix
---- ------------ --------- ------------ ---------
   1     2993.000       305     2993.000       305
   2     1241.000       940     4234.000      1245
   3      823.000      1564     5057.000      2809
   4      969.000      2204     6026.000      5013
   5     1263.000      2812     7289.000      7825

background region(s)
--------------------
annulus(502,512,50,100)

 reg       counts    pixels
---- ------------ ---------
    all      8656.000     23572

The -t and -e switches can be used to apply timing and exposure corrections, respectively, to the data. Just a heads up though: these corrections are meant to be used qualitatively, since applying more accurate correction factors is a whole complex, mission-dependent situation. The algorithm for applying these simple corrections goes like this:

C =  Raw Counts in Source Region
Ac=  Area of Source Region
Tc=  Exposure time for Source Data
Ec=  Average exposure in Source Region, from exposure map

B=   Raw Counts in Background Region
Ab=  Area of Background Region
Tb=  (Exposure) time for Background Data
Eb=  Average exposure in Background Region, from exposure map

Then, Net Counts in the Source region is

Net=  C - B * (Ac*Tc*Ec)/(Ab*Tb*Eb)

with the standard propagation of errors for the Error on Net. The net rate would then be

Net Rate = Net/(Ac*Tc*Ec)

The average exposure in each region gets calculated by summing up the pixel values in the exposure map for the given region and then dividing by the number of pixels in that region. Exposure maps often get generated at a block factor > 1 (like, block 4 means each exposure pixel contains 4x4 pixels at full resolution) and funcnts will deal with the blocking automatically. Using the -e switch, you can supply both source and background exposure files (separated by ;), if you have separate source and background data files. If you do not supply a background exposure file to go with a separate background data file, funcnts assumes that exposure already has been applied to the background data file. On top of that, it assumes that the error on the pixels in the background data file is zero.

🚨 NB: The -e switch assumes that the exposure map overlays the image file exactly, except for the block factor. Each pixel in the image is scaled by the block factor to access the corresponding pixel in the exposure map. If your exposure map does not line up exactly with the image, do not use the -e exposure correction. In this case, it still is possible to perform exposure correction if both the image and the exposure map have valid WCS information: use the -w switch so that the transformation from image pixel to exposure pixel uses the WCS information. That is, each pixel in the image region will be transformed first from image coordinates to sky coordinates, then from sky coordinates to exposure coordinates. Please note that using -w can increase the time required to process the exposure correction considerably. 🫢

A time correction can be applied to both source and background data using the -t switch. The value for the correction can either be a numeric constant or the name of a header parameter in the source (or background) file:

[sh] funcnts -t 23.4 ...            # number for source
[sh] funcnts -t "LIVETIME;23.4" ... # param for source, numeric for bkgd

When a time correction is specified, it is applied to the net counts as well (see algorithm above), so that the units of surface brightness become cnts/area**2/sec. πŸ’…


The -i (interval) switch is used to run funcnts on multiple column-based intervals with only a single pass through the data. It is equivalent to running funcnts several times with a different column filter added to the source and background data each time. For each interval, the full funcnts output is generated, with a linefeed character (^L) inserted between each run. In addition, the output for each interval will contain the interval specification in its header. Intervals are very useful for generating X-ray hardness ratios efficiently. Of course, they are only supported when the input data are contained in a table. 😭

Two formats are supported for interval specification. The most general format is semi-colon-delimited list of filters to be used as intervals:

funcnts -i "pha=1:5;pha=6:10;pha=11:15" snr.ev "circle(502,512,22)" ...

Conceptually, this will be equivalent to running funcnts three times:

funcnts snr.ev'[pha=1:5]' "circle(502,512,22)"
funcnts snr.ev'[pha=6:10]' "circle(502,512,22)"
funcnts snr.ev'[pha=11:15]' "circle(502,512,22)"

However, using the -i switch will require only one pass through the data. ✨

Note that complex filters can be used to specify intervals:

funcnts -i "pha=1:5&&pi=4;pha=6:10&&pi=5;pha=11:15&&pi=6" snr.ev ...

so basically funcnts just runs your data through each filter one at a time, in order, and spits out three separate funcnts outputs β€” and they're separated by the line-feed character. that's the whole vibe. πŸŽ€

and honestly? even though the intent here is supporting intervals for hardness ratios, the filters you specify don't actually have to be intervals at all. nor does one "interval" filter have to have anything to do with another one. like, they can be total strangers. look:

funcnts -i "pha=1:5;pi=6:10;energy=11:15" snr.ev "circle(502,512,22)" ...

that right there is literally equivalent to running funcnts three separate times with filter specs that have nothing to do with each other. slay. ✨


πŸ’– a second interval format (yes, there's another one) πŸ’–

there's a second interval format, and it's for the simple case where a single column is used to specify multiple homogeneous intervals for that column. in this format you put the column name first, then the intervals after it:

funcnts -i "pha;1:5;6:10;11:15" snr.ev "circle(502,512,22)" ...

this is equivalent to the first example, bestie β€” it just requires way less typing. funcnts will simply prepend "pha=" before each of the intervals you specified.

heads up: this format does not contain the "=" character in the column argument. no =, that's the whole point. don't add one. πŸ™ˆ

🌸 value columns: the -v situation 🌸

ordinarily, when funcnts runs on a FITS binary table (or a raw event table), one integral count is accumulated for each row (event) inside a given region. that's the default, no cap.

but the -v "scol[;bcol]" (value column) switch changes the game β€” it'll accumulate counts using the value from the column you specified, for the given event. πŸ’…

  • if you specify only a single column, it gets used for both the source and background regions.
  • if you specify two separate columns, separated by a semi-colon, you can set them separately for source and background.
  • the special token $none lets you say "value column for one of them but not the other". so for example, 'pha;$none' will use the pha column for the source but use integral counts for the background β€” while '$none;pha' does the exact converse. πŸ¦‹

and if your value column is of type logical, then the value used will be 1 for T and 0 for F. simple as that.

value columns get used, for example, to integrate probabilities instead of integral counts. iykyk. ✨


πŸŽ€ rdb output + a very important note πŸŽ€

if you use the -T (rdb table) switch, the output will conform to starbase/rdb data base format: tabs get inserted between columns instead of spaces, and line-feed gets inserted between tables. tidy. πŸ‘‘

finally β€” and this one matters β€” note that funcnts is an image program, even though it can be run directly on FITS binary tables. that means image filtering is applied to the rows in order to ensure you get the same results whether you're using a table or the equivalent binned image. πŸ’–

but because of that, the number of counts found using funcnts can differ from the number of events found using row-filter programs such as fundisp or funtable. for more information about these differences, see the discussion of Region Boundaries.

✨ SEE ALSO ✨

see funtools(7) for a list of Funtools help pages πŸ’œ


and that's the whole page, bestie!! 🫢 you read all of it, i'm so proud. this was version 1.4.5, dated April 14, 2011 β€” a certified classic, still iconic, still giving main character energy. go count some photons. πŸ’…βœ¨πŸŒΈ

πŸ“¦ where this page comes from