Skip to content

Commit

Permalink
ENH: allow fully automatic unit detection for derived field
Browse files Browse the repository at this point in the history
  • Loading branch information
neutrinoceros committed May 24, 2022
1 parent 161b447 commit 60e9414
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 37 deletions.
29 changes: 17 additions & 12 deletions yt/data_objects/selection_objects/data_selection_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from yt.fields.field_exceptions import NeedsGridType
from yt.funcs import fix_axis, is_sequence, iter_fields, validate_width_tuple
from yt.geometry.selection_routines import compose_selector
from yt.units import YTArray, dimensions as ytdims
from yt.units import YTArray
from yt.utilities.exceptions import (
GenerationInProgress,
YTBooleanObjectError,
Expand Down Expand Up @@ -242,22 +242,27 @@ def _generate_fields(self, fields_to_generate):
# field accesses
units = getattr(fd, "units", "")
if units == "":
dimensions = ytdims.dimensionless
sunits = ""
dimensions = 1
else:
dimensions = units.dimensions
units = str(
sunits = str(
units.get_base_equivalent(self.ds.unit_system.name)
)
if fi.dimensions != dimensions:
dimensions = units.dimensions

if fi.dimensions is None:
mylog.warning(
"Field %s was added without specifying units or dimensions, "
"auto setting units to %s",
fi.name,
sunits,
)
elif fi.dimensions != dimensions:
raise YTDimensionalityError(fi.dimensions, dimensions)
fi.units = units
fi.units = sunits
fi.dimensions = dimensions
self.field_data[field] = self.ds.arr(fd, units)
mylog.warning(
"Field %s was added without specifying units, "
"assuming units are %s",
fi.name,
units,
)

try:
fd.convert_to_units(fi.units)
except AttributeError:
Expand Down
24 changes: 7 additions & 17 deletions yt/fields/derived_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,9 @@ class DerivedField:
arguments (field, data)
units : str
A plain text string encoding the unit, or a query to a unit system of
a dataset. Powers must be in python syntax (** instead of ^). If set
to "auto" the units will be inferred from the units of the return
value of the field function, and the dimensions keyword must also be
set (see below).
a dataset. Powers must be in Python syntax (** instead of ^). If set
to 'auto' or None (default), units will be inferred from the return value
of the field function.
take_log : bool
Describes whether the field should be logged
validators : list
Expand All @@ -94,8 +93,7 @@ class DerivedField:
fields or that get aliased to themselves, we can specify a different
desired output unit than the unit found on disk.
dimensions : str or object from yt.units.dimensions
The dimensions of the field, only needed if units="auto" and only used
for error checking.
The dimensions of the field, only used for error checking with units='auto'.
nodal_flag : array-like with three components
This describes how the field is centered within a cell. If nodal_flag
is [0, 0, 0], then the field is cell-centered. If any of the components
Expand Down Expand Up @@ -162,18 +160,10 @@ def __init__(

# handle units
self.units: Optional[Union[str, bytes, Unit]]
if units is None:
self.units = ""
if units in (None, "auto"):
self.units = None
elif isinstance(units, str):
if units.lower() == "auto":
if dimensions is None:
raise RuntimeError(
"To set units='auto', please specify the dimensions "
"of the field with dimensions=<dimensions of field>!"
)
self.units = None
else:
self.units = units
self.units = units
elif isinstance(units, Unit):
self.units = str(units)
elif isinstance(units, bytes):
Expand Down
16 changes: 12 additions & 4 deletions yt/fields/field_info_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,11 +489,19 @@ def alias(
if units is None:
# We default to CGS here, but in principle, this can be pluggable
# as well.
u = Unit(self[original_name].units, registry=self.ds.unit_registry)
if u.dimensions is not dimensionless:
units = str(self.ds.unit_system[u.dimensions])

# self[original_name].units may be set to `None` at this point
# to signal that units should be autoset later
oru = self[original_name].units
if oru is None:
units = None
else:
units = self[original_name].units
u = Unit(oru, registry=self.ds.unit_registry)
if u.dimensions is not dimensionless:
units = str(self.ds.unit_system[u.dimensions])
else:
units = oru

self.field_aliases[alias_name] = original_name
function = TranslationFunc(original_name)
if deprecate is not None:
Expand Down
4 changes: 0 additions & 4 deletions yt/fields/tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,6 @@ def density_alias(field, data):
def unitless_data(field, data):
return np.ones(data[("gas", "density")].shape)

ds.add_field(
("gas", "density_alias_no_units"), sampling_type="cell", function=density_alias
)
ds.add_field(
("gas", "density_alias_auto"),
sampling_type="cell",
Expand All @@ -340,7 +337,6 @@ def unitless_data(field, data):
units="auto",
dimensions="temperature",
)
assert_raises(YTFieldUnitError, get_data, ds, ("gas", "density_alias_no_units"))
assert_raises(YTFieldUnitError, get_data, ds, ("gas", "density_alias_wrong_units"))
assert_raises(
YTFieldUnitParseError, get_data, ds, ("gas", "density_alias_unparseable_units")
Expand Down

0 comments on commit 60e9414

Please sign in to comment.