1-
2-
31import numpy as np
42import roman_datamodels .stnode as rds
53
@@ -17,30 +15,24 @@ class IntegralNonLinearity(ReferenceType):
1715 method creates the asdf reference file.
1816
1917 Example creation
20- from wfi_reference_pipeline.reference_types.integral_non_linearity.integral_non_linearity import simulate_inl_correction_array
2118 from wfi_reference_pipeline.resources.make_dev_meta import MakeDevMeta
2219 from wfi_reference_pipeline.reference_types.integral_non_linearity.integral_non_linearity import IntegralNonLinearity
2320
24- for det in range(1, 19):
25- arr = simulate_inl_correction_array()
26- # Create meta data object for INL ref file
27- tmp = MakeDevMeta(ref_type='INTEGRALNONLINEARITY')
28- # Update meta per detector to get the right values from the form and update description
29- tmp.meta_integral_non_linearity.instrument_detector = f"WFI{det:02d}"
30- tmp.meta_integral_non_linearity.description = (
31- "To support new integral non-linearity correction development for B21. "
32- "The integral non-linearity correction is applied to each channel, "
33- "numbered left to right from 1 to 32, with each channel containing 128 "
34- "pixels in length."
35- )
36- # Update the file name to match the detector
37- fl_name = 'new_roman_inl_' + tmp.meta_integral_non_linearity.instrument_detector
38- # Instantiate an object and write the file out
39- rfp_inl = IntegralNonLinearity(meta_data=tmp.meta_integral_non_linearity,
40- ref_type_data=arr,
41- clobber=True,
42- outfile=fl_name+'.asdf')
43- rfp_inl.generate_outfile()
21+ arr - assumed here to be a numpy array that is derived in the instrument coordinate reference frame and properly
22+ transformed into the science coordinate reference frame for ingestion into the class.
23+
24+ tmp = MakeDevMeta(ref_type='INTEGRALNONLINEARITY')
25+ rfp_inl = IntegralNonLinearity(meta_data=tmp.meta_integral_non_linearity,
26+ ref_type_data=arr)
27+ rfp_inl.generate_outfile()
28+
29+ Documentation and important notes. This effect comes from the Analog to Digital converters that read out the detector
30+ array. The dimensions of the detector are 4096x4096 with 32 A/D converters and amplifiers reading out every 128 pixels
31+ together at the same time. The correction implemented adjust the measured value to the actual value which varies across
32+ all possible values in UNIT16 from 0 to 65535. This module expects that the transformation from instrument to science
33+ coordinate reference system has already taken place when the input data is passed into the class as ref_type_data.
34+
35+ See https://roman-docs.stsci.edu/data-handbook/wfi-data-levels-and-products/coordinate-systems
4436 """
4537
4638 def __init__ (
@@ -96,10 +88,52 @@ def __init__(
9688 if len (self .meta_data .description ) == 0 :
9789 self .meta_data .description = "Roman WFI integral non linearity reference file."
9890
99- self .inl_correction = ref_type_data
100- _ , num_values = np .shape (ref_type_data )
101- self .value_array = np .linspace (0 , 65535 , num_values , dtype = np .uint16 )
102- # TODO look at references for channel id number - https://roman-docs.stsci.edu/data-handbook-home/wfi-data-format/coordinate-systems
91+ if ref_type_data is None and file_list is None :
92+ msg = "RFP is simulating the INL correction"
93+ print (msg )
94+
95+ ref_type_data = np .asarray (simulate_inl_correction_array ())
96+
97+ # If INL correction arrays are provded as input into class then check everything needed
98+ # for the array to be valid.
99+ elif ref_type_data is not None :
100+ # Convert to numpy array and enforce dtype in one go
101+ ref_type_data = np .asarray (ref_type_data , dtype = np .float64 )
102+
103+ # Print message only for detectors that need LR flip
104+ flip_lr_detectors = {
105+ "WFI03" , "WFI06" , "WFI09" ,
106+ "WFI12" , "WFI15" , "WFI18"
107+ }
108+ if self .meta_data .instrument_detector in flip_lr_detectors :
109+ msg = (
110+ "RFP using input ref_type_data assumed to be transformed into the "
111+ "proper science coordinate reference frame."
112+ )
113+ print (msg )
114+
115+ # Validate array shape
116+ if ref_type_data .ndim != 2 :
117+ raise ValueError (
118+ "IntegralNonLinearity expects ref_type_data to be a 2D array "
119+ "with shape (32, 65536)."
120+ )
121+
122+ n_chan , n_val = ref_type_data .shape
123+ if n_chan != 32 or n_val != 65536 :
124+ raise ValueError (
125+ "Invalid INL correction array shape. "
126+ f"Expected (32, 65536), got ({ n_chan } , { n_val } )."
127+ )
128+
129+ # Set attributes
130+ self .inl_correction = ref_type_data
131+ self .value_array = np .linspace (0 , 65535 , n_val , dtype = np .uint16 )
132+
133+ elif file_list is not None :
134+ raise ValueError (
135+ "Module currently not capable to support file list input."
136+ )
103137
104138 self .outfile = outfile
105139
@@ -117,44 +151,41 @@ def update_data_quality_array(self):
117151
118152 def _make_inl_table (self ):
119153 """
120- Construct the integral non linearity correction table to populate the data model.
121- """
122- inl_table = {}
123-
124- # Assuming self.inl_correction is shaped (32, N)
125- # Each row corresponds to a science chunk (0–31)
126- for sci_chunk in range (32 ):
127- # Reverse channel mapping: 0→32, 1→31, ..., 31→1 for a detector that is
128- # needing to be flipped left right from detector to science coordinates
129- # NOTE: other detectors
130- channel = 32 - sci_chunk
131-
132- inl_table [str (sci_chunk )] = {
133- 'channel' : channel ,
134- 'correction' : self .inl_correction [sci_chunk ]
135- }
154+ Populate the INL table following the Roman INL reference schema.
136155
137- return inl_table
156+ This method is explicit to map instrument channel number and index in the
157+ instrument coordinate reference frame to the science channel number and index
158+ in the science coorcinate reference frame.
138159
160+ Science channels are always numbered from bottom left to right for each detector
161+ from 1-32. The science coordinate reference frame is how the pixels on the detector
162+ look at the sky - after the hardware has been placed and some rotated to account
163+ for the position of detector electronics.
164+
165+ Instrument channels are indexed from 0-31 with the origin including detector electronics
166+ always in the lower left. The transformation from instrument to science coordinates is
167+ illustrated in https://roman-docs.stsci.edu/data-handbook/wfi-data-levels-and-products/coordinate-systems
168+
169+ """
170+ table = {}
171+ for science_chan in range (32 ):
172+ key = f"science_channel_{ science_chan + 1 :02d} "
173+ table [key ] = {
174+ "instrument_channel" : science_chan ,
175+ "correction" : self .inl_correction [science_chan ],
176+ }
177+ return table
139178
140179 def populate_datamodel_tree (self ):
141180 """
142181 Build the Roman datamodel tree for the integral non-linearity reference.
143182 """
144- try :
145- # Placeholder until official datamodel exists
146- inl_ref = rds .IntegralNonLinearity ()
147- except AttributeError :
148- inl_ref = {"meta" : {},
149- "inl_table" : {},
150- "value" : {}
151- }
152-
153- inl_ref ["meta" ] = self .meta_data .export_asdf_meta ()
154- inl_ref ["inl_table" ] = self ._make_inl_table ()
155- inl_ref ["value" ] = self .value_array
156-
157- return inl_ref
183+ inl_datamodel = rds .IntegralnonlinearityRef ()
184+ inl_datamodel ["meta" ] = self .meta_data .export_asdf_meta ()
185+ inl_datamodel ["inl_table" ] = self ._make_inl_table ()
186+ inl_datamodel ["value" ] = self .value_array
187+
188+ return inl_datamodel
158189
159190
160191def simulate_inl_correction_array ():
0 commit comments