예제 #1
0
    def update_params(self, new_Bn = True, **kwargs):
	"""Update all parameters with new values and initialize all matrices
	
	kwargs
	------
	new_B_n:	boolean, if True, recalculate B field and electron density
	
	"""
	self.__dict__.update(kwargs)

	if self.B_gauss:
	    if not self.kL:
		self.kL = 1. / self.r_abell
		kwargs['kL'] = self.kL
	    self.Lcoh = 1. / self.kH
	    kwargs['Lcoh'] = self.Lcoh
	    self.bfield	= Bgaus(**kwargs)		# init gaussian turbulent field

	self.Nd	= int(self.r_abell / self.Lcoh)	# number of domains, no expansion assumed
	self.r	= np.linspace(self.Lcoh, self.r_abell + self.Lcoh, int(self.Nd))

	if new_Bn:
	    self.new_B_n()

	self.T1		= np.zeros((3,3,self.Nd),np.complex)	# Transfer matrices
	self.T2		= np.zeros((3,3,self.Nd),np.complex)
	self.T3		= np.zeros((3,3,self.Nd),np.complex)
	self.Un		= np.zeros((3,3,self.Nd),np.complex)


	return
예제 #2
0
    def update_params(self, new_Bn=True, **kwargs):
        """Update all parameters with new values and initialize all matrices
	
	kwargs
	------
	new_B_n:	boolean, if True, recalculate B field and electron density
	
	"""
        self.__dict__.update(kwargs)

        if self.B_gauss:
            if not self.kL:
                self.kL = 1. / self.r_abell
                kwargs['kL'] = self.kL
            self.Lcoh = 1. / self.kH
            kwargs['Lcoh'] = self.Lcoh
            self.bfield = Bgaus(**kwargs)  # init gaussian turbulent field

        self.Nd = int(self.r_abell /
                      self.Lcoh)  # number of domains, no expansion assumed
        self.r = np.linspace(self.Lcoh, self.r_abell + self.Lcoh, int(self.Nd))

        if new_Bn:
            self.new_B_n()

        self.T1 = np.zeros((3, 3, self.Nd), np.complex)  # Transfer matrices
        self.T2 = np.zeros((3, 3, self.Nd), np.complex)
        self.T3 = np.zeros((3, 3, self.Nd), np.complex)
        self.Un = np.zeros((3, 3, self.Nd), np.complex)

        return
