Skip to content

Commit

Permalink
Merge pull request #80 from SasView/76-basic-outline-of-new-data-types
Browse files Browse the repository at this point in the history
76 basic outline of new data types
  • Loading branch information
lucas-wilkins authored Aug 6, 2024
2 parents fc433c3 + ffb0110 commit 8ccf70f
Show file tree
Hide file tree
Showing 8 changed files with 2,522 additions and 107 deletions.
3 changes: 2 additions & 1 deletion sasdata/data.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from dataclasses import dataclass
from units_temp import Quantity, NamedQuantity
from quantities.quantities import Quantity, NamedQuantity
from sasdata.metadata import MetaData

import numpy as np

Expand Down
36 changes: 28 additions & 8 deletions sasdata/metadata.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Generic, TypeVar
from typing import TypeVar

from numpy._typing import ArrayLike

Expand All @@ -8,14 +8,11 @@
class RawMetaData:
pass

class MetaData:
pass


FieldDataType = TypeVar("FieldDataType")
OutputDataType = TypeVar("OutputDataType")

class Accessor(Generic[FieldDataType, OutputDataType]):
class Accessor[FieldDataType, OutputDataType]:
def __init__(self, target_field: str):
self._target_field = target_field

Expand All @@ -33,18 +30,29 @@ def __init__(self, target_field: str, units_field: str | None = None):
super().__init__(target_field)
self._units_field = units_field

def _get_units(self) -> Unit:
def _units(self) -> Unit:
pass

def _raw_values(self) -> ArrayLike:
pass

@property
def value(self) -> Quantity[ArrayLike]:
return Quantity(self._raw_values(), self._units())


class StringAccessor(Accessor[str, str]):

def _raw_values(self) -> str:
pass

class StringAccessor(Accessor[str]):
@property
def value(self) -> str:
return self._raw_values()

#
# Quantity specific accessors, provides helper methods for quantities with known dimensionality
#

class LengthAccessor(QuantityAccessor):
@property
Expand All @@ -61,4 +69,16 @@ class TemperatureAccessor(QuantityAccessor):


class AbsoluteTemperatureAccessor(QuantityAccessor):
pass
pass


#
# Main metadata object
#


class MetaData:
def __init__(self, raw: RawMetaData):
self._raw = raw

# Put the structure of the metadata that should be exposed to a power-user / developer in here
168 changes: 168 additions & 0 deletions sasdata/quantities/_units_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
from dataclasses import dataclass
from typing import Sequence, Self, TypeVar

import numpy as np

from sasdata.quantities.unicode_superscript import int_as_unicode_superscript


class Dimensions:
"""
Note that some SI Base units are
For example, moles and angular measures are dimensionless from this perspective, and candelas are
"""
def __init__(self,
length: int = 0,
time: int = 0,
mass: int = 0,
current: int = 0,
temperature: int = 0):

self.length = length
self.time = time
self.mass = mass
self.current = current
self.temperature = temperature

def __mul__(self: Self, other: Self):

if not isinstance(other, Dimensions):
return NotImplemented

return Dimensions(
self.length + other.length,
self.time + other.time,
self.mass + other.mass,
self.current + other.current,
self.temperature + other.temperature)

def __truediv__(self: Self, other: Self):

if not isinstance(other, Dimensions):
return NotImplemented

return Dimensions(
self.length - other.length,
self.time - other.time,
self.mass - other.mass,
self.current - other.current,
self.temperature - other.temperature)

def __pow__(self, power: int):

if not isinstance(power, int):
return NotImplemented

return Dimensions(
self.length * power,
self.time * power,
self.mass * power,
self.current * power,
self.temperature * power)

def __eq__(self: Self, other: Self):
if isinstance(other, Dimensions):
return (self.length == other.length and
self.time == other.time and
self.mass == other.mass and
self.current == other.current and
self.temperature == other.temperature)

return NotImplemented

def __hash__(self):
""" Unique representation of units using Godel like encoding"""

two_powers = 0
if self.length < 0:
two_powers += 1

if self.time < 0:
two_powers += 2

if self.mass < 0:
two_powers += 4

if self.current < 0:
two_powers += 8

if self.temperature < 0:
two_powers += 16

return 2**two_powers * 3**abs(self.length) * 5**abs(self.time) * \
7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature)

def __repr__(self):
s = ""
for name, size in [
("L", self.length),
("T", self.time),
("M", self.mass),
("C", self.current),
("K", self.temperature)]:

if size == 0:
pass
elif size == 1:
s += f"{name}"
else:
s += f"{name}{int_as_unicode_superscript(size)}"

return s

class Unit:
def __init__(self,
si_scaling_factor: float,
dimensions: Dimensions,
name: str | None = None,
ascii_symbol: str | None = None,
symbol: str | None = None):

self.scale = si_scaling_factor
self.dimensions = dimensions
self.name = name
self.ascii_symbol = ascii_symbol
self.symbol = symbol

def _components(self, tokens: Sequence["UnitToken"]):
pass

def __mul__(self: Self, other: Self):
if not isinstance(other, Unit):
return NotImplemented

return Unit(self.scale * other.scale, self.dimensions * other.dimensions)

def __truediv__(self: Self, other: Self):
if not isinstance(other, Unit):
return NotImplemented

return Unit(self.scale / other.scale, self.dimensions / other.dimensions)

def __rtruediv__(self: Self, other: Self):
if isinstance(other, Unit):
return Unit(other.scale / self.scale, other.dimensions / self.dimensions)
elif isinstance(other, (int, float)):
return Unit(other / self.scale, self.dimensions ** -1)
else:
return NotImplemented

def __pow__(self, power: int):
if not isinstance(power, int):
return NotImplemented

return Unit(self.scale**power, self.dimensions**power)

def equivalent(self: Self, other: Self):
return self.dimensions == other.dimensions

def __eq__(self: Self, other: Self):
return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5

class UnitGroup:
def __init__(self, name: str, units: list[Unit]):
self.name = name
self.units = sorted(units, key=lambda unit: unit.scale)
Loading

0 comments on commit 8ccf70f

Please sign in to comment.