Browse Source

Initial commit

readme
haavee 10 months ago
commit
0cc706deac
  1. 4308
      ES085A/ES085A.vix
  2. 4322
      ES085A/es085a.vix
  3. 6
      ES085A/es085a_cont.lis
  4. 241
      ES085A/test_ES085A.py
  5. 64
      ES085A/test_description.txt
  6. 0
      README.md
  7. 51
      main

4308
ES085A/ES085A.vix

File diff suppressed because it is too large

4322
ES085A/es085a.vix

File diff suppressed because it is too large

6
ES085A/es085a_cont.lis

@ -0,0 +1,6 @@
ES085A es085a.vix Prod es085a_cont.ms es085a.ms.UVF
+ 24427/ES085A_No0018.cor - No0018 No0018 128 2.0 noX 94 GOOD DONE PROD prod_cont es085a.vix "STABLE-3.5 r2092" -- eo:setup_ref_station=Ef
+ 24427/ES085A_No0019.cor - No0019 No0019 128 2.0 noX 59 GOOD DONE PROD prod_cont es085a.vix "STABLE-3.5 r2092" -- eo:setup_ref_station=Ef
+ 24427/ES085A_No0033.cor - No0033 No0033 128 2.0 noX 96 GOOD DONE PROD prod_cont es085a.vix "STABLE-3.5 r2092" -- eo:setup_ref_station=Ef
+ foo/bar.cor - No0033 No0033 128 2.0 noX 96 GOOD DONE PROD prod_cont es085a.vix "STABLE-3.5 r2092" -- eo:setup_ref_station=Ef

241
ES085A/test_ES085A.py

