• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dstndstn / tractor / 29e2b480-83b0-4232-9970-ec9a2db26092

05 Mar 2026 05:26PM UTC coverage: 45.246% (-0.7%) from 45.946%
29e2b480-83b0-4232-9970-ec9a2db26092

Pull #141

circleci

dstndstn
version number bullshit
Pull Request #141: Add reorganized GPU optimizer

713 of 2206 branches covered (32.32%)

126 of 287 new or added lines in 8 files covered. (43.9%)

28 existing lines in 6 files now uncovered.

3360 of 7426 relevant lines covered (45.25%)

0.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.35
/tractor/pointsource.py
1
from __future__ import print_function
1✔
2
import numpy as np
1✔
3

4
from tractor.utils import MultiParams
1✔
5
from tractor.patch import ModelMask
1✔
6
from tractor import ducks
1✔
7

8

9
class BasicSource(ducks.Source):
1✔
10
    def getPosition(self):
1✔
11
        return self.pos
1✔
12

13
    def setPosition(self, position):
1✔
14
        self.pos = position
×
15

16

17
class SingleProfileSource(BasicSource):
1✔
18
    '''
19
    A mix-in class for Source objects that have a single profile, eg, PointSources,
20
    Dev, Exp, and Sersic galaxies, and also FixedCompositeGalaxy (surprising but true)
21
    but not CompositeGalaxy.
22
    '''
23

24
    def getBrightness(self):
1✔
25
        return self.brightness
1✔
26

27
    def setBrightness(self, brightness):
1✔
28
        self.brightness = brightness
×
29

30
    def getBrightnesses(self):
1✔
31
        return [self.getBrightness()]
1✔
32

33
    def getUnitFluxModelPatches(self, *args, **kwargs):
1✔
34
        return [self.getUnitFluxModelPatch(*args, **kwargs)]
1✔
35

36
    def getModelPatch(self, img, minsb=None, modelMask=None, **kwargs):
1✔
37
        counts = img.getPhotoCal().brightnessToCounts(self.brightness)
1✔
38
        if counts == 0:
1!
39
            return None
×
40
        # HACK
41
        if not np.isfinite(np.float32(counts)):
1!
42
            return None
×
43
        if minsb is None:
1✔
44
            minsb = img.modelMinval
1✔
45
        minval = minsb / counts
1✔
46
        upatch = self.getUnitFluxModelPatch(img, minval=minval,
1✔
47
                                            modelMask=modelMask, **kwargs)
48

49
        if upatch is None:
1✔
50
            return None
1✔
51
        if upatch.patch is not None:
1!
52
            assert(np.all(np.isfinite(upatch.patch)))
1✔
53

54
        # print('getModelPatch: unit-patch sum', np.sum(upatch.patch), 'counts', counts,
55
        #      'scaled sum:', np.sum(upatch.patch * counts))
56

57
        p = upatch * counts
1✔
58
        if p.patch is not None:
1!
59
            assert(np.all(np.isfinite(p.patch)))
1✔
60
        return p
1✔
61

62

63
class PointSource(MultiParams, SingleProfileSource):
1✔
64
    '''
65
    An implementation of a point source, characterized by its position
66
    and brightness.
67
    '''
68

69
    def __init__(self, pos, br):
1✔
70
        '''
71
        PointSource(pos, brightness)
72
        '''
73
        super(PointSource, self).__init__(pos, br)
1✔
74
        # if not None, fixedRadius determines the size of unit-flux
75
        # model Patches produced for this PointSource.
76
        self.fixedRadius = None
1✔
77
        # if not None, minradius determines the minimum size of unit-flux
78
        # models
79
        self.minRadius = None
1✔
80

81
    def copy(self):
1✔
82
        c = super(PointSource, self).copy()
×
83
        c.fixedRadius = self.fixedRadius
×
84
        c.minRadius = self.minRadius
×
85
        return c
×
86

87
    @staticmethod
1✔
88
    def getNamedParams():
1✔
89
        return dict(pos=0, brightness=1)
1✔
90

91
    def getSourceType(self):
1✔
92
        return 'PointSource'
1✔
93

94
    def __str__(self):
1✔
95
        return (self.getSourceType() + ' at ' + str(self.pos) +
1✔
96
                ' with ' + str(self.brightness))
97

98
    def __repr__(self):
1✔
99
        return (self.getSourceType() + '(' + repr(self.pos) + ', ' +
1✔
100
                repr(self.brightness) + ')')
101

102
    def getUnitFluxModelPatch(self, img, minval=0., derivs=False,
1✔
103
                              modelMask=None, **kwargs):
104
        (px, py) = img.getWcs().positionToPixel(self.getPosition(), self)
1✔
105
        H, W = img.shape
1✔
106
        psf = self._getPsf(img)
1✔
107
        # quit early if the requested position is way outside the image bounds
108
        r = self.fixedRadius
1✔
109
        if r is None:
1!
110
            r = psf.getRadius()
1✔
111
        if px + r < 0 or px - r > W or py + r < 0 or py - r > H:
1!
112
            return None
×
113

114
        clipExtent = None
1✔
115
        if modelMask is None:
1!
116
            # max extent
117
            clipExtent = [0, W, 0, H]
1✔
118

119
        patch = psf.getPointSourcePatch(px, py, minval=minval,
1✔
120
                                        radius=self.fixedRadius,
121
                                        derivs=derivs,
122
                                        minradius=self.minRadius,
123
                                        modelMask=modelMask,
124
                                        clipExtent=clipExtent)
