Skip to content

Commit

Permalink
Merge pull request #277 from sibirrer/main
Browse files Browse the repository at this point in the history
refactoring LOS classes and split into individual LOS and population
  • Loading branch information
nkhadka21 authored Nov 15, 2024
2 parents e979f7b + 54c2457 commit d04efde
Show file tree
Hide file tree
Showing 12 changed files with 183 additions and 173 deletions.
28 changes: 11 additions & 17 deletions slsim/FalsePositives/false_positive.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import numpy as np
from slsim.lens import Lens


class FalsePositive(Lens):
"""Class to manage individual false positive. Here, false positives refer to a
configuration that includes an elliptical galaxy at the center with blue galaxies
Expand All @@ -12,8 +13,7 @@ def __init__(
deflector_class,
cosmo,
test_area=4 * np.pi,
los_config=None,
los_dict=None,
los_class=None,
):
"""
:param source_class: A Source class instance or list of Source class instance
Expand All @@ -23,21 +23,16 @@ def __init__(
:param cosmo: astropy.cosmology instance
:param test_area: area of disk around one lensing galaxies to be investigated
on (in arc-seconds^2).
:param los_config: LOSConfig instance which manages line-of-sight (LOS) effects
and Gaussian mixture models in a simulation or analysis context.
:param los_dict: line of sight dictionary (optional, takes these values instead
of drawing from distribution) Takes "gamma" = [gamma1, gamma2] and
"kappa" = kappa as entries
:type los_dict: dict
:param los_class: line of sight dictionary (optional, takes these values instead of drawing from distribution)
:type los_class: ~LOSIndividual() class object
"""
Lens.__init__(self,
source_class=source_class,
deflector_class=deflector_class,
cosmo=cosmo,
test_area=test_area,
los_config=los_config,
los_dict=los_dict,
)
source_class=source_class,
deflector_class=deflector_class,
cosmo=cosmo,
test_area=test_area,
los_class=los_class,
)

def lenstronomy_kwargs(self, band=None):
"""Generates lenstronomy dictionary conventions for the class object.
Expand Down Expand Up @@ -72,5 +67,4 @@ def lenstronomy_kwargs(self, band=None):
"kwargs_lens_light": combined_kwargs_lens_light,
"kwargs_ps": kwargs_ps,
}

return kwargs_model, kwargs_params
return kwargs_model, kwargs_params
16 changes: 8 additions & 8 deletions slsim/FalsePositives/false_positive_pop.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from slsim.FalsePositives.false_positive import FalsePositive
from slsim.ParamDistributions.los_config import LOSConfig
from slsim.lens_pop import draw_test_area
from slsim.LOS.los_pop import LOSPop
import random


Expand All @@ -15,22 +15,22 @@ def __init__(
elliptical_galaxy_population,
blue_galaxy_population,
cosmo=None,
los_config=None,
los_pop=None,
source_number_choice=[1, 2, 3],
weights_for_source_number=None,
test_area_factor=1
):
"""
Args:
:param elliptical_galaxy_population: Deflector population as an deflectors class
:param elliptical_galaxy_population: Deflector population as a deflectors class
instance.
:param blue_galaxy_population: Source population as an sources class inatnce.
:param blue_galaxy_population: Source population as a sources class inatnce.
:param cosmo: astropy.cosmology instance
:param los_config: LOSConfig instance which manages line-of-sight (LOS) effects
:param los_pop: LOSPop instance which manages line-of-sight (LOS) effects
and Gaussian mixture models in a simulation or analysis context.
:param source_number_choice: A list of integers to choose source number from. If
None, defaults to [1, 2, 3].
:param weights: A list of weights corresponding to the probabilities of
:param weights_for_source_number: A list of weights corresponding to the probabilities of
selecting each value in source_number_choice. If None, all choices are equally
likely. Defaults to None.
:param test_area_factor: A multiplicative factor of a test_area. A test area is
Expand All @@ -44,9 +44,9 @@ def __init__(
self._choice = source_number_choice
self._weights = weights_for_source_number
self._test_area_factor = test_area_factor
self.los_config = los_config
self.los_config = los_pop
if self.los_config is None:
self.los_config = LOSConfig()
self.los_config = LOSPop()

def draw_deflector(self):
"""Draw and prepare a deflector (lens) with tolerance-based z_max.
Expand Down
Empty file added slsim/LOS/__init__.py
Empty file.
38 changes: 38 additions & 0 deletions slsim/LOS/los_individual.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@


class LOSIndividual(object):
"""
class to store the quantities of an individual line of sight
"""
def __init__(self, kappa=None, gamma=None):
"""
:param gamma: [gamma1, gamma2] (takes these values if present)
:type gamma: list of floats
:param kappa: convergence (takes this values if present)
:type kappa: float
"""
if kappa is None:
kappa = 0
if gamma is None:
gamma = [0, 0]
self._kappa = kappa
self._gamma = gamma

