コード例 #1
0
ファイル: noise.py プロジェクト: simonsobs/mapsims
    def _load_inverse_variance_map(self,
                                   tube,
                                   output_units="uK_CMB",
                                   band=None):
        """ Internal function to return a preloaded inverse var map or load one from a from file.
            By default this just returns None so an inv_var map is computed from a white noise level
            and a hits map
        """
        survey = self.get_survey(tube)
        noise_indices = self.get_noise_indices(tube, band)
        # If the survey has a set of preloaded invariance maps use them
        if hasattr(survey,
                   "get_ivar_maps") and survey.get_ivar_maps() is not None:
            ret = np.array(survey.get_ivar_maps())[noise_indices]
        elif (hasattr(survey, "get_ivar_map_filenames")
              and survey.get_ivar_map_filenames() is not None):

            ivar_map_filenames = survey.get_ivar_map_filenames()
            if ivar_map_filenames is None:
                return None
            ivar_map_filenames = [ivar_map_filenames[i] for i in noise_indices]
            ivar_maps = []
            for ivar_map_filename in ivar_map_filenames:
                ivar_maps.append(self._load_map(ivar_map_filename))
            ret = np.array(ivar_maps)
        else:
            return None

        for i in range(self.channel_per_tube):
            freq = self.tubes[tube][i].center_frequency
            unit_conv = (1 * u.uK_CMB).to_value(
                u.Unit(output_units), equivalencies=u.cmb_equivalencies(freq))
            ret[i] /= unit_conv**2.0  # divide by square since the default is 1/uK^2
        return ret
コード例 #2
0
ファイル: alms.py プロジェクト: simonsobs/so_pysm_models
    def get_emission(
        self,
        freqs: u.GHz,
        fwhm: [u.arcmin, None] = None,
        weights=None,
        output_units=u.uK_RJ,
    ):
        """Return map in uK_RJ at given frequency or array of frequencies

        Parameters
        ----------
        freqs : list or ndarray
            Frequency or frequencies in GHz at which compute the signal
        fwhm : float (optional)
            Smooth the input alms before computing the signal, this can only be used
            if the class was initialized with `precompute_output_map` to False.
        output_units : str
            Output units, as defined in `pysm.convert_units`, by default this is
            "uK_RJ" as expected by PySM.
        Returns
        -------
        output_maps : ndarray
            Output maps array with the shape (num_freqs, 1 or 3 (I or IQU), npix)
        """

        freqs = pysm.utils.check_freq_input(freqs)
        weights = pysm.utils.normalize_weights(freqs, weights)

        try:
            output_map = self.output_map
        except AttributeError:
            if fwhm is None:
                alm = self.alm
            else:
                alm = hp.smoothalm(self.alm,
                                   fwhm=fwhm.to_value(u.radian),
                                   pol=True,
                                   inplace=False)

            output_map = self.compute_output_map(alm)

        output_units = u.Unit(output_units)
        assert output_units in [u.uK_RJ, u.uK_CMB]
        if output_units == u.uK_RJ:

            convert_to_uK_RJ = (np.ones(len(freqs), dtype=np.double) *
                                u.uK_CMB).to_value(
                                    u.uK_RJ,
                                    equivalencies=u.cmb_equivalencies(freqs *
                                                                      u.GHz))

            if len(freqs) == 1:
                scaling_factor = convert_to_uK_RJ[0]
            else:
                scaling_factor = np.trapz(convert_to_uK_RJ * weights, x=freqs)

            return output_map.value * scaling_factor << u.uK_RJ
        elif output_units == output_map.unit:
            return output_map
コード例 #3
0
    def __init__(
        self,
        simulation,
        parameters: Union[Dict[str, Any], str,
                          MbsParameters] = MbsParameters(),
        instrument=None,
        detector_list=None,
        channel_list=None,
    ):
        self.sim = simulation
        self.imo = self.sim.imo

        if isinstance(parameters, MbsParameters):
            self.params = parameters
        elif isinstance(parameters, str):
            self.params = MbsParameters.from_dict(
                simulation.parameters[parameters])
        else:
            self.params = MbsParameters.from_dict(parameters)

        self.instrument = instrument
        self.det_list = detector_list
        self.ch_list = channel_list
        self.pysm_units = u.Unit(self.params.units)
