コード例 #1
0
    def validate(self):
        """Validate parameters."""
        super().validate()
        if "PMRA" not in self._parent.params or "PMDEC" not in self._parent.params:
            # Check ecliptic coordinates proper motion.
            if ("PMELONG" not in self._parent.params
                    or "PMELAT" not in self._parent.params):
                raise MissingParameter(
                    "DDK", "DDK model needs proper motion parameters.")

        if hasattr(self._parent, "PX"):
            if self._parent.PX.value <= 0.0 or self._parent.PX.value is None:
                raise TimingModelError("DDK model needs a valid `PX` value.")
        else:
            raise MissingParameter("Binary_DDK", "PX",
                                   "DDK model needs PX from"
                                   "Astrometry.")
コード例 #2
0
ファイル: model_builder.py プロジェクト: dlakaplan/PINT
    def choose_model(self, param_inpar):
        """Choose the model components based on the parfile.

        Parameter
        ---------
        param_inpar: dict
            Dictionary of the unique parameters in .par file with the key is the
        parfile line. :func:`parse_parfile` returns this dictionary.

        Return
        ------
        list
            List of selected components.
        dict
            Conflict components dictionary, where the key the component name,
            the value is a list of component names that are conflicted with the
            components in the key.
        list
            A list of parameters that are in the .parfile but not in the PINT
            defined parameters.

        Note
        ----
        The selection algorithm:
            #. Look at the BINARY in the par file and catche the indicated binary model
            #. Translate para file parameters to the pint parameter name
            #. Go over the parameter-component map and pick up the components based
               on the parameters in parfile.
                #. Select the components that have its unique parameters in the parfile.
                   In other words, select the components that have one parameter to
                   on component mapping.
                #. Log the conflict components, one parameter to mulitple components mapping.
        """
        selected_components = set()
        param_count = Counter()
        # 1. iteration read parfile with a no component timing_model to get
        # the overall control parameters. This will get us the binary model name
        # build the base fo the timing model
        # pint_param_dict, unknown_param = self._pintify_parfile(param_inpar)
        binary = param_inpar.get("BINARY", None)
        if binary is not None:
            binary = binary[0]
            binary_cp = self.all_components.search_binary_components(binary)
            selected_components.add(binary_cp.__class__.__name__)
        # 2. Get the component list from the parameters in the parfile.
        # 2.1 Check the aliases of input parameters.
        # This does not include the repeating parameters, but it should not
        # matter in the component selection.
        param_not_in_pint = []  # For parameters not initialized in PINT yet.
        param_components_inpar = {}
        for pp in param_inpar.keys():
            try:
                p_name, first_init = self.all_components.alias_to_pint_param(
                    pp)
            except UnknownParameter:
                param_not_in_pint.append(pp)
                continue

            param_count[p_name] += 1

            # For the case that the indexed parameter maps to a component, but
            # the parameter with the provided index is not initialized yet.
            if p_name != first_init:
                param_not_in_pint.append(pp)

            p_cp = self.all_components.param_component_map.get(
                first_init, None)
            if p_cp:
                param_components_inpar[p_name] = p_cp
        # Back map the possible_components and the parameters in the parfile
        # This will remove the duplicate components.
        conflict_components = defaultdict(set)  # graph for confilict
        for k, cps in param_components_inpar.items():
            # If `timing_model` in param --> component mapping skip
            # Timing model is the base.
            if "timing_model" in cps:
                continue
            # Check if it is a binary component, if yes, skip. It is controlled
            # by the BINARY tag
            if self.all_components.components[
                    cps[0]].category == "pulsar_system":
                if binary is None:
                    raise MissingBinaryError(
                        f"Pulsar binary/pulsar system model is"
                        f" decided by the parameter 'BINARY'. "
                        f" Please indicate the binary model "
                        f" before using parameter {k}, which is"
                        f" a binary model parameter.")
                else:
                    continue

            if len(
                    cps
            ) == 1:  # No conflict, parameter only shows in one component.
                selected_components.add(cps[0])
                continue
            # Has conflict, same parameter shows in different components
            # Only record the conflict here and do nothing, if there is any
            # component unique parameter show in the parfile, the component will
            # be selected.
            if len(cps) > 1:
                # Add conflict to the conflict graph
                for cp in cps:
                    temp_cf_cp = copy.deepcopy(cps)
                    temp_cf_cp.remove(cp)
                    conflict_components[cp].update(set(temp_cf_cp))
                continue
        # Check if the selected component in the confilict graph. If it is
        # remove the selected componens with its conflict components.
        for ps_cp in selected_components:
            cf_cps = conflict_components.get(ps_cp, None)
            if cf_cps is not None:  # Had conflict, but resolved.
                for cf_cp in cf_cps:
                    del conflict_components[cf_cp]
                del conflict_components[ps_cp]
        # Check if there are components from the same category
        selected_cates = {}
        for cp in selected_components:
            cate = self.all_components.component_category_map[cp]
            if cate not in selected_cates.keys():
                selected_cates[cate] = cp
            else:
                exisit_cp = selected_cates[cate]
                raise TimingModelError(
                    f"Component '{cp}' and '{exisit_cp}' belong to the"
                    f" same category '{cate}'. Only one component from"
                    f" the same category can be used for a timing model."
                    f" Please check your input (e.g., .par file).")

        return selected_components, conflict_components, param_not_in_pint
