Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: deprecate OkForAsymmetry and Golden, and provide guidance for their replacement #50

Merged
merged 26 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
273 changes: 126 additions & 147 deletions README.md

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions bin/makeDefectMarkdown.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env ruby

# generates a Markdown table of the defect bits and their descriptions

require 'json'

unless ENV.has_key? 'QADB'
$stderr.puts 'you need to source environment variables'
exit 1
end

def puts_row(columns)
puts "| #{columns.join ' | '} |"
end

puts_row ['Bit', 'Name', 'Description', 'Additional Notes']
puts_row 4.times.map{|i|'---'}

File.open("#{ENV['QADB']}/qadb/defect_definitions.json") do |defect_file|
defect_defs = JSON.load defect_file
defect_defs.each do |defect_def|
defect_cols = ['bit_number', 'bit_name', 'description', 'documentation'].map do |k|
if k == 'bit_name'
"`#{defect_def[k]}`"
else
defect_def[k].to_s
end
end
puts_row defect_cols
end
end
122 changes: 122 additions & 0 deletions qadb/defect_definitions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
[
{
"bit_number": 0,
"bit_name": "TotalOutlier",
"description": "Outlier FD electron N/F, but not `TerminalOutlier` or `MarginalOutlier`",
"documentation": ""
},
{
"bit_number": 1,
"bit_name": "TerminalOutlier",
"description": "Outlier FD electron N/F of first or last QA bin of run",
"documentation": ""
},
{
"bit_number": 2,
"bit_name": "MarginalOutlier",
"description": "Marginal FD electron outlier N/F, within one standard deviation of cut line",
"documentation": ""
},
{
"bit_number": 3,
"bit_name": "SectorLoss",
"description": "FD electron N/F diminished for several consecutive QA bins",
"documentation": "For older datasets (RG-A,B,K,M pass 1), this bit _replaced_ the assignment of `TotalOutlier`, `TerminalOutlier`, and `MarginalOutlier`; newer datasets only add the `SectorLoss` bit and do not remove the outlier bits."
},
{
"bit_number": 4,
"bit_name": "LowLiveTime",
"description": "Live time < 0.9",
"documentation": "This assignment of this bit may be correlated with a low fraction of events with a defined (nonzero) helicity."
},
{
"bit_number": 5,
"bit_name": "Misc",
"description": "Miscellaneous defect, documented as comment",
"documentation": "This bit is often assigned to all QA bins within a run, but in some cases, may only be assigned to the relevant QA bins. The analyzer must decide whether data assigned with the `Misc` bit should be excluded from their analysis; the comment is provided for this purpose. Analyzers are also encouraged to check the Hall B log book for further details."
},
{
"bit_number": 6,
"bit_name": "TotalOutlierFT",
"description": "Outlier FT electron N/F, but not `TerminalOutlierFT` or `MarginalOutlierFT`",
"documentation": "_cf_. `TotalOutlier`."
},
{
"bit_number": 7,
"bit_name": "TerminalOutlierFT",
"description": "Outlier FT electron N/F of first or last QA bin of run",
"documentation": "_cf_. `TerminalOutlier`."
},
{
"bit_number": 8,
"bit_name": "MarginalOutlierFT",
"description": "Marginal FT electron outlier N/F, within one standard deviation of cut line",
"documentation": "_cf_. `MarginalOutlier`."
},
{
"bit_number": 9,
"bit_name": "LossFT",
"description": "FT electron N/F diminished for several consecutive QA bins",
"documentation": "_cf_. `SectorLoss`."
},
{
"bit_number": 10,
"bit_name": "BSAWrong",
"description": "Beam Spin Asymmetry is the wrong sign",
"documentation": "This bit is assigned per run. The asymmetry is significant, but the sign is opposite than expected; analyzers must therefore _flip_ the helicity sign."
},
{
"bit_number": 11,
"bit_name": "BSAUnknown",
"description": "Beam Spin Asymmetry is unknown, likely because of low statistics",
"documentation": "This bit is assigned per run. There are not enough data to determine if the helicity sign is correct for this run."
},
{
"bit_number": 12,
"bit_name": "TSAWrong",
"description": "Target Spin Asymmetry is the wrong sign",
"documentation": "__Not yet used.__"
},
{
"bit_number": 13,
"bit_name": "TSAUnknown",
"description": "Target Spin Asymmetry is unknown, likely because of low statistics",
"documentation": "__Not yet used.__"
},
{
"bit_number": 14,
"bit_name": "DSAWrong",
"description": "Double Spin Asymmetry is the wrong sign",
"documentation": "__Not yet used.__"
},
{
"bit_number": 15,
"bit_name": "DSAUnknown",
"description": "Double Spin Asymmetry is unknown, likely because of low statistics",
"documentation": "__Not yet used.__"
},
{
"bit_number": 16,
"bit_name": "ChargeHigh",
"description": "FC Charge is abnormally high",
"documentation": "NOTE: the assignment criteria of this bit are still under study."
},
{
"bit_number": 17,
"bit_name": "ChargeNegative",
"description": "FC Charge is negative",
"documentation": "The FC charge is calculated from the charge readout at QA bin boundaries. Normally the later charge readout is higher than the earlier; this bit is assigned when the opposite happens."
},
{
"bit_number": 18,
"bit_name": "ChargeUnknown",
"description": "FC Charge is unknown; the first and last time bins _always_ have this defect",
"documentation": "QA bin boundaries are at scaler charge readouts. The first QA bin, before any readout, has no initial charge; the last QA bin, after all scaler readouts, has no final charge. Therefore, the first and last QA bins have an unknown, but likely _very small_ charge accumulation."
},
{
"bit_number": 19,
"bit_name": "PossiblyNoBeam",
"description": "Both N and F are low, indicating the beam was possibly off",
"documentation": "NOTE: the assignment criteria of this bit are still under study."
}
]
3 changes: 1 addition & 2 deletions src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ This directory contains the Groovy source code to access the QA database
provided by `coatjava` (at `$COATJAVA/bin/run-groovy`)
- see example scripts in `examples/` directory
- a standard usage of QA cuts is demonstrated in
`examples/cutAsymmetry.groovy`, where the QA criteria for a spin asymmetry
analysis are applied
[`examples/cutCustom.groovy`](examples/cutCustom.groovy)
- usage notes:
- include the `QADB` class with `import clasqa.QADB`, then instantiate
- the `QADB` class provides several methods for accessing the QA info;
Expand Down
100 changes: 98 additions & 2 deletions src/clasqa/QADB.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package clasqa

