-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable unit support on all functional models #6183
Conversation
@astrofrog - I have manually cancelled the last travis, sadly the skipping is not working as expected in PRs, but I'm on it. |
ede09c5
to
267d948
Compare
@nden asked if you plan to address non-functional models, like projections, mapping, etc. |
edeffbc
to
df8ef51
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do have plans to fully test this functionality over at my synphot
work but that won't happen before v2.0 release.
CHANGES.rst
Outdated
model evaluation and fitting, added support for units on Gaussian1D | ||
and added a new Blackbody1D model. [#4855] | ||
model evaluation and fitting, added support for units on most | ||
functional models and added a new Blackbody1D model. [#4855, #6183] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BlackBody1D (both B's are capitalized in the code).
@@ -1475,22 +1475,17 @@ def prepare_inputs(self, *inputs, **kwargs): | |||
for i in range(len(inputs)): | |||
|
|||
input_name = self.inputs[i] | |||
input_unit = self.input_units.get(input_name, None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change assumes self.input_units
is now always a dictionary. The deleted code allowed for None
type and a dictionary. Is this intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point it's always a dict. The check for None is done earlier.
|
||
return amplitude * np.sin(2 * np.pi * frequency * x + 2 * np.pi * phase) | ||
argument = TWOPI * (frequency * x + phase) | ||
if isinstance(argument, Quantity): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it is not Quantity and you want to attach radian unit to it, I get it. But here, you are multiplying radian unit to it but only if it is a Quantity, which I do not get. What are you trying to do here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder about this too. The code works but I'm not sure I understand why it's done this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes - I admit it's a little confusing - the reason for this is because if frequency and x are quantities, argument will end up being dimensionless because TWOPI doesn't have a unit (even though it should be radians). But then np.sin(dimensionless) crashes.
Another option would be to do argument = argument.value
which would work too. I might do that instead since that will work too, and will put a comment.
except ImportError: | ||
HAS_SCIPY = False | ||
|
||
# TODO: GaussianAbsorption1D doesn't work with units because the 1- part doesn't |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. GaussianAbsorption1D
as it is now is only useful as a multiplicative factor to a continuum to create an absorption line. Otherwise, you can create absorption line without it by using continuum_model - Gaussian1D
. (This model might had been written a long time ago before the math operators worked properly?) I wonder if GaussianAbsorption1D
is used by anyone at all. Maybe deprecate it for future removal? That way, we can just say "this model is deprecated and does not support units".
Related reading: http://synphot.readthedocs.io/en/latest/synphot/spectrum.html#gaussian-absorption
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could first ask if anyone is using it on astropy-dev - another option is to make the continuum level a parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I opened an issue to discuss this: #6195
z = np.exp(-x.value**2 - y.value**2) * model['evaluation'][0][2].unit | ||
args = [x, y, z] | ||
fitter = LevMarLSQFitter() | ||
m_new = fitter(m, *args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This simply tests that fitter does not crash but does not test whether the fitted values are actually correct (both values and units).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is correct - I should at least make sure that the resulting models have units - however I think checking the fitted parameter values is beyond the scopes of these tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I've now expanded that to make sure the resulting model has units and that they are compatible with the starting units.
|
||
|
||
@pytest.mark.parametrize('model', MODELS) | ||
def test_models_bounding_box(model): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is there a need to test this as part of this PR? Bounding box seems unrelated here. If your goal is to test whether bounding box has the correct unit, that does not seem to be tested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because for some models, bounding_box crashed if units were present, so I had to fix those and wanted a regression test. I agree we could check the actual values, but the purpose here is first and foremost to check that it does anything at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated this to now check for the actual accuracy of the bounding box values too
@astrofrog This looks good to me. |
@nden - if you'd like to work on rotations that would be great! Just ping me if you run into any issues. |
…lso don't use any negative values for independent variables since that causes issues for some of the models which are not well defined in those cases (for example when the independent variable is radius)
874b438
to
d435d22
Compare
@nden - just for info I plan to look into power laws and polynomials (in a separate PR), but I'll leave rotations for you to take a look at. This is ready for a final review/merge. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just nitpicks on the tests. Also, maybe a squash? (I am notorious for asking people to squash, so if you disagree on that, you don't have to.)
# values one by one. | ||
for i in range(len(model['bounding_box'])): | ||
bbox = m.bounding_box | ||
print(bbox) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
print function does not seem necessary. Is this leftover from debugging phase?
SCIPY_MODELS = set([Sersic1D, Sersic2D, AiryDisk2D]) | ||
|
||
@pytest.mark.parametrize('model', MODELS) | ||
def test_models_evaluatate_without_units(model): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
evaluatate -> evaluate (multiple occurrences)
@pllim - I've implemented the fixes. In general I think we should only ask people to squash if they spend say 10 commits making trivial changes, or if they accidentally add then remove a large file, but for 600+ line changes, I don't think it's unreasonable to have a dozen commits. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. I'll add a "merge when CI pass" tag. @nden has until then to disagree. 😉
The goal of this PR is to add tests for all functional models with units and fix any issues that come up. This is now ready for review. The only thing is that the absorption gaussian can't be made unit-aware because of the
1 -
term - there is no way to attach a unit to1
(I personally find this model strange since really one should do Const1D - Gaussian1D). Maybe we should replace1
by a model parameter, but I suggest deferring that to a separate PR (I'm trying to keep my modeling PRs from getting out of control...)