Skip to content

Commit

Permalink
Remove all core function wrappers
Browse files Browse the repository at this point in the history
  • Loading branch information
WilliamJamieson committed Oct 11, 2023
1 parent 844ad41 commit f30e939
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 49 deletions.
4 changes: 2 additions & 2 deletions src/stcal/ramp_fitting/ols_cas22/_core.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ cpdef enum RampJumpDQ:
JUMP_DET = 4


cdef float threshold(Thresh thresh, float slope)
cpdef float threshold(Thresh thresh, float slope)
cdef float get_power(float s)
cdef deque[stack[RampIndex]] init_ramps(int[:, :] dq)
cdef ReadPatternMetadata metadata_from_read_pattern(list[list[int]] read_pattern, float read_time)
cpdef ReadPatternMetadata metadata_from_read_pattern(list[list[int]] read_pattern, float read_time)
40 changes: 38 additions & 2 deletions src/stcal/ramp_fitting/ols_cas22/_core.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,16 @@ Functions
Return the power from Casertano+22, Table 2
threshold
Compute jump threshold
- cpdef gives a python wrapper, but the python version of this method
is considered private, only to be used for testing
init_ramps
Find initial ramps for each pixel, accounts for DQ flags
- A python wrapper, _init_ramps_list, that adjusts types so they can
be directly inspected in python exists for testing purposes only.
metadata_from_read_pattern
Read the read pattern and derive the baseline metadata parameters needed
- cpdef gives a python wrapper, but the python version of this method
is considered private, only to be used for testing
"""
from libcpp.stack cimport stack
from libcpp.deque cimport deque
Expand Down Expand Up @@ -108,7 +114,7 @@ cdef inline float get_power(float signal):
return PTABLE[1][i]


cdef inline float threshold(Thresh thresh, float slope):
cpdef inline float threshold(Thresh thresh, float slope):
"""
Compute jump threshold
Expand Down Expand Up @@ -198,9 +204,39 @@ cdef inline deque[stack[RampIndex]] init_ramps(int[:, :] dq):
return pixel_ramps


def _init_ramps_list(np.ndarray[int, ndim=2] dq):
"""
This is a wrapper for init_ramps so that it can be fully inspected from pure
python. A cpdef cannot be used in that case becase a stack has no direct python
analog. Instead this function turns that stack into a list ordered in the same
order as the stack; meaning that, the first element of the list is the top of
the stack.
Note this function is for testing purposes only and so is marked as private
within this private module
"""
cdef deque[stack[RampIndex]] raw = init_ramps(dq)

# Have to turn deque and stack into python compatible objects
cdef RampIndex index
cdef stack[RampIndex] ramp
cdef list out = []
cdef list stack_out
for ramp in raw:
stack_out = []
while not ramp.empty():
index = ramp.top()
ramp.pop()
# So top of stack is first item of list
stack_out = [index] + stack_out

out.append(stack_out)

return out


