class UnitAdaptor(Adaptor): """Scalar conversion of units """ def __init__(self, name): self._register = UnitRegistry() super().__init__(name) def before_model_run(self, data_handle: DataHandle): """Register unit definitions in registry before model run """ units = data_handle.read_unit_definitions() for unit in units: self._register.define(unit) def convert(self, data_array, to_spec, coefficients): data = data_array.data from_spec = data_array.spec try: quantity = self._register.Quantity(data, from_spec.unit) except UndefinedUnitError: raise ValueError('Cannot convert from undefined unit {}'.format( from_spec.unit)) try: converted_quantity = quantity.to(to_spec.unit) except UndefinedUnitError as ex: raise ValueError('Cannot convert undefined unit {}'.format( to_spec.unit)) from ex except DimensionalityError as ex: msg = 'Cannot convert unit from {} to {}' raise ValueError(msg.format(from_spec.unit, to_spec.unit)) from ex return converted_quantity.magnitude def get_coefficients(self, data_handle, from_spec, to_spec): # override with no-op - all the work is done in convert with scalar operations pass def generate_coefficients(self, from_spec, to_spec): # override with no-op - all the work is done in convert with scalar operations pass def parse_unit(self, unit_string): """Parse a unit string (abbreviation or full) into a Unit object Parameters ---------- unit : str Returns ------- quantity : :class:`pint.Unit` """ try: unit = self._register.parse_units(unit_string) except UndefinedUnitError: self.logger.warning("Unrecognised unit: %s", unit_string) unit = None return unit
def depths(self): if self.__depths is None: var = None for v in ['depth', 'deptht']: if v in self._dataset.variables: var = self._dataset.variables[v] break ureg = UnitRegistry() unit = ureg.parse_units(var.units.lower()) self.__depths = ureg.Quantity(var[:], unit).to(ureg.meters).magnitude self.__depths.flags.writeable = False return self.__depths
def depths(self): if self.__depths is None: var = None for v in ['depth', 'deptht']: if v in self._dataset.variables: var = self._dataset.variables[v] break ureg = UnitRegistry() unit = ureg.parse_units(var.units.lower()) self.__depths = ureg.Quantity( var[:], unit ).to(ureg.meters).magnitude self.__depths.flags.writeable = False return self.__depths
def test_service_port_units(osparc_simcore_root_dir): ureg = UnitRegistry() data = yaml.safe_load( (osparc_simcore_root_dir / "packages/models-library/tests/image-meta.yaml").read_text()) print(ServiceDockerData.schema_json(indent=2)) service_meta = ServiceDockerData.parse_obj(data) for input_nameid, input_meta in service_meta.inputs.items(): assert input_nameid # validation valid_unit: Unit = ureg.parse_units(input_meta.unit) assert isinstance(valid_unit, Unit) assert valid_unit.dimensionless
def test_service_port_units(project_tests_dir: Path): ureg = UnitRegistry() data = yaml.safe_load( (project_tests_dir / "data" / "image-meta.yaml").read_text()) print(ServiceDockerData.schema_json(indent=2)) service_meta = ServiceDockerData.parse_obj(data) assert service_meta.inputs for input_nameid, input_meta in service_meta.inputs.items(): assert input_nameid # validation valid_unit: Unit = ureg.parse_units(input_meta.unit) assert isinstance(valid_unit, Unit) assert valid_unit.dimensionless
def depths(self) -> np.ndarray: if self.__depths is None: var = None # Look through possible dimension names for v in self.depth_dimensions: # Depth is usually a "coordinate" variable if v in list(self._dataset.coords.keys()): # Get DataArray for depth var = self.get_dataset_variable(v) break if var is not None: ureg = UnitRegistry() unit = ureg.parse_units(var.attrs['units'].lower()) self.__depths = ureg.Quantity(var.values, unit).to(ureg.meters).magnitude else: self.__depths = np.array([0]) self.__depths.setflags(write=False) # Make immutable return self.__depths
def depths(self) -> np.ndarray: """Finds, caches, and returns the valid depths for the dataset. """ if self.__depths is None: var = None for v in self.nc_data.depth_dimensions: # Depth is usually a "coordinate" variable if v in self.nc_data.dataset.coords: # Get DataArray for depth var = self.nc_data.get_dataset_variable(v) break if var is not None: ureg = UnitRegistry() unit = ureg.parse_units(var.attrs['units'].lower()) self.__depths = ureg.Quantity(var[:].values, unit).to(ureg.meters).magnitude else: self.__depths = np.array([0]) self.__depths.flags.writeable = False return self.__depths
def depths(self): if self.__depths is None: var = None for v in self.depth_dimensions: # Depth is usually a "coordinate" variable if v in list(self._dataset.coords.keys()): # Get DataArray for depth var = self._dataset.variables[v] break if var is not None: ureg = UnitRegistry() unit = ureg.parse_units(var.attrs['units'].lower()) self.__depths = ureg.Quantity( var[:].values, unit ).to(ureg.meters).magnitude else: self.__depths = np.array([0]) self.__depths.flags.writeable = False return self.__depths
def depths(self): """Finds, caches, and returns the valid depths for the dataset. """ if self.__depths is None: var = None # Look through possible dimension names for v in self.nc_data.depth_dimensions: # Depth is usually a "coordinate" variable if v in self.nc_data.dataset.coords: # Get DataArray for depth var = self.nc_data.get_dataset_variable(v) break if var is not None: ureg = UnitRegistry() unit = ureg.parse_units(var.attrs['units'].lower()) self.__depths = ureg.Quantity(var.values, unit).to(ureg.meters).magnitude else: self.__depths = np.array([0]) # Make immutable self.__depths.setflags(write=False) return self.__depths
def __post_init__(self, reg: pint.UnitRegistry) -> None: # VSS uses some Pascal-cased data types, so lower-case them then let super type check them. # noinspection PyCallByClass object.__setattr__(self, 'datatype', self.datatype.lower()) # Process enum into a more agreeable format. if self.enum is not None: if self.datatype != 'string': raise ValueError( f'enum provided for non-string datatype {self.datatype}') # noinspection PyCallByClass object.__setattr__(self, 'enum', set(self.enum)) # Set min and max based on datatype for numeric signals. if self.datatype not in ('string', 'boolean'): if 'int' in self.datatype: try: low, high = INT_BOUNDS[self.datatype] except KeyError: raise ValueError( f'unrecognized datatype {self.datatype}') from None elif self.datatype == 'float': low, high = FLOAT_BOUNDS elif self.datatype == 'double': low, high = DOUBLE_BOUNDS else: raise ValueError(f'unrecognized datatype {self.datatype}') if self.min is None: object.__setattr__(self, 'min', low) else: object.__setattr__(self, 'min', max(self.min, low)) if self.max is None: object.__setattr__(self, 'max', high) else: object.__setattr__(self, 'max', min(self.max, high)) # Ensure default value matches datatype and bounds. if self.default is not None: if (isinstance(self.default, float) and self.datatype not in ('float', 'double')) or \ (isinstance(self.default, int) and 'int' not in self.datatype) or \ (isinstance(self.default, bool) and self.datatype != 'boolean') or \ (isinstance(self.default, str) and self.datatype != 'string'): raise ValueError( f'default value type {type(self.default)} does not match ' f'expected datatype {self.datatype}') if self.datatype == 'string': if self.enum is not None and self.default not in self.enum: raise ValueError( f'default value {self.default} is illegal for enum {self.enum}' ) elif self.datatype != 'bool': if self.clamp(self.default) != self.default: raise ValueError( f'default value {self.default} is illegal for datatype {self.datatype}' ) # Parse pint unit from unit string. try: # noinspection PyCallByClass object.__setattr__(self, 'pint_unit', reg.parse_units(self.unit)) except Exception: raise ValueError(f'illegal unit {self.unit!r}') from None # Non-numeric data types must not have a unit. if self.datatype in ('string', 'boolean') and not self.pint_unit.dimensionless: raise ValueError( f'datatype {self.datatype} is not compatible with unit {self.pint_unit:~}' ) # Ensure namespace is valid. if len(self.namespace) == 0: raise ValueError('namespace must contain at least one key') for key in self.namespace: if len(key) == 0: raise ValueError('namespace cannot contain an empty key') self.__type_check()