Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion stixpy/calibration/detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ def get_srm():
-------

"""
drm_save = read_genx("/Users/shane/Projects/STIX/git/stix_drm_20220713.genx")
# drm_save = read_genx("/Users/shane/Projects/STIX/git/stix_drm_20220713.genx")
drm_save = np.load('/home/jmitchell/software/stixpy-dev/stixpy/config/data/detector/')

drm = drm_save["SAVEGEN0"]["SMATRIX"] * u.count / u.keV / u.photon
energies_in = drm_save["SAVEGEN0"]["EDGES_IN"] * u.keV
energies_in_width = np.diff(energies_in)
Expand Down
107 changes: 107 additions & 0 deletions stixpy/calibration/elut.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from pathlib import Path

import numpy as np

from stixpy.calibration.detector import get_sci_channels
from stixpy.io.readers import read_elut, read_elut_index
import datetime

__all__ = ["get_elut", "get_elut_correction"]

def get_elut(date):
r"""
Get the energy lookup table (ELUT) for the given date

Combines the ELUT with the science energy channels for the same date.

Parameters
----------
date `astropy.time.Time`
Date to look up the ELUT.

Returns
-------

"""

# date = datetime.datetime(2023,11,15,0,0,0)

root = Path(__file__).parent.parent
elut_index_file = Path(root, *["config", "data", "elut", "elut_index.csv"])

elut_index = read_elut_index(elut_index_file)
elut_info = elut_index.at(date)
if len(elut_info) == 0:
raise ValueError(f"No ELUT for for date {date}")
elif len(elut_info) > 1:
raise ValueError(f"Multiple ELUTs for for date {date}")
start_date, end_date, elut_file = list(elut_info)[0]
sci_channels = get_sci_channels(date)

# print('ELUT_FILENAME = ' , elut_file)

elut_table = read_elut(elut_file, sci_channels)

return elut_table

def get_elut_correction(e_ind, pixel_data):
r"""
Get ELUT correction factors

Only correct the low and high energy edges as internal bins are contiguous.

Parameters
----------
e_ind : `np.ndarray`
Energy channel indices
pixel_data : `~stixpy.products.sources.CompressedPixelData`
Pixel data

Returns
-------

