コード例 #1
0
ファイル: test_utils.py プロジェクト: AGILESCIENCE/Agilepy
class AgilepyUtilsUT(unittest.TestCase):
    def setUp(self):
        self.currentDirPath = Path(__file__).parent.absolute()
        self.agilepyconfPath = os.path.join(self.currentDirPath,
                                            "conf/agilepyconf.yaml")

        self.config = AgilepyConfig()
        self.config.loadBaseConfigurations(self.agilepyconfPath)

        self.agilepyLogger = AgilepyLogger()

        self.agilepyLogger.initialize(
            self.config.getConf("output", "outdir"),
            self.config.getConf("output", "logfilenameprefix"),
            self.config.getConf("output", "verboselvl"))

        self.datadir = os.path.join(self.currentDirPath, "data")

        self.outDir = Path(self.config.getOptionValue("outdir"))

        if self.outDir.exists() and self.outDir.is_dir():
            self.agilepyLogger.reset()
            shutil.rmtree(self.outDir)

        self.tmpDir = Path("./tmp")
        self.tmpDir.mkdir(exist_ok=True)

    def tearDown(self):
        self.agilepyLogger.reset()
        if self.tmpDir.exists() and self.tmpDir.is_dir():
            shutil.rmtree(self.tmpDir)

    def test_display_sky_map(self):

        pu = PlottingUtils(self.config, self.agilepyLogger)

        smooth = 4
        fileFormat = ".png"
        title = "testcase"
        cmap = "CMRmap"
        regFiles = [
            Utils._expandEnvVar("$AGILE/catalogs/2AGL_2.reg"),
            Utils._expandEnvVar("$AGILE/catalogs/2AGL_2.reg")
        ]
        regFileColors = ["yellow", "blue"]


        file = pu.displaySkyMap(
                    self.datadir+"/testcase_EMIN00100_EMAX00300_01.cts.gz", \
                    smooth = smooth,
                    fileFormat = fileFormat,
                    title = title,
                    cmap = cmap,
                    regFiles = regFiles,
                    regFileColors=regFileColors,
                    catalogRegions = "2AGL",
                    catalogRegionsColor = "red",
                    saveImage=True,
                    normType="linear")

        assert True == os.path.isfile(file)

    def test_display_sky_map_single_mode_3_imgs(self):

        pu = PlottingUtils(self.config, self.agilepyLogger)

        smooth = 4
        fileFormat = ".png"
        title = "testcase"
        cmap = "CMRmap"
        regFiles = [
            Utils._expandEnvVar("$AGILE/catalogs/2AGL_2.reg"),
            Utils._expandEnvVar("$AGILE/catalogs/2AGL_2.reg")
        ]
        regFileColors = ["yellow", "blue"]
        img = self.datadir + "/testcase_EMIN00100_EMAX00300_01.cts.gz"

        file = pu.displaySkyMapsSingleMode(
                    [img, img, img], \
                    smooth = smooth,
                    fileFormat = fileFormat,
                    titles = [title+"_1", title+"_2", title+"_3"],
                    cmap = cmap,
                    regFiles = regFiles,
                    regFileColors=regFileColors,
                    catalogRegions = "2AGL",
                    catalogRegionsColor = "red",
                    saveImage=True,
                    normType="linear")

        assert True == os.path.isfile(file)

    def test_display_sky_map_single_mode_2_imgs(self):

        pu = PlottingUtils(self.config, self.agilepyLogger)

        smooth = 4
        fileFormat = ".png"
        title = "testcase"
        cmap = "CMRmap"
        regFiles = [
            Utils._expandEnvVar("$AGILE/catalogs/2AGL_2.reg"),
            Utils._expandEnvVar("$AGILE/catalogs/2AGL_2.reg")
        ]
        regFileColors = ["yellow", "blue"]
        img = self.datadir + "/testcase_EMIN00100_EMAX00300_01.cts.gz"

        file = pu.displaySkyMapsSingleMode(
                    [img, img], \
                    smooth = smooth,
                    fileFormat = fileFormat,
                    titles = [title+"_1", title+"_2", title+"_3"],
                    cmap = cmap,
                    regFiles = regFiles,
                    regFileColors=regFileColors,
                    catalogRegions = "2AGL",
                    catalogRegionsColor = "red",
                    saveImage=True,
                    normType="linear")

        assert True == os.path.isfile(file)

    def test_plot_data_availability(self):

        outputDir = self.currentDirPath.joinpath(
            "output", "test_plot_data_availability")
        outputDir.mkdir(parents=True, exist_ok=True)
        os.environ["TEST_LOGS_DIR"] = str(outputDir)

        config = AgilepyConfig()
        config.loadBaseConfigurations(
            self.currentDirPath.joinpath("conf",
                                         "test_plot_data_availability.yaml"))

        pu = PlottingUtils(config, self.agilepyLogger)

        dataDir = Path(self.datadir).joinpath("test_plot_data_availability")

        pu.plotDataAvailability(dataDir.joinpath("EVT.qfile"),
                                dataDir.joinpath("EVT.index"),
                                saveImage=True)

        #pu.plotDataAvailability(dataDir.joinpath("LOG.qfile"), dataDir.joinpath("LOG.index"), saveImage=True)

    def test_initialize_logger_verboselvl_2(self):
        sleep(1.0)
        self.agilepyLogger.reset()

        self.config.loadBaseConfigurations(
            os.path.join(self.currentDirPath,
                         "conf/agilepyconf_verbose_2.yaml"))

        logfilePath = self.agilepyLogger.initialize(
            self.config.getOptionValue("outdir"),
            self.config.getOptionValue("logfilenameprefix"),
            self.config.getOptionValue("verboselvl"))

        assert True == logfilePath.is_file()

        with open(logfilePath, "r") as f:
            linesNumber = len(f.readlines())
            assert 1 == linesNumber

        self.agilepyLogger.debug(self, "%s %s", "Debug", "message")
        self.agilepyLogger.info(self, "%s %s", "Info", "message")
        self.agilepyLogger.warning(self, "%s %s", "Warning", "message")
        self.agilepyLogger.critical(self, "%s %s", "Critical", "message")

        with open(logfilePath, "r") as f:
            linesNumber = len(f.readlines())
            assert 5 == linesNumber

    def test_initialize_logger_verboselvl_1(self):
        sleep(1.0)
        self.agilepyLogger.reset()

        self.config.loadBaseConfigurations(
            os.path.join(self.currentDirPath,
                         "conf/agilepyconf_verbose_1.yaml"))

        logfilePath = self.agilepyLogger.initialize(
            self.config.getOptionValue("outdir"),
            self.config.getOptionValue("logfilenameprefix"),
            self.config.getOptionValue("verboselvl"))

        assert True == logfilePath.is_file()

        with open(logfilePath, "r") as f:
            linesNumber = len(f.readlines())
            assert 1 == linesNumber

        self.agilepyLogger.debug(self, "%s %s", "Debug", "message")
        self.agilepyLogger.info(self, "%s %s", "Info", "message")
        self.agilepyLogger.warning(self, "%s %s", "Warning", "message")
        self.agilepyLogger.critical(self, "%s %s", "Critical", "message")

        with open(logfilePath, "r") as f:
            linesNumber = len(f.readlines())
            assert 5 == linesNumber

    def test_initialize_logger_verboselvl_0(self):
        sleep(1.0)
        self.agilepyLogger.reset()

        self.config.loadBaseConfigurations(
            os.path.join(self.currentDirPath,
                         "conf/agilepyconf_verbose_0.yaml"))

        logfilePath = self.agilepyLogger.initialize(
            self.config.getOptionValue("outdir"),
            self.config.getOptionValue("logfilenameprefix"),
            self.config.getOptionValue("verboselvl"))

        assert True == logfilePath.is_file()

        with open(logfilePath, "r") as f:
            linesNumber = len(f.readlines())
            assert 1 == linesNumber

        self.agilepyLogger.debug(self, "%s %s", "Debug", "message")
        self.agilepyLogger.info(self, "%s %s", "Info", "message")
        self.agilepyLogger.warning(self, "%s %s", "Warning", "message")
        self.agilepyLogger.critical(self, "%s %s", "Critical", "message")

        with open(logfilePath, "r") as f:
            linesNumber = len(f.readlines())
            assert 5 == linesNumber

    def test_filterAP(self):

        print(self.datadir + "/E1q1_604800s_emin100_emax10000_r2.ap")
        print(self.currentDirPath)
        product = AstroUtils.AP_filter(
            self.datadir + "/E1q1_604800s_emin100_emax10000_r2.ap", 1,
            174142800, 447490800, self.currentDirPath)
        with open(product, "r") as f:
            linesNumber = len(f.readlines())
            assert 4 == linesNumber

        os.remove(os.path.join(self.currentDirPath, "result.txt"))
        os.remove(os.path.join(self.currentDirPath, product))

    """
    Time conversions
        # https://tools.ssdc.asi.it/conversionTools
        # https://heasarc.gsfc.nasa.gov/cgi-bin/Tools/xTime/xTime.pl?time_in_i=&time_in_c=&time_in_d=&time_in_j=&time_in_m=58871.45616898&time_in_sf=&time_in_wf=&time_in_sl=&time_in_sni=&time_in_snu=&time_in_s=&time_in_h=&time_in_sz=&time_in_ss=&time_in_sn=&timesys_in=u&timesys_out=u&apply_clock_offset=yes
    """

    def test_astro_utils_time_mjd_to_agile_seconds(self):
        sec_tolerance = 0.001
        tt = AstroUtils.time_mjd_to_agile_seconds(
            58871.45616898)  # 506861812.99987227
        assert abs(506861813 - tt) <= sec_tolerance

    def test_astro_utils_time_agile_seconds_to_mjd(self):
        sec_tolerance = 0.0000001
        mjd = AstroUtils.time_agile_seconds_to_mjd(507391426.9447)
        assert abs(58877.58595999 - mjd) <= sec_tolerance

    def test_astro_utils_time_utc_to_jd(self):

        tol = 0.00000001

        dt = "2020-01-23T10:56:53.000"

        jd = AstroUtils.time_fits_to_jd(dt)

        assert abs(2458871.95616898 - jd) <= tol

    def test_astro_utils_time_utc_to_mjd(self):

        tol = 0.00000001

        dt = "2020-01-23T10:56:53.000"

        mjd = AstroUtils.time_fits_to_mjd(dt)

        assert abs(58871.45616898 - mjd) <= tol

    def test_astro_utils_time_utc_to_tt(self):

        tol = 0.0001

        agileseconds = AstroUtils.time_fits_to_agile_seconds(
            "2020-01-23T10:56:53.000")

        assert abs(506861813 - agileseconds) <= tol

    def test_astro_utils_time_agile_seconds_to_jd(self):
        jd = AstroUtils.time_agile_seconds_to_jd(449582332)
        assert jd == pytest.approx(2458208.99921296, 0.00001)

    def test_astro_utils_time_agile_seconds_to_utc(self):

        sec_tol = 1
        """
        utc = AstroUtils.time_tt_to_utc(506861813)
        dt = datetime.strptime(utc, '%Y-%m-%dT%H:%M:%S.%f')

        assert dt.year == 2020
        assert dt.month == 1
        assert dt.day == 23
        assert dt.hour == 10
        assert dt.minute == 56
        assert abs(53 - dt.second) <= sec_tol
        """

        # This date would result in "0 days"
        sec_tolerance = 0.0000001
        fitstime = AstroUtils.time_agile_seconds_to_fits(449582332)
        dt = datetime.strptime(fitstime, '%Y-%m-%dT%H:%M:%S.%f')

        assert dt.year == 2018
        assert dt.month == 3
        assert dt.day == 31
        assert dt.hour == 11
        assert dt.minute == 58
        assert abs(52 - dt.second) <= sec_tol

    def test_astro_utils_time_mjd_to_fits(self):

        sec_tol = 1

        fitstime = AstroUtils.time_mjd_to_fits(58871.45616898)

        dt = datetime.strptime(fitstime, '%Y-%m-%dT%H:%M:%S.%f')

        assert dt.year == 2020
        assert dt.month == 1
        assert dt.day == 23
        assert dt.hour == 10
        assert dt.minute == 56
        assert abs(53 - dt.second) <= sec_tol

    def test_astro_utils_time_fits_to_mjd_2(self):

        sec_tol = 0.00000001

        mjd = AstroUtils.time_fits_to_mjd("2020-01-23T10:56:53.000")

        assert abs(58871.45616898 - mjd) <= sec_tol

    def test_get_first_and_last_line_in_file(self):

        line1 = '/ASDC_PROC2/FM3.119_2/EVT/agql2004151750_2004151850.EVT__FM.gz 514057762.000000 514061362.000000 EVT\n'
        line2 = '/ASDC_PROC2/FM3.119_2/EVT/agql2004152249_2004160008.EVT__FM.gz 514075704.000000 514080437.000000 EVT\n'
        line3 = '/ASDC_PROC2/FM3.119_2/EVT/agql2004160008_2004160045.EVT__FM.gz 514080437.000000 514082644.000000 EVT\n'

        # I test: 1 line
        test_file = self.tmpDir.joinpath("test_file1.txt")
        with open(test_file, "w") as f:
            f.write(line1)
        (first, last) = Utils._getFirstAndLastLineInFile(test_file)
        assert first == line1
        assert last == line1

        # II test: 2 lines
        test_file = self.tmpDir.joinpath("test_file2.txt")
        with open(test_file, "w") as f:
            f.write(line1)
            f.write(line2)
        (first, last) = Utils._getFirstAndLastLineInFile(test_file)
        assert first == line1
        assert last == line2

        # III test: 3 lines
        test_file = self.tmpDir.joinpath("test_file3.txt")
        with open(test_file, "w") as f:
            f.write(line1)
            f.write(line2)
            f.write(line3)
        (first, last) = Utils._getFirstAndLastLineInFile(test_file)
        assert first == line1
        assert last == line3

    """
コード例 #2
0
class AGBaseAnalysis:
    def __init__(self, configurationFilePath):

        self.config = AgilepyConfig()

        # load only "input" and "output" sections
        self.config.loadBaseConfigurations(
            Utils._expandEnvVar(configurationFilePath))

        # Creating output directory

        self.outdir = self.config.getConf("output", "outdir")

        Path(self.outdir).mkdir(parents=True, exist_ok=True)

        self.logger = AgilepyLogger()

        self.logger.initialize(
            self.outdir, self.config.getConf("output", "logfilenameprefix"),
            self.config.getConf("output", "verboselvl"))

        self.plottingUtils = PlottingUtils(self.config, self.logger)

        if "AGILE" not in os.environ:
            raise AGILENotFoundError("$AGILE is not set.")

        if "PFILES" not in os.environ:
            raise PFILESNotFoundError("$PFILES is not set.")

    def getAnalysisDir(self):
        """It returns the path of the output directory

        Returns:
            path (str) : the path of the output directory
        """

        if self.outdir.exists() and self.outdir.is_dir():
            return str(self.outdir)

        else:
            self.logger.warning(self, "OutputDirectory not found")

    def deleteAnalysisDir(self):
        """It deletes the output directory where all the products of the analysis are written.

        Args:

        Returns:
            True if the directory is succesfully deleted, False otherwise.

        """
        outDir = Path(self.config.getConf("output", "outdir"))

        if outDir.exists() and outDir.is_dir():
            self.logger.info(self, "Removing directory %s", str(outDir))
            self.logger.reset()
            rmtree(outDir)
        else:
            self.logger.warning(self,
                                "Output directory %s exists? %r is dir? %r",
                                str(outDir), outDir.exists(), outDir.is_dir())
            return False

        return True

    def setOptions(self, **kwargs):
        """It updates configuration options specifying one or more key=value pairs at once.

        Args:
            kwargs: key-values pairs, separated by a comma.

        Returns:
            None

        Raises:
            ConfigFileOptionTypeError: if the type of the option value is not wrong.
            ConfigurationsNotValidError: if the values are not coherent with the configuration.
            CannotSetHiddenOptionError: if the option is hidden.
            OptionNotFoundInConfigFileError: if the option is not found.

        Note:
            The ``config`` attribute is initialized by reading the corresponding
            yaml configuration file, loading its contents in memory. Updating the values
            held by this object will not affect the original values written on disk.

        Example:

            >>> aganalysis.setOptions(mapsize=60, binsize=0.5)
            True

        """
        return self.config.setOptions(**kwargs)

    def getOption(self, optionName):
        """It reads an option value from the configuration.

        Args:
            optionName (str): the name of the option.

        Returns:
            The option value

        Raises:
            OptionNotFoundInConfigFileError: if the optionName is not found in the configuration.
        """

        return self.config.getOptionValue(optionName)

    def printOptions(self, section=None):
        """It prints the configuration options in the console.

        Args:
            section (str): you can specify a configuration file section to be printed out.

        Returns:
            None
        """
        return self.config.printOptions(section)
コード例 #3
0
class SourcesLibraryUT(unittest.TestCase):
    def setUp(self):
        self.currentDirPath = Path(__file__).parent.absolute()
        self.agilepyconfPath = os.path.join(self.currentDirPath,
                                            "conf/agilepyconf.yaml")
        self.xmlsourcesconfPath = os.path.join(self.currentDirPath,
                                               "conf/sourceconf.xml")
        self.agsourcesconfPath = os.path.join(self.currentDirPath,
                                              "conf/sourceconf.txt")

        self.config = AgilepyConfig()
        self.config.loadConfigurations(self.agilepyconfPath, validate=True)

        self.logger = AgilepyLogger()
        self.logger.initialize(
            self.config.getConf("output", "outdir"),
            self.config.getConf("output", "logfilenameprefix"),
            self.config.getConf("output", "verboselvl"))

        outDir = Path(
            os.path.join(os.environ["AGILE"],
                         "agilepy-test-data/unittesting-output/api"))

        if outDir.exists() and outDir.is_dir():
            shutil.rmtree(outDir)

        self.sl = SourcesLibrary(self.config, self.logger)

    @staticmethod
    def get_free_params(source):

        return {
            "curvature": source.spectrum.curvature.free,
            "pivot_energy": source.spectrum.pivotEnergy.free,
            "index": source.spectrum.index.free,
            "pos": source.spatialModel.pos.free,
            "flux": source.spectrum.flux.free
        }

    def test_load_file_with_wrong_extension(self):

        xmlsourcesconfPath = os.path.join(self.currentDirPath,
                                          "conf/sourceconf.wrongext")

        self.assertRaises(SourceModelFormatNotSupported,
                          self.sl.loadSourcesFromFile, xmlsourcesconfPath)

    def test_load_wrong_file(self):

        xmlsourcesconfPath = os.path.join(self.currentDirPath,
                                          "conf/idontexitst.txt")

        self.assertRaises(FileNotFoundError, self.sl.loadSourcesFromFile,
                          xmlsourcesconfPath)

    def test_load_from_catalog(self):

        added = self.sl.loadSourcesFromCatalog("2AGL")
        self.assertEqual(175, len(added))
        self.assertEqual(175, len(self.sl.sources))

        self.assertRaises(FileNotFoundError, self.sl.loadSourcesFromCatalog,
                          "paperino")

    def test_load_catalog_from_catalog_filtering_on_distances(self):

        added = self.sl.loadSourcesFromCatalog("2AGL", rangeDist=(70, 80))

        self.assertEqual(15, len(added))
        self.assertEqual(15, len(self.sl.sources))

        self.sl.sources = []
        added = self.sl.loadSourcesFromCatalog("2AGL", rangeDist=(0, 10))
        self.assertEqual(9, len(added))
        self.assertEqual(9, len(self.sl.sources))

        self.sl.sources = []
        added = self.sl.loadSourcesFromCatalog("2AGL", rangeDist=(0, 20))
        self.assertEqual(14, len(added))
        self.assertEqual(14, len(self.sl.sources))

    def test_load_sources_from_xml_file(self):

        added = self.sl.loadSourcesFromFile(self.xmlsourcesconfPath)

        self.assertEqual(2, len(added))
        self.assertEqual(2, len(self.sl.sources))

        sources = self.sl.selectSources('name == "2AGLJ2021+4029"')
        self.assertEqual(1, len(sources))
        source = sources.pop()
        self.assertEqual(119.3e-08, source.spectrum.get("flux"))
        self.assertEqual(1.75, source.spectrum.get("index"))
        self.assertEqual(78.2375, source.spatialModel.get("pos")[0])
        self.assertEqual(True, source.spatialModel.get("dist") > 0)

        sources = self.sl.selectSources('name == "2AGLJ2021+3654"')
        self.assertEqual(1, len(sources))
        source = sources.pop()
        self.assertEqual(70.89e-08, source.spectrum.get("flux"))
        self.assertEqual(1.38, source.spectrum.get("index"))
        self.assertEqual(75.2562, source.spatialModel.get("pos")[0])
        self.assertEqual(True, source.spatialModel.get("dist") > 0)

    def test_load_sources_from_txt_file(self):
        agsourcesconfPath = os.path.join(self.currentDirPath,
                                         "conf/sourceconf_for_load_test.txt")

        added = self.sl.loadSourcesFromFile(agsourcesconfPath)

        self.assertEqual(10, len(added))
        self.assertEqual(10, len(self.sl.sources))

        sources = self.sl.selectSources('name == "2AGLJ1801-2334"')
        self.assertEqual(1, len(sources))
        source = sources.pop()

        self.assertEqual(3.579e-07, source.spectrum.get("flux"))
        self.assertEqual(3.37991, source.spectrum.get("index"))
        self.assertEqual(6.16978, source.spatialModel.get("pos")[0])

        # testing fixflags
        f0 = {
            "curvature": 0,
            "pivot_energy": 0,
            "index": 0,
            "pos": 0,
            "flux": 0
        }  # special case
        f1 = {
            "curvature": 0,
            "pivot_energy": 0,
            "index": 0,
            "pos": 0,
            "flux": 1
        }
        f2 = {
            "curvature": 0,
            "pivot_energy": 0,
            "index": 0,
            "pos": 1,
            "flux": 0
        }
        f3 = {
            "curvature": 0,
            "pivot_energy": 0,
            "index": 0,
            "pos": 1,
            "flux": 1
        }
        f4 = {
            "curvature": 0,
            "pivot_energy": 0,
            "index": 1,
            "pos": 0,
            "flux": 0
        }
        f5 = {
            "curvature": 0,
            "pivot_energy": 0,
            "index": 1,
            "pos": 0,
            "flux": 1
        }

        f7 = {
            "curvature": 0,
            "pivot_energy": 0,
            "index": 1,
            "pos": 1,
            "flux": 1
        }

        f28 = {
            "curvature": 1,
            "pivot_energy": 1,
            "index": 1,
            "pos": 0,
            "flux": 0
        }
        f30 = {
            "curvature": 1,
            "pivot_energy": 1,
            "index": 1,
            "pos": 1,
            "flux": 0
        }
        f32 = {
            "curvature": 0,
            "pivot_energy": 0,
            "index": 0,
            "pos": 2,
            "flux": 0
        }  # special case

        fs = [f0, f1, f2, f3, f4, f5, f7, f28, f30, f32]

        for i in range(len(fs)):
            ff = i
            if ff == 6: ff = 7
            elif ff == 7: ff = 28
            elif ff == 8: ff = 30
            elif ff == 9: ff = 32

            self.assertDictEqual(
                fs[i], SourcesLibraryUT.get_free_params(self.sl.sources[i]))

    def test_source_file_parsing(self):

        sourceFile = os.path.join(self.currentDirPath,
                                  "data/testcase_2AGLJ2021+4029.source")

        res = self.sl.parseSourceFile(sourceFile)

        self.assertEqual(True, bool(res))

        self.assertEqual(True, isinstance(res.multiFlux.value, float))
        self.assertEqual(0, res.multiFlux.value)
        self.assertEqual(True, isinstance(res.multiSqrtTS.value, float))
        self.assertEqual(0, res.multiSqrtTS.value)
        self.assertEqual(None, res.multiDist.value)

        sourceFile = os.path.join(self.currentDirPath,
                                  "data/testcase_2AGLJ2021+3654.source")

        res = self.sl.parseSourceFile(sourceFile)
        self.assertEqual(True, bool(res))
        self.assertEqual(True, isinstance(res.multiFlux.value, float))
        self.assertEqual(6.69108e-15, res.multiFlux.value)
        self.assertEqual(None, res.multiDist.value)

    def load_source_from_catalog_without_scaling(self):

        sources = self.sl.loadSourcesFromCAT2()

        self.assertEqual(175, len(sources))

        self.assertEqual(7.45398e-08, sources[0].getFlux())

    def load_source_from_catalog_with_scaling(self):

        self.config.setOptions(emin=10, emax=1000)

        sources = self.sl.loadSourcesFromCAT2()

        self.assertEqual(175, len(sources))

        self.assertEqual(6.940938928095228e-07, sources[0].getFlux())

    def test_select_sources_with_selection_string(self):

        self.sl.loadSourcesFromFile(self.xmlsourcesconfPath)
        self.assertEqual(2, len(self.sl.sources))

        sources = self.sl.selectSources(
            'name == "2AGLJ2021+3654" AND dist > 0 AND flux > 0')
        self.assertEqual(1, len(sources))

        sourceFile = os.path.join(self.currentDirPath,
                                  "data/testcase_2AGLJ2021+3654.source")

        source = self.sl.parseSourceFile(sourceFile)

        self.sl.updateMulti(source)

        sources = self.sl.selectSources(
            'name == "2AGLJ2021+3654" AND dist > 0 AND flux > 0')
        self.assertEqual(1, len(sources))
        """
        MAP sqrtTS con multiSqrtTS
        """
        sources = self.sl.selectSources('sqrtTS == 10')
        self.assertEqual(1, len(sources))

    def test_select_sources_with_selection_lambda(self):

        self.sl.loadSourcesFromFile(self.xmlsourcesconfPath)

        sources = self.sl.selectSources(lambda name: name == "2AGLJ2021+3654")
        self.assertEqual(1, len(sources))

        sourceFile = os.path.join(self.currentDirPath,
                                  "data/testcase_2AGLJ2021+3654.source")

        source = self.sl.parseSourceFile(sourceFile)
        self.sl.updateMulti(source)

        sources = self.sl.selectSources(
            lambda name, dist, flux: name == "2AGLJ2021+3654" and dist > 0 and
            flux > 0)
        self.assertEqual(1, len(sources))

    def test_free_sources_with_selection_string(self):

        self.sl.loadSourcesFromFile(self.xmlsourcesconfPath)
        sourceFile = os.path.join(self.currentDirPath,
                                  "data/testcase_2AGLJ2021+3654.source")
        source = self.sl.parseSourceFile(sourceFile)
        self.sl.updateMulti(source)

        sources = self.sl.freeSources(
            'name == "2AGLJ2021+3654" AND dist > 0 AND flux > 0', "flux",
            False)

        self.assertEqual(1, len(sources))
        self.assertEqual(0, sources[0].spectrum.getFree("flux"))
        self.assertEqual("0", sources[0].spectrum.getFree("flux",
                                                          strRepr=True))

        sources = self.sl.freeSources(
            'name == "2AGLJ2021+3654" AND dist > 0 AND flux > 0', "flux", True)
        self.assertEqual(1, sources[0].spectrum.getFree("flux"))
        self.assertEqual("1", sources[0].spectrum.getFree("flux",
                                                          strRepr=True))

        sources = self.sl.freeSources(
            'name == "2AGLJ2021+3654" AND dist > 0 AND flux > 0', "index",
            True)
        self.assertEqual(1, sources[0].spectrum.getFree("index"))
        self.assertEqual("1", sources[0].spectrum.getFree("index",
                                                          strRepr=True))

        sources = self.sl.freeSources(
            'name == "2AGLJ2021+3654" AND dist > 0 AND flux > 0', "index",
            False)
        self.assertEqual(0, sources[0].spectrum.getFree("index"))
        self.assertEqual("0", sources[0].spectrum.getFree("index",
                                                          strRepr=True))

    def test_free_sources_with_selection_lambda(self):

        self.sl.loadSourcesFromFile(self.xmlsourcesconfPath)
        sourceFile = os.path.join(self.currentDirPath,
                                  "data/testcase_2AGLJ2021+3654.source")
        source = self.sl.parseSourceFile(sourceFile)
        self.sl.updateMulti(source)

        sources = self.sl.freeSources(
            lambda name, dist, flux: name == "2AGLJ2021+3654" and dist > 0 and
            flux > 0, "flux", False)
        self.assertEqual(1, len(sources))
        self.assertEqual(0, sources[0].spectrum.getFree("flux"))

        sources = self.sl.freeSources(
            lambda name, dist, flux: name == "2AGLJ2021+3654" and dist > 0 and
            flux > 0, "flux", True)
        self.assertEqual(1, sources[0].spectrum.getFree("flux"))

        sources = self.sl.freeSources(
            lambda name, dist, flux: name == "2AGLJ2021+3654" and dist > 0 and
            flux > 0, "index", True)
        self.assertEqual(1, sources[0].spectrum.getFree("index"))

        sources = self.sl.freeSources(
            lambda name, dist, flux: name == "2AGLJ2021+3654" and dist > 0 and
            flux > 0, "index", False)
        self.assertEqual(0, sources[0].spectrum.getFree("index"))

    def test_write_to_file_xml(self):

        self.config = AgilepyConfig()

        self.config.loadConfigurations(self.agilepyconfPath, validate=True)

        self.sl.loadSourcesFromFile(self.xmlsourcesconfPath)

        outfileName = "write_to_file_testcase"

        outputFile = Path(self.sl.writeToFile(outfileName, fileformat="xml"))

        self.assertEqual(True, outputFile.exists())

        sourcesxml = parse(outputFile).getroot()

        self.assertEqual(2, len(sourcesxml))

    def test_write_to_file_txt(self):

        self.config = AgilepyConfig()

        self.config.loadConfigurations(self.agilepyconfPath, validate=True)

        sourcesFile = os.path.join(
            self.currentDirPath, "conf/sourcesconf_for_write_to_file_txt.txt")

        self.sl.loadSourcesFromFile(sourcesFile)

        outfileName = "write_to_file_testcase"

        outputFile = Path(self.sl.writeToFile(outfileName, fileformat="txt"))

        self.assertEqual(True, outputFile.exists())

        with open(outputFile) as of:
            lines = of.readlines()

        self.assertEqual(
            "1.57017e-07 80.3286 1.12047 2.16619 0 2 _2AGLJ2032+4135 0 0 0 0 0.5 5.0 20 10000 0 100",
            lines[0].strip())
        self.assertEqual(
            "1.69737e-07 79.9247 0.661449 1.99734 0 2 CYGX3 0 0 0 0 0.5 5.0 20 10000 0 100",
            lines[1].strip())
        self.assertEqual(
            "1.19303e-06 78.2375 2.12298 1.75823 3 2 _2AGLJ2021+4029 0 1 3307.63 0 0.5 5.0 20.0 10000.0  0 100",
            lines[2].strip())

    def test_add_source(self):

        self.config = AgilepyConfig()

        self.config.loadConfigurations(self.agilepyconfPath, validate=True)

        self.sl.loadSourcesFromFile(self.xmlsourcesconfPath)

        newSourceDict = {"a": 10}
        self.assertRaises(SourceParamNotFoundError, self.sl.addSource,
                          "newsource", newSourceDict)

        newSourceDict = {
            "glon": 250,
            "glat": 30,
            "spectrumType": "LogPaperone"
        }
        self.assertRaises(SpectrumTypeNotFoundError, self.sl.addSource,
                          "newsource", newSourceDict)

        newSourceDict = {
            "glon": 250,
            "glat": 30,
            "spectrumType": "LogParabola"
        }
        self.assertRaises(SourceParamNotFoundError, self.sl.addSource, "",
                          newSourceDict)
        self.assertRaises(SourceParamNotFoundError, self.sl.addSource, None,
                          newSourceDict)

        newSourceObj = self.sl.addSource("newsource", newSourceDict)
        self.assertEqual(True, isinstance(newSourceObj, Source))

        newSource = self.sl.selectSources('name == "newsource"').pop()

        self.assertEqual(0, newSource.spectrum.get("flux"))
        self.assertEqual(0, newSource.spectrum.get("curvature"))
        self.assertEqual("newsource", newSource.name)
        self.assertEqual(148.52505082279242,
                         newSource.spatialModel.get("dist"))

        newSourceDict = {
            "glon": 250,
            "glat": 30,
            "spectrumType": "LogParabola",
            "flux": 1,
            "curvature": 2
        }

        newSourceObj = self.sl.addSource("newsource2", newSourceDict)
        self.assertEqual(True, isinstance(newSourceObj, Source))

        newSource = self.sl.selectSources('name == "newsource2"').pop()
        self.assertEqual(1, newSource.spectrum.get("flux"))
        self.assertEqual(2, newSource.spectrum.get("curvature"))
        self.assertEqual(250, newSource.spatialModel.get("pos")[0])
        self.assertEqual(30, newSource.spatialModel.get("pos")[1])
        self.assertEqual("newsource2", newSource.name)

        self.assertEqual(None, self.sl.addSource("newsource2", newSourceDict))

    def test_convert_catalog_to_xml(self):

        catalogFile = "$AGILE/catalogs/2AGL.multi"

        outfile = self.sl.convertCatalogToXml(catalogFile)

        sourcesxml = parse(outfile).getroot()

        self.assertEqual(175, len(sourcesxml))

        added = self.sl.loadSourcesFromFile(outfile)

        self.assertEqual(175, len(added))

        self.assertEqual(175, len(self.sl.sources))

    def test_backup_restore(self):
        self.config = AgilepyConfig()

        self.config.loadConfigurations(self.agilepyconfPath, validate=True)

        self.sl.loadSourcesFromFile(self.xmlsourcesconfPath)
        """
        for s in self.sl.getSources():
            print(s)
        """

        self.assertEqual(2, len(self.sl.sources))

        self.sl.backupSL()

        self.sl.deleteSources('name=="2AGLJ2021+4029"')

        self.assertEqual(1, len(self.sl.sources))

        self.sl.deleteSources('name=="2AGLJ2021+3654"')

        self.assertEqual(0, len(self.sl.sources))

        self.sl.restoreSL()

        self.assertEqual(2, len(self.sl.sources))
コード例 #4
0
ファイル: AGEng.py プロジェクト: antonioaddis/Agilepy
class AGEng:
    """This class contains the high-level API methods you can use to run engineering analysis.

    This class requires you to setup a ``yaml configuration file`` to specify the software's behaviour.

    Class attributes:

    Attributes:
        config (:obj:`AgilepyConfig`): it is used to read/update configuration values.
        logger (:obj:`AgilepyLogger`): it is used to log messages with different importance levels.
    """
    def __init__(self, configurationFilePath):
        """AGEng constructor.

        Args:
            configurationFilePath (str): the relative or absolute path to the yaml configuration file.

        Example:
            >>> from agilepy.api import AGEng
            >>> ageng = AGEng('agconfig.yaml')

        """

        self.config = AgilepyConfig()

        self.config.loadConfigurations(configurationFilePath, validate=True)

        self.outdir = join(self.config.getConf("output", "outdir"), "eng_data")

        Path(self.outdir).mkdir(parents=True, exist_ok=True)

        self.logger = AgilepyLogger()

        self.logger.initialize(
            self.outdir, self.config.getConf("output", "logfilenameprefix"),
            self.config.getConf("output", "verboselvl"))

        self.plottingUtils = PlottingUtils(self.config, self.logger)

    def visibilityPlot(self,
                       tmin,
                       tmax,
                       src_x,
                       src_y,
                       ref,
                       zmax=60,
                       step=1,
                       writeFiles=True,
                       computeHistogram=True,
                       logfilesIndex=None,
                       saveImage=True,
                       fileFormat="png",
                       title="Visibility Plot"):
        """ It computes the angular separations between the center of the
        AGILE GRID field of view and the coordinates for a given position in the sky,
        given by src_ra and src_dec.

        Args:
            tmin (float): inferior observation time limit to analize.
            tmax (float): superior observation time limit to analize.
            src_x (float): source position x (unit: degrees)
            src_y (float): source position y (unit: degrees)
            zmax (float): maximum zenith distance of the source to the center of the detector (unit: degrees)
            step (integer): time interval in seconds between 2 consecutive points in the resulting plot. Minimum accepted value: 0.1 s.
            writeFiles (bool): if True, two text files with the separions data will be written on file.
            logfilesIndex (str) (optional): the index file for the logs files. If specified it will ovverride the one in the configuration file.
            saveImage (bool): If True, the image will be saved on disk
            fileFormat (str): The output format of the image
            title (str): The plot title

        Returns:
            separations (List): the angular separations
            ti_tt (List):
            tf_tt (List):
            ti_mjd (List):
            tf_mjd (List):
            skyCordsFK5.ra.deg
            skyCordsFK5.dec.deg
        """
        separations, ti_tt, tf_tt, ti_mjd, tf_mjd, src_ra, src_dec, sepFile = self._computePointingDistancesFromSource(
            tmin, tmax, src_x, src_y, ref, zmax, step, writeFiles,
            logfilesIndex)

        vis_plot = self.plottingUtils.visibilityPlot(separations, ti_tt, tf_tt,
                                                     ti_mjd, tf_mjd, src_ra,
                                                     src_dec, zmax, step,
                                                     saveImage, self.outdir,
                                                     fileFormat, title)
        hist_plot = None

        if computeHistogram:
            hist_plot = self.plottingUtils.visibilityHisto(
                separations, ti_tt, tf_tt, src_ra, src_dec, zmax, step,
                saveImage, self.outdir, fileFormat, title)

        return vis_plot, hist_plot

    def _computePointingDistancesFromSource(self, tmin, tmax, src_x, src_y,
                                            ref, zmax, step, writeFiles,
                                            logfilesIndex):
        """ It computes the angular separations between the center of the
        AGILE GRID field of view and the coordinates for a given position in the sky,
        given by src_ra and src_dec.

        Args:
            tmin (float): inferior observation time limit to analize.
            tmax (float): superior observation time limit to analize.
            src_x (float): source position x (unit: degrees)
            src_y (float): source position y (unit: degrees)
            zmax (float): maximum zenith distance of the source to the center of the detector (unit: degrees)
            step (integer): time interval in seconds between 2 consecutive points in the resulting plot. Minimum accepted value: 0.1 s.
            writeFiles (bool): if True, two text files with the separions data will be written on file.
            logfilesIndex (str) (optional): the index file for the logs files. If specified it will ovverride the one in the configuration file.


        Returns:
            separations (List): the angular separations
            ti_tt (List):
            tf_tt (List):
            ti_mjd (List):
            tf_mjd (List):
            skyCordsFK5.ra.deg
            skyCordsFK5.dec.deg
        """
        self.logger.info(
            self,
            "Computing pointing distances from source (%f, %f) %s in [%f, %f]",
            src_x, src_y, ref, tmin, tmax)

        if not logfilesIndex:
            logfilesIndex = self.config.getConf("input", "logfile")

        if ref == "equ":
            skyCordsFK5 = SkyCoord(ra=src_x * u.degree,
                                   dec=src_y * u.degree,
                                   frame='fk5')

        elif ref == "gal":
            skyCordsGAL = SkyCoord(l=src_x * u.degree,
                                   b=src_y * u.degree,
                                   frame='galactic')
            #skyCordsICRS = skyCordsGAL.transform_to('icrs')
            skyCordsFK5 = skyCordsGAL.transform_to('fk5')

        else:
            self.logger.critical(self,
                                 "Reference system '%s' is not supported", ref)
            raise WrongCoordinateSystemError(
                "Reference system '%s' is not supported" % (ref))

        if step < 0.1:
            self.logger.critical(self, "step %f cannot be < 0.1", step)
            raise ValueError("'step' %f cannot be < 0.1" % (step))

        self.logger.debug(self, "Galactict coords: l:%f b:%f",
                          skyCordsGAL.l.deg, skyCordsGAL.b.deg)

        self.logger.debug(self, "FK5 coords: ra:%f dec:%f ",
                          skyCordsFK5.ra.deg, skyCordsFK5.dec.deg)

        logFiles = self._getLogsFileInInterval(logfilesIndex, tmin, tmax)

        self.logger.info(self, "%d log files satisfy the interval %f-%f",
                         len(logFiles), tmin, tmax)

        if not logFiles:
            self.logger.warning(
                self,
                "No log files can are compatible with tmin %f and tmax %f",
                tmin, tmax)
            return [], [], [], [], [], skyCordsFK5.ra.deg, skyCordsFK5.dec.deg

        total = len(logFiles)
        tmin_start = tmin
        tmax_start = tmax

        separation_tot = None
        ti_tt_tot = None
        tf_tt_tot = None
        init = False

        self.logger.info(self, "Computing pointing distances. Please wait..")

        for idx, logFile in enumerate(logFiles):

            idx = idx + 1

            self.logger.info(self, "%d/%d %s", idx, total, logFile)

            if idx == 1 or idx == total:
                doTimeMask = True
            else:
                doTimeMask = False

            separation, ti_tt, tf_tt = self._computeSeparationPerFile(
                doTimeMask, logFile, tmin_start, tmax_start, skyCordsFK5, zmax,
                step)

            if not init:
                separation_tot = separation
                ti_tt_tot = ti_tt
                tf_tt_tot = tf_tt
                init = True
            else:
                separation_tot = np.concatenate((separation_tot, separation),
                                                axis=0)
                ti_tt_tot = np.concatenate((ti_tt_tot, ti_tt), axis=0)
                tf_tt_tot = np.concatenate((tf_tt_tot, tf_tt), axis=0)

            self.logger.debug(self, "Total computed separations: %d",
                              len(separation_tot))

        # Conversion TT => MJD
        self.logger.info(
            self, "Converting ti_tt_tot from TT to MJD..Number of elements=%d",
            len(ti_tt_tot))
        ti_mjd = AstroUtils.time_nparray_mjd_to_tt(ti_tt_tot)

        self.logger.info(
            self, "Converting tf_tt_tot from TT to MJD..Number of elements=%d",
            len(tf_tt_tot))
        tf_mjd = AstroUtils.time_nparray_mjd_to_tt(tf_tt_tot)
        """
        self.logger.info(self, "Computig meantimes..Number of elements=%d", len(ti_mjd))
        meantimes = (ti_mjd+tf_mjd)/2.

        if writeFiles:
            zmax = zmax*u.deg
            ttotal_under_zmax = np.sum(tf_tt_tot[separation_tot<zmax]-ti_tt_tot[separation_tot<zmax])
            ttotal_above_zmax = np.sum(tf_tt_tot[separation_tot>zmax]-ti_tt_tot[separation_tot>zmax])
            kk = open(join(self.outdir,"times_bins_vs_separation.txt"), "w")
            filesep = open(join(self.outdir,'time_vs_separation_agile.txt'), 'w')
            for i in np.arange(len(separation_tot)):
                filesep.write("{} {}\n".format(meantimes[i], separation_tot[i]))
                kk.write("{} {} {}\n".format(ti_tt_tot[i], tf_tt_tot[i], separation_tot[i]))
            filesep.close()
            kk.close()
        """

        self.logger.debug(self, "separation_tot len: %d", len(separation_tot))
        self.logger.debug(self, "ti_tt_tot len: %d", len(ti_tt_tot))
        self.logger.debug(self, "tf_tt_tot len: %d", len(tf_tt_tot))

        filenamePath = None
        if writeFiles:

            filename = f"offaxis_distances_{tmin}_{tmax}"
            outdirPath = Path(self.outdir).joinpath("offaxis_data")
            outdirPath.mkdir(parents=True, exist_ok=True)
            filenamePath = outdirPath.joinpath(filename)
            np.save(filenamePath, separation_tot.value, allow_pickle=True)
            self.logger.info(self, "Produced: %s", filenamePath)

        return separation_tot, ti_tt_tot, tf_tt_tot, ti_mjd, tf_mjd, skyCordsFK5.ra.deg, skyCordsFK5.dec.deg, filenamePath

    def _computeSeparationPerFile(self, doTimeMask, logFile, tmin_start,
                                  tmax_start, skyCordsFK5, zmax, step):

        logFile = AgilepyConfig._expandEnvVar(logFile)
        hdulist = fits.open(logFile)
        SC = hdulist[1].data
        self.logger.debug(self, "Total events: %f", len(SC["TIME"]))
        self.logger.debug(self, "tmin: %f", tmin_start)
        self.logger.debug(self, "tmin log file: %f", SC["TIME"][0])
        self.logger.debug(self, "tmax: %f", tmax_start)
        self.logger.debug(self, "tmax log file: %f", SC["TIME"][-1])

        self.logger.debug(self, "Do time mask? %d", doTimeMask)

        if doTimeMask:

            self.logger.debug(self, "How many times are >= tmin_start? %d",
                              np.sum(SC['TIME'] >= tmin_start))
            self.logger.debug(self, "How many times are <= tmax_start? %d",
                              np.sum(SC['TIME'] <= tmax_start))

            # Filtering out
            booleanMask = np.logical_and(SC['TIME'] >= tmin_start,
                                         SC['TIME'] <= tmax_start)
            TIME = SC['TIME'][booleanMask]
            ATTITUDE_RA_Y = SC['ATTITUDE_RA_Y'][booleanMask]
            ATTITUDE_DEC_Y = SC['ATTITUDE_DEC_Y'][booleanMask]
            self.logger.debug(
                self, "Time mask: %d values skipped" %
                (np.sum(np.logical_not(booleanMask))))

        else:
            TIME = SC['TIME']
            ATTITUDE_RA_Y = SC['ATTITUDE_RA_Y']
            ATTITUDE_DEC_Y = SC['ATTITUDE_DEC_Y']

        hdulist.close()

        # This is to avoid problems with moments for which the AGILE pointing was set to RA=NaN, DEC=NaN
        booleanMaskRA = np.logical_not(np.isnan(ATTITUDE_RA_Y))
        booleanMaskDEC = np.logical_not(np.isnan(ATTITUDE_DEC_Y))

        booleanMaskRADEC = np.logical_or(booleanMaskRA, booleanMaskDEC)

        TIME = TIME[booleanMaskRA]
        ATTITUDE_RA_Y = ATTITUDE_RA_Y[booleanMaskRADEC]
        ATTITUDE_DEC_Y = ATTITUDE_DEC_Y[booleanMaskRADEC]

        self.logger.debug(
            self,
            "Not-null mask RA/DEC (at least one NULL): %d values skipped" %
            (np.sum(np.logical_not(booleanMaskRADEC))))

        deltatime = 0.1  # AGILE attitude is collected every 0.1 s

        # tmin = np.min(TIME)
        # tmax = np.max(TIME)

        index_ti = 0
        index_tf = len(TIME) - 1

        self.logger.debug(self, "Step is: %f", step)

        indexstep = int(step * 10)  # if step 0.1 , indexstep=1 => all values
        # if step 1 , indexstep=10 => one value on 10 values

        self.logger.debug(self, "indexstep is: %f", indexstep)

        # creating arrays filled with zeros
        src_raz = np.zeros(len(TIME[index_ti:index_tf:indexstep]))
        src_decz = np.zeros(len(TIME[index_ti:index_tf:indexstep]))

        self.logger.debug(self, "Number of separations to be computed: %f",
                          index_tf / indexstep)

        # filling the just created arrays with our coordinates of interest
        src_ra = src_raz + skyCordsFK5.ra
        src_dec = src_decz + skyCordsFK5.dec

        c1 = SkyCoord(src_ra, src_dec, unit='deg', frame='icrs')
        c2 = SkyCoord(ATTITUDE_RA_Y[index_ti:index_tf:indexstep],
                      ATTITUDE_DEC_Y[index_ti:index_tf:indexstep],
                      unit='deg',
                      frame='icrs')
        #        print 'c1=', len(c1), 'c2=', len(c2) # to ensure c1 and c2 have the same length
        sep = c2.separation(c1)

        self.logger.debug(self,
                          "Number of computed separation: %f" % (len(sep)))

        return np.asfarray(sep), TIME[index_ti:index_tf:indexstep], TIME[
            index_ti:index_tf:indexstep] + deltatime

    def _getLogsFileInInterval(self, logfilesIndex, tmin, tmax):

        self.logger.debug(self, "Selecting files from %s [%d to %d]",
                          logfilesIndex, tmin, tmax)

        logsFiles = []

        with open(logfilesIndex, "r") as lfi:
            lines = [line.strip() for line in lfi.readlines()]

        for line in lines:
            elements = line.split(" ")
            logFilePath = elements[0]
            logFileTmin = float(elements[1])
            logFileTmax = float(elements[2])

            if logFileTmin <= tmax and tmin <= logFileTmax:
                # print(logFileTmin,",",logFileTmax)
                logsFiles.append(logFilePath)

        return logsFiles
コード例 #5
0
ファイル: utils_test.py プロジェクト: antonioaddis/Agilepy
class AgilepyUtilsUT(unittest.TestCase):
    def setUp(self):
        self.currentDirPath = Path(__file__).parent.absolute()
        self.agilepyconfPath = os.path.join(self.currentDirPath,
                                            "conf/agilepyconf.yaml")

        self.config = AgilepyConfig()
        self.config.loadConfigurations(self.agilepyconfPath, validate=False)

        self.agilepyLogger = AgilepyLogger()

        self.agilepyLogger.initialize(
            self.config.getConf("output", "outdir"),
            self.config.getConf("output", "logfilenameprefix"),
            self.config.getConf("output", "verboselvl"))

        self.datadir = os.path.join(self.currentDirPath, "data")

        self.outDir = Path(self.config.getOptionValue("outdir"))

        if self.outDir.exists() and self.outDir.is_dir():
            shutil.rmtree(self.outDir)

    def tearDown(self):
        self.agilepyLogger.reset()

    def test_display_sky_map(self):

        pu = PlottingUtils(self.config, self.agilepyLogger)

        smooth = 4
        fileFormat = ".png"
        title = "testcase"
        cmap = "CMRmap"
        regFilePath = self.config._expandEnvVar("$AGILE/catalogs/2AGL_2.reg")


        file = pu.displaySkyMap(
                    self.datadir+"/testcase_EMIN00100_EMAX00300_01.cts.gz", \
                    smooth = smooth,
                    fileFormat = fileFormat,
                    title = title,
                    cmap = cmap,
                    regFilePath = regFilePath,
                    catalogRegions = None,
                    catalogRegionsColor = "red",
                    saveImage=True)

        self.assertEqual(True, os.path.isfile(file))

    def test_display_sky_map_single_mode(self):

        pu = PlottingUtils(self.config, self.agilepyLogger)

        smooth = 4
        fileFormat = ".png"
        title = "testcase"
        cmap = "CMRmap"
        regFilePath = self.config._expandEnvVar("$AGILE/catalogs/2AGL_2.reg")
        img = self.datadir + "/testcase_EMIN00100_EMAX00300_01.cts.gz"

        file = pu.displaySkyMapsSingleMode(
                    [img, img, img], \
                    smooth = smooth,
                    fileFormat = fileFormat,
                    titles = [title+"_1", title+"_2", title+"_3"],
                    cmap = cmap,
                    regFilePath = regFilePath,
                    catalogRegions = None,
                    catalogRegionsColor = "red",
                    saveImage=True)

        self.assertEqual(True, os.path.isfile(file))

    def test_display_light_curve(self):

        pu = PlottingUtils(self.config, self.agilepyLogger)

        file_lc = self.datadir + "/lc-4.txt"

        self.assertRaises(ValueError, pu.plotLc, file_lc, 1500, 1000, True)

        #with self.assertRaises(ValueError, pu.plotLc, file_lc, 1500, 1000, True) as cm:
        #        input("..")
        #            print(cm)

        # self.assertEqual(True, os.path.isfile(file))

    def test_initialize_logger_verboselvl_2(self):
        sleep(1.0)
        self.agilepyLogger.reset()
        self.config.setOptions(force=True, verboselvl=2)

        logfilePath = self.agilepyLogger.initialize(
            self.config.getOptionValue("outdir"),
            self.config.getOptionValue("logfilenameprefix"),
            self.config.getOptionValue("verboselvl"))

        self.assertEqual(True, logfilePath.is_file())

        with open(logfilePath, "r") as f:
            linesNumber = len(f.readlines())
            self.assertEqual(1, linesNumber)

        self.agilepyLogger.debug(self, "%s %s", "Debug", "message")
        self.agilepyLogger.info(self, "%s %s", "Info", "message")
        self.agilepyLogger.warning(self, "%s %s", "Warning", "message")
        self.agilepyLogger.critical(self, "%s %s", "Critical", "message")

        with open(logfilePath, "r") as f:
            linesNumber = len(f.readlines())
            self.assertEqual(5, linesNumber)

    def test_initialize_logger_verboselvl_1(self):
        sleep(1.0)
        self.agilepyLogger.reset()
        self.config.setOptions(force=True, verboselvl=1)

        logfilePath = self.agilepyLogger.initialize(
            self.config.getOptionValue("outdir"),
            self.config.getOptionValue("logfilenameprefix"),
            self.config.getOptionValue("verboselvl"))

        self.assertEqual(True, logfilePath.is_file())

        with open(logfilePath, "r") as f:
            linesNumber = len(f.readlines())
            self.assertEqual(1, linesNumber)

        self.agilepyLogger.debug(self, "%s %s", "Debug", "message")
        self.agilepyLogger.info(self, "%s %s", "Info", "message")
        self.agilepyLogger.warning(self, "%s %s", "Warning", "message")
        self.agilepyLogger.critical(self, "%s %s", "Critical", "message")

        with open(logfilePath, "r") as f:
            linesNumber = len(f.readlines())
            self.assertEqual(5, linesNumber)

    def test_initialize_logger_verboselvl_0(self):
        sleep(1.0)
        self.agilepyLogger.reset()
        self.config.setOptions(force=True, verboselvl=0)

        logfilePath = self.agilepyLogger.initialize(
            self.config.getOptionValue("outdir"),
            self.config.getOptionValue("logfilenameprefix"),
            self.config.getOptionValue("verboselvl"))

        self.assertEqual(True, logfilePath.is_file())

        with open(logfilePath, "r") as f:
            linesNumber = len(f.readlines())
            self.assertEqual(1, linesNumber)

        self.agilepyLogger.debug(self, "%s %s", "Debug", "message")
        self.agilepyLogger.info(self, "%s %s", "Info", "message")
        self.agilepyLogger.warning(self, "%s %s", "Warning", "message")
        self.agilepyLogger.critical(self, "%s %s", "Critical", "message")

        with open(logfilePath, "r") as f:
            linesNumber = len(f.readlines())
            self.assertEqual(5, linesNumber)

    """
    Time conversions
        # https://tools.ssdc.asi.it/conversionTools
        # https://heasarc.gsfc.nasa.gov/cgi-bin/Tools/xTime/xTime.pl?time_in_i=&time_in_c=&time_in_d=&time_in_j=&time_in_m=58871.45616898&time_in_sf=&time_in_wf=&time_in_sl=&time_in_sni=&time_in_snu=&time_in_s=&time_in_h=&time_in_sz=&time_in_ss=&time_in_sn=&timesys_in=u&timesys_out=u&apply_clock_offset=yes
    """

    def test_astro_utils_time_mjd_to_tt(self):
        sec_tolerance = 0.001
        tt = AstroUtils.time_mjd_to_tt(58871.45616898)  # 506861812.99987227
        self.assertEqual(True, abs(506861813 - tt) <= sec_tolerance)

    def test_astro_utils_time_tt_to_mjd(self):
        sec_tolerance = 0.0000001
        mjd = AstroUtils.time_tt_to_mjd(507391426.9447)
        self.assertEqual(True, abs(58877.58595999 - mjd) <= sec_tolerance)

    def test_astro_utils_time_jd_to_civil(self):

        tol = 0.044

        civ = AstroUtils.jd_to_civil(2458871.95616898)
        self.assertEqual(civ[0], 2020)
        self.assertEqual(civ[1], 1)
        self.assertEqual(True, abs(23 - civ[2]) <= tol)
        # it should be 2020, 1, 23)........

    def test_astro_utils_time_utc_to_jd(self):

        tol = 0.00000001

        dt = datetime.strptime("2020-01-23T10:56:53", '%Y-%m-%dT%H:%M:%S')

        jd = AstroUtils.to_jd(dt)

        self.assertEqual(True, abs(2458871.95616898 - jd) <= tol)

    def test_astro_utils_time_utc_to_mjd(self):

        tol = 0.00000001

        dt = datetime.strptime("2020-01-23T10:56:53", '%Y-%m-%dT%H:%M:%S')

        mjd = AstroUtils.to_jd(dt, fmt="mjd")

        self.assertEqual(True, abs(58871.45616898 - mjd) <= tol)

    def test_astro_utils_time_utc_to_tt(self):

        tol = 0.0001

        tt = AstroUtils.time_utc_to_tt("2020-01-23T10:56:53")

        self.assertEqual(True, abs(506861813 - tt) <= tol)

    def test_astro_utils_time_tt_to_utc(self):

        sec_tol = 1

        utc = AstroUtils.time_tt_to_utc(506861813)
        dt = datetime.strptime(utc, '%Y-%m-%dT%H:%M:%S')

        self.assertEqual(dt.year, 2020)
        self.assertEqual(dt.month, 1)
        self.assertEqual(dt.day, 23)
        self.assertEqual(dt.hour, 10)
        self.assertEqual(dt.minute, 56)
        self.assertEqual(True, abs(53 - dt.second) <= sec_tol)

    def test_astro_utils_time_mjd_to_utc(self):

        sec_tol = 1

        utc = AstroUtils.time_mjd_to_utc(58871.45616898)

        dt = datetime.strptime(utc, '%Y-%m-%dT%H:%M:%S')

        self.assertEqual(dt.year, 2020)
        self.assertEqual(dt.month, 1)
        self.assertEqual(dt.day, 23)
        self.assertEqual(dt.hour, 10)
        self.assertEqual(dt.minute, 56)
        self.assertEqual(True, abs(53 - dt.second) <= sec_tol)

    def test_astro_utils_time_utc_to_mjd(self):

        sec_tol = 0.00000001

        mjd = AstroUtils.time_utc_to_mjd("2020-01-23T10:56:53")

        self.assertEqual(True, abs(58871.45616898 - mjd) <= sec_tol)
コード例 #6
0
class SourceModelUT(unittest.TestCase):
    def setUp(self):
        self.currentDirPath = Path(__file__).parent.absolute()

        self.test_logs_dir = Path(self.currentDirPath).joinpath(
            "test_logs", "SourceModelUT")
        self.test_logs_dir.mkdir(parents=True, exist_ok=True)

        os.environ["TEST_LOGS_DIR"] = str(self.test_logs_dir)

        outDir = Path(os.path.join(os.environ["AGILE"])).joinpath(
            "agilepy-test-data/unittesting-output/core")
        if outDir.exists() and outDir.is_dir():
            shutil.rmtree(outDir)

        self.sourcesTxt = os.path.join(self.currentDirPath,
                                       "test_data/sources.txt")
        self.sourcesXml = os.path.join(self.currentDirPath,
                                       "test_data/sources.xml")

        self.agilepyConf = os.path.join(self.currentDirPath,
                                        "conf/agilepyconf.yaml")

        self.config = AgilepyConfig()
        self.config.loadBaseConfigurations(self.agilepyConf)
        self.config.loadConfigurationsForClass("AGAnalysis")

        self.logger = AgilepyLogger()
        self.logger.initialize(
            self.config.getConf("output", "outdir"),
            self.config.getConf("output", "logfilenameprefix"),
            self.config.getConf("output", "verboselvl"))

        self.sl = SourcesLibrary(self.config, self.logger)

    def test_parse_source_XML_format(self):

        xmlRoot = parse(self.sourcesXml).getroot()

        sources = []

        for sourceRoot in xmlRoot:

            source = Source.parseSourceXMLFormat(sourceRoot)

            sources.append(source)

        assert sources[0].get("flux")["value"] == 7.45398e-08
        assert sources[0].get("pos")["value"] == (92.4102, -10.3946)

        assert sources[1].get("flux")["value"] == 41.6072e-08
        assert sources[1].get("pos")["value"] == (119.677, 10.544)

        assert sources[2].get("flux")["value"] == 969.539e-08
        assert sources[2].get("index2")["value"] == 1.3477
        assert sources[2].get("index2")["free"] == 1
        assert sources[2].get("pos")["value"] == (263.585, -2.84083)

        assert sources[3].get("flux")["value"] == 35.79e-08
        assert sources[3].get("curvature")["value"] == 0.682363
        assert sources[3].get("curvature")["max"] == 3
        assert sources[3].get("pos")["value"] == (6.16978, -0.0676943)

    def test_parse_source_TXT_format(self):

        sources = []

        with open(self.sourcesTxt, "r") as txtFile:

            for line in txtFile:

                if line == "\n":
                    continue

                source = Source.parseSourceTXTFormat(line)

                sources.append(source)

        assert sources[0].name == "2AGLJ2021+4029"
        assert sources[0].get("flux")["value"] == 119.3e-08
        assert sources[0].get("pos")["value"] == (78.2375, 2.12298)

        assert sources[1].name == "2AGLJ2021+3654"
        assert sources[1].get("flux")["value"] == 70.89e-08
        assert sources[1].get("pos")["value"] == (75.2562, 0.151831)

    def test_init(self):

        source = PointSource(name="test-source")
        source.spectrum = Spectrum.getSpectrum("PowerLaw")
        assert "PointSource" == type(source.spatialModel).__name__

    def test_get(self):

        source = PointSource(name="test-source")
        source.spectrum = Spectrum.getSpectrum("PowerLaw")
        assert "PointSource" == type(source.spatialModel).__name__

        assert len(source.get("flux").keys()) == 6
        assert source.getVal("flux") == None

        with pytest.raises(SourceParameterNotFound):
            source.get("fluxxx")

        with pytest.raises(SourceParameterNotFound):
            source.getVal("fluxxx")

    def test_set(self):

        source = PointSource(name="test-source")
        source.spectrum = Spectrum.getSpectrum("PowerLaw")
        assert "PointSource" == type(source.spatialModel).__name__

        source.set("flux", {"min": 1})
        source.setVal("flux", 10)
        assert source.spectrum.flux["min"] == 1
        assert source.spectrum.flux["value"] == 10

        with pytest.raises(SourceParameterNotFound):
            source.set("fluxxx", {"value": 10})

        with pytest.raises(ValueError):
            source.set("fluxxx", 10)

        with pytest.raises(SourceParameterNotFound):
            source.setVal("fluxxx", 100)

    def test_str(self):
        source = PointSource(name="test-source")
        source.spectrum = Spectrum.getSpectrum("PowerLaw")
        source.setVal("flux", 100)
        source.setVal("index", 1000)
        source.setVal("pos", (30, 15))

        print(source)
        """
コード例 #7
0
class AGAnalysis:
    """This class contains the high-level API methods you can use to run scientific analysis.

    This class is a wrapper around the ``SourcesLibrary``, ``ScienceTools`` and ``AgilepyConfig`` classes and implements
    high level functionality.

    This class requires you to setup a ``yaml configuration file`` to specify the software's behaviour
    and a ``xml descriptor file`` that contains a list of all the sources you want to consider.

    Class attributes:

    Attributes:
        config (:obj:`AgilepyConfig`): it is used to read/update configuration values.
        logger (:obj:`AgilepyLogger`): it is used to log messages with different importance levels.
        sourcesLibrary (:obj:`SourcesLibrary`): it contains the list of the sources and several useful methods.

    """
    def __init__(self, configurationFilePath, sourcesFilePath):
        """AGAnalysis constructor.

        Args:
            configurationFilePath (str): the relative or absolute path to the yaml configuration file.
            sourcesFilePath (str): the relative or absolute path to the xml sources descriptor file.

        Raises:
            FileExistsError: if the output directory already exists.
            AGILENotFoundError: if the AGILE environment variable is not set.
            PFILESNotFoundError: if the PFILES environment variable is not set.

        Example:
            >>> from agilepy.api import AGAnalysis
            >>> aganalysis = AGAnalysis('agconfig.yaml', 'sources.xml')

        """

        self.config = AgilepyConfig()

        self.config.loadConfigurations(configurationFilePath, validate=True)

        self.config.printOptions("output")

        self.outdir = self.config.getConf("output", "outdir")

        if exists(self.outdir):
            raise FileExistsError(
                "The output directory %s already exists! Please, delete it or specify another output directory!"
                % (self.outdir))

        self.logger = agilepyLogger

        self.logger.initialize(self.outdir,
                               self.config.getConf("output", "logfilename"),
                               self.config.getConf("output", "debuglvl"))

        self.sourcesLibrary = SourcesLibrary()

        self.sourcesLibrary.loadSources(sourcesFilePath)

        if "AGILE" not in os.environ:
            raise AGILENotFoundError("$AGILE is not set.")

        if "PFILES" not in os.environ:
            raise PFILESNotFoundError("$PFILES is not set.")

    def setOptions(self, **kwargs):
        """It updates configuration options specifying one or more key=value pairs at once.

        Args:
            \*\*kwargs: key-values pairs, separated by a comma.

        Returns:
            None

        Raises:
            ConfigFileOptionTypeError: if the type of the option value is not wrong.
            CannotSetHiddenOptionError: if the option is hidden.
            OptionNotFoundInConfigFileError: if the option is not found.

        Note:
            The ``config`` attribute is initialized by reading the corresponding
            yaml configuration file, loading its contents in memory. Updating the values
            held by this object will not affect the original values written on disk.

        Example:

            >>> aganalysis.setOptions(mapsize=60, binsize=0.5)
            True

        """
        return self.config.setOptions(**kwargs)

    def resetOptions(self):
        """It resets the configuration options to their original values.

        Returns:
            None.
        """
        return self.config.reset()

    def printOptions(self, section=None):
        """It prints the configuration options in the console.

        Args:
            section (str): you can specify a configuration file section to be printed out.

        Returns:
            None
        """
        return self.config.printOptions(section)

    def generateMaps(self):
        """It generates (one or more) counts, exposure, gas and int maps and a ``maplist file``.

        The method's behaviour varies according to several configuration options (see docs here).

        Note:
            It resets the configuration options to their original values before exiting.

        Returns:
            The absolute path to the generated ``maplist file``.

        Raises:
            ScienceToolInputArgMissing: if not all the required configuration options have been set.

        Example:
            >>> aganalysis.generateMaps()
            /home/rt/agilepy/output/testcase.maplist4

        """
        fovbinnumber = self.config.getOptionValue("fovbinnumber")
        energybins = self.config.getOptionValue("energybins")

        initialFovmin = self.config.getOptionValue("fovradmin")
        initialFovmax = self.config.getOptionValue("fovradmax")

        initialFileNamePrefix = self.config.getOptionValue("filenameprefix")
        outdir = self.config.getOptionValue("outdir")

        mapListFileContent = []
        maplistFilePath = join(outdir, initialFileNamePrefix + ".maplist4")

        for stepi in range(0, fovbinnumber):

            if fovbinnumber == 1:
                bincenter = 30
                fovmin = initialFovmin
                fovmax = initialFovmax
            else:
                bincenter, fovmin, fovmax = AGAnalysis._updateFovMinMaxValues(
                    fovbinnumber, initialFovmin, initialFovmax, stepi + 1)

            for bgCoeffIdx, stepe in enumerate(energybins):

                if Parameters.checkEnergyBin(stepe):

                    emin = stepe[0]
                    emax = stepe[1]

                    skymapL = Parameters.getSkyMap(emin, emax)
                    skymapH = Parameters.getSkyMap(emin, emax)
                    fileNamePrefix = Parameters.getMapNamePrefix(
                        emin, emax, stepi + 1)

                    self.logger.info(
                        self,
                        "Map generation => fovradmin %s fovradmax %s bincenter %s emin %s emax %s fileNamePrefix %s \
                                            skymapL %s skymapH %s", [
                            fovmin, fovmax, bincenter, emin, emax,
                            fileNamePrefix, skymapL, skymapH
                        ])

                    self.config.setOptions(
                        filenameprefix=initialFileNamePrefix + "_" +
                        fileNamePrefix)
                    self.config.setOptions(fovradmin=fovmin, fovradmax=fovmax)
                    self.config.addOptions("selection", emin=emin, emax=emax)
                    self.config.addOptions("maps",
                                           skymapL=skymapL,
                                           skymapH=skymapH)

                    ctsMapGenerator.configure(self.config)
                    expMapGenerator.configure(self.config)
                    gasMapGenerator.configure(self.config)
                    intMapGenerator.configure(self.config)

                    self.config.addOptions("maps",
                                           expmap=expMapGenerator.outfilePath,
                                           ctsmap=ctsMapGenerator.outfilePath)

                    if not ctsMapGenerator.allRequiredOptionsSet(self.config) or \
                       not expMapGenerator.allRequiredOptionsSet(self.config) or \
                       not gasMapGenerator.allRequiredOptionsSet(self.config) or \
                       not intMapGenerator.allRequiredOptionsSet(self.config):

                        raise ScienceToolInputArgMissing(
                            "Some options have not been set.")

                    f1 = ctsMapGenerator.call()
                    self.logger.info(
                        self, "Science tool ctsMapGenerator produced %s", [f1])

                    f2 = expMapGenerator.call()
                    self.logger.info(
                        self, "Science tool expMapGenerator produced %s", [f2])

                    f3 = gasMapGenerator.call()
                    self.logger.info(
                        self, "Science tool gasMapGenerator produced %s", [f3])

                    f4 = intMapGenerator.call()
                    self.logger.info(
                        self, "Science tool intMapGenerator produced %s", [f4])


                    mapListFileContent.append( ctsMapGenerator.outfilePath + " " + \
                                               expMapGenerator.outfilePath + " " + \
                                               gasMapGenerator.outfilePath + " " + \
                                               str(bincenter) + " " + \
                                               str(self.config.getOptionValue("galcoeff")[bgCoeffIdx]) + " " + \
                                               str(self.config.getOptionValue("isocoeff")[bgCoeffIdx]) )

                else:

                    self.logger.warning(
                        self,
                        "Energy bin [%s, %s] is not supported. Map generation skipped.",
                        [stepe[0], stepe[1]])

        with open(maplistFilePath, "a") as mlf:
            for line in mapListFileContent:
                mlf.write(line + "\n")

        self.logger.info(self, "Maplist file created in %s", [maplistFilePath])

        self.config.reset()

        return maplistFilePath

    def mle(self, maplistFilePath):
        """It performs a maximum likelihood estimation analysis on every source withing the ``sourceLibrary``, producing one output file per source.

        The outputfiles have the ``.source`` extension.

        The method's behaviour varies according to several configuration options (see docs here).

        Note:
            It resets the configuration options to their original values before exiting.

        Args:
            maplistFilePath (str): the absolute path to the ``maplist file`` generated by ``generateMaps()``.

        Returns:
            A list of absolute paths to the output ``.source`` files.

        Raises:
            MaplistIsNone: is the input argument is None.

        Example:
            >>> maplistFilePath = aganalysis.generateMaps()
            >>> aganalysis.mle(maplistFilePath)
            [/home/rt/agilepy/output/testcase0001_2AGLJ2021+4029.source /home/rt/agilepy/output/testcase0001_2AGLJ2021+3654.source]

        """
        if not maplistFilePath:
            raise MaplistIsNone(
                "The 'maplistFilePath' input argument is None. Please, pass a valid path to a maplist \
                                 file as argument (perhaps you want to call generateMaps() first). "
            )

        sourceListFilename = "sourceLibrary" + (str(
            multi.callCounter).zfill(5))
        sourceListAgileFormatFilePath = self.sourcesLibrary.writeToFile(
            outfileNamePrefix=join(self.outdir, sourceListFilename),
            fileformat="AG")

        self.config.addOptions("selection",
                               maplist=maplistFilePath,
                               sourcelist=sourceListAgileFormatFilePath)

        multisources = self.sourcesLibrary.getSourcesNames()
        self.config.addOptions("selection", multisources=multisources)

        multi.configure(self.config)

        sourceFiles = multi.call()

        if len(sourceFiles) == 0:
            self.logger.warning(self, "The number of .source files is 0.", [])

        self.logger.info(self, "AG_multi produced: %s",
                         [" ".join(sourceFiles)])

        for sourceFile in sourceFiles:

            multiOutputData = SourcesLibrary.parseSourceFile(sourceFile)

            mapCenterL = float(self.config.getOptionValue("glon"))
            mapCenterB = float(self.config.getOptionValue("glat"))
            self.sourcesLibrary.updateMulti(multiOutputData, mapCenterL,
                                            mapCenterB)

        self.config.reset()

        return sourceFiles

    def freeSources(self, selection, parameterName, free):
        """It can set to True or False a parameter's ``free`` attribute of one or more source.

        Example of source model:
        ::

            <source name="2AGLJ2021+4029" type="PointSource">
                <spectrum type="PLExpCutoff">
                  <parameter name="Flux" free="1"  value="119.3e-08"/>
                  <parameter name="Index" free="0" scale="-1.0" value="1.75" min="0.5" max="5"/>
                  <parameter name="CutoffEnergy" free="0" value="3307.63" min="20" max="10000"/>
                </spectrum>
                <spatialModel type="PointSource" location_limit="0" free="0">
                  <parameter name="GLON" value="78.2375" />
                  <parameter name="GLAT" value="2.12298" />
                </spatialModel>
            </source>

        The sources can be selected with the ``selection`` argument, supporting either ``lambda functions`` and
        ``boolean expression strings``.

        The selection criteria can be expressed using the following ``Source`` class's parameters:

        - Name: the unique code identifying the source.
        - Dist: the distance of the source from the center of the maps (generated with ``generateMaps()``)
        - Flux: the flux value.
        - SqrtTS: the radix square of the ts.

        Warning:

            The Dist, Flux and SqrtTS parameters are available only after the maximum likelihood estimation analysis.


        The supported ``parameterName`` are:

        - Flux
        - Index
        - Index1
        - Index2
        - CutoffEnergy
        - PivotEnergy
        - Curvature
        - Index2
        - Loc


        Args:
            selection (lambda or str): a lambda function or a boolean expression string specifying the selection criteria.
            parameterName (str): the ``free`` attribute of this parameter will be updated.
            free (bool): the boolean value used.

        Returns:
            The list of the sources matching the selection criteria.

        Note:
            The ``sourcesLibrary`` attribute is initialized by reading the corresponding
            xml descriptor file, loading its contents in memory. Updating the values
            held by this object will not affect the original values written on disk.

        Example:

            The following calls are equivalent and they set to True the ``free`` attribute of the "Flux" parameter for all the sources
            named "2AGLJ2021+4029" which distance from the map center is greater than zero and which flux value
            is greater than zero.

            >>> aganalysis.freeSources(lambda Name, Dist, Flux : Name == "2AGLJ2021+4029" and Dist > 0 and Flux > 0, "Flux", True)
            [..]

            >>> aganalysis.freeSources('Name == "2AGLJ2021+4029" AND Dist > 0 AND Flux > 0', "Flux", True)
            [..]



        """
        return self.sourcesLibrary.freeSources(selection, parameterName, free)

    def selectSources(self, selection):
        """It returns the sources matching the selection criteria from the ``sourcesLibrary``.

        Args:
            selection (lambda or str): a lambda function or a boolean expression string specifying the selection criteria.

        Returns:
            List of sources.
        """
        return self.sourcesLibrary.selectSources(selection)

    def deleteSources(self, selection):
        """It deletes the sources matching the selection criteria from the ``sourcesLibrary``.

        Args:
            selection (lambda or str): a lambda function or a boolean expression string specifying the selection criteria.

        Returns:
            The list containing the deleted sources.
        """
        return self.sourcesLibrary.deleteSources(selection)

    @staticmethod
    def _updateFovMinMaxValues(fovbinnumber, fovradmin, fovradmax, stepi):
        # print("\nfovbinnumber {}, fovradmin {}, fovradmax {}, stepi {}".format(fovbinnumber, fovradmin, fovradmax, stepi))
        A = float(fovradmax) - float(fovradmin)
        B = float(fovbinnumber)
        C = stepi

        binleft = ((A / B) * C)
        binright = (A / B)
        bincenter = binleft - binright / 2.0
        fovmin = bincenter - (A / B) / 2.0
        fovmax = bincenter + (A / B) / 2.0

        # print("bincenter {}, fovmin {}, fovmax {}".format(bincenter, fovmin, fovmax))

        return bincenter, fovmin, fovmax