125
        return patch
1✔
126

127
    def _getPsf(self, img):
1✔
128
        return img.getPsf()
1✔
129

130
    def getParamDerivatives(self, img, fastPosDerivs=True, modelMask=None,
1✔
131
                            **kwargs):
132
        '''
133
        returns [ Patch, Patch, ... ] of length numberOfParams().
134
        '''
135
        # Short-cut the case where we're only fitting fluxes, and the
136
        # band of the image is not being fit.
137

138
        pos_frozen = self.isParamFrozen('pos') or (self.pos.numberOfParams() == 0)
1✔
139
        bright_frozen = self.isParamFrozen('brightness')
1✔
140

141
        photo = img.getPhotoCal()
1✔
142
        counts0 = photo.brightnessToCounts(self.brightness)
1✔
143
        if pos_frozen and not bright_frozen:
1!
144
            bsteps = self.brightness.getStepSizes(img)
×
145
            bvals = self.brightness.getParams()
×
146
            allzero = True
×
147
            for i, bstep in enumerate(bsteps):
×
148
                oldval = self.brightness.setParam(i, bvals[i] + bstep)
×
NEW
149
                countsi = photo.brightnessToCounts(self.brightness)
×
150
                self.brightness.setParam(i, oldval)
×
151
                if countsi != counts0:
×
152
                    allzero = False
×
153
                    break
×
154
            if allzero:
×
155
                return [None] * self.numberOfParams()
×
156

157
        pos = self.getPosition()
1✔
158
        wcs = img.getWcs()
1✔
159

160
        minsb = img.modelMinval
1✔
161
        if counts0 > 0:
1!
162
            minval = minsb / counts0
1✔
163
        else:
164
            minval = None
×
165

166
        derivs = (not pos_frozen) and fastPosDerivs
1✔
167
        patchdx, patchdy = None, None
1✔
168

169
        if derivs:
1!
170
            patches = self.getUnitFluxModelPatch(img, minval=minval,
1✔
171
                                                 derivs=True,
172
                                                 modelMask=modelMask,
173
                                                 **kwargs)
174
            if patches is None:
1!
175
                return [None] * self.numberOfParams()
×
176
            if not isinstance(patches, tuple):
1!
177
                patch0 = patches
1✔
178
            else:
179
                patch0, patchdx, patchdy = patches
×
180
        else:
181
            patch0 = self.getUnitFluxModelPatch(img, minval=minval,
×
182
                                                modelMask=modelMask,
183
                                                **kwargs)
184

185
        if patch0 is None:
1!
186
            return [None] * self.numberOfParams()
×
187
        # check for intersection of patch0 with img
188
        H, W = img.shape
1✔
189
        if not patch0.overlapsBbox((0, W, 0, H)):
1!
190
            return [None] * self.numberOfParams()
×
191
        derivs = []
1✔
192

193
        # Position
194
        if not pos_frozen:
1!
195
            if patchdx is not None and patchdy is not None:
1!
196
                derivs.extend(wcs.pixelDerivsToPositionDerivs(pos, self, counts0, patch0,
×
197
                                                              patchdx, patchdy))
198
            elif counts0 == 0:
1!
199
                derivs.extend([None] * pos.numberOfParams())
×
200
            else:
201
                # Step params
202
                psteps = pos.getStepSizes(img)
1✔
203
                pvals = pos.getParams()
1✔
204
                for i, pstep in enumerate(psteps):
1✔
205
                    pdiff = pstep
1✔
206
                    p0 = patch0
1✔
207
                    oldval = pos.setParam(i, pvals[i] + pstep)
1✔
208
                    patchx = self.getUnitFluxModelPatch(img, minval=minval,
1✔
209
                                                        modelMask=modelMask,
210
                                                        **kwargs)
211
                    if getattr(pos, 'symmetric_derivs', False):
1!
212
                        pos.setParam(i, pvals[i] - pstep)
×
213
                        patchmx = self.getUnitFluxModelPatch(img, minval=minval,
×
214
                                                             modelMask=modelMask,
215
                                                             **kwargs)
216
                        p0 = patchmx
×
217
                        pdiff = pstep * 2
×
218

219
                    pos.setParam(i, oldval)
1✔
220
                    if patchx is None:
1!
221
                        dx = patch0 * (-1 * counts0 / pstep)
×
222
                    else:
223
                        dx = (patchx - p0) * (counts0 / pdiff)
1✔
224
                    dx.setName('d(ptsrc)/d(pos%i)' % i)
1✔
225
                    derivs.append(dx)
1✔
226

227
        # Brightness
228
        if not bright_frozen:
1!
229
            bsteps = self.brightness.getStepSizes(img)
1✔
230
            bvals = self.brightness.getParams()
1✔
231
            for i, bstep in enumerate(bsteps):
1✔
232
                oldval = self.brightness.setParam(i, bvals[i] + bstep)
1✔
233
                countsi = photo.brightnessToCounts(self.brightness)
1✔
234
                self.brightness.setParam(i, oldval)
1✔
235
                if countsi == counts0:
1!
NEW
236
                    df = None
×
237
                else:
238
                    df = patch0 * ((countsi - counts0) / bstep)
1✔
239
                    df.setName('d(ptsrc)/d(bright%i)' % i)
1✔
240
                derivs.append(df)
1✔
241
        return derivs
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc