Skip to content
This repository has been archived by the owner on Jan 31, 2022. It is now read-only.

Feature docs migration: Round II: Three macros and fitting.fitScanData #123

Merged
merged 14 commits into from
Jul 4, 2018
Merged
36 changes: 30 additions & 6 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
# serve to show the default.

import sys, os
import datetime
import string

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
Expand All @@ -21,12 +23,28 @@

# -- General configuration -----------------------------------------------------

# List all authors here
authors = [
'Cameron Bravo',
'Mykhailo Dalchenko',
'Brian Dorney',
'Louis Moureaux',
'Jared Sturdy',
]

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinxcontrib.napoleon', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
extensions = [
'sphinx.ext.autodoc',
'sphinxcontrib.napoleon',
'sphinx.ext.intersphinx',
'sphinx.ext.coverage',
'sphinx.ext.pngmath',
'sphinx.ext.todo',
'sphinx.ext.viewcode']

# Disable numpy docstrings for Napoleon, because they eat headers such as
# "Examples"
Expand All @@ -46,7 +64,7 @@

# General information about the project.
project = u'gempython.gemplotting'
copyright = u'2018, Cameron Bravo, Mykhailo Dalchenko, Brian Dorney, Louis Moureaux, Jared Sturdy'
copyright = '%d %s' % (datetime.date.today().year, string.join(authors, ', '))

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
Expand Down Expand Up @@ -189,7 +207,7 @@
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'gemplotting.tex', u'gemplotting Documentation',
u'Cameron Bravo, Mykhailo Dalchenko, Brian Dorney, Louis Moureaux, Jared Sturdy', 'manual'),
string.join(authors, ', '), 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
Expand Down Expand Up @@ -219,9 +237,15 @@
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'gemplotting', u'gemplotting Documentation',
[u'Cameron Bravo, Mykhailo Dalchenko, Brian Dorney, Louis Moureaux, Jared Sturdy'], 1),
authors, 1),
('man/gemPlotter', 'gemPlotter.py', u'Plot time evolution of scan results',
[u'Cameron Bravo, Mykhailo Dalchenko, Brian Dorney, Louis Moureaux, Jared Sturdy'], 1),
authors, 1),
('man/gemTreeDrawWrapper', 'gemTreeDrawWrapper.py', u'Make X-Y plots for each scan date',
authors, 1),
('man/packageFiles4Docker', 'packageFiles4Docker.py', u'Creates a tarball containing data',
authors, 1),
('man/clusterAnaScurve', 'clusterAnaScurve.py', u'Analyze S-curves using the LSF cluster',
authors, 1),
]

# If true, show URL addresses after external links.
Expand All @@ -235,7 +259,7 @@
# dir menu entry, description, category)
texinfo_documents = [
('index', 'gemplotting', u'gemplotting Documentation',
u'Mykhailo Dalchenko, Evaldas Juska, RobertKing, Andrew Peck, Jared Sturdy', 'gemplotting', 'One line description of project.',
string.join(authors, ', '), 'gemplotting', 'GEM commissioning plotting tools.',
'Miscellaneous'),
]

Expand Down
4 changes: 4 additions & 0 deletions doc/fitting.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
``gempython.gemplotting.fitting`` --- Fitting tools
===================================================

This package contains only one module,
:py:mod:`gempython.gemplotting.fitting.fitScanData`, that provides functions to
fit S-curve data.

.. automodule:: gempython.gemplotting.fitting.fitScanData
:members:
:undoc-members:
Expand Down
18 changes: 5 additions & 13 deletions doc/macros.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,22 @@ Macros
:undoc-members:
:show-inheritance:

.. automodule:: clusterAnaScurve
:members:
:undoc-members:
:show-inheritance:

.. toctree::
:maxdepth: 1

man/clusterAnaScurve
man/gemPlotter

.. automodule:: gemSCurveAnaToolkit
:members:
:undoc-members:
:show-inheritance:

.. automodule:: gemTreeDrawWrapper
:members:
:undoc-members:
:show-inheritance:
.. toctree::
:maxdepth: 1

.. automodule:: packageFiles4Docker
:members:
:undoc-members:
:show-inheritance:
man/gemTreeDrawWrapper
man/packageFiles4Docker

.. automodule:: plotSCurveFitResults
:members:
Expand Down
4 changes: 4 additions & 0 deletions doc/man/clusterAnaScurve.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.. automodule:: clusterAnaScurve
:members:
:undoc-members:
:show-inheritance:
4 changes: 4 additions & 0 deletions doc/man/gemTreeDrawWrapper.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.. automodule:: gemTreeDrawWrapper
:members:
:undoc-members:
:show-inheritance:
4 changes: 4 additions & 0 deletions doc/man/packageFiles4Docker.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.. automodule:: packageFiles4Docker
:members:
:undoc-members:
:show-inheritance:
169 changes: 140 additions & 29 deletions fitting/fitScanData.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,123 @@
"""
``fitScanData`` --- S-curves fits
---------------------------------
"""

import numpy as np
import ROOT as r
from gempython.gemplotting.utils.anaInfo import dict_calSF

class DeadChannelFinder(object):
r"""
Finds channels that returned no data during an S-curve scan ("dead"
channels).

This class has only one function, :py:meth:`feed`, that takes an entry from
the S-curve tree and updates the results.

Example:
Typical usage:

.. code-block:: python

finder = DeadChannelFinder()
for event in file.scurveTree:
finder.feed(event)
pass
# Use the results...

Attributes:
isDead (numpy.ndarray): 2D array of ``bool``, indexed as
``[vfat][channel]``. Each entry is ``True`` if the channel is
dead, ``False`` otherwise.
"""
def __init__(self):
self.isDead = [ np.ones(128, dtype=bool) for i in range(24) ]

def feed(self, event):
"""
Takes an entry from the S-curve tree and updates the results
accordingly.
"""
self.isDead[event.vfatN][event.vfatCH] = False

class ScanDataFitter(DeadChannelFinder):
def __init__(self, calDAC2Q_m=None, calDAC2Q_b=None, isVFAT3=False):
"""
calDAC2Q_m - list of slope values for "fC = m * cal_dac + b" equation, ordered by vfat position
calDAC2Q_b - as calDAC2Q_m but for intercept b
isVFAT3 - if using VFAT3
"""
r"""
Fits S-curves.

This class is used in two steps:

#. The data to fit is passed to the object using :py:meth:`feedHisto`,
:py:meth:`readFile` or repeated calls to :py:meth:`feed`.
#. The fit is performed by calling :py:meth:`fit`.

One cannot count on all attributes being present before calling
:py:meth:`fit`.

.. note::

If the :py:attr:`calDAC2Q_m` and :py:attr:`calDAC2Q_b` were supplied at
construction time, values will be in charge units instead of DAC units.

See :program:`anaUltraScurve.py` for example usage.

Attributes:
Nev (ndict): 2D array of ``int``, indexed as ``[vfat][channel]``.

scanFuncs (ndict): 2D array of ``TF1``, indexed as ``[vfat][channel]``.
After fitting, each entry contains the fit function for the
corresponding channel. The functions are color-coded:

======= =====================================
Color Meaning
======= =====================================
Black Default
Blue Fit Converged
Gray Dead channel or empty input histogram
Orange Fit result is empty
======= =====================================

scanHistos (ndict): 2D array of ``TH1``, indexed as
``[vfat][channel]``, that contain the S-curve results (number of
events vs charge)

scanCount (ndict): 2D array of ``int``, indexed as ``[vfat][channel]``.
Each entry contains the total number of events for the corresponding
channel.

scanFitResults (ndict): 3D array of ``float``, indexed as
``[idx][vfat][channel]``, that contain the fit results. ``idx`` has
the following meaning:

0. S-curve mean in either DAC units or charge (threshold of
comparator)
1. S-curve width in either DAC units or charge (noise seen by
comparator)
2. S-curve pedestal in hits (e.g. at 0 VCAL this is the
noise)
3. :math:`\chi^2` of the fit
4. Same as ``self.scanCount[vfat][channel]``
5. Number of degrees of freedom (NDF) of the fit

calDAC2Q_m (numpy.ndarray): Calibration of ``calDAC`` to charge for each
VFAT. This corresponds to :math:`m` in

.. math::

Q = m * \mathtt{calDAC} + b

If no value was given at construction time, this is an arrays of
ones.

calDAC2Q_b (numpy.ndarray): Calibration of ``calDAC`` to charge for each
VFAT. This corresponds to :math:`b` in

.. math::

Q = m * \mathtt{calDAC} + b

If no value was given at construction time, this is an arrays of
zeros.

isVFAT3 (bool): Whether the detector under consideration uses VFAT3
"""

def __init__(self, calDAC2Q_m=None, calDAC2Q_b=None, isVFAT3=False):
super(ScanDataFitter, self).__init__()

from gempython.utils.nesteddict import nesteddict as ndict
Expand Down Expand Up @@ -74,6 +170,7 @@ def __init__(self, calDAC2Q_m=None, calDAC2Q_b=None, isVFAT3=False):
return

def feed(self, event):
# Docs inherited from parent class
super(ScanDataFitter, self).feed(event)

charge = self.calDAC2Q_m[event.vfatN]*event.vcal+self.calDAC2Q_b[event.vfatN]
Expand Down Expand Up @@ -103,6 +200,16 @@ def feed(self, event):
return

def feedHisto(self, vfatN, vfatCH, histo, nEvts=None):
"""
Feed the fitter with data stored in an histogram.

Args:
vfatN (int): The VFAT under consideration
vfatCH (int): The channel under consideration
histo (int): The data for the channel under consideration
nEvts (int): Override :py:attr`Nev` for the channel under
consideration (else the maximum value in the histogram is used)
"""
self.scanHistos[vfatN][vfatCH] = histo
self.isDead[vfatN][vfatCH] = False
if nEvts is None:
Expand All @@ -115,25 +222,10 @@ def feedHisto(self, vfatN, vfatCH, histo, nEvts=None):

def fit(self, debug=False):
"""
Iteratively fits all scurves
Note: if the user supplied calDAC2Q_m and calDAC2Q_b at
construction then output container will have relevant
parameters in charge units instead of DAC units
Iteratively fits all scurves, and populates the relevant class
attributes.

Returns self.scanFitResults

[0][vfat][ch] = scurve mean in either DAC units or charge (threshold of comparator)
[1][vfat][ch] = scurve width in either DAC units or charge (noise seen by comparator)
[2][vfat][ch] = scurve pedestal in hits (e.g. at 0 VCAL this is the noise)
[3][vfat][ch] = fitChi2
[4][vfat][ch] = self.scanCount[vfat][ch]
[5][vfat][ch] = fitNDF

The TF1 is color coded:
Black - Default
Blue - Fit Converged
Gray - Dead channel or empty input histogram
Orange - Fit result is empty
Returns: The filled :py:attr:`scanFitResults`
"""

r.gROOT.SetBatch(True)
Expand Down Expand Up @@ -288,15 +380,34 @@ def fit(self, debug=False):
return self.scanFitResults

def getFunc(self, vfat, ch):
"""Returns the fit function for the given VFAT and channel"""
return self.scanFuncs[vfat][ch]

def readFile(self, treeFileName):
"""
Reads data from an ``scurveData.root`` file produced by
``ultraScurve.py``.
"""
inF = r.TFile(treeFileName)
for event in inF.scurveTree :
self.feed(event)
return

def fitScanData(treeFileName, isVFAT3=False, calFileName=None):
"""
Helper function to fit scan data. Creates a :py:class:`ScanDataFitter`,
loads the data and returns the results of :py:meth:`ScanDataFitter.fit`.

Args:
treeFileName (string): Path to the ``TFile`` that contains the scan data
isVFAT3 (bool): Whether the detector uses VFAT3
calFileName (string): Path to the file that contains calibration data

.. seealso::

:py:func:`gempython.gemplotting.utils.anautilities.parseCalFile` for the
format of the calibration file.
"""
from gempython.gemplotting.utils.anautilities import parseCalFile

# Get the fitter
Expand Down
Loading