def XYZ100_to_CIECAM02(self, XYZ100, on_negative_A="raise"):
        """Computes CIECAM02 appearance correlates for the given tristimulus
        value(s) XYZ (normalized to be on the 0-100 scale).

        Example: ``vc.XYZ100_to_CIECAM02([30.0, 45.5, 21.0])``

        :param XYZ100: An array-like of tristimulus values. These should be
          given on the 0-100 scale, not the 0-1 scale. The array-like should
          have shape ``(..., 3)``; e.g., you can use a simple 3-item list
          (shape = ``(3,)``), or to efficiently perform multiple computations
          at once, you could pass a higher-dimensional array, e.g. an image.
        :arg on_negative_A: A known infelicity of the CIECAM02 model is that
          for some inputs, the achromatic signal :math:`A` can be negative,
          which makes it impossible to compute :math:`J`, :math:`C`,
          :math:`Q`, :math:`M`, or :math:`s` -- only :math:`h`: and :math:`H`
          are spared. (See, e.g., section 2.6.4.1 of :cite:`Luo-CIECAM02` for
          discussion.) This argument allows you to specify a strategy for
          handling such points. Options are:

          * ``"raise"``: throws a :class:`NegativeAError` (a subclass of
            :class:`ValueError`)
          * ``"nan"``: return not-a-number values for the affected
            elements. (This may be particularly useful if converting a large
            number of points at once.)

        :returns: A named tuple of type :class:`JChQMsH`, with attributes
          ``J``, ``C``, ``h``, ``Q``, ``M``, ``s``, and ``H`` containing the
          CIECAM02 appearance correlates.

        """

        #### Argument checking

        XYZ100 = np.asarray(XYZ100, dtype=float)
        if XYZ100.shape[-1] != 3:
            raise ValueError("XYZ100 shape must be (..., 3)")

        #### Step 1

        RGB = broadcasting_matvec(M_CAT02, XYZ100)

        #### Step 2

        RGB_C = self.D_RGB * RGB

        #### Step 3

        RGBprime = broadcasting_matvec(M_HPE_M_CAT02_inv, RGB_C)

        #### Step 4

        RGBprime_signs = np.sign(RGBprime)

        tmp = (self.F_L * RGBprime_signs * RGBprime / 100) ** 0.42
        RGBprime_a = RGBprime_signs * 400 * (tmp / (tmp + 27.13)) + 0.1

        #### Step 5

        a = broadcasting_matvec([1, -12. / 11, 1. / 11], RGBprime_a)
        b = broadcasting_matvec([1. / 9, 1. / 9, -2. / 9], RGBprime_a)
        h_rad = np.arctan2(b, a)
        h = np.rad2deg(h_rad) % 360

        # #### Step 6

        # hprime = h, unless h < 20.14, in which case hprime = h + 360.
        hprime = np.select([h < h_i[0], True], [h + 360, h])
        # we use 0-based indexing, so our i is one less than the reference
        # formulas' i.
        i = np.searchsorted(h_i, hprime, side="right") - 1
        tmp = (hprime - h_i[i]) / e_i[i]
        H = H_i[i] + ((100 * tmp)
                      / (tmp + (h_i[i + 1] - hprime) / e_i[i + 1]))

        #### Step 7

        A = ((broadcasting_matvec([2, 1, 1. / 20], RGBprime_a) - 0.305)
             * self.N_bb)

        if on_negative_A == "raise":
            if np.any(A < 0):
                raise NegativeAError("attempted to convert a tristimulus "
                                     "value whose achromatic signal was "
                                     "negative, and on_negative_A=\"raise\"")
        elif on_negative_A == "nan":
            A = np.select([A < 0, True], [np.nan, A])
        else:
            raise ValueError("Invalid on_negative_A argument: got %r, "
                             "expected \"raise\" or \"nan\""
                             % (on_negative_A,))

        #### Step 8

        J = 100 * (A / self.A_w) ** (self.c * self.z)

        #### Step 9

        Q = self._J_to_Q(J)

        #### Step 10

        e = (12500. / 13) * self.N_c * self.N_cb * (np.cos(h_rad + 2) + 3.8)
        t = (e * np.sqrt(a ** 2 + b ** 2)
             / broadcasting_matvec([1, 1, 21. / 20], RGBprime_a))

        C = t**0.9 * (J / 100)**0.5 * (1.64 - 0.29**self.n)**0.73
        M = C * self.F_L**0.25
        s = 100 * (M / Q)**0.5

        return JChQMsH(J, C, h, Q, M, s, H)
Computes CIECAM02 appearance correlates for the given tristimulus
        value(s) XYZ (normalized to be on the 0-100 scale).

        Example: ``vc.XYZ100_to_CIECAM02([30.0, 45.5, 21.0])``

        :param XYZ100: An array-like of tristimulus values. These should be
          given on the 0-100 scale, not the 0-1 scale. The array-like should
          have shape ``(..., 3)``; e.g., you can use a simple 3-item list
          (shape = ``(3,)``), or to efficiently perform multiple computations
          at once, you could pass a higher-dimensional array, e.g. an image.
        :arg on_negative_A: A known infelicity of the CIECAM02 model is that
          for some inputs, the achromatic signal :math:`A` can be negative,
          which makes it impossible to compute :math:`J`, :math:`C`,
          :math:`Q`, :math:`M`, or :math:`s` -- only :math:`h`: and :math:`H`
          are spared. (See, e.g., section 2.6.4.1 of :cite:`Luo-CIECAM02` for
          discussion.) This argument allows you to specify a strategy for
          handling such points. Options are:

          * ``"raise"``: throws a :class:`NegativeAError` (a subclass of
            :class:`ValueError`)
          * ``"nan"``: return not-a-number values for the affected
            elements. (This may be particularly useful if converting a large
            number of points at once.)

        :returns: A named tuple of type :class:`JChQMsH`, with attributes
          ``J``, ``C``, ``h``, ``Q``, ``M``, ``s``, and ``H`` containing the
          CIECAM02 appearance correlates.