@ -0,0 +1,241 @@
from __future__ import print_function
import os, sys, shutil, shlex, argparse, tempfile, subprocess, datetime, unittest
# Some functional handywork
method = lambda f : lambda *args, **kwargs: f(*args, **kwargs)
# j2ms2 + tConvert come as a pair.
# We need to be able to specify where to take the binaries from.
# Default: environment "j2ms2" and "tConvert".
# When built using cmake in a build directory (CMake favours out-of-source builds) the binaries are accessible as
# path/to/build_dir/apps/[j2ms2|tConvert]/\\1
# However, when installed, they're accessible as:
# path/to/install_dir/bin/{j2ms2,tConvert}
# from python shutil source code (https://github.com/python/cpython/blob/master/Lib/shutil.py)
def _access_check(fn, mode):
return (os.path.exists(fn) and os.access(fn, mode)
and not os.path.isdir(fn))
def which(cmd):
for p in os.environ.get('PATH', os.defpath).split(':'):
candidate = os.path.join(p, cmd)
if _access_check(candidate, os.F_OK|os.X_OK):
return candidate
return None
# This class captures the binaries from $PATH - if any.
# None if not found
class EnvironmentBinaries(object):
_j2ms2_ = which('j2ms2')
_tConvert_ = which('tConvert')
# path argument baseclass for Argparse;
# will set "__path" to value of the option on the command line
class BaseAction(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs):
if nargs is not None:
raise ValueError("nargs not allowed")
super(BaseAction, self).__init__(option_strings, dest, **kwargs)
def __call__(self, parser, namespace, values, option_string=None):
self.path__ = values
setattr(namespace, self.dest, self)
# Modify base class to extract / return paths for j2ms2/tConvert:
# You provide the functions to generate the path to the binary/ies
# via keywword args: SetBinaries(...., j2ms2 = lambda s: ..., tConvert=lambda s: ...)
def SetBinaries(cls, **kwargs):
class NewClass(cls):
def __init__(self, *a, **k):
super(cls, self).__init__(*a, **k)
self._j2ms2_fn_, self. _tConvert_fn_ = (kwargs.get('j2ms2'), kwargs.get('tConvert'))
def get_j2ms2(self):
return self._j2ms2_fn_(self)
def get_tConvert(self):
return self._tConvert_fn_(self)
j2ms2 = property(get_j2ms2)
tConvert = property(get_tConvert)
return NewClass
def Raise(err):
raise RuntimeError(err)
# Create classes that can be given to Argparse as action and default
# into immediately useful objects
DefaultBinaries = SetBinaries(EnvironmentBinaries,
j2ms2=lambda _: EnvironmentBinaries._j2ms2_ or Raise("No j2ms2 found in your $PATH"),
tConvert=lambda _: EnvironmentBinaries._tConvert_ or Raise("No tConvert found in your $PATH"))
InstallDirAction = SetBinaries(BaseAction, j2ms2=lambda s: os.path.join(s.path__, "bin/j2ms2"),
tConvert=lambda s: os.path.join(s.path__, "bin/tConvert"))
BuildDirAction = SetBinaries(BaseAction, j2ms2=lambda s: os.path.join(s.path__, "apps/j2ms2/j2ms2"),
tConvert=lambda s: os.path.join(s.path__, "apps/tConvert/tConvert"))
# Following https://stackoverflow.com/a/17259773
# Strip out options specifically meant for us and replace sys.argv[1:] with what remains
parsert = argparse.ArgumentParser(add_help=True)
mutexgr = parsert.add_mutually_exclusive_group()
mutexgr.add_argument('--cmake-install-dir', help="test j2ms2/tConvert binaries from install_dir/bin",
dest='binaries', action=InstallDirAction, default=DefaultBinaries())
mutexgr.add_argument('--cmake-build-dir', help="test j2ms2/tConvert binaries from build_dir/app/{j2ms2,tConvert}/",
dest='binaries', action=BuildDirAction, default=DefaultBinaries())
options, args = parsert.parse_known_args()
sys.argv[1:] = args
## Yes, technically tests should be able to run in any order
## but we're testing a toolchain here where e.g. step N+1
## uses a product generated in step N
unittest.TestLoader.sortTestMethodsUsing = None
## Shorthands
RUN = subprocess.Popen
SHLEX = lambda arg : shlex.split(arg) if isinstance(arg, type("")) else arg
# Some preformatted messages
artefacts_msg = \
"""********************************************************************************
* Automatic cleanup inhibited; artefacts left for post-mortem in *
* {0:^70} *
********************************************************************************
""".format
class TTC(unittest.TestCase):
# unittest calls setUp()/tearDown() around _each testfunction_
# so if we want to persist some stuff between calls we better go through classvariables ...
__curdir__ = None
__rootdir__ = None
__tempdir__ = None
__workdir__ = None
__cleanup__ = True
__lasttst__ = False
def setUp(self):
if TTC.__curdir__:
return
print("Preparing test environment ...", end='')
# we must
# (1) know where we are
TTC.__curdir__ = os.getcwd()
# (2) where this specific file is (to find the raw and gold data files)
TTC.__rootdir__ = os.path.abspath( os.path.dirname(__file__) )
# (3) create a temporary directory where to create new data files to compare
TTC.__tempdir__ = tempfile.mkdtemp()
#sys.stdout.write( "\n=======> Created tmp dir: {0}\n".format(self.tempDir) )
# j2ms2 expects the directory containing data to have the same name as the vexfile
TTC.__workdir__ = os.path.join( TTC.__tempdir__, 'ES085A' )
TTC.__cleanup__ = True
# And finally, we change directory to the newly created temp dir
# and populate it with the files as necessary
os.mkdir( TTC.__workdir__ )
os.chdir( TTC.__workdir__ )
# symlink SFXC job 24427 into the work dir
os.symlink( os.path.join(TTC.__rootdir__, '24427'), './24427' )
# Copy two VEX files and the lis file across
for f in ['ES085A.vix', 'es085a.vix', 'es085a_cont.lis']:
shutil.copyfile( os.path.join(TTC.__rootdir__, f), os.path.join(TTC.__workdir__, f) )
print( " done" )
def AtestFoo(self):
self.assertEqual(1+1, 2)
# Attempt to run j2ms2 in lisfile mode:
# j2ms2 -v <lisfile>
def testA(self):
rv = None
cmd = "{0} -v es085a_cont.lis".format( options.binaries.j2ms2 )
print("Start", cmd)
with open( os.path.join(TTC.__workdir__, 'j2ms2-lisfile.log'), 'w' ) as lf:
lf.write( datetime.datetime.now().isoformat() + '\n' )
lf.write( cmd + '\n' )
lf.flush()
process = RUN(SHLEX(cmd), stdout=lf, stderr=lf)
process.wait()
rv = process.returncode
lf.write( "\nj2ms2 exited with return code {0}".format(rv) )
TTC.__cleanup__ = TTC.__cleanup__ and (rv==0)
# Demand that j2ms2 ran w/o error
self.assertEqual(rv, 0)
# Attempt to run j2ms2 in "manual" mode, i.e. specifiying cor files & output MS file on the command line
# j2ms2 -o <ms> 24427/*.cor
__glob__ = method(lambda _: " ".join( glob.glob(os.path.join(TTC.__workdir__, "24427/*.cor")) ))
def testB(self):
rv = None
#cmd = "{0} -o es085a.ms 24427/*.cor".format( options.binaries.j2ms2 )
#" ".join(glob.glob(os.path.joinTTC.__workdir__)) )
cmd = "{0} -o es085a.ms {1}".format( options.binaries.j2ms2, TTC.__glob__())
print("Start", cmd)
with open( os.path.join(TTC.__workdir__, 'j2ms2-manual.log'), 'w' ) as lf:
lf.write( datetime.datetime.now().isoformat() + '\n' )
lf.write( cmd + '\n' )
lf.flush()
process = RUN(SHLEX(cmd), stdout=lf, stderr=lf)
process.wait()
rv = process.returncode
lf.write( "\nj2ms2 exited with return code {0}".format(rv) )
TTC.__cleanup__ = TTC.__cleanup__ and (rv==0)
# Demand that j2ms2 ran w/o error
self.assertEqual(rv, 0)
# Run tConvert on the lis-file MS
def testC(self):
rv = None
cmd = "{0} es085a_cont.ms ES085A_CONT.IDI".format( options.binaries.tConvert )
print("Start", cmd)
with open( os.path.join(TTC.__workdir__, 'tConvert-lisfile.log'), 'w' ) as lf:
lf.write( datetime.datetime.now().isoformat() + '\n' )
lf.write( cmd + '\n' )
lf.flush()
process = RUN(SHLEX(cmd), stdout=lf, stderr=lf)
process.wait()
rv = process.returncode
lf.write( "\ntConvert exited with return code {0}".format(rv) )
TTC.__cleanup__ = TTC.__cleanup__ and (rv==0)
# Demand that tConvert ran w/o error
self.assertEqual(rv, 0)
# Run tConvert on the manual MS
def testD(self):
rv = None
cmd = "{0} es085a.ms ES085A.IDI".format( options.binaries.tConvert )
print("Start", cmd)
with open( os.path.join(TTC.__workdir__, 'tConvert-manual.log'), 'w' ) as lf:
lf.write( datetime.datetime.now().isoformat() + '\n' )
lf.write( cmd + '\n' )
lf.flush()
process = RUN(SHLEX(cmd), stdout=lf, stderr=lf)
process.wait()
rv = process.returncode
lf.write( "\ntConvert exited with return code {0}".format(rv) )
TTC.__cleanup__ = TTC.__cleanup__ and (rv==0)
# Demand that tConvert ran w/o error
self.assertEqual(rv, 0)
# Leave this one as last test case to trigger potential cleanup
# in the associated tearDown() call
def testZ(self):
TTC.__lasttst__ = True
self.assertEqual(1+1, 2)
#### tearDown()
def tearDown(self):
if not TTC.__lasttst__:
return
# go back to where we started from (mainly pointless but it might
# affect being able to rm the tree if an active process is still
# using (part of) the tree as current dir
os.chdir( TTC.__curdir__ )
# if cleanup not inhibited ... do clean up
if TTC.__cleanup__:
shutil.rmtree( TTC.__tempdir__ )
else:
print( artefacts_msg(TTC.__workdir__) )
if __name__ == '__main__':
unittest.main()