예제 #3
0
class PhotALPs_ICM(object):
    """
    Class for photon ALP conversion in galaxy clusters and the intra cluster medium (ICM) 

    Attributes
    ----------
    Lcoh:	coherence length / domain size of turbulent B-field in the cluster in kpc
    B:		field strength of transverse component of the cluster B-field, in muG
    r_abell:	size of cluster filled with the constant B-field in kpc
    g:		Photon ALP coupling in 10^{-11} GeV^-1
    m:		ALP mass in neV
    n:		thermal electron density in the cluster, in 10^{-3} cm^-3
    Nd:		number of domains, Lcoh/r_abell
    Psin:	random angle in domain n between transverse B field and propagation direction
    T1:		Transfer matrix 1 (3x3xNd)-matrix
    T2:		Transfer matrix 2 (3x3xNd)-matrix		
    T3:		Transfer matrix 3 (3x3xNd)-matrix
    Un:		Total transfer matrix in all domains (3x3xNd)-matrix
    Dperp:	Mixing matrix parameter Delta_perpedicular in n-th domain
    Dpar:	Mixing matrix parameter Delta_{||} in n-th domain
    Dag:	Mixing matrix parameter Delta_{a\gamma} in n-th domain
    Da:		Mixing matrix parameter Delta_{a} in n-th domain
    alph:	Mixing angle
    Dosc:	Oscillation Delta

    EW1: 	Eigenvalue 1 of mixing matrix
    EW2:	Eigenvalue 2 of mixing matrix
    EW3:	Eigenvalue 3 of mixing matrix

    Notes
    -----
    For Photon - ALP mixing theory see e.g. De Angelis et al. 2011 and also Horns et al. 2012
    http://adsabs.harvard.edu/abs/2011PhRvD..84j5030D
    http://adsabs.harvard.edu/abs/2012PhRvD..86g5024H
    """
    def __init__(self, **kwargs):
	"""
	init photon axion conversion in intracluster medium

	Parameters
	----------
	Lcoh:		coherence length / domain size of turbulent B-field in the cluster in kpc, default: 10 kpc
	B:		field strength of transverse component of the cluster B-field, in muG, default: 1 muG
	r_abell:	size of cluster filled with the constant B-field in kpc. default: 1500 * h
	g:		Photon ALP coupling in 10^{-11} GeV^-1, default: 1.
	m:		ALP mass in neV, default: 1.
	n:		thermal electron density in the cluster, in 10^{-3} cm^-3, default: 1.

	Bn_const:	boolean, if True n and B are constant all over the cluster
			if False than B and n are modeled, see notes
	Bgauss:		boolean, if True, B field calculated from gaussian turbulence spectrum,
			if False then domain-like structure is assumed.

	kH:		float, upper wave number cutoff, should be at at least > 1. / osc. wavelength (default = 200 / (1 kpc))
	kL:		float, lower wave number cutoff, should be of same size as the system (default = 1 / (r_abell kpc))
	q:  		float, power-law turbulence spectrum (default: q = 11/3 is Kolmogorov type spectrum)
	dkType:		string, either linear, log, or random. Determine the spacing of the dk intervals 	
	dkSteps: 	int, number of dkSteps. For log spacing, number of steps per decade / number of decades ~ 10
			should be chosen.

	r_core:		Core radius for n and B modeling in kpc, default: 200 kpc
	beta:		power of n dependence, default: 2/3
	eta:		power with what B follows n, see Notes. Typical values: 0.5 <= eta <= 1. default: 1.

	Returns
	-------
	Nothing.

	Notes
	-----

	If Bn_const = False then electron density is modeled according to Carilli & Taylor (2002) Eq. 2:
	    n_e(r)  = n * (1 - (r/r_core)**2.)**(-3/2*beta)
	with typical values of r_core = 200 kpc and beta = 2/3.

	The magnetic field is supposed to follow n_e(r) with (Feretti et al. 2012, p. 41, section 7.1)
	    B(r) = B * (n_e(r)/n) ** eta
	with typical values 1 muG <= B <= 15muG and 0.5 <= eta <= 1
	"""
# --- Set the defaults 
	kwargs.setdefault('g',1.)
	kwargs.setdefault('m',1.)
	kwargs.setdefault('B',1.)
	kwargs.setdefault('n',1.)
	kwargs.setdefault('Lcoh',10.)
	kwargs.setdefault('r_abell',100.)
	kwargs.setdefault('r_core',200.)
	kwargs.setdefault('E_GeV',1.)

	kwargs.setdefault('B_gauss',False)
	kwargs.setdefault('kL',0.)
	kwargs.setdefault('kH',15.)
	kwargs.setdefault('q',-11. / 3.)
	kwargs.setdefault('dkType','log')
	kwargs.setdefault('dkSteps',0)

	kwargs.setdefault('Bn_const',True)
	kwargs.setdefault('beta',2. / 3.)
	kwargs.setdefault('eta',1.)
# --------------------
	self.update_params(**kwargs)

	super(PhotALPs_ICM,self).__init__()


	return
	

    def update_params(self, new_Bn = True, **kwargs):
	"""Update all parameters with new values and initialize all matrices
	
	kwargs
	------
	new_B_n:	boolean, if True, recalculate B field and electron density
	
	"""
	self.__dict__.update(kwargs)

	if self.B_gauss:
	    if not self.kL:
		self.kL = 1. / self.r_abell
		kwargs['kL'] = self.kL
	    self.Lcoh = 1. / self.kH
	    kwargs['Lcoh'] = self.Lcoh
	    self.bfield	= Bgaus(**kwargs)		# init gaussian turbulent field

	self.Nd	= int(self.r_abell / self.Lcoh)	# number of domains, no expansion assumed
	self.r	= np.linspace(self.Lcoh, self.r_abell + self.Lcoh, int(self.Nd))

	if new_Bn:
	    self.new_B_n()

	self.T1		= np.zeros((3,3,self.Nd),np.complex)	# Transfer matrices
	self.T2		= np.zeros((3,3,self.Nd),np.complex)
	self.T3		= np.zeros((3,3,self.Nd),np.complex)
	self.Un		= np.zeros((3,3,self.Nd),np.complex)


	return

    def new_B_n(self):
	"""
	Recalculate Bfield and density, if Kolmogorov turbulence is set to true, new random values for B and Psi are calculated.
	"""

	if self.B_gauss:
	    Bt		= self.bfield.Bgaus(self.r)	# calculate first transverse component
	    self.bfield.new_random_numbers()		# new random numbers
	    Bu		= self.bfield.Bgaus(self.r)	# calculate second transverse component
	    self.B	= np.sqrt(Bt ** 2. + Bu ** 2.)	# calculate total transverse component 
	    self.Psin	= np.arctan2(Bt , Bu)		# and angle to x2 (t) axis -- use atan2 to get the quadrants right

	if self.Bn_const:
	    self.n		= self.n * np.ones(int(self.Nd))	# assuming a constant electron density over all domains
	    if not self.B_gauss:
		self.B		= self.B * np.ones(int(self.Nd))	# assuming a constant B-field over all domains
	else:
	    if np.isscalar(self.n):
		n0 = self.n
	    else:
		n0 = self.n[0]
