;************************************************************************
     pro for_intlos,rbite,thbite,phbite,alfabite,losoffbite,LosPramsStruct,ObsPramsStruct,$
GridPramsStruct,r3D,theta3D,phi3D,r2,cmer2,x2,tau2,dlos,doprint,verbose=verbose

;+
; Name: FOR_INTLOS
;
; Purpose:  	DETERMINE spherical coords along LOS in viewer's coordinate system, also determine line of sight vector and differential line of sight dlos

;
; INPUTS
;
;    rbite,thbite,phbite - reduced POS array
;	  (rbite is radial position at the intersection of the LOS and the X=0 plane)
;	  (remember thbite goes 0 to 360 counterclockwise)
;    alfabite - limb
;    LosPramsStruct,ObsPramsStruct,GridPramsStruct
;    doprint = whether to print verbose stuff
;    losoffbite -- GridPramsStruct.LOSoffset -- if an array, put into bite size
;     for_get_grid will store one of four things here:
;      1) if USER and POS set and LOSOFFSET not inputted as keyword, will be the distance of the user points
;	(inputted in spherical geometry r3D,theta3D,phi3D)
;	along the LOS from the intersection of the LOS with either X=0 or TS (XLOS, or TLOS/TAU)
;      2) if USER and POS set and LOSOFFSET inputted by user, will be the sum of that input 
;	and the LOS distance as in 1
;      3) if USER and POS not set, all LOSOFFSET information will be ignored
;		because integration will be centered on X=0 (XLOS) or TS (TLOS, TAU)
;	*note -- I had thought to center integrals on the USER point, but that way madness lies
;	 the  integral should be over infinite LOS, and if not, optimized to includes most emission/brightness
;	  -- which is why we center on X=0 or TS
;	 however, we will need to be careful to zero out LOSOFFSET and to deal with above solar disk
;	 as we would for nonuser Grids
;      4) if USER not set, and POS set, and LOSOFFSET inputted
;	will be the user input value
;      5) if USER not set, and POS not set, and LOSOFFSET inputted
;	 will be ignored (should in fact be forced to 0)
;
; KEYWORD INPUT
;	verbose
;
; OUTPUTS
;
;	r3D,theta3D,phi3D - spherical coords along LOS, in viewer's system of dimension [nlos,nactualbite]
;		(note nlos can be reduced from original input if OBSLOSLIMIT is set to a maximum radius)
;	r2- pos position of LOS intersection but put in [nlos,nactualbite] matrix 
;	cmer2 - central meridian in [nlos,nactualbite] matrix
;	tau2 LOS angle along line of sight, dimension [nlos,nactualbite]
;	x2 - line of sight position vector, dimension [nlos,nactualbite]
;		for both XLOS and TLOS - XLOS forces parallel lines of sight
;	dlos - differential line of sight, dimension [nlos,nactualbite]
;		this is dx -- even if we use TAU it is converted
;		used in for_euvsxrcalc and for_radiocalc although no longer explicitly
;		used in for_integrate (int_tabulated function doesn't need as such - implicit in array)
;
; Called by: FOR_INTENSINT
;
; History: Written by Sarah Gibson 2015
;-
;  	Feb 5 2016: changed definition of Tau so
;		mimics XLOS in that it is always negative
;		behind POS independent of limb -- see
;		comments below
;	Feb 2018: added Elongation capability
;		for lines of sight not close to sun
;		applications e.g. to heliospheric imaging
;       July 2018: added test for POS before elongation problem
;	April 2020: BUG FIX -- inconsistency between tau2 and dlos
;	Nov 2020: BUG FIX -- asymmetric integrations in tau were being thwarted
;		by recalculation associated with obsloslimit
;	Jan 2021: edited VERBOSE stuff
;	Sep 2021: fix(round so that nlos didn't end up long
;	Oct 2021: added fix for USER LOS=tau integration 
;		LOSoffset needs to be taken into account in determiing POS intersection
;		of line of sight
;	Dec 2021 - Jan 2022: passed through distobs, defined tau2 for xlos case
;		added comments
;	March 2022 -- fixed bug where dlos didn't have LOS dimension
;		added TLOS capability
;		also introduced RBITEUSE so that RBITE not changed
;		also changed xoffset --> losoffset
;               removed unnecessary rloslimit change of nlos for tau -- after all
;                those points removed later for all choices LOSUSE
;		 and dealt with in for_intensint by reducing to *rmoduse* etc
;	April 2022 -- added keyword nostretch
;	July 2022 - fixed problem with USER losoffset
;	Sept 2022 -- added back revised conditional for POS ne 0 TLOS
;		(search on shouldnt)
;            was printing unnecessary error	
;       Oct 2022 -- changed array ( to [ (with help from Vema Panditi)
;		added some clarifying comments about how LOSOFFSET used
;		fixed bug where stretch was multiplying by < 1 everywhere
;		updated integrals to ignore losoff unless POS set
;		and fixed bug where TLOS was not centering on POS shifted by LOSOFFSET
;		and simplified TLOS vs TAU formulation and overal structure of code
;	Nov 2022 -- moved all points below r3d=1  to 1000 to ignore
;	Apr 2023 -- commented out NEVIII blocking disk
;       May 2022 -- gave same capability for other UV specpol lines
;                       (OVI, LYA-- be careful esp. for LYA 
;                       -- just for tests because 
;                       generally chromo not treated in FORWARD)
;	Jan 2025 -- removed moving points to 1000 since that caused
;		problems for constant density toy model
;		Better to put in a check where the model is evaluated
;		in for_intensint for r=0 for some of these
;		for now commented out
;		also fixed a print statement about taumax change to include
;		 highbite removal
;	Mar 2025 -- updated last single value atan
;		   changed nostretch default to 1 (no stretching) because concerned it makes sense in general
;       Apr 2025 --
;             changed limits on losmin to double precision pi over two
;		* but there is still a tlitch right there so made just close

;
;  useful variables

; for debugging
;verbose=1
;doprint=1

pid2= !dpi/2.d0
nactualbite=n_elements(rbite)

;
; distance from observer
; usually 1 AU 215 Rsun
; but e.g. Probe, Orbiter will change this
; so in future this can be a parameter
;  note if one goes too close in, the Thomson Sphere shrinks to be partly inside the Sun.

distobs=GridPramsStruct.DistObs

;
; check elongation --
; if rbite greater than about 5, print warning
; better to step constant angle
;

elongbite=atan(rbite,distobs)

if max(elongbite) gt 0.02 and strupcase(LosPramsStruct.LosUse) eq 'XLOS' and ObsPramsStruct.Pos eq 0  then begin
 print,'WARNING: max elongation=',max(elongbite)
 print,'Consider switching LOSUSE to TAU or TLOS for nonparallel lines of sight.'
endif

;
; figure out starting point on LOS (losmin)
;	 and number of points along LOS (nlos)
 
losmin=LosPramsStruct.LosMin
nlos=LosPramsStruct.NLos
losint=LosPramsStruct.LosInt

;
; scale by elongation if LOSUSE=TLOS
;    UNLESS keyword "nostretch" is set
;    ***WARNING --- I am rethinking the value of NOSTRETCH and changing default to 1 (off) 
;   note this will give LOSMIN, LOSINT dimension of rbite (POS)
;

if tag_exist(LosPramsStruct,'NoStretch') then begin
  if is_number(LosPramsStruct.NoStretch) then nostretch=LosPramsStruct.NoStretch $
    else nostretch=1
endif else nostretch=1

if strupcase(LosPramsStruct.LosUse) eq 'TLOS' and nostretch eq 0 then begin
 if verbose eq 1 then begin
    print,'losmin orig',minmax(losmin)
    print,'losint orig',minmax(losint)
 endif
 if ObsPramsStruct.Pos eq 0 then begin
  losmaxorig=losmin+losint*(nlos-1)
; this was a bug
;  losmin=losmin*distobs*sin(elongbite)
;  losmaxnew=losmaxorig*distobs*sin(elongbite)
  losmin=losmin/cos(elongbite)
  losmaxnew=losmaxorig/cos(elongbite)
  losint=(losmaxnew-losmin)/(nlos-1)  
 endif
endif

; POS dimension matrix
bitenorm = replicate(1.,(nactualbite))
; LOS dimension matrix
losnorm = replicate(1.,nlos)

losintbite= losint*bitenorm

cmer2=losnorm#phbite
r2 = losnorm#rbite
th2=losnorm#thbite
elong2=losnorm#elongbite
alfa2=losnorm#alfabite
losoff2=losnorm#losoffbite
tau2=r2*0.d0

;  IF INTEGRATING OVER TAU -- LOS ANGLE
;    or TLOS -- constant distance along (not parallel) LOS

if strupcase(LosPramsStruct.LosUse) eq 'TAU' $
  or strupcase(LosPramsStruct.LosUse) eq 'TLOS' then begin

       if ObsPramsStruct.Pos ne 0 then begin
           if strupcase(LosPramsStruct.LosUse) eq 'TAU' then print,'for_intlos,losuse=tau,pos=',ObsPramsStruct.Pos,': shouldnt happen'
           if strupcase(LosPramsStruct.LosUse) eq 'TLOS' and ObsPramsStruct.Pos ne 2 $
		then print,'for_intlos, losuse=tlos, pos=',ObsPramsStruct.Pos,': shouldnt happen'
	endif
;
; calculate spherical coords along lines of sight
;

; commented out below -- the old way assuming zero elongation
;   (new version should reduce to this for elong=0)
;
;        dlos = losint*r2/cos(tau2)/cos(tau2)
;        r3D=(1/cos(tau2))*r2
;
;        theta3D = acos(cos(tau2)*cos(th2))
;
; changed below to abs(sin(th2)))
;   (remember th2 is 0 to 360)
;  so that now tau negative is always behind plane of sky
;  so integration along LOS occurs in same order as for XLOS
;  matters for lyman absorption, or integration asymmetric about POS
;
;  (alfa identifies which limb we are looking at)
;
;        phi2=atan(tan(tau2)*(1/abs(sin(th2))))
;        phi3D = alfa2*(pid2 - phi2)
;

;
; now recalculate more generally
;
; it's helpful to first calculate
; 3D coordinates of point along (no longer necessarily parallel) lines of sight
; the loci of intersections of LOS with its closest appoach to sun
; make up the Thomson sphere
;
; note r2 and th2 are still plane of sky (X=0 plane) intersecting radius and polar angle
; can convert these to spherical coordinates of point of intersection of LOS with Thomson Sphere 
; rp2, thp2, php2
;
; essentially we are making our LOS spacing cluster at the TS. This makes sense for white light
; but not other observables -- but, only WL will be out so far as for this to matter
; and it is not wrong, just perhaps not optimal LOS spacing for other wavelengths, anyway
; (it is optimal for WL, which is the point)

	rp2=r2*cos(elong2)
	rpbite=rbite*cos(elongbite)

; don't actually use these next, but useful for reference
;
;	thp2=acos(cos(elong2)*cos(th2))
;
;  elong between 0 and pi/2, so cos(elong) always positive
;
;  cos(th2),cos(thp2) positive in north, negative in south as they should be
;     thp2 has lost information about east west, but that is ok, it is retained in alfa
;
;	php2 = asin(sin(elong2)/sin(thp2))
;  which is equivalent to
;	php2 = atan(tan(elong2)*(1/abs(sin(th2))))

; now make sure tau is within -pi/2 to pi/2 limits
; and if elong significant make sure in front of the observer
; so maximum positive angle is pi/2-elong
;
; note if we put a value for taumin close to -1.57 we can get into the
; situation of all bite points needing to be changed because elongbite is > 0
;
; in this case we will keep the number of points and change the resolution
; because this is a hard cutoff and we need to apply it for all
; cases (including CoMP, etc. which has problems with matrices where NLOS varies from point to point)
;
;        low=-1.57d0
;        highbite=1.57d0-elongbite
        low=-.99999d0*!dpi/2.d0
        highbite=.99999d0*!dpi/2.d0-elongbite
;        low=-!dpi/2.d0
;        highbite=!dpi/2.d0-elongbite
;
; ALSO we need to check if there is a LOS offset 
; either because of user keyword input LOSOFFSET and POS slice
; or because doing gridtype='USER' and POS slice (both of which will only happen for TLOS and POS=2),
; and we need to adjust our center for POS integral accordingly

        if strupcase(LosPramsStruct.LosUse) eq 'TLOS' then begin
	 if ObsPramsStruct.Pos eq 2 then begin
; this is the angle from the TS, just for reference
;	  gamma2 = atan(losoffbite,rpbite)
; and this is the radial distance to it
;  but this is also for reference because we are going to measure
;   tau from the TS still, since that is where teh right angle is
;	  rpbitep=sqrt(rpbite*rpbite + losoffbite*losoffbite)
          losoffbiteuse=losoffbite
	 endif else begin
	  losoffbiteuse=0.
         endelse
; this next includes losoffbiteuse explicitly and is in radians
         tauminbite=atan(losmin+losoffbiteuse,rpbite)>low
; note this next does not include losoffbiteuse explicitly (although an adjustment
;	has been made to aovid going below -pid2 that takes it into account) 
;	and it is in distance units
         losminbite=tan(tauminbite)*rpbite-losoffbiteuse
; this nextdoes include losoffbiteuse explicitly
         losmaxbite=losminbite+losoffbiteuse+losintbite*(nlos-1)
; as does this
         taumaxbite=atan(losmaxbite,rpbite)
         losmaxbitenew=tan(taumaxbite<highbite)*rpbite
         losintbitenew=(losmaxbitenew-losminbite-losoffbiteuse)/(nlos-1)  
; and this next is where we shift the start point losmin to include losoffbiteuse
;  thus centering the integral on the position losoffbite - IF POS slice
         x2 = (losnorm#(losminbite+losoffbiteuse) + findgen(nlos)#losintbitenew)
	 tau2=atan(x2,rp2)
	 if verbose eq 1 then begin
            print,'losmin update',minmax(losminbite)
            print,'losmax update',minmax(losmaxbitenew)
         endif
        endif else begin
; now doing LOS = TAU (constant angle in radians)
; we ignore losoffbite for TAU because always an integral
;  but add it *0 to get dimensions right
         tauminbite=losmin + losoffbite*0. >low 
         taumaxbite=tauminbite+losintbite*(nlos-1)
	 losintbitenew=(taumaxbite<highbite-tauminbite)/(nlos-1)  
         tau2 = (losnorm#tauminbite + findgen(nlos)#losintbitenew)
	 x2=rp2*tan(tau2)
	 if verbose eq 1 then begin
            print,'taumin update',minmax(tauminbite)
            print,'taumax update',minmax(taumaxbite<highbite)
         endif
	endelse
	if verbose eq 1 then begin
            print,'losint update',minmax(losintbitenew)
	endif
        testmax=where(taumaxbite ge highbite,tmx) 
	if tmx gt 0 then begin
         if verbose ne 0 and doprint eq 1 then begin
	  print,"**********"
	  print,"**********"
	  print,"**********"
	  print,"Adjusting spacing on "+strtrim(string(tmx),2)+" points so that no points are behind observer"
	  print,"pid2 minus elong minmax="
	  print,strtrim(string(minmax(highbite)),2)
	  print,"elong minmax="
	  print,strtrim(string(minmax(elongbite)),2)
	  print,"**********"
	  print,"**********"
	  print,"**********"
  	 endif
  	endif

;
; relative to rp2, dlos and r3D are same as parallel LOS case above
;

        r3D=(1/cos(tau2))*rp2

;        dlos = losint*rp2/cos(tau2)/cos(tau2)
; this was a bug -- need to use losintbite to be consistent with tau2
;
;   	note -- for TAU, losintbite is by definition dtau and constant
;		and has units of radians so needs to be converted
;		to units of length
;
        if strupcase(LosPramsStruct.LosUse) eq 'TAU' then $
	   dlos = (losnorm#losintbitenew)*rp2/cos(tau2)/cos(tau2) $
;
; (i.e., los = rp2*tan(tau), so dlos= rp2*sec^2(tau) dtau
;
 	else dlos = losnorm#losintbitenew

;
; theta3D reduces to parallel case for elong2=0
;  and to thp2 for tau2=0
; (note tau+elong=out of sky plane angle xi)
; 

        argument=cos(tau2+elong2)*cos(th2)
        theta3D = acos(argument)

;
;  tau2+elong2 is constrained to be always less than  pi/2
;     so cos(theta3D) is positive in the north, negative in the south
;	and does not know about east/west

	phi2 = asin(sin(tau2+elong2)/sin(theta3D))

;  which is equivalent to
;	phi2 = atan(tan(tau2+elong2)*(1/abs(sin(th2))))
;
; for elong2=0 this reduces to old version

;
; this next takes care of eastern/western hemisphere
;  (remember, phbite info is central merdian and is preserved in cmer2;
;	so here we just need to know +/- from central meridian)
;

        phi3D = alfa2*(pid2 - phi2)

;
; Sanity check:
;
; theta3D unchanged at equator (theta3D=thp2=th2=90.)
;  php2=elong2; phi2=tau2+elong2
;
; th2=0 (POS polar angle 0) thp2=elong2; theta3D = tau2+elong2
;  (should never be exactly 0 because we removed
;  these points in for_intpos.pro)
;  php2=pi/2; phi2=pi/2
;
;    at tau2 = 0 
;	cos(theta3D) = cos(elong2)*cos(th2) = cos(thp2)
;	phi3D=pi/2 - php2
;
;    foreground (relative to Thomson sphere)
;	at tau2=pi/2-elong2; cos(theta3d)=0; theta3d=90 
;	 sin(phi2)=1., phi2=pi/2, phi3D=0
;
;    background 
;	at tau2 = -elong2 
;	  cos(theta3D) = cos(th2)  
;	  phi3D=pi/2
;	   (intersection wit POS)
;	at tau2 = -pi/2 
;	  cos(theta3D) = sin(elong2)*cos(th2)
;	  sin(phi2)= cos(elong2)/sin(theta3D)
;	  (for elong2=0 symmetric with foreground) 

endif

;  IF INTEGRATING OVER X -- LOS DISTANCE
;
; Note by definition -- these are parallel lines of sight
; if far out, there will be a warning suggesting switch to TAU
;

if strupcase(LosPramsStruct.LosUse) eq 'XLOS' then begin
     th2 = losnorm#acos(cos(thbite))
     ph2 = losnorm#alfabite*pid2
     y2 = r2*sin(th2)*sin(ph2)
     z2 = r2*cos(th2)
     if strupcase(GridPramsStruct.GridType) eq 'CARRMAP' and strupcase(GridPramsStruct.Limb) eq 'CMER' then begin
       y2=r2*0.d0
       z2=r2
       r2=abs(r2)
     endif
     xlos = losmin + losint*findgen(nlos)
; losoffset -- only should be used for /POS, see above
     if ObsPramsStruct.Pos ne 0 then begin
      if ObsPramsStruct.Pos eq 2 then print,'for_intlos, losuse=xlos, pos=2: shouldnt happen'
      x2 = xlos#bitenorm + losoff2
     endif else x2 = xlos#bitenorm
     dlos = losint*r2/r2

    if is_number(LosPramsStruct.DoDisk) then if LosPramsStruct.DoDisk ne 0. then if strpos(strupcase(ObsPramsStruct.Instrument),'OMP') lt 0 and $
         strupcase(ObsPramsStruct.Instrument) ne 'CORMAG' and $
;         strupcase(ObsPramsStruct.IClass) ne 'UV SPECTROPOLARIMETERS' and $
;         strpos(strupcase(ObsPramsStruct.Instrument),'OVI') lt 0 and $
;         strpos(strupcase(ObsPramsStruct.Instrument),'NEVIII') lt 0 and $
;         strpos(strupcase(ObsPramsStruct.Instrument),'MGIX') lt 0 and $
;         strupcase(ObsPramsStruct.Instrument) ne 'LYA' and $
         strupcase(ObsPramsStruct.Instrument) ne 'WL' and $
         strupcase(ObsPramsStruct.Instrument) ne 'KCOR' then begin

     	      incres=1
     	      if tag_exist(LosPramsStruct,'IncRes') then if is_number(LosPramsStruct.IncRes) then incres=LosPramsStruct.IncRes
     	      if strupcase(ObsPramsStruct.Instrument) eq 'RADIO' then incres=1

	      if incres ne 0 then begin
;
;  use higher resolution above disk
;  first calculate how fine a step to take if taking nlos steps above (positive x) the x=0 plane of sky
;       to finish at the closest point towards the Earth in the original LOS integral (thus, putting all
;       the points that otherwise lie behind the Sun to use in increasing (doubling if symmetric) the resolution in front of it)
;
;  be careful with POS slices: if losoffset is set it can throw things off here
;
                    if ObsPramsStruct.Pos eq 0 then losintfine=abs(losmin+losint*double(nlos-1))/double(nlos-1) else losintfine=losint/2.d0
                    xlos = losintfine*findgen(nlos)
;		print,'losint=',losint,' losintfine=',losintfine
                    x2disk = xlos#bitenorm
                    shiftdisk=(LosPramsStruct.DoDisk^2-y2^2-z2^2)
                    frontbelowdisk = where(r2 le LosPramsStruct.DoDisk,dfront)
                    if dfront gt 0 then begin
;
; there is a possibility of sqrt(neg) number here if rounding error on number close to zero
;
			if min(shiftdisk[frontbelowdisk]) lt -1d-8 then stop
                        x2[frontbelowdisk]=x2disk[frontbelowdisk]+sqrt(abs(shiftdisk[frontbelowdisk]))
                        dlos[frontbelowdisk] = losintfine
	 	    endif
              endif else begin
;
; may eventaully add incres=2 option to have half the points in the first
; scale height - for now the other option is zero, throw away points
;
                    shiftdisk=(LosPramsStruct.DoDisk^2-y2^2-z2^2)
                    backbelowdisk = where(r2 le LosPramsStruct.DoDisk and x2 lt 0.,c)
                    frontbelowdisk = where(r2 le LosPramsStruct.DoDisk and x2 gt 0.,d)
                    if d gt 0 then x2[frontbelowdisk]=x2[frontbelowdisk]+sqrt(shiftdisk[frontbelowdisk])
;
; set points behind sun to so far away signal should be negligable
;
;                    if c gt 0 then x2[backbelowdisk]=-1000.d0
;  changed to inside sun - will be dealt with in for_intensint
                    if c gt 0 then x2[backbelowdisk]=0.d0
              endelse
    endif

    r3D=sqrt(r2*r2+x2*x2)
    theta3D = acos(z2/r3D)
    phi3D = atan(y2,x2)
;
; should really define tau2 otherwise it will be zero and do weird
; things to XPOLG calculation in for_pinpoint
; but be careful because this implicitly assumes parallel lines of sight
;  AND it is the angular distance from the X=0 plane
;   NOT the Thomson Sphere
;
    tau2=asin(x2/r3D)
endif

; if r is ge rloslimit  set to far away
;

rloslimit=ObsPramsStruct.ObsLosLimit
;if rloslimit le 1. then rloslimit=1000.
;test = where(r3D ge rloslimit,p)
;if p gt 0 and strupcase(ObsPramsStruct.Instrument) ne 'RADIO' then r3D[test]=1000.d0

; changed and 0 point will be dealt with in for_intensint
if rloslimit gt 1. then begin
 test = where(r3D ge rloslimit,p)
 if p gt 0 and strupcase(ObsPramsStruct.Instrument) ne 'RADIO' then r3D[test]=0.d0
endif

;
; it is possible for r3D to be less than one
;  if distobs is pushed very close to the Sun
;  of if user points are combined with an losoffset
;  this will be dealt with by setting to NaN which
;  will result in NaNs for all LOS-integrated quantities
; note this can't happen for XLOS except for
;  throwaway points below disk that will be sent to r3D>1000
;  as defined above; these individual points along the LOS will
;  be ignored in for_intlos, but the entire LOS does not need to be 
;  thrown away since the foreground points are good.
;
; be careful though because sometimes r3d=1.0 exactly will trigger
; the NaN -- might be better to just throw all of them to r3D>1000
; -- trying that now
;  **THIS HAS CHANGED - throwaway points are being put to r=0

test = where(r3D lt 1.0,p)
if p gt 0 then $
;    r3D[test]=1000.d0
    r3D[test]=0.d0

; below were some other things I tried
;
;if p gt 0 and strupcase(ObsPramsStruct.Instrument) ne 'RADIO' and $
;   strupcase(GridPramsStruct.GridType) ne 'USER' $
;   then r3D[test]=sqrt(-1)
;if p gt 0 and $
;   strupcase(GridPramsStruct.GridType) eq 'USER' $
;   then r3D[test]=1000.d0

; for debugging (if set above)
;verbose=0
;doprint=0

end