64
ES085A/test_description.txt

@ -0,0 +1,64 @@
Test numerical reproduceability of j2ms2 and tConvert
-----------------------------------------------------
This test contains three raw datasets:
- raw SFXC output files from job 24427 (24427/*.cor), from experiment ES085A.
This data was chosen since it has a strong maser line, easily observable off center in subband 3.
- known-good MeasurementSet produced from that: gold/es085a_cont.ms
- known-good FITS-IDI file produced from gold/es085a_cont.ms: gold/ES085A_CONT.IDI
And two support files:
- VEX file(s) for the experiment ES085A.vix and es085a.vix, who are identical/one is symlink of the other
(The j2ms2 tool expects a VEX file name of the name of the directory it is run in under certain circumstances).
- The "lis" file, used to control the input to j2ms2 to exactly (re)produce a given MeasurementSet
Then, starting from raw correlator output, VEX file and "lis" file four data sets are created:
- j2ms2 is run with lisfile; lisfile incorporates all cor files from the job, producing MeasurementSet "es085a_cont.ms"
- j2ms2 is run "manually" by specifying "-o es085a.ms job/*.cor" on the command line; producing MeasurementSet "es085a.ms"
- both MeasurementSets are converted to FITS-IDI using `tConvert`:
es085a_cont.ms => ES085A_CONT.IDI
es085a.ms => ES085A.IDI
The following tests are performed
=================================
Verify that "all data" from the correlator files is written to the MeasurementSets and FITS-IDI files
-----------------------------------------------------------------------------------------------------
The jive-toolchain-verify tool "compare-ms-idi.py" is used to compute a six-way diff between the two "gold" datasets and the four newly created ones. The tool collects integrated weight and integration time per baseline per source, but does not look at the data content itself.
Any differences reported are due to the toolchain-under-test skipping (or duplicating) data in either of the data formats; the diff output will tell what happened in which format(s).
Verify that all meta data (antenna, source, frequency setup properties) are correctly propagated
-------------------------------------------------------------------------------------------------
The jive-toolchain-verify tool "compare-ms-idi-meta.py" tool is used to compute a six-way diff between the meta data from the two "gold" datasets and the four data files generated by the toolchain under test.
Any differences reported should be due to the new toolchain writing different meta data to (one of) the other formats.
Verification of numerical equality of the data written to the MeasurementSet(s)
-------------------------------------------------------------------------------
A batch script in combination of a postprocessing module for the jiveplot tool is run.
The jiveplot tool is used to:
- select three time ranges (scan 18 19 33 of ES085A)
- compute amplitude+phase (y-axes) as function of frequency (x-axis)
- those are integrated (summed) for each baseline/subband/polarization/source/scan
These values are extracted from two MeasurementSets: gold and toolchain-under-test produced MeasurementSet.
A special-purpose written postprocessing module for jiveplot compares the datasets in two successive plot actions.
It keeps the last plot data set and if a new dataset is plotted, it computes the difference between the x- and y-values found in all data sets and accumulates those differences separately for x and y.
If any of the accumulated differences exceeds the tolerance (currently 1e-7) the postprocessing tool prints a warning.
############################################################
# Todo #
############################################################
Extract the same data from FITS-IDI and compare for numerical equivalence

0
README.md

51
main

@ -0,0 +1,51 @@
#!/bin/sh
# This script should be executed from the directory that contains the script
if [ ! -f .run_main_from_here ]; then
echo "Please change working directory to where this script is located and run it from there."
exit 1
fi
# Loop over all defined tests and execute them
python="python"
live=
skip=
testopts=
for av; do
# check if we're supposed to skip the next arg
if [ -n "${skip}" -a "${skip}" != ".T." ]; then
eval "${skip}=${av}"
skip=
elif [ -z "${skip}" ]; then
case ${av} in
# hellup
-h)
bn=`basename $0`
echo "${bn} [-h] [-n] [--python=[path/to/]python]"
echo " -h show this message, exit succesfully"
echo " -n show which tests would be run in which order but do not run 'm"
echo " --python=.. provide path to different python version (default: ${python})"
exit 0
;;
# Strip out options for _this_ script
--python)
skip="python"
;;
--python=*)
python=`echo "${av}" | sed 's/^--python=//'`
;;
# Fake?
-n)
live="echo"
;;
# anything else passed on verbatim to test
*)
testopts="${testopts} ${av}"
;;
esac
fi
done
# OK that's the command line parsed
for f in `find . -type f -a -name test*.py`; do
${live} exec ${python} ${f} ${testopts}
done
Loading…
Cancel
Save