# check for double beta profile
	    try:		
		if np.isscalar(self.n2):
		    n2 = self.n2
		else:
		    n2 = self.n2[0]
		try:	# check for two different beta values
		    self.beta2	
		    self.n =  n0 * (np.ones(int(self.Nd)) + self.r**2./self.r_core**2.)**(-1.5 * self.beta) +\
			    n2 * (np.ones(int(self.Nd)) + self.r**2./self.r_core2**2.)**(-1.5 * self.beta2) 
		    self.B = self.B * (self.n / (n0 + n2) )**self.eta
		except NameError:
		    self.n =  np.sqrt(n0**2. * (np.ones(int(self.Nd)) + self.r**2./self.r_core**2.)**(-3. * self.beta) +\
			    n2**2. * (np.ones(int(self.Nd)) + self.r**2./self.r_core2**2.)**(-3. * self.beta) )
		    self.B = self.B * (self.n / np.sqrt(n0**2. + n2**2.) )**self.eta
	    except AttributeError:
		self.n =  n0 * (np.ones(int(self.Nd)) + self.r**2./self.r_core**2.)**(-1.5 * self.beta)
		self.B = self.B * (self.n / n0 )**self.eta
	return

    def new_random_psi(self):
	"""
	Calculate new random psi values

	Parameters:
	-----------
	None

	Returns:
	--------
	Nothing
	"""
	self.Psin	= 2. * np.pi * rand(1,int(self.Nd))[0]	# angle between photon propagation on B-field in i-th domain 
	return

    def __setDeltas(self):
	"""
	Set Deltas of mixing matrix for each domain
	
	Parameters
	----------
	None (self only)

	Returns
	-------
	Nothing
	"""

	self.Dperp	= Delta_pl_kpc(self.n,self.E) + 2.*Delta_QED_kpc(self.B,self.E)		# np.arrays , self.Nd-dim
	self.Dpar	= Delta_pl_kpc(self.n,self.E) + 3.5*Delta_QED_kpc(self.B,self.E)	# np.arrays , self.Nd-dim
	self.Dag	= Delta_ag_kpc(self.g,self.B)						# np.array, self.Nd-dim
	self.Da		= Delta_a_kpc(self.m,self.E) * np.ones(int(self.Nd))			# np.ones, so that it is np.array, self.Nd-dim
	self.alph	= 0.5 * np.arctan2(2. * self.Dag , (self.Dpar - self.Da)) 
	self.Dosc	= np.sqrt((self.Dpar - self.Da)**2. + 4.*self.Dag**2.)

	return

    def __setEW(self):
	"""
	Set Eigenvalues
	
	Parameters
	----------
	None (self only)

	Returns
	-------
	Nothing
	"""
	# Eigen values are all self.Nd-dimensional
	self.__setDeltas()
	self.EW1 = self.Dperp
	self.EW2 = 0.5 * (self.Dpar + self.Da - self.Dosc)
	self.EW3 = 0.5 * (self.Dpar + self.Da + self.Dosc)
	return
	

    def __setT1n(self):
	"""
	Set T1 in all domains
	
	Parameters
	----------
	None (self only)

	Returns
	-------
	Nothing
	"""
	c = np.cos(self.Psin)
	s = np.sin(self.Psin)
	self.T1[0,0,:]	= c*c
	self.T1[0,1,:]	= -1. * c*s
	self.T1[1,0,:]	= self.T1[0,1]
	self.T1[1,1,:]	= s*s
	return

    def __setT2n(self):
	"""
	Set T2 in all domains
	
	Parameters
	----------
	None (self only)

	Returns
	-------
	Nothing
	"""
	c = np.cos(self.Psin)
	s = np.sin(self.Psin)
	ca = np.cos(self.alph)
	sa = np.sin(self.alph)
	self.T2[0,0,:] = s*s*sa*sa
	self.T2[0,1,:] = s*c*sa*sa
	self.T2[0,2,:] = -1. * s * sa *ca

	self.T2[1,0,:] = self.T2[0,1]
	self.T2[1,1,:] = c*c*sa*sa
	self.T2[1,2,:] = -1. * c *ca * sa

	self.T2[2,0,:] = self.T2[0,2]
	self.T2[2,1,:] = self.T2[1,2]
	self.T2[2,2,:] = ca * ca
	return

    def __setT3n(self):
	"""
	Set T3 in all domains
	
	Parameters
	----------
	None (self only)

	Returns
	-------
	Nothing
	"""
	c = np.cos(self.Psin)
	s = np.sin(self.Psin)
	ca = np.cos(self.alph)
	sa = np.sin(self.alph)
	self.T3[0,0,:] = s*s*ca*ca
	self.T3[0,1,:] = s*c*ca*ca
	self.T3[0,2,:] = s*sa*ca

	self.T3[1,0,:] = self.T3[0,1]
	self.T3[1,1,:] = c*c*ca*ca
	self.T3[1,2,:] = c * sa *ca

	self.T3[2,0,:] = self.T3[0,2]
	self.T3[2,1,:] = self.T3[1,2]
	self.T3[2,2,:] = sa*sa
	return

    def __setUn(self):
	"""
	Set Transfer Matrix Un in n-th domain
	
	Parameters
	----------
	None (self only)

	Returns
	-------
	Nothing
	"""
	self.Un = np.exp(1.j * self.EW1 * self.Lcoh) * self.T1 + \
	np.exp(1.j * self.EW2 * self.Lcoh) * self.T2 + \
	np.exp(1.j * self.EW3 * self.Lcoh) * self.T3
	return

    def SetDomainN(self):
	"""
	Set Transfer matrix in all domains and multiply it

	Parameters
	----------
	None (self only)

	Returns
	-------
	Transfer matrix as 3x3 complex numpy array
	"""
	if not self.Nd == self.Psin.shape[0]:
	    raise TypeError("Number of domains (={0:n}) is not equal to number of angles (={1:n})!".format(self.Nd,self.Psin.shape[0]))
	self.__setEW()
	self.__setT1n()
	self.__setT2n()
	self.__setT3n()
	self.__setUn()	# self.Un contains now all 3x3 matrices in all self.Nd domains
	# do the martix multiplication
	for i in range(self.Un.shape[2]):
	    if not i:
		U = self.Un[:,:,i]
	    else:
		U = np.dot(U,self.Un[:,:,i])	# first matrix on the left
	return U
