Skip to content
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

fix silenced unit conversion bugs #2897

Conversation

neutrinoceros
Copy link
Member

@neutrinoceros neutrinoceros commented Sep 12, 2020

PR Summary

Another bug discovered in #2851
Opening as a draft to expose the bug in CI before I apply the fix.

update:
Apparently the main issue is that fake_random_ds has a very fragile api: the kwargs fields and units have default values but there is no mechanism to prevent using non-default fields without explicitly specifying their units. As a results we have a bunch of tests where we build a "temperature" field with units "cm/s".
These should now be fixed here, but I'm not fixing fake_random_ds's api as a whole for now (too much work for the time I have on my hands).

A different error is revealed in
yt/data_objects/tests/test_covering_grid.py::test_smoothed_covering_grid_2d_dataset, I'll try to investigate on this one.

@neutrinoceros
Copy link
Member Author

neutrinoceros commented Sep 12, 2020

So the error raised in
yt/data_objects/tests/test_covering_grid.py::test_smoothed_covering_grid_2d_dataset
is

    def test_smoothed_covering_grid_2d_dataset():
        ds = fake_random_ds([32, 32, 1], nprocs=4)
        ds.periodicity = (True, True, True)
        scg = ds.smoothed_covering_grid(1, [0.0, 0.0, 0.0], [32, 32, 1])
>       assert_equal(scg["density"].shape, [32, 32, 1])

yt/data_objects/tests/test_covering_grid.py:218:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
yt/data_objects/data_containers.py:268: in __getitem__
    f = self._determine_fields([key])[0]
yt/data_objects/data_containers.py:1473: in _determine_fields
    ftype, fname = self._tupleize_field(field)
yt/data_objects/data_containers.py:1443: in _tupleize_field
    finfo = self.ds._get_field_info(field)
yt/data_objects/static_output.py:808: in _get_field_info
    self.index
yt/data_objects/static_output.py:517: in index
    self.create_field_info()
yt/data_objects/static_output.py:601: in create_field_info
    self.field_info.load_all_plugins(self.default_fluid_type)
yt/fields/field_info_container.py:382: in load_all_plugins
    self.find_dependencies(loaded)
yt/fields/field_info_container.py:395: in find_dependencies
    deps, unavailable = self.check_derived_fields(loaded)
yt/fields/field_info_container.py:473: in check_derived_fields
    fd = fi.get_dependencies(ds=self.ds)
yt/fields/derived_field.py:247: in get_dependencies
    e[self.name]
yt/fields/field_detector.py:123: in __missing__
    vv = finfo(self)
yt/fields/derived_field.py:288: in __call__
    dd = self._function(self, data)
yt/fields/vector_operations.py:240: in _tangential
    data[ftype, f"{basename}_spherical_theta"] ** 2.0
yt/fields/field_detector.py:123: in __missing__
    vv = finfo(self)
yt/fields/derived_field.py:288: in __call__
    dd = self._function(self, data)
yt/fields/vector_operations.py:273: in _spherical_theta_component
    vectors = obtain_relative_velocity_vector(
yt/utilities/lib/misc_utilities.pyx:661: in yt.utilities.lib.misc_utilities.obtain_relative_velocity_vector
    vyg.convert_to_units(vxg.units)
/Users/clm/.pyenv/versions/3.8.5/envs/yt_dev/lib/python3.8/site-packages/unyt/array.py:617: in convert_to_units
    (conv_factor, offset) = self.units.get_conversion_factor(
/Users/clm/.pyenv/versions/3.8.5/envs/yt_dev/lib/python3.8/site-packages/unyt/unit_object.py:687: in get_conversion_factor
    return _get_conversion_factor(self, other_units, dtype)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

old_units = (dimensionless), new_units = cm/s, dtype = dtype('float64')

    def _get_conversion_factor(old_units, new_units, dtype):
        """
        Get the conversion factor between two units of equivalent dimensions. This
        is the number you multiply data by to convert from values in `old_units` to
        values in `new_units`.

        Parameters
        ----------
        old_units: str or Unit object
            The current units.
        new_units : str or Unit object
            The units we want.
        dtype: NumPy dtype
            The dtype of the conversion factor

        Returns
        -------
        conversion_factor : float
            `old_units / new_units`
        offset : float or None
            Offset between the old unit and new unit.

        """
        if old_units.dimensions != new_units.dimensions:
>           raise UnitConversionError(
                old_units, old_units.dimensions, new_units, new_units.dimensions
            )
E           unyt.exceptions.UnitConversionError: Cannot convert between 'dimensionless' (dim '1') and 'cm/s' (dim '(length)/(time)').

Which is happening for field == ('gas', 'velocity_spherical_theta') (and likely others down the road)
I think the root problem is that ('gas', 'velocity_spherical_theta') is meaningless for a 2D cartesian dataset.
I think the solution is to add a validator (possibly from a class that's not defined yet).

@neutrinoceros
Copy link
Member Author

I was able to track the error down to

# Is this safe?

So in 2D, we apparently replace a velocity field with ('index', 'zeros'), which is dimensionless, hence the error.
I guess the answer to this comment "is this safe ?" is "no, it's not :/"

@neutrinoceros neutrinoceros changed the title fix a unit conversion bug fix silenced unit conversion bugs Sep 12, 2020
@neutrinoceros
Copy link
Member Author

neutrinoceros commented Sep 12, 2020

ok so I think I'm headed in the right direction.
Right now on travis, excluding answer tests, we get only one error:

2642======================================================================
2643ERROR: yt.visualization.tests.test_fits_image.test_fits_image
2644----------------------------------------------------------------------
2645Traceback (most recent call last):
2646  File "/home/travis/build/yt-project/yt/venv/lib/python3.6/site-packages/nose/case.py", line 198, in runTest
2647    self.test(*self.arg)
2648  File "/home/travis/build/yt-project/yt/yt/testing.py", line 803, in true_wrapper
2649    return func(*args, **kwargs)
2650  File "/home/travis/build/yt-project/yt/yt/visualization/tests/test_fits_image.py", line 64, in test_fits_image
2651    ds2.index
2652  File "/home/travis/build/yt-project/yt/yt/data_objects/static_output.py", line 517, in index
2653    self.create_field_info()
2654  File "/home/travis/build/yt-project/yt/yt/data_objects/static_output.py", line 601, in create_field_info
2655    self.field_info.load_all_plugins(self.default_fluid_type)
2656  File "/home/travis/build/yt-project/yt/yt/fields/field_info_container.py", line 382, in load_all_plugins
2657    self.find_dependencies(loaded)
2658  File "/home/travis/build/yt-project/yt/yt/fields/field_info_container.py", line 395, in find_dependencies
2659    deps, unavailable = self.check_derived_fields(loaded)
2660  File "/home/travis/build/yt-project/yt/yt/fields/field_info_container.py", line 473, in check_derived_fields
2661    fd = fi.get_dependencies(ds=self.ds)
2662  File "/home/travis/build/yt-project/yt/yt/fields/derived_field.py", line 247, in get_dependencies
2663    e[self.name]
2664  File "/home/travis/build/yt-project/yt/yt/fields/field_detector.py", line 123, in __missing__
2665    vv = finfo(self)
2666  File "/home/travis/build/yt-project/yt/yt/fields/derived_field.py", line 288, in __call__
2667    dd = self._function(self, data)
2668  File "/home/travis/build/yt-project/yt/yt/fields/astro_fields.py", line 121, in _entropy
2669    tr = data[ftype, "kT"] * data[ftype, "El_number_density"] ** mgammam1
2670  File "/home/travis/build/yt-project/yt/yt/fields/field_detector.py", line 123, in __missing__
2671    vv = finfo(self)
2672  File "/home/travis/build/yt-project/yt/yt/fields/derived_field.py", line 288, in __call__
2673    dd = self._function(self, data)
2674  File "/home/travis/build/yt-project/yt/yt/fields/fluid_fields.py", line 137, in _kT
2675    return (pc.kboltz * data[ftype, "temperature"]).in_units("keV")
2676  File "/home/travis/build/yt-project/yt/venv/lib/python3.6/site-packages/unyt/array.py", line 809, in in_units
2677    new_units, self.dtype
2678  File "/home/travis/build/yt-project/yt/venv/lib/python3.6/site-packages/unyt/unit_object.py", line 687, in get_conversion_factor
2679    return _get_conversion_factor(self, other_units, dtype)
2680  File "/home/travis/build/yt-project/yt/venv/lib/python3.6/site-packages/unyt/unit_object.py", line 933, in _get_conversion_factor
2681    old_units, old_units.dimensions, new_units, new_units.dimensions
2682unyt.exceptions.UnitConversionError: Cannot convert between 'cm**3*g/s**2' (dim '(length)**3*(mass)/(time)**2') and 'keV' (dim '(length)**2*(mass)/(time)**2').

Apparently this happens because FITSProjection has the side effect of transforming everything into a projected quantity (*= cm), without renaming fields, so we end up with a "temperature" field that's really represented in K*cm. I think the correct fix would be to call this a "projected_temperature" instead, even if this mean breaking some user code;

update: indeed the problem is more general than this. The projection object in FITSProjection is created by ds.proj, which I don't know the definition of.

update2: the units of projected fields are actually conditional, see

if self.weight_field is None:

so we only end up in this situation if there's no weight field.

@neutrinoceros neutrinoceros force-pushed the hotfix_broken_unyt_conversion branch 2 times, most recently from 0391e3a to eb96cc3 Compare September 12, 2020 17:51
@@ -470,6 +471,8 @@ def check_derived_fields(self, fields_to_check=None):
fi = self[field]
try:
fd = fi.get_dependencies(ds=self.ds)
except UnitConversionError:
Copy link
Member

Choose a reason for hiding this comment

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

I have a suspicion that this is going to cause a lot of heartache. I do think it's the right thing to do.

One way we might make it an awful lot easier would be to make it much more obvious how to have the units deduced. As of right now, I don't think that's possible.

Copy link
Member Author

Choose a reason for hiding this comment

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

One way we might make it an awful lot easier would be to make it much more obvious how to have the units deduced. As of right now, I don't think that's possible.

at what level ? within the code base or from the user perspective ?

@matthewturk
Copy link
Member

matthewturk commented Sep 12, 2020 via email

@matthewturk
Copy link
Member

matthewturk commented Sep 12, 2020 via email

@neutrinoceros
Copy link
Member Author

Ok, I think this is back to a state where only answer tests on Travis, + maybe a handful of test on Jenkins should still fail.
Appeveyor pass this is a win, and I'll open for review when we're down to just answer tests miss.

@neutrinoceros
Copy link
Member Author

About the remaining failures on Jenkins:
yt/frontends/boxlib/tests/test_outputs.py::test_CastroDataset loads a 2D Castro dataset and fails because somehow, particle_velocity_{ax} (with ax in "xyz") fields are dimensionless, while the bulk_velocity vector isn't, so there's a crash in the relative velocity calculation. Right now I really don't get how those fields end up being dimensionless.

@matthewturk
Copy link
Member

I'm also not sure how they end up dimensionless without that just being another error that's propagating to where we're seeing it.

Base automatically changed from master to main January 20, 2021 15:27
@neutrinoceros
Copy link
Member Author

Just rebased to run tests again. There's a actually a non-zero chance that the remaining bugs magically went away because a lot of the issues I was trying to address here were solved on the main branch already (by @cphyc !). 🤞🏻

@neutrinoceros
Copy link
Member Author

Nope, the dimensionless -> cm issue still here. I hope we can discuss this in a future triage

@neutrinoceros
Copy link
Member Author

I think it's unlikely I'll ever manage to summon the will to fix this. Closing for now.

@neutrinoceros neutrinoceros deleted the hotfix_broken_unyt_conversion branch July 1, 2021 17:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants