Пример #1
0
    def __init__(self, dist, data, l_min=0):
        """Constructor.

        :param dist: Radius of the spherical surface.
        :type dist: float
        :param data: List of tuples with the two multipolar numbers and
                     the data as :py:class:`~.TimeSeries`.
        :type data: list of tuple ``(l, m, timeseries)``
        :ivar l_min: l smaller than ``l_min`` are dropped.
        :type l_min: int

        """

        self.dist = float(dist)
        self.radius = self.dist  # This is just an alias
        self.radius_series = None

        self.l_min = l_min

        # Associate multipoles to list of timeseries
        multipoles_list_ts = {}

        # Now we populate the multipoles_ts_list dictionary. This is a
        # dictionary with keys (l, m) and with values lists of timeseries. If
        # the key is not already present, we create it with value an empty
        # list, then we append the timeseries to this list
        for mult_l, mult_m, ts in data:  # mult = "multipole"
            if mult_l >= l_min:
                lm_list = multipoles_list_ts.setdefault((mult_l, mult_m), [])
                # This means: multipoles[(mult_t, mult_m)] = []
                lm_list.append(ts)
                # At the end we have:
                # multipoles[(mult_t, mult_m)] = [ts1, ts2, ...]

        # Now self._multipoles is a dictionary in which all the timeseries are
        # collapse in a single one. So it is a straightforward map (l, m) -> ts
        self._multipoles = {
            lm: timeseries.combine_ts(ts)
            for lm, ts in multipoles_list_ts.items()
        }
        self.available_l = sorted(
            {mult_l
             for mult_l, _ in self._multipoles.keys()})
        self.l_max = max(self.available_l)
        self.available_m = sorted(
            {mult_m
             for _, mult_m in self._multipoles.keys()})
        self.available_lm = set(self._multipoles.keys())

        # Check if all the (l, m) from l_min to l_max are available
        all_lm = set()
        for mult_l in range(self.l_min, max(self.available_l) + 1):
            for mult_m in range(-mult_l, mult_l + 1):
                all_lm.add((mult_l, mult_m))

        # set subtraction
        self.missing_lm = all_lm - self.available_lm

        # Data is in the format expected by __init__
        self.data = [(lm[0], lm[1], ts) for lm, ts in self._multipoles.items()]
Пример #2
0
    def test_combine_ts(self):

        times1 = np.linspace(0, 2 * np.pi, 100)
        sins1 = np.sin(times1)
        times2 = np.linspace(np.pi, 3 * np.pi, 100)
        coss1 = np.cos(times2)

        # A sine wave + half a cos wave
        expected_early = np.append(sins1, np.cos(times2[50:]))
        expected_late = np.append(sins1[:50], np.cos(times2))

        ts1 = ts.TimeSeries(times1, sins1)
        ts2 = ts.TimeSeries(times2, coss1)

        self.assertTrue(
            np.allclose(
                ts.combine_ts([ts1, ts2], prefer_late=False).y, expected_early
            )
        )

        self.assertTrue(
            np.allclose(ts.combine_ts([ts1, ts2]).y, expected_late)
        )

        # Here we test two timeseries with same tmin
        times4 = np.linspace(0, 2 * np.pi, 100)
        times5 = np.linspace(0, 3 * np.pi, 100)
        sins4 = np.sin(times4)
        coss5 = np.sin(times5)

        ts4 = ts.TimeSeries(times4, sins4)
        ts5 = ts.TimeSeries(times5, coss5)

        self.assertTrue(
            np.allclose(
                ts.combine_ts([ts1, ts2], prefer_late=False).y, expected_early
            )
        )

        self.assertTrue(
            np.allclose(ts.combine_ts([ts4, ts5], prefer_late=True).y, coss5)
        )
Пример #3
0
    def __getitem__(self, key):
        if key not in self:
            raise KeyError(f"{key} not available")

        if key not in self._vars:
            # We read all the files associated to variable key
            folders = self._vars_readers[key]
            series = [f[key] for f in folders.values()]
            self._vars[key] = ts.combine_ts(series)

        return self._vars[key]
Пример #4
0
    def _populate_ah_vars(self, sd):
        # First, we find all the files related to apparent horizons. These
        # have names like BH_diagnostics.ah1.gp
        self._ah_files = {}

        rx_ah_filename = re.compile(r"^BH_diagnostics.ah(\d+).gp$")
        for path in sd.allfiles:
            filename = os.path.split(path)[-1]
            matched = rx_ah_filename.search(filename)
            if matched is not None:
                ah_index = int(matched.group(1))
                self._ah_files.setdefault(ah_index, []).append(path)

        # Next, we find what variables they contain. This should be pretty
        # standard, but we can make our code more robust by not assuming too
        # much. We read one header and find the variables, then read all the
        # other files assuming the have the same variables. A complication is
        # that variables names have blank spaces. We turn them into underscores.

        self._num_ah_horizons = len(self._ah_files.keys())

        # We continue only if we find some files
        if self._num_ah_horizons > 0:

            first_ah_file = next(iter(self._ah_files.values()))[0]
            with open(first_ah_file, "r") as fil:
                # Here we read the first lines_to_read into header
                # We strip the new line
                header = []
                for line in fil:
                    # We read the header, which starts with #
                    if line.startswith("#"):
                        header.append(line.strip())
                    else:
                        break

            # Now, we parse the header and associate variable name with column
            # where the data is. The header looks like:
            #
            # # apparent horizon 1/3
            # #
            # # column  1 = cctk_iteration
            # # column  2 = cctk_time
            # # column  3 = centroid_x
            # # column  4 = centroid_y
            # # column  5 = centroid_z
            #
            # We scan the columns with a regex.
            # 1. ^ $ means that we match the entire string
            # 2. \#[\s]column[\s]+ matches the literal '# column ' with any
            #    number of spaces.
            # 3. Then we match the number
            # 4. We match another literal ' = ' with the sapces
            # 5. Finally we match the name of the variable matching letters
            #    and symbols
            rx_column = re.compile(
                r"\#[\s]column[\s]+(\d+)[\s]=[\s]([a-zA-Z_0-9\s()-/]+)$")

            # Here is where we store the map
            self._ah_vars_columns = {}

            for line in header:
                matched = rx_column.match(line)
                if matched is not None:
                    # Columns counting start from 1, so we must subtract one to
                    # be with 0-based indexing
                    column_number = int(matched.group(1)) - 1
                    name = matched.group(2)

                    # We need to know where the time is
                    if name == "cctk_time":
                        time_column = column_number

                    # We exclude some variables we don't want in OneHorizon
                    # (e.g., we don't want cctk_time)
                    if name in self._exclude_ah_vars:
                        continue

                    # Spaces to underscores
                    name = name.replace(" ", "_")
                    # We remove parentheses
                    name = name.replace("(", "")
                    name = name.replace(")", "")
                    # We change / to -
                    name = name.replace("/", "-")
                    self._ah_vars_columns[name] = column_number

            # Now we are ready to populate, we read all the data first. Then, we
            # select all the columns
            for ah_index, files in self._ah_files.items():
                # We create an empty dictionary in self._ah_vars[ah_index]
                self._ah_vars.setdefault(ah_index, {})

                # We read all the data
                alldata = [np.loadtxt(f, unpack=True, ndmin=2) for f in files]
                for var_name, column_number in self._ah_vars_columns.items():
                    # Here we select the time column and the data column for all
                    # the data in each file and we convert them into TimeSeries
                    data_ts = combine_ts([
                        TimeSeries(data[time_column], data[column_number])
                        for data in alldata
                    ])
                    self._ah_vars[ah_index][var_name] = data_ts
Пример #5
0
 def __getitem__(self, key):
     # We read all the files associated to variable key
     folders = self._vars[key]
     series = [f.load(key) for f in folders.values()]
     return ts.combine_ts(series)
    def test_MultipoleOneDet(self):

        ts_comb = ts.combine_ts([self.ts1, self.ts2])

        data = [(2, 2, self.ts1), (2, 2, self.ts2)]
        data2 = [(2, 2, self.ts1), (2, -2, self.ts2)]
        data3 = [(2, 2, self.ts1), (1, 1, self.ts2)]

        # Combinging ts
        mult1 = mp.MultipoleOneDet(100, data)
        # Different multipoles
        mult2 = mp.MultipoleOneDet(100, data2)
        # l_min != 0
        mult3 = mp.MultipoleOneDet(100, data3, l_min=2)

        self.assertEqual(mult1.dist, 100)
        self.assertEqual(mult1.radius, 100)
        self.assertEqual(mult1.l_min, 0)
        self.assertEqual(mult3.l_min, 2)

        # test __call__
        self.assertEqual(mult1(2, 2), ts_comb)
        self.assertEqual(mult2(2, 2), self.ts1)
        self.assertEqual(mult2(2, -2), self.ts2)

        # test copy()
        self.assertEqual(mult1.copy(), mult1)
        self.assertIsNot(mult1.copy(), mult1)

        # test available_
        self.assertCountEqual(mult1.available_l, {2})
        self.assertCountEqual(mult2.available_l, {2})
        self.assertCountEqual(mult1.available_m, {2})
        self.assertCountEqual(mult2.available_m, {2, -2})
        self.assertCountEqual(mult1.available_lm, {(2, 2)})
        self.assertCountEqual(mult2.available_lm, {(2, 2), (2, -2)})
        self.assertCountEqual(mult3.available_l, {2})
        self.assertCountEqual(mult3.available_m, {2})
        self.assertCountEqual(mult3.missing_lm, {(2, -2), (2, -1), (2, 0),
                                                 (2, 1)})

        # test contains
        self.assertIn((2, 2), mult1)
        self.assertIn((2, -2), mult2)
        self.assertEqual(mult2[(2, 2)], self.ts1)

        # test_iter
        # Notice the order. It is increasing in (l, m)
        expected = [(2, -2, self.ts2), (2, 2, self.ts1)]
        for data, exp in zip(mult2, expected):
            self.assertCountEqual(data, exp)

        # test __len__
        self.assertEqual(len(mult1), 1)
        self.assertEqual(len(mult2), 2)

        # test keys()
        self.assertCountEqual(mult1.keys(), [(2, 2)])

        # test __eq__()
        self.assertNotEqual(mult1, mult2)
        self.assertNotEqual(mult1, 1)
        self.assertEqual(mult1, mult1)

        # test __str__()
        self.assertIn("(2, 2)", mult1.__str__())
        self.assertIn("missing", mult3.__str__())