"""

energy_mask = pixel_data.energy_masks.energy_mask.astype(bool)
elut = get_elut(pixel_data.time_range.center)
ebin_edges_low = np.zeros((32, 12, 32), dtype=float)
ebin_edges_low[..., 1:] = elut.e_actual
ebin_edges_low = ebin_edges_low[..., energy_mask]
ebin_edges_high = np.zeros((32, 12, 32), dtype=float)
ebin_edges_high[..., 0:-1] = elut.e_actual
ebin_edges_high[..., -1] = np.nan
ebin_edges_high = ebin_edges_high[..., energy_mask]
ebin_widths = ebin_edges_high - ebin_edges_low
ebin_sci_edges_low = elut.e[..., 0:-1].value
ebin_sci_edges_low = ebin_sci_edges_low[..., energy_mask]
ebin_sci_edges_high = elut.e[..., 1:].value
ebin_sci_edges_high = ebin_sci_edges_high[..., energy_mask]
e_cor_low = (ebin_edges_high[..., e_ind[0]] - ebin_sci_edges_low[..., e_ind[0]]) / ebin_widths[..., e_ind[0]]
e_cor_high = (ebin_sci_edges_high[..., e_ind[-1]] - ebin_edges_low[..., e_ind[-1]]) / ebin_widths[..., e_ind[-1]]

numbers = np.array([
0, 1, 2, 3, 4, 5, 6, 7, 13, 14, 15, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31
])

bins = ebin_sci_edges_high - ebin_sci_edges_low

det_indices_top24 = np.array([0, 1, 2, 3, 4, 5, 6, 7, 13, 14, 15, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31])

det_indices_full = np.where(pixel_data.detector_masks.__dict__['masks'] == 1 )[1]

det_indices = [d for i,d in enumerate(det_indices_top24) if d in det_indices_full]

# det_indices = np.where(self.detector_masks.__dict__['masks'] == 1 )[1]

pix_indices = np.where(pixel_data.pixel_masks.__dict__['masks'] == 1 )[1]

bins_actual_1 = ebin_widths[det_indices, :, :]
bins_actual = bins_actual_1[:, pix_indices, :].mean(axis=1).mean(axis=0)


cor = bins / bins_actual
# print('ELUT_COR = ',cor)

return e_cor_high, e_cor_low, cor
4 changes: 4 additions & 0 deletions stixpy/calibration/energy.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ def get_elut(date):
sci_channels = get_sci_channels(date)
elut_table = read_elut(elut_file, sci_channels)


# print('science_channels = ',np.shape(sci_channels))
# print('elut_table_channels = ',np.shape(elut_table))

return elut_table


Expand Down
79 changes: 79 additions & 0 deletions stixpy/calibration/flare_location.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@

import logging

import astropy.units as u
import matplotlib.pyplot as plt
import numpy as np
from astropy.coordinates import SkyCoord
from sunpy.coordinates import HeliographicStonyhurst, Helioprojective, frames
from sunpy.map import Map, make_fitswcs_header
from sunpy.time import TimeRange
from xrayvision.clean import vis_clean
from xrayvision.imaging import vis_to_image, vis_to_map
from xrayvision.mem import mem, resistant_mean

from stixpy.calibration.visibility import calibrate_visibility, create_meta_pixels, create_visibility
from stixpy.coordinates.frames import STIXImaging
from stixpy.coordinates.transforms import get_hpc_info
from stixpy.imaging.em import em
from stixpy.map.stix import STIXMap # noqa
from stixpy.product import Product



__all__ = [
"estimate_flare_location",
]

def estimate_flare_location(sci_file,time_range_sci):

energy_range = [6.,10.] * u.keV
imsize = [512,512] * u.pixel
pixel = [10, 10] * u.arcsec / u.pixel

cpd_sci = Product(sci_file)

meta_pixels_sci = create_meta_pixels(
cpd_sci, time_range=time_range_sci, energy_range=energy_range, flare_location=[0, 0] * u.arcsec, no_shadowing=True
)

vis = create_visibility(meta_pixels_sci)

vis_tr = TimeRange(vis.meta["time_range"])
roll, solo_xyz, pointing = get_hpc_info(vis_tr.start, vis_tr.end)
solo = HeliographicStonyhurst(*solo_xyz, obstime=vis_tr.center, representation_type="cartesian")
center_hpc = SkyCoord(0 * u.deg, 0 * u.deg, frame=Helioprojective(obstime=vis_tr.center, observer=solo))

cal_vis = calibrate_visibility(vis, flare_location=center_hpc)

# order by sub-collimator e.g. 10a, 10b, 10c, 9a, 9b, 9c ....
isc_10_7 = [3, 20, 22, 16, 14, 32, 21, 26, 4, 24, 8, 28]
idx = np.argwhere(np.isin(cal_vis.meta["isc"], isc_10_7)).ravel()

vis10_7 = cal_vis[idx]

bp_image = vis_to_image(vis10_7, imsize, pixel_size=pixel)

vis_tr = TimeRange(vis.meta["time_range"])
roll, solo_xyz, pointing = get_hpc_info(vis_tr.start, vis_tr.end)
solo = HeliographicStonyhurst(*solo_xyz, obstime=vis_tr.center, representation_type="cartesian")
coord_stix = center_hpc.transform_to(STIXImaging(obstime=vis_tr.start, obstime_end=vis_tr.end, observer=solo))
header = make_fitswcs_header(
bp_image, coord_stix, telescope="STIX", observatory="Solar Orbiter", scale=[10, 10] * u.arcsec / u.pix
)
fd_bp_map = Map((bp_image, header))

max_pixel = np.argwhere(fd_bp_map.data == fd_bp_map.data.max()).ravel() * u.pixel
# because WCS axes and array are reversed
max_stix = fd_bp_map.pixel_to_world(max_pixel[1], max_pixel[0])

hpc_x = max_stix.transform_to(frames.Helioprojective).Tx.value
hpc_y = max_stix.transform_to(frames.Helioprojective).Ty.value

stx_x = max_stix.Tx.value
stx_y = max_stix.Ty.value

dictionary = {'stx':np.array([stx_x,stx_y]),
'hpc':np.array([hpc_x,hpc_y])}

return dictionary
146 changes: 135 additions & 11 deletions stixpy/calibration/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
import astropy.units as u
import numpy as np
from astropy.table import Table
import xraydb
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we already use roentgen should either migrate to xraydb or keep using


__all__ = ["get_grid_transmission", "_calculate_grid_transmission"]

from stixpy.coordinates.frames import STIXImaging


def get_grid_transmission(flare_location: STIXImaging):
def get_grid_transmission(ph_energy, flare_location: STIXImaging):
r"""
Return the grid transmission for the 32 sub-collimators corrected for internal shadowing.

Expand All @@ -32,18 +33,141 @@ def get_grid_transmission(flare_location: STIXImaging):
front = Table.read(grid_info / "grid_param_front.txt", format="ascii", names=column_names)
rear = Table.read(grid_info / "grid_param_rear.txt", format="ascii", names=column_names)

transmission_front = _calculate_grid_transmission(front, flare_location)
transmission_rear = _calculate_grid_transmission(rear, flare_location)
total_transmission = transmission_front * transmission_rear
# ;; Orientation of the slits of the grid as seen from the detector side
grid_orient_front_all = front['o']
grid_orient_rear_all = rear['o']

pitch_front_all = front['p']
pitch_rear_all = rear['p']

thickness_front_all = front['thick']
thickness_rear_all = rear['thick']

sc = front['sc']


# fpath = loc_file( 'CFL_subcoll_transmission.txt', path = getenv('STX_GRID') )
column_names_cfl = ["subc_n", "subc_label", "intercept", "slope[1/deg]"]

subcol_transmission = Table.read(grid_info / "CFL_subcoll_transmission.txt", format="ascii", names=column_names_cfl)

subc_n_all = subcol_transmission['subc_n']
subc_label = subcol_transmission['subc_label']
intercept_all = subcol_transmission['intercept']
slope_all = subcol_transmission['slope[1/deg]']


muvals = xraydb.material_mu('W', ph_energy * 1e3, density=19.30, kind='total') / 10 # in units of mm^-1
L = 1 / muvals
# print('L = ', L)
# trans = np.exp(-0.4 / L)
subc_transm=L

det_indices_top24 = np.array([0, 1, 2, 3, 4, 5, 6, 7, 13, 14, 15, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31])
# det_all = np.arange(0,32,1)

# det_indices_top24 = [0, 1, 2, 3, 4, 5, 6, 7, 13, 14, 15, 19,
# 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
idx_full = det_indices_top24
idx = [i for i,x in enumerate(sc-1) if x in det_indices_top24]
# print('idx = ', idx)
# print('lenidx = ',len(idx))
# idx = det_all

# # for i,idx in enumerate(idx_full):
# print(grid_orient_front_all)

grid_orient_front = grid_orient_front_all[idx]
pitch_front = pitch_front_all[idx]
thickness_front = thickness_front_all[idx]

# ;;------ Rear grid
grid_orient_rear = grid_orient_rear_all[idx]
pitch_rear = pitch_rear_all[idx]
thickness_rear = thickness_rear_all[idx]

grid_orient_avg = (grid_orient_front + grid_orient_rear) / 2

flare_loc_deg = flare_location / 3600 #. ;; Convert coordinates to deg
theta = flare_loc_deg[0] * np.cos(np.deg2rad(grid_orient_avg)) + flare_loc_deg[1] * np.sin(np.deg2rad(grid_orient_avg))
# print('THETA = ', theta)

# ;;------ Subcollimator tranmsission at low energies
# idx = np.where(subc_n_all eq (subc_n+1))

intercept = intercept_all[det_indices_top24]
slope = slope_all[det_indices_top24]
subc_transm_low_e = intercept + slope * theta

# ;;------ Transmission of front and rear grid
slit_to_pitch = np.sqrt(subc_transm_low_e)

slit_front = slit_to_pitch*pitch_front
slit_rear = slit_to_pitch*pitch_rear

transm_front = stx_grid_transmission(pitch_front, slit_front, thickness_front, L)
transm_rear = stx_grid_transmission(pitch_rear, slit_rear, thickness_rear, L)

# subc_transm.append(transm_front * transm_rear)

subc_transm = transm_front * transm_rear

# transmission_front = _calculate_grid_transmission(front, flare_location)
# transmission_rear = _calculate_grid_transmission(rear, flare_location)
# total_transmission = transmission_front * transmission_rear

# The finest grids are made from multiple layers for the moment remove these and set 1
final_transmission = np.ones(32)
sc = front["sc"]
finest_scs = [11, 12, 13, 17, 18, 19] # 1a, 2a, 1b, 2c, 1c, 2b
idx = np.argwhere(np.isin(sc, finest_scs, invert=True)).ravel()
final_transmission[sc[idx] - 1] = total_transmission[idx]

return final_transmission
# final_transmission = np.ones(32)
# sc = front["sc"]
# finest_scs = [11, 12, 13, 17, 18, 19] # 1a, 2a, 1b, 2c, 1c, 2b
# idx = np.argwhere(np.isin(sc, finest_scs, invert=True)).ravel()
# final_transmission[sc[idx] - 1] = total_transmission[idx]

# print('subc = ',subc_transm)
return subc_transm


def stx_grid_transmission(pitch, slit, thickness, L):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a docstring and test


ds = 5e-3
dh = 5e-2
Comment on lines +133 to +134
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic numbers need at least comment and ideally units


# n_energies = np.shape(L)[0]
# n_subc = np.shape(pitch)

slit_rep = slit.reshape(1, len(slit))
pitch_rep = pitch.reshape(1, len(pitch))
H_rep = thickness.reshape(1, len(thickness))
L_rep = L.reshape(len(L), 1)

# print('slit = ',slit[0])
# print('pitch = ',pitch[0])
# print('h_rep = ',thickness[0])

# slit_rep = np.tile(slit, (n_energies, 1))
# pitch_rep = np.tile(pitch, (n_energies, 1))
# H_rep = np.tile(thickness, (n_energies, 1))
# L_rep = np.tile(L, (n_subc, 1)).T

# print(np.shape(slit_rep))
# print(np.shape(pitch_rep))
# print(np.shape(H_rep))
# print(np.shape(L_rep))

# ;; Transmission for a wedge shape model for grid imperfections
g0 = slit_rep / pitch_rep + (pitch_rep - slit_rep) / pitch_rep * np.exp( - H_rep / L_rep )
ttt = L_rep / dh * ( 1. - np.exp(- dh / L_rep ) )
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ask paolo

g1 = 2. * ds / pitch_rep * (ttt - np.exp( - H_rep / L_rep ))

# print('g0 = ',g0[:,0])
# print('g1 = ',g1[:,0])

g_transmission = g0 + g1

# print('transmission = ',g_transmission)

return g_transmission


def _calculate_grid_transmission(grid_params, flare_location):
Expand Down
Loading