import groovy.json.JsonSlurper
import groovy.json.JsonOutput
import groovy.json.JsonParserType
import clasqa.Tools

class QADB {
Expand Down Expand Up @@ -30,7 +31,7 @@ class QADB {
// concatenate trees
qaTree = [:]
chargeTree = [:]
slurper = new JsonSlurper()
slurper = new JsonSlurper().setType(JsonParserType.INDEX_OVERLAY)
def dbDir = new File(dbDirN)
def dbFilter = ~/.*Tree.json$/
def slurpAction = { tree,branch ->
Expand Down Expand Up @@ -106,6 +107,33 @@ class QADB {
chargeTotal = 0
chargeCounted = false
chargeCountedFiles = []
dep_warned_Golden = false
dep_warned_OkForAsymmetry = false
allowMiscBitList = []
}

//...............................
// deprecation warnings
//```````````````````````````````
private void deprecationGuidance() {
System.err.print('''| INSTEAD: use the general methods
| - use `QADB::checkForDefect` to choose which defects you want
| to filter out, then use `QADB::pass` on each event
| - for runs with the `Misc` defect bit (bit 5) assigned:
| - use `QADB::getComment` to check the QADB comment, which
| explains why this bit was assigned for the run
| - use `QADB::allowMiscBit` to ignore the `Misc` bit for certain
| runs that you want to allow in your analysis (`OkForAsymmetry`
| internally does this for a specific list of RG-A runs)
''')
}

private void warningBanner(boolean first) {
if(first) {
System.err.print("\nWARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING\n|\n")
} else {
System.err.print("|\nWARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING\n\n")
}
}


Expand All @@ -114,6 +142,20 @@ class QADB {
//```````````````````````````````
// returns false if the event is in a file with *any* defect
public boolean golden(int runnum_, int evnum_) {
if(!dep_warned_Golden) {
dep_warned_Golden = true
warningBanner(true)
System.err.print('''| WARNING: `QADB::golden` is DEPRECATED
| - you may still use this method, but since many more defect bits have been
| defined, and "Golden" means "no defect bits set", you may find that
| requiring your data to be "Golden" is too strict for your analysis
| - NOTE: QADBs for Run Groups A, B, K, and M for Pass1 data only use
| defect bits 0 to 9, whereas newer QADBs define many more bits
| - in some cases, none of the data are "Golden"
''')
deprecationGuidance()
warningBanner(false)
}
def foundHere = query(runnum_,evnum_)
return foundHere && defect==0
}
Expand All @@ -125,6 +167,23 @@ class QADB {
// if true, this event is good for a spin asymmetry analysis
public boolean OkForAsymmetry(int runnum_, int evnum_) {

if(!dep_warned_OkForAsymmetry) {
dep_warned_OkForAsymmetry = true
warningBanner(true)
System.err.print('''| WARNING: `QADB::OkForAsymmetry` is DEPRECATED
| - you may still use this method, but `OkForAsymmetry` does
| not include NEW defect bits that have been recently defined
''')
deprecationGuidance()
System.err.print('''| EXAMPLE:
| - see '$QADB/src/tests/testOkForAsymmetry.groovy' for a
| preferred, equivalent implementation; from there, you may
| customize your QA criteria and use the new defect bits
''')
warningBanner(false)
}


// perform lookup
def foundHere = query(runnum_,evnum_)
if(!foundHere) return false;
Expand Down Expand Up @@ -170,13 +229,30 @@ class QADB {
if(state) mask |= (0x1 << defectBit)
}
}
public void checkForDefect(String bitStr, boolean state=true) { // alias
setMaskBit(bitStr, state)
}
// access the custom mask, if you want to double-check it
public int getMask() { return mask }
// then call this method to check your custom QA cut for a given
// run number and event number
public boolean pass(int runnum_, int evnum_) {
def foundHere = query(runnum_,evnum_)
return foundHere && !(defect & mask)
if(!foundHere) {
return false
}
def use_mask = mask
if(hasDefectBit(5)) {
if(runnum_ in allowMiscBitList) {
use_mask &= ~(0x1 << 5) // set `use_mask`'s Misc bit to 0
}
}
return foundHere && !(defect & use_mask)
}

// ignore certain runs for the `Misc` bit assignment
public void allowMiscBit(int runnum_) {
allowMiscBitList.add(runnum_)
}


Expand All @@ -186,6 +262,7 @@ class QADB {
// --- access this file's info
public int getRunnum() { return found ? runnum.toInteger() : -1 }
public int getFilenum() { return found ? filenum.toInteger() : -1 }
public int getBinnum() { return getFilenum() }
public String getComment() { return found ? comment : "" }
public int getEvnumMin() { return found ? evnumMin : -1 }
public int getEvnumMax() { return found ? evnumMax : -1 }
Expand Down Expand Up @@ -319,6 +396,18 @@ class QADB {
// result of query
return found
}
// aliases
public boolean queryByBinnum(int runnum_, int binnum_) {
return queryByFilenum(runnum_, binnum_)
}

// check if this bin number exists
public boolean hasBinnum(int runnum_, int binnum_) {
if(qaTree["$runnum_"] != null) {
return qaTree["$runnum_"]["$binnum_"] != null
}
return false
}


// get maximum file number for a given run (useful for QADB validation)
Expand All @@ -330,6 +419,9 @@ class QADB {
}
return maxFilenum
}
public int getMaxBinnum(int runnum_) { // alias
return getMaxFilenum(runnum_)
}



Expand Down Expand Up @@ -387,4 +479,8 @@ class QADB {
private def mask
private def asymMask
private def allowForOkForAsymmetry
private def allowMiscBitList

private boolean dep_warned_Golden
private boolean dep_warned_OkForAsymmetry
}
4 changes: 2 additions & 2 deletions src/examples/chargeSum.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ while(reader.hasEvent()) {

// accumulate charge; note that although the call to
// QADB::accumulateCharge() charge happens for each
// event within a DST file that passed the QA cuts, that
// file's charge will only be accumulated once, so
// event within a QA bin that passed the QA cuts, that
// bin's charge will only be accumulated once, so
// overcounting is not possible
qa.accumulateCharge()

Expand Down
14 changes: 7 additions & 7 deletions src/examples/cutCustom.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ QADB qa = new QADB()

// custom QA cut definition
// - decide which defects you want to check for; an event will not pass
// the QA cut if the associated file has any of the specified defects
// the QA cut if the associated bin has any of the specified defects
// - set to true to check the bit
// - set to false to ignore the bit (by default, all bits are ignored)
qa.setMaskBit('TotalOutlier',false)
qa.setMaskBit('TerminalOutlier',false)
qa.setMaskBit('MarginalOutlier',false)
qa.setMaskBit('SectorLoss',true) // this is the only bit we check here
qa.setMaskBit('LowLiveTime',false)
qa.setMaskBit('Misc',false)
qa.checkForDefect('TotalOutlier',false)
qa.checkForDefect('TerminalOutlier',false)
qa.checkForDefect('MarginalOutlier',false)
qa.checkForDefect('SectorLoss',true) // this is the only bit we check here
qa.checkForDefect('LowLiveTime',false)
qa.checkForDefect('Misc',false)

// print the defect bit mask
println "\ndefect mask = " + qa.util.printBinary(qa.getMask(),16) + "\n"
Expand Down
Loading
Loading