예제 #4
0
class PhotALPs_ICM(object):
    """
    Class for photon ALP conversion in galaxy clusters and the intra cluster medium (ICM) 

    Attributes
    ----------
    Lcoh:	coherence length / domain size of turbulent B-field in the cluster in kpc
    B:		field strength of transverse component of the cluster B-field, in muG
    r_abell:	size of cluster filled with the constant B-field in kpc
    g:		Photon ALP coupling in 10^{-11} GeV^-1
    m:		ALP mass in neV
    n:		thermal electron density in the cluster, in 10^{-3} cm^-3
    Nd:		number of domains, Lcoh/r_abell
    Psin:	random angle in domain n between transverse B field and propagation direction
    T1:		Transfer matrix 1 (3x3xNd)-matrix
    T2:		Transfer matrix 2 (3x3xNd)-matrix		
    T3:		Transfer matrix 3 (3x3xNd)-matrix
    Un:		Total transfer matrix in all domains (3x3xNd)-matrix
    Dperp:	Mixing matrix parameter Delta_perpedicular in n-th domain
    Dpar:	Mixing matrix parameter Delta_{||} in n-th domain
    Dag:	Mixing matrix parameter Delta_{a\gamma} in n-th domain
    Da:		Mixing matrix parameter Delta_{a} in n-th domain
    alph:	Mixing angle
    Dosc:	Oscillation Delta

    EW1: 	Eigenvalue 1 of mixing matrix
    EW2:	Eigenvalue 2 of mixing matrix
    EW3:	Eigenvalue 3 of mixing matrix

    Notes
    -----
    For Photon - ALP mixing theory see e.g. De Angelis et al. 2011 and also Horns et al. 2012
    http://adsabs.harvard.edu/abs/2011PhRvD..84j5030D
    http://adsabs.harvard.edu/abs/2012PhRvD..86g5024H
    """
    def __init__(self, **kwargs):
        """
	init photon axion conversion in intracluster medium

	Parameters
	----------
	Lcoh:		coherence length / domain size of turbulent B-field in the cluster in kpc, default: 10 kpc
	B:		field strength of transverse component of the cluster B-field, in muG, default: 1 muG
	r_abell:	size of cluster filled with the constant B-field in kpc. default: 1500 * h
	g:		Photon ALP coupling in 10^{-11} GeV^-1, default: 1.
	m:		ALP mass in neV, default: 1.
	n:		thermal electron density in the cluster, in 10^{-3} cm^-3, default: 1.

	Bn_const:	boolean, if True n and B are constant all over the cluster
			if False than B and n are modeled, see notes
	Bgauss:		boolean, if True, B field calculated from gaussian turbulence spectrum,
			if False then domain-like structure is assumed.

	kH:		float, upper wave number cutoff, should be at at least > 1. / osc. wavelength (default = 200 / (1 kpc))
	kL:		float, lower wave number cutoff, should be of same size as the system (default = 1 / (r_abell kpc))
	q:  		float, power-law turbulence spectrum (default: q = 11/3 is Kolmogorov type spectrum)
	dkType:		string, either linear, log, or random. Determine the spacing of the dk intervals 	
	dkSteps: 	int, number of dkSteps. For log spacing, number of steps per decade / number of decades ~ 10
			should be chosen.

	r_core:		Core radius for n and B modeling in kpc, default: 200 kpc
	beta:		power of n dependence, default: 2/3
	eta:		power with what B follows n, see Notes. Typical values: 0.5 <= eta <= 1. default: 1.

	Returns
	-------
	Nothing.

	Notes
	-----

	If Bn_const = False then electron density is modeled according to Carilli & Taylor (2002) Eq. 2:
	    n_e(r)  = n * (1 - (r/r_core)**2.)**(-3/2*beta)
	with typical values of r_core = 200 kpc and beta = 2/3.

	The magnetic field is supposed to follow n_e(r) with (Feretti et al. 2012, p. 41, section 7.1)
	    B(r) = B * (n_e(r)/n) ** eta
	with typical values 1 muG <= B <= 15muG and 0.5 <= eta <= 1
	"""
        # --- Set the defaults
        kwargs.setdefault('g', 1.)
        kwargs.setdefault('m', 1.)
        kwargs.setdefault('B', 1.)
        kwargs.setdefault('n', 1.)
        kwargs.setdefault('Lcoh', 10.)
        kwargs.setdefault('r_abell', 100.)
        kwargs.setdefault('r_core', 200.)
        kwargs.setdefault('E_GeV', 1.)

        kwargs.setdefault('B_gauss', False)
        kwargs.setdefault('kL', 0.)
        kwargs.setdefault('kH', 15.)
        kwargs.setdefault('q', -11. / 3.)
        kwargs.setdefault('dkType', 'log')
        kwargs.setdefault('dkSteps', 0)

        kwargs.setdefault('Bn_const', True)
        kwargs.setdefault('beta', 2. / 3.)
        kwargs.setdefault('eta', 1.)
        # --------------------
        self.update_params(**kwargs)

        super(PhotALPs_ICM, self).__init__()

        return

    def update_params(self, new_Bn=True, **kwargs):
        """Update all parameters with new values and initialize all matrices
	
	kwargs
	------
	new_B_n:	boolean, if True, recalculate B field and electron density
	
	"""
        self.__dict__.update(kwargs)

        if self.B_gauss:
            if not self.kL:
                self.kL = 1. / self.r_abell
                kwargs['kL'] = self.kL
            self.Lcoh = 1. / self.kH
            kwargs['Lcoh'] = self.Lcoh
            self.bfield = Bgaus(**kwargs)  # init gaussian turbulent field

        self.Nd = int(self.r_abell /
                      self.Lcoh)  # number of domains, no expansion assumed
        self.r = np.linspace(self.Lcoh, self.r_abell + self.Lcoh, int(self.Nd))

        if new_Bn:
            self.new_B_n()

        self.T1 = np.zeros((3, 3, self.Nd), np.complex)  # Transfer matrices
        self.T2 = np.zeros((3, 3, self.Nd), np.complex)
        self.T3 = np.zeros((3, 3, self.Nd), np.complex)
        self.Un = np.zeros((3, 3, self.Nd), np.complex)

        return

    def new_B_n(self):
        """
	Recalculate Bfield and density, if Kolmogorov turbulence is set to true, new random values for B and Psi are calculated.
	"""

        if self.B_gauss:
            Bt = self.bfield.Bgaus(
                self.r)  # calculate first transverse component
            self.bfield.new_random_numbers()  # new random numbers
            Bu = self.bfield.Bgaus(
                self.r)  # calculate second transverse component
            self.B = np.sqrt(Bt**2. +
                             Bu**2.)  # calculate total transverse component
            self.Psin = np.arctan2(
                Bt, Bu
            )  # and angle to x2 (t) axis -- use atan2 to get the quadrants right

        if self.Bn_const:
            self.n = self.n * np.ones(
                int(self.Nd
                    ))  # assuming a constant electron density over all domains
            if not self.B_gauss:
                self.B = self.B * np.ones(int(
                    self.Nd))  # assuming a constant B-field over all domains
        else:
            if np.isscalar(self.n):
                n0 = self.n
            else:
                n0 = self.n[0]