コード例 #4
0
ファイル: noise.py プロジェクト: simonsobs/mapsims
    def simulate(
        self,
        tube,
        output_units="uK_CMB",
        seed=None,
        nsplits=1,
        mask_value=None,
        atmosphere=True,
        hitmap=None,
        white_noise_rms=None,
    ):
        """Create a random realization of the noise power spectrum

        Parameters
        ----------

        tube : str
            Specify a tube (for SO: ST0-ST3, LT0-LT6) see the `tubes` attribute
        output_units : str
            Output unit supported by PySM.units, e.g. uK_CMB or K_RJ
        seed : integer or tuple of integers, optional
            Specify a seed. The seed is converted to a tuple if not already
            one and appended to (0,0,6,tube_id) to avoid collisions between
            tubes, with the signal sims and with ACT noise sims, where
            tube_id is the integer ID of the tube.
        nsplits : integer, optional
            Number of splits to generate. The splits will have independent noise
            realizations, with noise power scaled by a factor of nsplits, i.e. atmospheric
            noise is assumed to average down with observing time the same way
            the white noise does. By default, only one split (the coadd) is generated.
        mask_value : float, optional
            The value to set in masked (unobserved) regions. By default, it uses
            the value in default_mask_value, which for healpix is healpy.UNSEEN
            and for CAR is numpy.nan.
        atmosphere : bool, optional
            Whether to include the correlated 1/f from the noise model. This is
            True by default. If it is set to False, then a pure white noise map
            is generated from the white noise power in the noise model, and
            the covariance between arrays is ignored.
        hitmap : string or map, optional
            Provide the path to a hitmap to override the default used for
            the tube. You could also provide the hitmap as an array
            directly.
        white_noise_rms : float or tuple of floats, optional
            Optionally scale the simulation so that the small-scale limit white noise
            level is white_noise_rms in uK-arcmin (either a single number or
            a pair for the dichroic array).

        Returns
        -------

        output_map : ndarray or ndmap
            Numpy array with the HEALPix or CAR map realization of noise.
            The shape of the returned array is (2,3,nsplits,)+oshape, where
            oshape is (npix,) for HEALPix and (Ny,Nx) for CAR.
            The first dimension of size 2 corresponds to the two different
            bands within a dichroic tube.
            See the `band_id` attribute of the Channel class
            to identify which is the index of a Channel in the array.

            The second dimension corresponds to independent split realizations
            of the noise, e.g. it is 1 for full mission.

            The third dimension corresponds to the three polarization
            Stokes components I,Q,U

            The last dimension is the number of pixels
        """
        assert nsplits >= 1
        if mask_value is None:
            mask_value = (default_mask_value["healpix"]
                          if self.healpix else default_mask_value["car"])

        # This seed tuple prevents collisions with the signal sims
        # but we should eventually switch to centralized seed
        # tracking.
        if seed is not None:
            try:
                iter(seed)
            except:
                seed = (seed, )
            tube_id = self.tubes[tube][0].tube_id
            seed = (0, 0, 6, tube_id) + seed
            np.random.seed(seed)

        # In the third row we return the correlation coefficient P12/sqrt(P11*P22)
        # since that can be used straightforwardly when the auto-correlations are re-scaled.
        ell, ps_T, ps_P, fsky, wnoise_power, weightsMap = self.get_noise_properties(
            tube,
            nsplits=nsplits,
            hitmap=hitmap,
            white_noise_rms=white_noise_rms,
            atmosphere=atmosphere,
        )

        if not (atmosphere):
            if self.apply_beam_correction:
                raise NotImplementedError(
                    "Beam correction is not currently implemented for pure-white-noise sims."
                )
            # If no atmosphere is requested, we use a simpler/faster method
            # that generates white noise in real-space.
            if self.healpix:
                ashape = (hp.nside2npix(self.nside), )
                sel = np.s_[:, None, None, None]
                pmap = self.pixarea_map
            else:
                ashape = self.shape[-2:]
                sel = np.s_[:, None, None, None, None]
                pmap = pixell.enmap.enmap(self.pixarea_map, self.wcs)
            spowr = np.sqrt(wnoise_power[sel] / pmap)
            output_map = spowr * np.random.standard_normal(
                (self.channel_per_tube, nsplits, 3) + ashape)
            output_map[:, :, 1:, :] = output_map[:, :, 1:, :] * np.sqrt(2.0)
        else:
            if self.healpix:
                npix = hp.nside2npix(self.nside)
                output_map = np.zeros(
                    (self.channel_per_tube, nsplits, 3, npix))
                for i in range(nsplits):
                    for i_pol in range(3):
                        output_map[:, i, i_pol] = np.array(
                            hp.synfast(
                                ps_T if i_pol == 0 else ps_P,
                                nside=self.nside,
                                pol=False,
                                new=True,
                                verbose=False,
                            ))
            else:
                output_map = pixell.enmap.zeros((2, nsplits, 3) + self.shape,
                                                self.wcs)
                ps_T = pixell.powspec.sym_expand(np.asarray(ps_T),
                                                 scheme="diag")
                ps_P = pixell.powspec.sym_expand(np.asarray(ps_P),
                                                 scheme="diag")
                # TODO: These loops can probably be vectorized
                for i in range(nsplits):
                    for i_pol in range(3):
                        output_map[:, i, i_pol] = pixell.curvedsky.rand_map(
                            (self.channel_per_tube, ) + self.shape,
                            self.wcs,
                            ps_T if i_pol == 0 else ps_P,
                            spin=0,
                        )

        for i in range(self.channel_per_tube):
            freq = self.tubes[tube][i].center_frequency
            if not (self.homogeneous):
                good = weightsMap[i] != 0
                # Normalize on the Effective sky fraction, see discussion in:
                # https://github.com/simonsobs/mapsims/pull/5#discussion_r244939311
                output_map[i, :, :,
                           good] /= np.sqrt(weightsMap[i][good][..., None,
                                                                None])
                output_map[i, :, :, np.logical_not(good)] = mask_value
            unit_conv = (1 * u.uK_CMB).to_value(
                u.Unit(output_units), equivalencies=u.cmb_equivalencies(freq))
            output_map[i] *= unit_conv
        return output_map
コード例 #5
0
ファイル: noise.py プロジェクト: simonsobs/mapsims
    def get_inverse_variance(self,
                             tube,
                             output_units="uK_CMB",
                             hitmap=None,
                             white_noise_rms=None):
        """Get the inverse noise variance in each pixel for the requested tube.
        In the noise model, all the splits and all the I,Q,U components have the 
        same position dependence of the noise variance. Each split just has `nsplits` 
        times the noise power (or `1/nsplits` the inverse noise variance) and the 
        Q,U components have 2x times the noise power (or 1/2 times the inverse 
        noise variance) of the intensity components. The inverse noise variance 
        provided by this function is for the `nsplits=1` intensity component. 
        Two maps are stored in the leading dimension, one for each of the 
        two correlated arrays in the dichroic tube.


        Parameters
        ----------

        tube : str
            Specify a tube (for SO: ST0-ST3, LT0-LT6) see the `tubes` attribute
        output_units : str
            Output unit supported by PySM.units, e.g. uK_CMB or K_RJ
        hitmap : string or map, optional
            Provide the path to a hitmap to override the default used for
            the tube. You could also provide the hitmap as an array
            directly.
        white_noise_rms : float or tuple of floats, optional
            Optionally scale the simulation so that the small-scale limit white noise
            level is white_noise_rms in uK-arcmin (either a single number or
            a pair for the dichroic array).

        Returns
        -------

        ivar_map : ndarray or ndmap
            Numpy array with the HEALPix or CAR map of the inverse variance
            in each pixel. The default units are uK^(-2). This is an extensive
            quantity that depends on the size of pixels.
            See the `band_id` attribute of the Channel class
            to identify which is the index of a Channel in the array.
        """
        ret = self._load_inverse_variance_map(tube, output_units=output_units)
        if ret is not None:
            return ret

        fsky, hitmaps = self._get_requested_hitmaps(tube, hitmap)
        wnoise_scale = self._get_wscale_factor(white_noise_rms, tube, fsky)
        sel = np.s_[:, None] if self.healpix else np.s_[:, None, None]
        whiteNoise = self.get_white_noise_power(tube,
                                                sky_fraction=1,
                                                units="sr")
        if whiteNoise is None:
            raise AssertionError(
                " Survey white noise level not specified. Cannot generate ivar_map"
            )
        power = whiteNoise[sel] * fsky[sel] * wnoise_scale[:, 0][sel]
        """
        We now have the physical white noise power uK^2-sr
        and the hitmap
        ivar = hitmap * pixel_area * fsky / <hitmap> / power
        """
        avgNhits = np.asarray(
            [self._average(hitmaps[i]) for i in range(self.channel_per_tube)])
        ret = hitmaps * self.pixarea_map * fsky[sel] / avgNhits[sel] / power
        # Convert to desired units
        for i in range(self.channel_per_tube):
            freq = self.tubes[tube][i].center_frequency
            unit_conv = (1 * u.uK_CMB).to_value(
                u.Unit(output_units), equivalencies=u.cmb_equivalencies(freq))
            ret[i] /= unit_conv**2.0  # divide by square since the default is 1/uK^2
        return ret