@cython.boundscheck(False)
@cython.wraparound(False)
cdef ReadPatternMetadata metadata_from_read_pattern(list[list[int]] read_pattern, float read_time):
cpdef ReadPatternMetadata metadata_from_read_pattern(list[list[int]] read_pattern, float read_time):
"""
Derive the input data from the the read pattern
Expand Down
36 changes: 1 addition & 35 deletions src/stcal/ramp_fitting/ols_cas22/_wrappers.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ import numpy as np
cimport numpy as np

from libcpp cimport bool
from libcpp.stack cimport stack
from libcpp.deque cimport deque

from stcal.ramp_fitting.ols_cas22._core cimport RampIndex, ReadPatternMetadata, Thresh, threshold
from stcal.ramp_fitting.ols_cas22._core cimport metadata_from_read_pattern as c_metadata_from_read_pattern
from stcal.ramp_fitting.ols_cas22._core cimport init_ramps as c_init_ramps
from stcal.ramp_fitting.ols_cas22._core cimport ReadPatternMetadata, Thresh

from stcal.ramp_fitting.ols_cas22._fixed cimport FixedValues
from stcal.ramp_fitting.ols_cas22._fixed cimport fixed_values_from_metadata as c_fixed_values_from_metadata
Expand All @@ -16,36 +12,6 @@ from stcal.ramp_fitting.ols_cas22._pixel cimport Pixel
from stcal.ramp_fitting.ols_cas22._pixel cimport make_pixel as c_make_pixel


def metadata_from_read_pattern(list[list[int]] read_pattern, float read_time):
return c_metadata_from_read_pattern(read_pattern, read_time)


def init_ramps(np.ndarray[int, ndim=2] dq):
cdef deque[stack[RampIndex]] raw = c_init_ramps(dq)

# Have to turn deque and stack into python compatible objects
cdef RampIndex index
cdef stack[RampIndex] ramp
cdef list out = []
cdef list stack_out
for ramp in raw:
stack_out = []
while not ramp.empty():
index = ramp.top()
ramp.pop()
# So top of stack is first item of list
stack_out = [index] + stack_out

out.append(stack_out)

return out


def run_threshold(float intercept, float constant, float slope):
cdef Thresh thresh = Thresh(intercept, constant)
return threshold(thresh, slope)


def fixed_values_from_metadata(np.ndarray[float, ndim=1] t_bar,
np.ndarray[float, ndim=1] tau,
np.ndarray[int, ndim=1] n_reads,
Expand Down
42 changes: 32 additions & 10 deletions tests/test_jump_cas22.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
import pytest
from numpy.testing import assert_allclose

from stcal.ramp_fitting.ols_cas22._wrappers import metadata_from_read_pattern
from stcal.ramp_fitting.ols_cas22._wrappers import init_ramps
from stcal.ramp_fitting.ols_cas22._wrappers import run_threshold, fixed_values_from_metadata, make_pixel
from stcal.ramp_fitting.ols_cas22._core import metadata_from_read_pattern, threshold
from stcal.ramp_fitting.ols_cas22._wrappers import fixed_values_from_metadata, make_pixel

from stcal.ramp_fitting.ols_cas22 import fit_ramps, Parameter, Variance, Diff, RampJumpDQ

Expand Down Expand Up @@ -76,13 +75,20 @@ def test_metadata_from_read_pattern(base_ramp_data):


def test_init_ramps():
"""Test turning dq flags into initial ramp splits"""
"""
Test turning dq flags into initial ramp splits
Note that because `init_ramps` itself returns a stack, which does not have
a direct python equivalent, we call the wrapper for `init_ramps` which
converts that stack into a list ordered in the same fashion as the stack
"""
from stcal.ramp_fitting.ols_cas22._core import _init_ramps_list

dq = np.array([[0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1],
[0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1],
[0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1],
[0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1]], dtype=np.int32)

ramps = init_ramps(dq)
ramps = _init_ramps_list(dq)
assert len(ramps) == dq.shape[1] == 16

# Check that the ramps are correct
Expand Down Expand Up @@ -115,12 +121,28 @@ def test_init_ramps():


def test_threshold():
"""Test the threshold object/fucnction)"""
intercept = np.float32(5.5)
constant = np.float32(1/3)
"""
Test the threshold object/fucnction
intercept - constant * log10(slope) = threshold
"""

assert intercept == run_threshold(intercept, constant, 1.0) # check intercept
assert np.float32(intercept - constant) == run_threshold(intercept, constant, 10.0) # check constant
# Create the python analog of the threshold struct
# Note that structs get mapped to/from python as dictionary objects with
# the keys being the struct members.
thresh = {
'intercept': np.float32(5.5),
'constant': np.float32(1/3)
}

# Check the 'intercept' is correctly interpreted.
# Since the log of the input slope is taken, log10(1) = 0, meaning that
# we should directly recover the intercept value in that case.
assert thresh['intercept'] == threshold(thresh, 1.0)

# Check the 'constant' is correctly interpreted.
# Since we know that the intercept is correctly identified and that `log10(10) = 1`,
# we can use that to check that the constant is correctly interpreted.
assert np.float32(thresh['intercept'] - thresh['constant']) == threshold(thresh, 10.0)


@pytest.fixture(scope="module")
Expand Down

0 comments on commit f30e939

Please sign in to comment.