@property
def convergence(self):
"""
line of sight convergence
:return: kappa
"""
return self._kappa

@property
def shear(self):
"""
line of sight shear
:return: gamma1, gamma2
"""
return self._gamma[0], self._gamma[1]
45 changes: 16 additions & 29 deletions slsim/ParamDistributions/los_config.py → slsim/LOS/los_pop.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from slsim.ParamDistributions.gaussian_mixture_model import GaussianMixtureModel
from slsim.ParamDistributions.kext_gext_distributions import LineOfSightDistribution
from slsim.LOS.los_individual import LOSIndividual
import numpy as np


class LOSConfig(object):
class LOSPop(object):
"""Configuration class for setting parameters related to line-of-sight (LOS) effects
and Gaussian mixture models in a simulation or analysis context.
Expand Down Expand Up @@ -41,8 +42,6 @@ def __init__(
nonlinear_los_bool=False,
nonlinear_correction_path=None,
no_correction_path=None,
gamma=None,
kappa=None,
):
"""
:param los_bool: Boolean to include line-of-sight distortions, default is True.
Expand All @@ -61,10 +60,6 @@ def __init__(
:type nonlinear_correction_path: str or None
:param no_correction_path: Path to the no non-linear correction distributions stored in an H5 file, default is None.
:type no_correction_path: str or None
:param gamma: [gamma1, gamma2] (takes these values if present)
:type gamma: list of floats
:param kappa: convergence (takes this values if present)
:type kappa: float
"""

self.mixgauss_gamma = mixgauss_gamma
Expand All @@ -75,39 +70,31 @@ def __init__(
self.nonlinear_los_bool = nonlinear_los_bool
self.nonlinear_correction_path = nonlinear_correction_path
self.no_correction_path = no_correction_path
if gamma is not None:
self._gamma = gamma
if kappa is not None:
self._kappa = kappa

def calculate_los_linear_distortions(self, source_redshift, deflector_redshift):
"""Calculate line-of-sight distortions in shear and convergence.
def draw_los(self, source_redshift, deflector_redshift):
"""Calculate line-of-sight distortions in shear and convergence for an individual realisation.
:param source_redshift: redshift of the source galaxy object.
:type source_redshift: float
:param deflector_redshift: redshift of the deflector galaxy object.
:type deflector_redshift: float
:return: kappa, gamma1, gamma2
:return: LOSIndividual class instance
"""
if not self.los_bool:
return 0, 0, 0
if hasattr(self, "_gamma") and hasattr(self, "_kappa"):
return self._gamma[0], self._gamma[1], self._kappa
return LOSIndividual(kappa=0, gamma=[0, 0])

if self.mixgauss_gamma and not self.nonlinear_los_bool:
if not hasattr(self, "_gamma"):
mixture = GaussianMixtureModel(
means=self.mixgauss_means,
stds=self.mixgauss_stds,
weights=self.mixgauss_weights,
)
gamma = np.abs(mixture.rvs(size=1))[0]
gamma_abs = np.abs(mixture.rvs(size=1))[0]
phi = 2 * np.pi * np.random.random()
gamma1 = gamma * np.cos(2 * phi)
gamma2 = gamma * np.sin(2 * phi)
self._gamma = [gamma1, gamma2]
if not hasattr(self, "_kappa"):
self._kappa = np.random.normal(loc=0, scale=0.05)
gamma1 = gamma_abs * np.cos(2 * phi)
gamma2 = gamma_abs * np.sin(2 * phi)
gamma = [gamma1, gamma2]
kappa = np.random.normal(loc=0, scale=0.05)
elif self.mixgauss_gamma and self.nonlinear_los_bool:
raise ValueError(
"Can only choose one method for external shear and convergence"
Expand All @@ -119,12 +106,12 @@ def calculate_los_linear_distortions(self, source_redshift, deflector_redshift):
nonlinear_correction_path=self.nonlinear_correction_path,
no_correction_path=self.no_correction_path,
)
gamma, self._kappa = LOS.get_kappa_gamma(
gamma_abs, kappa = LOS.get_kappa_gamma(
z_source, z_lens, self.nonlinear_los_bool
)
phi = 2 * np.pi * np.random.random()
gamma1 = gamma * np.cos(2 * phi)
gamma2 = gamma * np.sin(2 * phi)
self._gamma = [gamma1, gamma2]
gamma1 = gamma_abs * np.cos(2 * phi)
gamma2 = gamma_abs * np.sin(2 * phi)
gamma = [gamma1, gamma2]

return self._gamma[0], self._gamma[1], self._kappa
return LOSIndividual(kappa=kappa, gamma=gamma)
60 changes: 22 additions & 38 deletions slsim/lens.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@
from lenstronomy.LensModel.Solver.lens_equation_solver import (
analytical_lens_model_support,
)
from slsim.ParamDistributions.los_config import LOSConfig
from slsim.Util.param_util import ellipticity_slsim_to_lenstronomy
from slsim.LOS.los_individual import LOSIndividual
from lenstronomy.LightModel.light_model import LightModel
from lenstronomy.Util import constants
from lenstronomy.Util import data_util
from lenstronomy.Util import util

from slsim.lensed_system_base import LensedSystemBase


class Lens(LensedSystemBase):
"""Class to manage individual lenses."""

Expand All @@ -26,8 +27,7 @@ def __init__(
lens_equation_solver="lenstronomy_analytical",
test_area=4 * np.pi,
magnification_limit=0.01,
los_config=None,
los_dict=None,
los_class=None,
):
"""
Expand Down Expand Up @@ -63,11 +63,8 @@ def __init__(
:param magnification_limit: absolute lensing magnification lower limit to
register a point source (ignore highly de-magnified images)
:type magnification_limit: float >= 0
:param los_config: LOSConfig instance which manages line-of-sight (LOS) effects
and Gaussian mixture models in a simulation or analysis context.
:param los_dict: line of sight dictionary (optional, takes these values instead of drawing from distribution)
Takes "gamma" = [gamma1, gamma2] and "kappa" = kappa as entries
:type los_dict: dict
:param los_class: line of sight dictionary (optional, takes these values instead of drawing from distribution)
:type los_class: ~LOSIndividual() class object
"""
self.deflector = deflector_class
self.cosmo = cosmo
Expand All @@ -77,7 +74,7 @@ def __init__(

if isinstance(source_class, list):
self.source = source_class
# choose a highest resdshift source to use conventionally use in lens
# chose a highest resdshift source to use conventionally use in lens
# mass model.
self.max_redshift_source_class = max(
self.source, key=lambda obj: obj.redshift)
Expand All @@ -101,13 +98,9 @@ def __init__(
z_source=float(self.max_redshift_source_class.redshift),
cosmo=self.cosmo,
)

self._los_linear_distortions_cache = None
self.los_config = los_config
if self.los_config is None:
if los_dict is None:
los_dict = {}
self.los_config = LOSConfig(**los_dict)
if los_class is None:
los_class = LOSIndividual()
self.los_class = los_class

@property
def image_number(self):
Expand Down Expand Up @@ -361,22 +354,31 @@ def source_redshift_list(self):
source_redshifts.append(source.redshift)
return source_redshifts

@property
def los_linear_distortions(self):
"""Line-of-sight distortions in shear and convergence.
:return: kappa, gamma1, gamma2
"""
kappa = self.los_class.convergence
gamma1, gamma2 = self.los_class.shear
return kappa, gamma1, gamma2

@property
def external_convergence(self):
"""
:return: external convergence
"""
_, _, kappa_ext = self.los_linear_distortions
return kappa_ext
return self.los_class.convergence

@property
def external_shear(self):
"""
:return: the absolute external shear
"""
gamma1, gamma2, _ = self.los_linear_distortions
gamma1, gamma2 = self.los_class.shear
return (gamma1**2 + gamma2**2) ** 0.5

@property
Expand Down Expand Up @@ -407,7 +409,7 @@ def _einstein_radius(self, source):
z_source=float(source.redshift),
cosmo=self.cosmo,
)
_, _, kappa_ext = self.los_linear_distortions
kappa_ext = self.los_class.convergence
gamma_pl = self.deflector.halo_properties
theta_E = _lens_cosmo.sis_sigma_v2theta_E(
float(self.deflector.velocity_dispersion(cosmo=self.cosmo))
Expand Down Expand Up @@ -449,24 +451,6 @@ def deflector_velocity_dispersion(self):
"""
return self.deflector.velocity_dispersion(cosmo=self.cosmo)

@property
def los_linear_distortions(self):
if self._los_linear_distortions_cache is None:
self._los_linear_distortions_cache = (
self._calculate_los_linear_distortions()
)
return self._los_linear_distortions_cache

def _calculate_los_linear_distortions(self):
"""Line-of-sight distortions in shear and convergence.
:return: kappa, gamma1, gamma2
"""
return self.los_config.calculate_los_linear_distortions(
source_redshift=self.max_redshift_source_class.redshift,
deflector_redshift=self.deflector_redshift,
)

def deflector_magnitude(self, band):
"""Apparent magnitude of the deflector for a given band.
Expand Down
Loading

0 comments on commit d04efde

Please sign in to comment.