コード例 #6
0
    def __init__(
        self,
        target_nside,
        output_units,
        has_polarization=True,
        line="10",
        include_high_galactic_latitude_clouds=False,
        polarization_fraction=0.001,
        theta_high_galactic_latitude_deg=20.0,
        random_seed=1234567,
        verbose=False,
        run_mcmole3d=False,
        map_dist=None,
        coord="C",
    ):

        """Class defining attributes for CO line emission.
        CO templates are extracted from Type 1 CO Planck maps.
        See further details in https://www.aanda.org/articles/aa/abs/2014/11/aa21553-13/aa21553-13.html

        Parameters
        ----------
        target_nside : int
            HEALPix NSIDE of the output maps
        output_units : str
            unit string as defined by `pysm.convert_units`, e.g. uK_RJ, K_CMB
        has_polarization : bool
            whether or not to simulate also polarization maps
        line : string
            CO rotational transitions.
            Accepted values : 10, 21, 32
        polarization_fraction: float
            polarisation fraction for polarised CO emission.
        include_high_galactic_latitude_clouds: bool
            If True it includes a simulation from MCMole3D to include
            high Galactic Latitude clouds.
            (See more details at http://giuspugl.github.io/mcmole/index.html)
        run_mcmole3d: bool
            If True it simulates  HGL cluds by running MCMole3D, otherwise it coadds
            a map of HGL emission.
        random_seed: int
            set random seed for mcmole3d simulations.
        theta_high_galactic_latitude_deg : float
            Angle in degree  to identify High Galactic Latitude clouds
            (i.e. clouds whose latitude b is `|b|> theta_high_galactic_latitude_deg`).
        map_dist : mpi4py communicator
            Read inputs across a MPI communicator, see pysm.read_map
        """

        self.line = line
        self.line_index = {"10": 0, "21": 1, "32": 2}[line]
        self.line_frequency = {
            "10": 115.271 * u.GHz,
            "21": 230.538 * u.GHz,
            "32": 345.796 * u.GHz,
        }[line]
        self.target_nside = target_nside

        self.template_nside = 512 if self.target_nside <= 512 else 2048

        super().__init__(nside=target_nside, map_dist=map_dist)

        self.remote_data = RemoteData(coord)

        self.planck_templatemap_filename = "co/HFI_CompMap_CO-Type1_{}_R2.00_ring.fits".format(
            self.template_nside
        )
        self.planck_templatemap = self.read_map(
            self.remote_data.get(self.planck_templatemap_filename),
            field=self.line_index,
            unit=u.K_CMB,
        )

        self.include_high_galactic_latitude_clouds = (
            include_high_galactic_latitude_clouds
        )
        self.has_polarization = has_polarization
        self.polarization_fraction = polarization_fraction
        self.theta_high_galactic_latitude_deg = theta_high_galactic_latitude_deg
        self.random_seed = random_seed
        self.run_mcmole3d = run_mcmole3d

        self.output_units = u.Unit(output_units)
        self.verbose = verbose