コード例 #3
0
ファイル: model_builder.py プロジェクト: dlakaplan/PINT
    def _setup_model(
        self,
        timing_model,
        pint_param_dict,
        original_name=None,
        setup=True,
        validate=True,
    ):
        """Fill up a timing model with parameter values and then setup the model.

        This function fills up the timing model parameter values from the input
        pintified parameter dictionary. If the parameter has not initialized yet,
        it will add the parameter to the timing model. For the repeatable parameters,
        it will search matching key value pair first. If the input parameter line's
        key-value matches the existing parameter, the parameter value and uncertainty
        will copy to the existing parameter. If there is no match, it will find an
        empty existing parameter, whose `key` is `None`, and fill it up. If no empyt
        parameter left, it will add a new parameter to it.

        Parameters
        ----------
        timing_model : pint.models.TimingModel
            Timing model to get setup.
        pint_param_dict: dict
            Pintified parfile dictionary which can be aquired by
            :meth:`ModelBuilder._pintify_parfile`
        origin_name : dict, optional
            A map from PINT name to the original input name.
        setup : bool, optional
            Whether to run the setup function in the timing model.
        validate : bool, optional
            Whether to run the validate funciotn in the timing model.
        """
        if original_name is not None:
            use_alias = True
        else:
            use_alias = False

        for pp, v in pint_param_dict.items():
            try:
                par = getattr(timing_model, pp)
            except AttributeError:
                # since the input is pintfied, it should be an uninitized indexed parameter
                # double check if the missing parameter an indexed parameter.
                pint_par, first_init = self.all_components.alias_to_pint_param(
                    pp)
                try:
                    prefix, _, index = split_prefixed_name(pint_par)
                except PrefixError:
                    par_hosts = self.all_components.param_component_map[
                        pint_par]
                    currnt_cp = timing_model.components.keys()
                    raise TimingModelError(
                        f"Parameter {pint_par} is recognized"
                        f" by PINT, but not used in the current"
                        f" timing model. It is used in {par_hosts},"
                        f" but the current timing model uses {currnt_cp}.")
                # TODO need to create a beeter API for _loacte_param_host
                host_component = timing_model._locate_param_host(first_init)
                timing_model.add_param_from_top(
                    getattr(timing_model, first_init).new_param(index),
                    host_component[0][0],
                )
                par = getattr(timing_model, pint_par)

            # Fill up the values
            param_line = len(v)
            if param_line < 2:
                if use_alias:  # Use the input alias as input
                    name = original_name[pp]
                else:
                    name = pp
                par.from_parfile_line(" ".join([name] + v))
            else:  # For the repeatable parameters
                lines = copy.deepcopy(v)  # Line queue.
                # Check how many repeatable parameters in the model.
                example_par = getattr(timing_model, pp)
                prefix, _, index = split_prefixed_name(pp)
                for li in lines:
                    # Creat a temp parameter with the idx bigger than all the existing indices
                    repeatable_map = timing_model.get_prefix_mapping(prefix)
                    new_max_idx = max(repeatable_map.keys()) + 1
                    temp_par = example_par.new_param(new_max_idx)
                    temp_par.from_parfile_line(" ".join(
                        [prefix + str(new_max_idx), li]))
                    if use_alias:  # Use the input alias as input
                        temp_par.use_alias = original_name[pp]
                    # Check current repeatable's key and value
                    # TODO need to change here when maskParameter name changes to name_key_value
                    empty_repeat_param = []
                    for idx, rp in repeatable_map.items():
                        rp_par = getattr(timing_model, rp)
                        if rp_par.compare_key_value(temp_par):
                            # Key and key value match, copy the new line to it
                            # and exit
                            rp_par.from_parfile_line(" ".join([rp, li]))
                            if use_alias:  # Use the input alias as input
                                rp_par.use_alias = original_name[pp]
                            break

                        if rp_par.key is None:
                            # Empty space for new repeatable parameter
                            empty_repeat_param.append(rp_par)

                    # There is no current repeatable parameter matching the new line
                    # First try to fill up an empty space.
                    if empty_repeat_param != []:
                        emt_par = empty_repeat_param.pop(0)
                        emt_par.from_parfile_line(" ".join([emt_par.name, li]))
                        if use_alias:  # Use the input alias as input
                            emt_par.use_alias = original_name[pp]
                    else:
                        # No empty space, add a new parameter to the timing model.
                        host_component = timing_model._locate_param_host(pp)
                        timing_model.add_param_from_top(
                            temp_par, host_component[0][0])

        if setup:
            timing_model.setup()
        if validate:
            timing_model.validate()
        return timing_model
