From 46c87e056b28d51de1c36ea583f50b3adcd4be49 Mon Sep 17 00:00:00 2001 From: Maurizio Tomasi Date: Tue, 7 Jun 2022 10:41:40 +0200 Subject: [PATCH 1/5] Generalize destriper tests for multiple coordinate systems --- ... => destriper_binned_map_ecliptic.fits.gz} | Bin ... destriper_destriped_map_ecliptic.fits.gz} | Bin ....gz => destriper_hit_map_ecliptic.fits.gz} | Bin ...s.gz => destriper_invnpp_ecliptic.fits.gz} | Bin ...fits.gz => destriper_npp_ecliptic.fits.gz} | Bin ...ts.gz => destriper_rcond_ecliptic.fits.gz} | Bin test/test_destriper.py | 21 ++++++++++++------ 7 files changed, 14 insertions(+), 7 deletions(-) rename test/destriper_reference/{destriper_binned_map.fits.gz => destriper_binned_map_ecliptic.fits.gz} (100%) rename test/destriper_reference/{destriper_destriped_map.fits.gz => destriper_destriped_map_ecliptic.fits.gz} (100%) rename test/destriper_reference/{destriper_hit_map.fits.gz => destriper_hit_map_ecliptic.fits.gz} (100%) rename test/destriper_reference/{destriper_invnpp.fits.gz => destriper_invnpp_ecliptic.fits.gz} (100%) rename test/destriper_reference/{destriper_npp.fits.gz => destriper_npp_ecliptic.fits.gz} (100%) rename test/destriper_reference/{destriper_rcond.fits.gz => destriper_rcond_ecliptic.fits.gz} (100%) diff --git a/test/destriper_reference/destriper_binned_map.fits.gz b/test/destriper_reference/destriper_binned_map_ecliptic.fits.gz similarity index 100% rename from test/destriper_reference/destriper_binned_map.fits.gz rename to test/destriper_reference/destriper_binned_map_ecliptic.fits.gz diff --git a/test/destriper_reference/destriper_destriped_map.fits.gz b/test/destriper_reference/destriper_destriped_map_ecliptic.fits.gz similarity index 100% rename from test/destriper_reference/destriper_destriped_map.fits.gz rename to test/destriper_reference/destriper_destriped_map_ecliptic.fits.gz diff --git a/test/destriper_reference/destriper_hit_map.fits.gz b/test/destriper_reference/destriper_hit_map_ecliptic.fits.gz similarity index 100% rename from test/destriper_reference/destriper_hit_map.fits.gz rename to test/destriper_reference/destriper_hit_map_ecliptic.fits.gz diff --git a/test/destriper_reference/destriper_invnpp.fits.gz b/test/destriper_reference/destriper_invnpp_ecliptic.fits.gz similarity index 100% rename from test/destriper_reference/destriper_invnpp.fits.gz rename to test/destriper_reference/destriper_invnpp_ecliptic.fits.gz diff --git a/test/destriper_reference/destriper_npp.fits.gz b/test/destriper_reference/destriper_npp_ecliptic.fits.gz similarity index 100% rename from test/destriper_reference/destriper_npp.fits.gz rename to test/destriper_reference/destriper_npp_ecliptic.fits.gz diff --git a/test/destriper_reference/destriper_rcond.fits.gz b/test/destriper_reference/destriper_rcond_ecliptic.fits.gz similarity index 100% rename from test/destriper_reference/destriper_rcond.fits.gz rename to test/destriper_reference/destriper_rcond_ecliptic.fits.gz diff --git a/test/test_destriper.py b/test/test_destriper.py index 866eca80..68bffd8a 100644 --- a/test/test_destriper.py +++ b/test/test_destriper.py @@ -10,7 +10,7 @@ from numpy.random import MT19937, RandomState, SeedSequence -def test_destriper(tmp_path): +def run_destriper_tests(tmp_path, coordinates: str): sim = lbs.Simulation( base_path=tmp_path / "destriper_output", start_time=0, duration_s=86400.0 ) @@ -61,13 +61,13 @@ def test_destriper(tmp_path): ref_map_path = Path(__file__).parent / "destriper_reference" - hit_map_filename = ref_map_path / "destriper_hit_map.fits.gz" + hit_map_filename = ref_map_path / f"destriper_hit_map_{coordinates}.fits.gz" # healpy.write_map(hit_map_filename, results.hit_map, dtype="int32", overwrite=True) np.testing.assert_allclose( results.hit_map, healpy.read_map(hit_map_filename, field=None, dtype=np.int32) ) - binned_map_filename = ref_map_path / "destriper_binned_map.fits.gz" + binned_map_filename = ref_map_path / f"destriper_binned_map_{coordinates}.fits.gz" # healpy.write_map( # binned_map_filename, # results.binned_map, @@ -80,7 +80,9 @@ def test_destriper(tmp_path): assert results.binned_map.shape == ref_binned.shape np.testing.assert_allclose(results.binned_map, ref_binned, rtol=1e-2, atol=1e-3) - destriped_map_filename = ref_map_path / "destriper_destriped_map.fits.gz" + destriped_map_filename = ( + ref_map_path / f"destriper_destriped_map_{coordinates}.fits.gz" + ) # healpy.write_map( # destriped_map_filename, # results.destriped_map, @@ -95,7 +97,7 @@ def test_destriper(tmp_path): results.destriped_map, ref_destriped, rtol=1e-2, atol=1e-3 ) - npp_filename = ref_map_path / "destriper_npp.fits.gz" + npp_filename = ref_map_path / f"destriper_npp_{coordinates}.fits.gz" # healpy.write_map( # npp_filename, # results.npp, @@ -108,7 +110,7 @@ def test_destriper(tmp_path): assert results.npp.shape == ref_npp.shape np.testing.assert_allclose(results.npp, ref_npp, rtol=1e-2, atol=1e-3) - invnpp_filename = ref_map_path / "destriper_invnpp.fits.gz" + invnpp_filename = ref_map_path / f"destriper_invnpp_{coordinates}.fits.gz" # healpy.write_map( # invnpp_filename, # results.invnpp, @@ -121,7 +123,7 @@ def test_destriper(tmp_path): assert results.invnpp.shape == ref_invnpp.shape np.testing.assert_allclose(results.invnpp, ref_invnpp, rtol=1e-2, atol=1e-3) - rcond_filename = ref_map_path / "destriper_rcond.fits.gz" + rcond_filename = ref_map_path / f"destriper_rcond_{coordinates}.fits.gz" # healpy.write_map( # rcond_filename, # results.rcond, @@ -131,3 +133,8 @@ def test_destriper(tmp_path): assert np.allclose( results.rcond, healpy.read_map(rcond_filename, field=None, dtype=np.float32) ) + + +def test_destriper(tmp_path): + for coordinates in ["ecliptic"]: + run_destriper_tests(tmp_path=tmp_path, coordinates=coordinates) From fae93a2f2d370d5863dd82de96016975f8336a9c Mon Sep 17 00:00:00 2001 From: Maurizio Tomasi Date: Tue, 7 Jun 2022 14:20:46 +0200 Subject: [PATCH 2/5] Implement E2G rotation in destriper and add tests --- litebird_sim/destriper/__init__.py | 68 +++++++++++++++--- litebird_sim/scan_map.py | 2 +- .../destriper_binned_map_galactic.fits.gz | Bin 0 -> 4881 bytes .../destriper_destriped_map_galactic.fits.gz | Bin 0 -> 4836 bytes .../destriper_hit_map_galactic.fits.gz | Bin 0 -> 2503 bytes .../destriper_invnpp_galactic.fits.gz | Bin 0 -> 18641 bytes .../destriper_npp_galactic.fits.gz | Bin 0 -> 9085 bytes .../destriper_rcond_galactic.fits.gz | Bin 0 -> 3699 bytes test/test_destriper.py | 37 +++++++--- 9 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 test/destriper_reference/destriper_binned_map_galactic.fits.gz create mode 100644 test/destriper_reference/destriper_destriped_map_galactic.fits.gz create mode 100644 test/destriper_reference/destriper_hit_map_galactic.fits.gz create mode 100644 test/destriper_reference/destriper_invnpp_galactic.fits.gz create mode 100644 test/destriper_reference/destriper_npp_galactic.fits.gz create mode 100644 test/destriper_reference/destriper_rcond_galactic.fits.gz diff --git a/litebird_sim/destriper/__init__.py b/litebird_sim/destriper/__init__.py index 1b02962b..263dcffe 100644 --- a/litebird_sim/destriper/__init__.py +++ b/litebird_sim/destriper/__init__.py @@ -15,6 +15,8 @@ from toast.tod.interval import Interval import toast.mpi +from litebird_sim.coordinates import CoordinateSystem, rotate_coordinates_e2g + toast.mpi.use_mpi = lbs.MPI_ENABLED @@ -26,6 +28,10 @@ class DestriperParameters: - ``nside``: the NSIDE parameter used to create the maps + - ``coordinate_system``: an instance of the :class:`.CoordinateSystem` enum. + It specifies if the map must be created in ecliptic (default) or + galactic coordinates. + - ``nnz``: number of components per pixel. The default is 3 (I/Q/U). - ``baseline_length``: number of consecutive samples in a 1/f noise @@ -64,6 +70,7 @@ class DestriperParameters: """ nside: int = 512 + coordinate_system: CoordinateSystem = CoordinateSystem.Ecliptic nnz: int = 3 baseline_length: int = 100 iter_max: int = 100 @@ -104,12 +111,20 @@ class DestriperResult: npp: Any = None invnpp: Any = None rcond: Any = None + coordinate_system: CoordinateSystem = CoordinateSystem.Ecliptic class _Toast2FakeCache: "This class simulates a TOAST2 cache" - def __init__(self, spin2ecliptic_quats, obs, bore2spin_quat, nside): + def __init__( + self, + spin2ecliptic_quats, + obs, + bore2spin_quat, + nside, + coordinates: CoordinateSystem, + ): self.obs = obs self.keydict = {"timestamps": obs.get_times()} @@ -141,13 +156,27 @@ def __init__(self, spin2ecliptic_quats, obs, bore2spin_quat, nside): logging.warning( "converting TODs for %s from %s to float64", obs.name[i], - str(pointings[i].dtype), + str(obs.tod[i].dtype), ) self.keydict[f"signal_{det}"] = np.array(obs.tod[i], dtype=np.float64) - self.keydict[f"pixels_{det}"] = healpix_base.ang2pix(curpnt[:, 0:2]) + theta_phi = curpnt[:, 0:2] + polangle = curpnt[:, 2] + + if coordinates == CoordinateSystem.Galactic: + theta_phi, polangle = rotate_coordinates_e2g( + pointings_ecl=theta_phi, pol_angle_ecl=polangle + ) + elif coordinates == CoordinateSystem.Ecliptic: + pass # Do nothing, "theta_phi" and "polangle" are ok + else: + assert ValueError( + "unable to handle coordinate system {coordinates} in `destripe`" + ) + + self.keydict[f"pixels_{det}"] = healpix_base.ang2pix(theta_phi) self.keydict[f"weights_{det}"] = np.stack( - (np.ones(nsamples), np.cos(2 * curpnt[:, 2]), np.sin(2 * curpnt[:, 2])) + (np.ones(nsamples), np.cos(2 * polangle), np.sin(2 * polangle)) ).transpose() def keys(self): @@ -172,10 +201,19 @@ def destroy(self, name): class _Toast2FakeTod: "This class simulates a TOAST2 TOD" - def __init__(self, spin2ecliptic_quats, obs, bore2spin_quat, nside): + def __init__( + self, + spin2ecliptic_quats, + obs, + bore2spin_quat, + nside, + coordinates: CoordinateSystem, + ): self.obs = obs self.local_samples = (0, obs.tod[0].size) - self.cache = _Toast2FakeCache(spin2ecliptic_quats, obs, bore2spin_quat, nside) + self.cache = _Toast2FakeCache( + spin2ecliptic_quats, obs, bore2spin_quat, nside, coordinates + ) def local_intervals(self, _): return [ @@ -215,9 +253,20 @@ def local_times(self): class _Toast2FakeData: "This class simulates a TOAST2 Data class" - def __init__(self, spin2ecliptic_quats, obs, bore2spin_quat, nside): + def __init__( + self, + spin2ecliptic_quats, + obs, + bore2spin_quat, + nside, + coordinates: CoordinateSystem, + ): self.obs = [ - {"tod": _Toast2FakeTod(spin2ecliptic_quats, x, bore2spin_quat, nside)} + { + "tod": _Toast2FakeTod( + spin2ecliptic_quats, x, bore2spin_quat, nside, coordinates + ) + } for x in obs ] self.bore2spin_quat = bore2spin_quat @@ -285,6 +334,7 @@ def destripe_observations( obs=observations, bore2spin_quat=bore2spin_quat, nside=params.nside, + coordinates=params.coordinate_system, ) mapmaker = OpMapMaker( nside=params.nside, @@ -306,6 +356,8 @@ def destripe_observations( result = DestriperResult() + result.coordinate_system = params.coordinate_system + if params.return_hit_map: result.hit_map = healpy.read_map( base_path / (params.output_file_prefix + "hits.fits"), diff --git a/litebird_sim/scan_map.py b/litebird_sim/scan_map.py index 9ec206c5..70771286 100644 --- a/litebird_sim/scan_map.py +++ b/litebird_sim/scan_map.py @@ -140,7 +140,7 @@ def scan_map_in_observations( elif all(item in maps.keys() for item in cur_obs.channel): input_names = cur_obs.channel else: - print( + raise ValueError( "The dictionary maps does not contain all the relevant" + "keys, please check the list of detectors and channels" ) diff --git a/test/destriper_reference/destriper_binned_map_galactic.fits.gz b/test/destriper_reference/destriper_binned_map_galactic.fits.gz new file mode 100644 index 0000000000000000000000000000000000000000..3154bbd114f25a483b2c71c59bd0c241ae6271e8 GIT binary patch literal 4881 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 KS~PROr~m*Q;;4xL literal 0 HcmV?d00001 diff --git a/test/destriper_reference/destriper_hit_map_galactic.fits.gz b/test/destriper_reference/destriper_hit_map_galactic.fits.gz new file mode 100644 index 0000000000000000000000000000000000000000..091eb579b091644e64ef57cd42f8af1b4e5f8338 GIT binary patch literal 2503 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 literal 0 HcmV?d00001 diff --git a/test/destriper_reference/destriper_npp_galactic.fits.gz b/test/destriper_reference/destriper_npp_galactic.fits.gz new file mode 100644 index 0000000000000000000000000000000000000000..d77aa5b685f5bc12afddf7be5f26f0f7dfebc523 GIT binary patch literal 9085 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 literal 0 HcmV?d00001 diff --git a/test/destriper_reference/destriper_rcond_galactic.fits.gz b/test/destriper_reference/destriper_rcond_galactic.fits.gz new file mode 100644 index 0000000000000000000000000000000000000000..8c09f0a2bbdee1e1814f3ca47b28fd417a696dcd GIT binary patch literal 3699 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 R009600|1@o0E56z001TaUMK(n literal 0 HcmV?d00001 diff --git a/test/test_destriper.py b/test/test_destriper.py index 68bffd8a..04ae036d 100644 --- a/test/test_destriper.py +++ b/test/test_destriper.py @@ -9,8 +9,18 @@ import healpy from numpy.random import MT19937, RandomState, SeedSequence +from litebird_sim import CoordinateSystem + + +COORDINATE_SYSTEM_STR = { + CoordinateSystem.Ecliptic: "ecliptic", + CoordinateSystem.Galactic: "galactic", +} + + +def run_destriper_tests(tmp_path, coordinates: CoordinateSystem): + coordinates_str = COORDINATE_SYSTEM_STR[coordinates] -def run_destriper_tests(tmp_path, coordinates: str): sim = lbs.Simulation( base_path=tmp_path / "destriper_output", start_time=0, duration_s=86400.0 ) @@ -49,6 +59,7 @@ def run_destriper_tests(tmp_path, coordinates: str): nnz=3, baseline_length=100, iter_max=10, + coordinate_system=coordinates, return_hit_map=True, return_binned_map=True, return_destriped_map=True, @@ -58,16 +69,19 @@ def run_destriper_tests(tmp_path, coordinates: str): ) results = lbs.destripe(sim, instr, params=params) + assert results.coordinate_system == coordinates ref_map_path = Path(__file__).parent / "destriper_reference" - hit_map_filename = ref_map_path / f"destriper_hit_map_{coordinates}.fits.gz" + hit_map_filename = ref_map_path / f"destriper_hit_map_{coordinates_str}.fits.gz" # healpy.write_map(hit_map_filename, results.hit_map, dtype="int32", overwrite=True) np.testing.assert_allclose( results.hit_map, healpy.read_map(hit_map_filename, field=None, dtype=np.int32) ) - binned_map_filename = ref_map_path / f"destriper_binned_map_{coordinates}.fits.gz" + binned_map_filename = ( + ref_map_path / f"destriper_binned_map_{coordinates_str}.fits.gz" + ) # healpy.write_map( # binned_map_filename, # results.binned_map, @@ -81,7 +95,7 @@ def run_destriper_tests(tmp_path, coordinates: str): np.testing.assert_allclose(results.binned_map, ref_binned, rtol=1e-2, atol=1e-3) destriped_map_filename = ( - ref_map_path / f"destriper_destriped_map_{coordinates}.fits.gz" + ref_map_path / f"destriper_destriped_map_{coordinates_str}.fits.gz" ) # healpy.write_map( # destriped_map_filename, @@ -97,7 +111,7 @@ def run_destriper_tests(tmp_path, coordinates: str): results.destriped_map, ref_destriped, rtol=1e-2, atol=1e-3 ) - npp_filename = ref_map_path / f"destriper_npp_{coordinates}.fits.gz" + npp_filename = ref_map_path / f"destriper_npp_{coordinates_str}.fits.gz" # healpy.write_map( # npp_filename, # results.npp, @@ -110,7 +124,7 @@ def run_destriper_tests(tmp_path, coordinates: str): assert results.npp.shape == ref_npp.shape np.testing.assert_allclose(results.npp, ref_npp, rtol=1e-2, atol=1e-3) - invnpp_filename = ref_map_path / f"destriper_invnpp_{coordinates}.fits.gz" + invnpp_filename = ref_map_path / f"destriper_invnpp_{coordinates_str}.fits.gz" # healpy.write_map( # invnpp_filename, # results.invnpp, @@ -123,7 +137,7 @@ def run_destriper_tests(tmp_path, coordinates: str): assert results.invnpp.shape == ref_invnpp.shape np.testing.assert_allclose(results.invnpp, ref_invnpp, rtol=1e-2, atol=1e-3) - rcond_filename = ref_map_path / f"destriper_rcond_{coordinates}.fits.gz" + rcond_filename = ref_map_path / f"destriper_rcond_{coordinates_str}.fits.gz" # healpy.write_map( # rcond_filename, # results.rcond, @@ -135,6 +149,9 @@ def run_destriper_tests(tmp_path, coordinates: str): ) -def test_destriper(tmp_path): - for coordinates in ["ecliptic"]: - run_destriper_tests(tmp_path=tmp_path, coordinates=coordinates) +def test_destriper_ecliptic(tmp_path): + run_destriper_tests(tmp_path=tmp_path, coordinates=CoordinateSystem.Ecliptic) + + +def test_destriper_galactic(tmp_path): + run_destriper_tests(tmp_path=tmp_path, coordinates=CoordinateSystem.Galactic) From 10309b927e0ca30f762acac2f151e51954b29a99 Mon Sep 17 00:00:00 2001 From: Maurizio Tomasi Date: Wed, 8 Jun 2022 10:16:13 +0200 Subject: [PATCH 3/5] Add test to check compatibility with MBS coordinates --- test/test_destriper.py | 101 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/test/test_destriper.py b/test/test_destriper.py index 04ae036d..876aafdc 100644 --- a/test/test_destriper.py +++ b/test/test_destriper.py @@ -155,3 +155,104 @@ def test_destriper_ecliptic(tmp_path): def test_destriper_galactic(tmp_path): run_destriper_tests(tmp_path=tmp_path, coordinates=CoordinateSystem.Galactic) + + +def test_destriper_coordinate_consistency(tmp_path): + # Here we check that MBS uses the same coordinate system as the destriper + # in «Galactic» mode: specifically, we create a noiseless TOD from a CMB + # map in Galactic coordinates and run the destriper asking to use Galactic + # coordinates again. Since the TOD was noiseless, the binned map should be + # the same as the input map, except for two features: + # + # 1. It does not cover the whole sky + # 2. A few pixels at the border of the observed region are not properly + # constrained and their value will be set to zero + # + # This test checks that «most» of the two maps agree, i.e., the 5% and 95% + # percentiles are smaller than some (small) threshold. + + sim = lbs.Simulation( + base_path="destriper_output", + start_time=0, + duration_s=10 * 86400.0, + ) + + sim.generate_spin2ecl_quaternions( + scanning_strategy=lbs.SpinningScanningStrategy( + spin_sun_angle_rad=np.deg2rad(30), # CORE-specific parameter + spin_rate_hz=0.5 / 60, # Ditto + # We use astropy to convert the period (4 days) in + # seconds + precession_rate_hz=1.0 / (4 * u.day).to("s").value, + ) + ) + instr = lbs.InstrumentInfo( + name="mock_instrument", + spin_boresight_angle_rad=np.deg2rad(65), + ) + + detectors = [ + lbs.DetectorInfo( + name="noiseless_detector", + sampling_rate_hz=10.0, + fwhm_arcmin=60.0, + bandcenter_ghz=40.0, + bandwidth_ghz=12.0, + ), + ] + + # We create two detectors, whose polarization angles are separated by π/2 + sim.create_observations( + detectors=detectors, + dtype_tod=np.float64, # Needed if you use the TOAST destriper + n_blocks_time=lbs.MPI_COMM_WORLD.size, + split_list_over_processes=False, + ) + + for obs in sim.observations: + lbs.get_pointings( + obs, + spin2ecliptic_quats=sim.spin2ecliptic_quats, + detector_quats=None, + bore2spin_quat=instr.bore2spin_quat, + ) + + params = lbs.MbsParameters( + make_cmb=True, + ) + mbs = lbs.Mbs( + simulation=sim, + parameters=params, + detector_list=detectors, + ) + (healpix_maps, file_paths) = mbs.run_all() + + lbs.scan_map_in_observations(obs=sim.observations, maps=healpix_maps) + + params = lbs.DestriperParameters( + nside=healpy.npix2nside(len(healpix_maps[detectors[0].name][0])), + coordinate_system=lbs.CoordinateSystem.Galactic, + return_hit_map=True, + return_binned_map=True, + return_destriped_map=True, + ) + + result = lbs.destripe(sim, instr, params) + + inp = healpix_maps[detectors[0].name] # Input CMB map in Galactic coordinates + out = result.binned_map # The binned map produced by the destriper + hit = result.hit_map # The hit map produced by the destriper + + # We do not consider unseen pixels nor pixels that have not been properly + # constrained by our mock detector + mask = (hit == 0) | (out == 0.0) + inp[mask] = np.NaN + out[mask] = np.NaN + + # Ideally this should be ≈0 + diff = inp - out + + # We check the closeness of `diff` to zero through the 5% and 95% percentiles + low_limit, high_limit = np.percentile(diff[np.isfinite(diff)], (0.05, 0.95)) + assert np.abs(low_limit) < 1e-9 + assert np.abs(high_limit) < 1e-9 From 17aba2e8c831140aea7c42270e13f4bc952119f2 Mon Sep 17 00:00:00 2001 From: Maurizio Tomasi Date: Wed, 8 Jun 2022 10:38:06 +0200 Subject: [PATCH 4/5] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38f27d56..32db5dc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - Each `Simulation` object creates random number generators (field `Simulation.random`), in a way that is safe even for MPI applications +- Support the production of maps in Galactic coordinates through the TOAST2 map-maker + - Make `make_bin_map` compute pixel indices instead of requiring them as input [#176](https://github.com/litebird/litebird_sim/pull/176) - Use a more robust algorithm to compute pointings [#175](https://github.com/litebird/litebird_sim/pull/175) From 46eaf1ae16454da1fc25d3fb713473959bc859af Mon Sep 17 00:00:00 2001 From: Maurizio Tomasi Date: Wed, 8 Jun 2022 10:45:19 +0200 Subject: [PATCH 5/5] Update .gitignore and add PyCharm project files --- .gitignore | 26 +++++++++++++++++++ .idea/inspectionProfiles/Project_Default.xml | 17 ++++++++++++ .../inspectionProfiles/profiles_settings.xml | 6 +++++ .idea/litebird_sim.iml | 15 +++++++++++ .idea/misc.xml | 7 +++++ .idea/modules.xml | 8 ++++++ .idea/vcs.xml | 6 +++++ 7 files changed, 85 insertions(+) create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/litebird_sim.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index e35fea85..5eeaa6cd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,29 @@ +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + *~ # Byte-compiled / optimized / DLL files diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..ad2dd076 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..105ce2da --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/litebird_sim.iml b/.idea/litebird_sim.iml new file mode 100644 index 00000000..07f841d1 --- /dev/null +++ b/.idea/litebird_sim.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..c26b3564 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..f6c88267 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file