# check for double beta profile
            try:
                if np.isscalar(self.n2):
                    n2 = self.n2
                else:
                    n2 = self.n2[0]
                try:  # check for two different beta values
                    self.beta2
                    self.n =  n0 * (np.ones(int(self.Nd)) + self.r**2./self.r_core**2.)**(-1.5 * self.beta) +\
                     n2 * (np.ones(int(self.Nd)) + self.r**2./self.r_core2**2.)**(-1.5 * self.beta2)
                    self.B = self.B * (self.n / (n0 + n2))**self.eta
                except NameError:
                    self.n =  np.sqrt(n0**2. * (np.ones(int(self.Nd)) + self.r**2./self.r_core**2.)**(-3. * self.beta) +\
                     n2**2. * (np.ones(int(self.Nd)) + self.r**2./self.r_core2**2.)**(-3. * self.beta) )
                    self.B = self.B * (self.n /
                                       np.sqrt(n0**2. + n2**2.))**self.eta
            except AttributeError:
                self.n = n0 * (np.ones(int(self.Nd)) + self.r**2. /
                               self.r_core**2.)**(-1.5 * self.beta)
                self.B = self.B * (self.n / n0)**self.eta
        return

    def new_random_psi(self):
        """
	Calculate new random psi values

	Parameters:
	-----------
	None

	Returns:
	--------
	Nothing
	"""
        self.Psin = 2. * np.pi * rand(1, int(self.Nd))[
            0]  # angle between photon propagation on B-field in i-th domain
        return

    def __setDeltas(self):
        """
	Set Deltas of mixing matrix for each domain
	
	Parameters
	----------
	None (self only)

	Returns
	-------
	Nothing
	"""

        self.Dperp = Delta_pl_kpc(self.n, self.E) + 2. * Delta_QED_kpc(
            self.B, self.E)  # np.arrays , self.Nd-dim
        self.Dpar = Delta_pl_kpc(self.n, self.E) + 3.5 * Delta_QED_kpc(
            self.B, self.E)  # np.arrays , self.Nd-dim
        self.Dag = Delta_ag_kpc(self.g, self.B)  # np.array, self.Nd-dim
        self.Da = Delta_a_kpc(self.m, self.E) * np.ones(int(
            self.Nd))  # np.ones, so that it is np.array, self.Nd-dim
        self.alph = 0.5 * np.arctan2(2. * self.Dag, (self.Dpar - self.Da))
        self.Dosc = np.sqrt((self.Dpar - self.Da)**2. + 4. * self.Dag**2.)

        return

    def __setEW(self):
        """
	Set Eigenvalues
	
	Parameters
	----------
	None (self only)

	Returns
	-------
	Nothing
	"""
        # Eigen values are all self.Nd-dimensional
        self.__setDeltas()
        self.EW1 = self.Dperp
        self.EW2 = 0.5 * (self.Dpar + self.Da - self.Dosc)
        self.EW3 = 0.5 * (self.Dpar + self.Da + self.Dosc)
        return

    def __setT1n(self):
        """
	Set T1 in all domains
	
	Parameters
	----------
	None (self only)

	Returns
	-------
	Nothing
	"""
        c = np.cos(self.Psin)
        s = np.sin(self.Psin)
        self.T1[0, 0, :] = c * c
        self.T1[0, 1, :] = -1. * c * s
        self.T1[1, 0, :] = self.T1[0, 1]
        self.T1[1, 1, :] = s * s
        return

    def __setT2n(self):
        """
	Set T2 in all domains
	
	Parameters
	----------
	None (self only)

	Returns
	-------
	Nothing
	"""
        c = np.cos(self.Psin)
        s = np.sin(self.Psin)
        ca = np.cos(self.alph)
        sa = np.sin(self.alph)
        self.T2[0, 0, :] = s * s * sa * sa
        self.T2[0, 1, :] = s * c * sa * sa
        self.T2[0, 2, :] = -1. * s * sa * ca

        self.T2[1, 0, :] = self.T2[0, 1]
        self.T2[1, 1, :] = c * c * sa * sa
        self.T2[1, 2, :] = -1. * c * ca * sa

        self.T2[2, 0, :] = self.T2[0, 2]
        self.T2[2, 1, :] = self.T2[1, 2]
        self.T2[2, 2, :] = ca * ca
        return

    def __setT3n(self):
        """
	Set T3 in all domains
	
	Parameters
	----------
	None (self only)

	Returns
	-------
	Nothing
	"""
        c = np.cos(self.Psin)
        s = np.sin(self.Psin)
        ca = np.cos(self.alph)
        sa = np.sin(self.alph)
        self.T3[0, 0, :] = s * s * ca * ca
        self.T3[0, 1, :] = s * c * ca * ca
        self.T3[0, 2, :] = s * sa * ca

        self.T3[1, 0, :] = self.T3[0, 1]
        self.T3[1, 1, :] = c * c * ca * ca
        self.T3[1, 2, :] = c * sa * ca

        self.T3[2, 0, :] = self.T3[0, 2]
        self.T3[2, 1, :] = self.T3[1, 2]
        self.T3[2, 2, :] = sa * sa
        return

    def __setUn(self):
        """
	Set Transfer Matrix Un in n-th domain
	
	Parameters
	----------
	None (self only)

	Returns
	-------
	Nothing
	"""
        self.Un = np.exp(1.j * self.EW1 * self.Lcoh) * self.T1 + \
        np.exp(1.j * self.EW2 * self.Lcoh) * self.T2 + \
        np.exp(1.j * self.EW3 * self.Lcoh) * self.T3
        return

    def SetDomainN(self):
        """
	Set Transfer matrix in all domains and multiply it

	Parameters
	----------
	None (self only)

	Returns
	-------
	Transfer matrix as 3x3 complex numpy array
	"""
        if not self.Nd == self.Psin.shape[0]:
            raise TypeError(
                "Number of domains (={0:n}) is not equal to number of angles (={1:n})!"
                .format(self.Nd, self.Psin.shape[0]))
        self.__setEW()
        self.__setT1n()
        self.__setT2n()
        self.__setT3n()
        self.__setUn(
        )  # self.Un contains now all 3x3 matrices in all self.Nd domains
        # do the martix multiplication
        for i in range(self.Un.shape[2]):
            if not i:
                U = self.Un[:, :, i]
            else:
                U = np.dot(U, self.Un[:, :, i])  # first matrix on the left
        return U