コード例 #4
0
ファイル: model_builder.py プロジェクト: dlakaplan/PINT
    def _pintify_parfile(self, parfile, allow_name_mixing=False):
        """Translate parfile parameter name to PINT style name.

        This function converts the parfile information to PINT understandable
        parameter name. It also returns the PINT unrecognized parameters and
        check if the parfile has illegal repeating lines.

        Parameters
        ----------
        parfile : str, file-like object, or parfile dictionary
            Parfile name, parfile StringIO, or the parfile dictionary returned
            by :func:`parse_parfile`.

        allow_name_mixing : bool, optional
            Flag for allowing the input to have mixing aliases names for the
            same parameter. For example, if this flag is true, one can have
            T2EFAC and EFAC, both of them maps to PINT parameter EFAC, present
            in the parfile at the same time.

        Returns
        -------
            pint_param_dict : dict
                Pintified parameter dictionary with the PINT name as key and list of
                parameter value-uncertainty lines as value. For the
                repeating parameters in the parfile, the value will contain
                mulitple lines.

            original_name_map : dict
                PINT name maps to the original .par file input names. PINT name
                is the key and the original name is in the value.

            unknown_param : dict
                The PINT unrecognized parameters in the format of a dictionary.
                The key is the unknown parameter name and the value is the
                parfile value lines.

        Raises
        ------
        TimingModelError
            If the parfile has mulitple line with non-repeating parameters.
        """
        pint_param_dict = defaultdict(list)
        original_name_map = defaultdict(list)
        unknown_param = defaultdict(list)
        repeating = Counter()
        if isinstance(parfile, (str, StringIO)):
            parfile_dict = parse_parfile(parfile)
        else:
            parfile_dict = parfile
        for k, v in parfile_dict.items():
            try:
                pint_name, init0 = self.all_components.alias_to_pint_param(k)
            except UnknownParameter:
                if k in ignore_params:  # Parameter is known but in the ingore list
                    continue
                else:  # Check ignored prefix
                    try:
                        pfx, idxs, idx = split_prefixed_name(k)
                        if pfx in ignore_prefix:  # It is an ignored prefix.
                            continue
                        else:
                            unknown_param[k] += v
                    except PrefixError:
                        unknown_param[k] += v
                continue
            pint_param_dict[pint_name] += v
            original_name_map[pint_name].append(k)
            repeating[pint_name] += len(v)
            # Check if this parameter is allowed to be repeated by PINT
            if len(pint_param_dict[pint_name]) > 1:
                if pint_name not in self.all_components.repeatable_param:
                    raise TimingModelError(
                        f"Parameter {pint_name} is not a repeatable parameter. "
                        f"However, mulitple line use it.")
        # Check if the name is mixed
        for p_n, o_n in original_name_map.items():
            if len(o_n) > 1:
                if not allow_name_mixing:
                    raise TimingModelError(
                        f"Parameter {p_n} have mixed input names/alias "
                        f"{o_n}. If you want to have mixing names, please use"
                        f" 'allow_name_mixing=True', and the output .par file "
                        f"will use '{original_name_map[pint_name][0]}'.")
            original_name_map[p_n] = o_n[0]

        return pint_param_dict, original_name_map, unknown_param