コード例 #7
0
    def test_convert_units(self):
        a1 = 1 * u.uK_CMB.to(u.uK_RJ,
                             equivalencies=u.cmb_equivalencies(300.0 * u.GHz))
        a2 = 1 * u.uK_RJ.to(u.uK_CMB,
                            equivalencies=u.cmb_equivalencies(300.0 * u.GHz))
        self.assertAlmostEqual(1.0, a1 * a2)
        a1 = 1 * u.uK_CMB.to(u.Unit("MJy/sr"),
                             equivalencies=u.cmb_equivalencies(300.0 * u.GHz))
        a2 = 1 * u.Unit("MJy/sr").to(
            u.uK_CMB, equivalencies=u.cmb_equivalencies(300.0 * u.GHz))
        self.assertAlmostEqual(1.0, a1 * a2)
        """Validation against ECRSC tables.
        https://irsasupport.ipac.caltech.edu/index.php?/Knowledgebase/
        Article/View/181/20/what-are-the-intensity-units-of-the-planck
        -all-sky-maps-and-how-do-i-convert-between-them
        These tables are based on the following tables:
        h = 6.626176e-26 erg*s
        k = 1.380662e-16 erg/L
        c = 2.997792458e1- cm/s
        T_CMB = 2.726
        The impact of the incorrect CMB temperature is especially impactful
        and limits some comparison to only ~2/3 s.f.
        """
        freqs = [30, 143, 857]
        uK_CMB_2_K_RJ = dict()
        uK_CMB_2_K_RJ[30] = 9.77074e-7
        uK_CMB_2_K_RJ[143] = 6.04833e-7
        uK_CMB_2_K_RJ[857] = 6.37740e-11

        for freq in freqs:
            self.assertAlmostEqual(
                uK_CMB_2_K_RJ[freq],
                1 * u.uK_CMB.to(
                    u.K_RJ, equivalencies=u.cmb_equivalencies(freq * u.GHz)),
            )

        K_CMB_2_MJysr = dict()
        K_CMB_2_MJysr[30] = 27.6515
        K_CMB_2_MJysr[143] = 628.272
        K_CMB_2_MJysr[857] = 22565.1

        for freq in freqs:
            self.assertAlmostEqual(
                K_CMB_2_MJysr[freq] /
                (1 *
                 u.K_RJ.to(u.MJy / u.sr,
                           equivalencies=u.cmb_equivalencies(freq * u.GHz))),
                1.0,
                places=4,
            )

        # Note that the MJysr definition seems to match comparatively poorly. The
        # definitions of h, k, c in the document linked above are in cgs and differ
        # from those on wikipedia. This may conflict with the scipy constants I use.

        uK_CMB_2_MJysr = dict()
        uK_CMB_2_MJysr[30] = 2.7e-5
        uK_CMB_2_MJysr[143] = 0.0003800
        uK_CMB_2_MJysr[857] = 1.43907e-6

        for freq in freqs:
            self.assertAlmostEqual(
                uK_CMB_2_MJysr[freq] /
                (1 *
                 u.uK_CMB.to(u.MJy / u.sr,
                             equivalencies=u.cmb_equivalencies(freq * u.GHz))),
                1.0,
                places=2,
            )
コード例 #8
0
def test_read_map_unit_dimensionless():
    m = pysm3.read_map("pysm_2/dust_temp.fits", nside=8, field=0)
    assert u.Unit("") == m.unit
コード例 #9
0
def test_read_map_unit():
    m = pysm3.read_map("pysm_2/dust_temp.fits", nside=8, field=0, unit="uK_RJ")
    assert u.Unit("uK_RJ") == m.unit
コード例 #10
0
ファイル: alms.py プロジェクト: simonsobs/so_pysm_models
    def __init__(
        self,
        filename,
        input_units="uK_CMB",
        input_reference_frequency=None,
        nside=None,
        target_shape=None,
        target_wcs=None,
        from_cl=False,
        from_cl_seed=None,
        precompute_output_map=True,
        has_polarization=True,
        map_dist=None,
    ):
        """Generic component based on Precomputed Alms

        Load a set of Alms from a FITS file and generate maps at the requested
        resolution and frequency assuming the CMB black body spectrum.
        A single set of Alms is used for all frequencies requested by PySM,
        consider that PySM expects the output of components to be in uK_RJ.
        See more details at https://so-pysm-models.readthedocs.io/en/latest/so_pysm_models/models.html

        Also note that the Alms are clipped to 3*nside-1 to avoid
        artifacts from high-ell components which cannot be properly represented
        by a low-nside map.

        Parameters
        ----------
        filename : string
            Path to the input Alms in FITS format
        input_units : string
            Input unit strings as defined by pysm.convert_units, e.g. K_CMB, uK_RJ, MJysr
        input_reference_frequency: float
            If input units are K_RJ or Jysr, the reference frequency
        nside : int
            HEALPix NSIDE of the output maps
        from_cl : bool
            If True, the input file contains C_ell instead of a_lm,
            they should provided with the healpy old ordering TT, TE, TB, EE, EB, BB, sorry.
        from_cl_seed : int
            Seed set just before synalm to simulate the alms from the C_ell,
            necessary to set it in order to get the same input map for different runs
            only used if `from_cl` is True
        precompute_output_map : bool
            If True (default), Alms are transformed into a map in the constructor,
            if False, the object only stores the Alms and generate the map at each
            call of the signal method, this is useful to generate maps convolved
            with different beams
        has_polarization : bool
            whether or not to simulate also polarization maps
            Default: True
        """

        self.nside = nside
        self.shape = target_shape
        self.wcs = target_wcs
        self.filename = filename
        self.input_units = u.Unit(input_units)
        self.has_polarization = has_polarization

        if from_cl:
            np.random.seed(from_cl_seed)
            cl = hp.read_cl(self.filename)
            if not self.has_polarization and cl.ndim > 1:
                cl = cl[0]
            # using healpy old ordering TT, TE, TB, EE, EB, BB
            alm = hp.synalm(cl, new=False, verbose=False)
        else:
            alm = np.complex128(
                hp.read_alm(self.filename,
                            hdu=(1, 2, 3) if self.has_polarization else 1))

        self.equivalencies = (None if input_reference_frequency is None else
                              u.cmb_equivalencies(input_reference_frequency))
        if precompute_output_map:
            self.output_map = self.compute_output_map(alm)

        else:
            self.alm = alm
コード例 #11
0
    def execute(self, write_outputs=False):
        """Run map simulations

        Execute simulations for all channels and write to disk the maps,
        unless `write_outputs` is False, then return them.
        """

        if self.run_pysm:
            sky_config = []
            preset_strings = []
            if self.pysm_components_string is not None:
                for model in self.pysm_components_string.split(","):
                    if model.startswith("SO"):
                        sky_config.append(get_so_models(model, self.nside))
                    else:
                        preset_strings.append(model)

            if len(preset_strings) > 0:
                input_reference_frame = "G"
                assert (
                    len(sky_config) == 0
                ), "Cannot mix PySM and SO models, they are defined in G and C frames"
            else:
                input_reference_frame = "C"

            self.pysm_sky = pysm.Sky(
                nside=self.nside,
                preset_strings=preset_strings,
                component_objects=sky_config,
                output_unit=u.Unit(self.unit),
            )

            if self.pysm_custom_components is not None:
                for comp_name, comp in self.pysm_custom_components.items():
                    self.pysm_sky.components.append(comp)

        if not write_outputs:
            output = {}

        # ch can be single channel or tuple of 2 channels (tube dichroic)
        for ch in self.channels:
            if not isinstance(ch, tuple):
                ch = [ch]
            output_map_shape = (len(ch), 3) + self.shape
            output_map = np.zeros(output_map_shape, dtype=np.float64)
            if self.run_pysm:
                for each, channel_map in zip(ch, output_map):
                    bandpass_integrated_map = self.pysm_sky.get_emission(
                        *each.bandpass).value
                    beam_width_arcmin = each.beam
                    # smoothing and coordinate rotation with 1 spherical harmonics transform
                    smoothed_map = hp.ma(
                        pysm.apply_smoothing_and_coord_transform(
                            bandpass_integrated_map,
                            fwhm=beam_width_arcmin,
                            lmax=3 * self.nside - 1,
                            rot=None if input_reference_frame
                            == self.pysm_output_reference_frame else
                            hp.Rotator(coord=(
                                input_reference_frame,
                                self.pysm_output_reference_frame,
                            )),
                            map_dist=None
                            if COMM_WORLD is None else pysm.MapDistribution(
                                nside=self.nside,
                                smoothing_lmax=3 * self.nside - 1,
                                mpi_comm=COMM_WORLD,
                            ),
                        ))

                    if smoothed_map.shape[0] == 1:
                        channel_map[0] += smoothed_map
                    else:
                        channel_map += smoothed_map

            output_map = output_map.reshape((len(ch), 1, 3) + self.shape)

            if self.other_components is not None:
                for comp in self.other_components.values():
                    kwargs = dict(tube=ch[0].tube, output_units=self.unit)
                    if function_accepts_argument(comp.simulate, "ch"):
                        kwargs.pop("tube")
                        kwargs["ch"] = ch
                    if function_accepts_argument(comp.simulate, "nsplits"):
                        kwargs["nsplits"] = self.nsplits
                    if function_accepts_argument(comp.simulate, "seed"):
                        kwargs["seed"] = self.num
                    component_map = comp.simulate(**kwargs)
                    if self.nsplits == 1:
                        component_map = component_map.reshape((len(ch), 1, 3) +
                                                              self.shape)
                    component_map[hp.mask_bad(component_map)] = np.nan
                    output_map = output_map + component_map

            for each, channel_map in zip(ch, output_map):
                if write_outputs:
                    for split, each_split_channel_map in enumerate(
                            channel_map):
                        filename = self.output_filename_template.format(
                            telescope=each.telescope
                            if each.tube is None else each.tube,
                            band=each.band,
                            nside=self.nside,
                            tag=self.tag,
                            num=self.num,
                            nsplits=self.nsplits,
                            split=split + 1,
                        )
                        warnings.warn("Writing output map " + filename)
                        if self.car:
                            pixell.enmap.write_map(
                                os.path.join(self.output_folder, filename),
                                each_split_channel_map,
                                extra=dict(units=self.unit),
                            )
                        else:
                            each_split_channel_map[np.isnan(
                                each_split_channel_map)] = hp.UNSEEN
                            each_split_channel_map = hp.reorder(
                                each_split_channel_map, r2n=True)
                            hp.write_map(
                                os.path.join(self.output_folder, filename),
                                each_split_channel_map,
                                coord=self.pysm_output_reference_frame,
                                column_units=self.unit,
                                dtype=np.float32,
                                overwrite=True,
                                nest=True,
                            )
                else:
                    if self.nsplits == 1:
                        channel_map = channel_map[0]
                    if not self.car:
                        channel_map[np.isnan(channel_map)] = hp.UNSEEN
                    output[each.tag] = channel_map
        if not write_outputs:
            return output