From 46fe2c7dc44d3ce8c6f4930a0d9ad724876220cb Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 30 Jul 2024 11:56:39 +0100 Subject: [PATCH 001/675] Added draft dataset types --- sasdata/dataset_types.py | 46 +++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/sasdata/dataset_types.py b/sasdata/dataset_types.py index cbe36825d..27848b3e2 100644 --- a/sasdata/dataset_types.py +++ b/sasdata/dataset_types.py @@ -2,8 +2,6 @@ from dataclasses import dataclass -import sasdata.quantities.units as units - # # VERY ROUGH DRAFT - FOR PROTOTYPING PURPOSES # @@ -19,7 +17,7 @@ class DatasetType: one_dim = DatasetType( name="1D I vs Q", required=["Q", "I"], - optional=["dI", "dQ", "Shadowfactor", "Qmean", "dQl", "dQw"], + optional=["dI", "dQ", "shadow"], expected_orders=[ ["Q", "I", "dI"], ["Q", "dQ", "I", "dI"]]) @@ -27,7 +25,7 @@ class DatasetType: two_dim = DatasetType( name="2D I vs Q", required=["Qx", "Qy", "I"], - optional=["dQx", "dQy", "dQz", "dI", "Qz", "ShadowFactor", "mask"], + optional=["dQx", "dQy", "dQz", "dI", "Qz", "shadow"], expected_orders=[ ["Qx", "Qy", "I"], ["Qx", "Qy", "I", "dI"], @@ -44,8 +42,8 @@ class DatasetType: sesans = DatasetType( name="SESANS", - required=["SpinEchoLength", "Depolarisation", "Wavelength"], - optional=["Transmission", "Polarisation"], + required=["z", "G"], + optional=["stuff", "other stuff", "more stuff"], expected_orders=[["z", "G"]]) dataset_types = {dataset.name for dataset in [one_dim, two_dim, sesans]} @@ -56,26 +54,22 @@ class DatasetType: # # The unit options should only be those compatible with the field # - -unit_kinds = { - "Q": units.inverse_length, - "I": units.inverse_length, - "Qx": units.inverse_length, - "Qy": units.inverse_length, - "Qz": units.inverse_length, - "dI": units.inverse_length, - "dQ": units.inverse_length, - "dQx": units.inverse_length, - "dQy": units.inverse_length, - "dQz": units.inverse_length, - "SpinEchoLength": units.length, - "Depolarisation": units.inverse_volume, - "Wavelength": units.length, - "Transmission": units.dimensionless, - "Polarisation": units.dimensionless, - "shadow": units.dimensionless, - "temperature": units.temperature, - "magnetic field": units.magnetic_flux_density +default_units = { + "Q": "1/A", + "I": "1/cm", + "Qx": "1/A", + "Qy": "1/A", + "Qz": "1/A", + "dI": "1/A", + "dQ": "1/A", + "dQx": "1/A", + "dQy": "1/A", + "dQz": "1/A", + "z": "A", + "G": "", + "shaddow": "", + "temperature": "K", + "magnetic field": "T" } # From 9ffbbe3aab0ce421deca18f20fdb3de06be8bc13 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 31 Jul 2024 11:46:44 +0100 Subject: [PATCH 002/675] Data sketch --- sasdata/data.py | 152 +----- sasdata/metadata.py | 861 ++----------------------------- sasdata/model_requirements.py | 575 +-------------------- sasdata/quantities/quantities.py | 146 ++++++ sasdata/transforms/operation.py | 19 + 5 files changed, 221 insertions(+), 1532 deletions(-) create mode 100644 sasdata/quantities/quantities.py create mode 100644 sasdata/transforms/operation.py diff --git a/sasdata/data.py b/sasdata/data.py index 788f3d7f5..8c2921a3c 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,150 +1,18 @@ -import typing +from dataclasses import dataclass +from units_temp import Quantity, NamedQuantity -import h5py import numpy as np -from h5py._hl.group import Group as HDF5Group -from sasdata import dataset_types -from sasdata.dataset_types import DatasetType -from sasdata.metadata import Metadata, MetadataEncoder -from sasdata.quantities.quantity import Quantity +from sasdata.model_requirements import ModellingRequirements -class SasData: - def __init__( - self, - name: str, - data_contents: dict[str, Quantity], - dataset_type: DatasetType, - metadata: Metadata, - verbose: bool = False, - ): - self.name = name - # validate data contents - if not all([key in dataset_type.optional or key in dataset_type.required for key in data_contents]): - raise ValueError(f"Columns don't match the dataset type: {[key for key in data_contents]}") - self._data_contents = data_contents - self._verbose = verbose - self.metadata = metadata - # TODO: Could this be optional? - self.dataset_type: DatasetType = dataset_type +@dataclass +class SASData: + abscissae: list[NamedQuantity[np.ndarray]] + ordinate: NamedQuantity[np.ndarray] + other: list[NamedQuantity[np.ndarray]] - # Components that need to be organised after creation - self.mask = None # TODO: fill out - self.model_requirements = None # TODO: fill out - - # TODO: Handle the other data types. - @property - def ordinate(self) -> Quantity: - match self.dataset_type: - case dataset_types.one_dim | dataset_types.two_dim: - return self._data_contents["I"] - case dataset_types.sesans: - return self._data_contents["Depolarisation"] - case _: - return None - - @property - def abscissae(self) -> Quantity: - match self.dataset_type: - case dataset_types.one_dim: - return self._data_contents["Q"] - case dataset_types.two_dim: - # Type hinting is a bit lacking. Assume each part of the zip is a scalar value. - data_contents = np.array( - list( - zip( - self._data_contents["Qx"].value, - self._data_contents["Qy"].value, - ) - ) - ) - # Use this value to extract units etc. Assume they will be the same for Qy. - reference_data_content = self._data_contents["Qx"] - # TODO: If this is a derived quantity then we are going to lose that - # information. - # - # TODO: Won't work when there's errors involved. On reflection, we - # probably want to avoid creating a new Quantity but at the moment I - # can't see a way around it. - return Quantity(data_contents, reference_data_content.units, name=self._data_contents["Qx"].name, id_header=self._data_contents["Qx"]._id_header) - case dataset_types.sesans: - return self._data_contents["SpinEchoLength"] - case _: - None - - def __getitem__(self, item: str): - return self._data_contents[item] - - def summary(self, indent=" "): - s = f"{self.name}\n" - - for data in sorted(self._data_contents, reverse=True): - s += f"{indent}{data}\n" - - s += "Metadata:\n" - s += "\n" - s += self.metadata.summary() - - return s - - @staticmethod - def from_json(obj): - return SasData( - name=obj["name"], - dataset_type=DatasetType( - name=obj["type"]["name"], - required=obj["type"]["required"], - optional=obj["type"]["optional"], - expected_orders=obj["type"]["expected_orders"], - ), - data_contents=obj["data_contents"], - metadata=Metadata.from_json(obj["metadata"]), - ) - - def _save_h5(self, sasentry: HDF5Group): - """Export data into HDF5 file""" - sasentry.attrs["name"] = self.name - self.metadata.as_h5(sasentry) - - # We export each data set into its own entry, so we only ever - # need sasdata01 - group = sasentry.create_group("sasdata01") - for idx, (key, sasdata) in enumerate(self._data_contents.items()): - sasdata.as_h5(group, key) - - - @staticmethod - def save_h5(data: dict[str, typing.Self], path: str | typing.BinaryIO): - with h5py.File(path, "w") as f: - for idx, (key, data) in enumerate(data.items()): - sasentry = f.create_group(f"sasentry{idx+1:02d}") - if not key.startswith("sasentry"): - sasentry.attrs["sasview_key"] = key - data._save_h5(sasentry) - - - -class SasDataEncoder(MetadataEncoder): - def default(self, obj): - match obj: - case DatasetType(): - return { - "name": obj.name, - "required": obj.required, - "optional": obj.optional, - "expected_orders": obj.expected_orders, - } - case SasData(): - return { - "name": obj.name, - "data_contents": obj._data_contents, - "type": obj.dataset_type, - "mask": obj.mask, - "metadata": obj.metadata, - "model_requirements": obj.model_requirements, - } - case _: - return super().default(obj) + metadata: MetaData + model_requirements: ModellingRequirements diff --git a/sasdata/metadata.py b/sasdata/metadata.py index d53c3102c..e3d05abba 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -1,849 +1,64 @@ -""" -Contains classes describing the metadata for a scattering run +from typing import Generic, TypeVar -The metadata is structures around the CANSas format version 1.1, found at -https://www.cansas.org/formats/canSAS1d/1.1/doc/specification.html +from numpy._typing import ArrayLike -Metadata from other file formats should be massaged to fit into the data classes presented here. -Any useful metadata which cannot be included in these classes represent a bug in the CANSas format. +from sasdata.quantities.quantities import Unit, Quantity -""" -import base64 -import json -import re -from dataclasses import dataclass, field, fields, is_dataclass -from typing import Any +class RawMetaData: + pass -import h5py -import numpy as np -from numpy import ndarray +class MetaData: + pass -from sasdata.quantities.quantity import Quantity -from sasdata.quantities.unit_parser import parse_unit -from sasdata.quantities.units import NamedUnit +FieldDataType = TypeVar("FieldDataType") +OutputDataType = TypeVar("OutputDataType") -def from_json_quantity(obj: dict) -> Quantity | None: - if obj is None: - return None +class Accessor(Generic[FieldDataType, OutputDataType]): + def __init__(self, target_field: str): + self._target_field = target_field - return Quantity(obj["value"], parse_unit(obj["units"])) - - -@dataclass(kw_only=True) -class Vec3: - """A three-vector of measured quantities""" - - x: Quantity[float] | None - y: Quantity[float] | None - z: Quantity[float] | None - - @staticmethod - def from_json(obj: dict) -> Quantity | None: - if obj is None: - return None - return Vec3( - x=from_json_quantity(obj["x"]), - y=from_json_quantity(obj["y"]), - z=from_json_quantity(obj["z"]), - ) - - def as_h5(self, f: h5py.Group): - """Export data onto an HDF5 group""" - if self.x: - self.x.as_h5(f, "x") - if self.y: - self.y.as_h5(f, "y") - if self.z: - self.z.as_h5(f, "z") - - -@dataclass(kw_only=True) -class Rot3: - """A measured rotation in 3-space""" - - roll: Quantity[float] | None - pitch: Quantity[float] | None - yaw: Quantity[float] | None - - @staticmethod - def from_json(obj: dict) -> Quantity | None: - if obj is None: - return None - return Rot3( - roll=from_json_quantity(obj["roll"]), - pitch=from_json_quantity(obj["pitch"]), - yaw=from_json_quantity(obj["yaw"]), - ) - - def as_h5(self, f: h5py.Group): - """Export data onto an HDF5 group""" - if self.roll: - self.roll.as_h5(f, "roll") - if self.pitch: - self.pitch.as_h5(f, "pitch") - if self.yaw: - self.yaw.as_h5(f, "yaw") - - -@dataclass(kw_only=True) -class Detector: - """ - Detector information - """ - - name: str | None - distance: Quantity[float] | None - offset: Vec3 | None - orientation: Rot3 | None - beam_center: Vec3 | None - pixel_size: Vec3 | None - slit_length: Quantity[float] | None - - def summary(self): - return ( - f"Detector:\n" - f" Name: {self.name}\n" - f" Distance: {self.distance}\n" - f" Offset: {self.offset}\n" - f" Orientation: {self.orientation}\n" - f" Beam center: {self.beam_center}\n" - f" Pixel size: {self.pixel_size}\n" - f" Slit length: {self.slit_length}\n" - ) - - @staticmethod - def from_json(obj): - return Detector( - name=obj["name"], - distance=from_json_quantity(obj["distance"]), - offset=Vec3.from_json(obj["offset"]), - orientation=Rot3.from_json(obj["orientation"]), - beam_center=Vec3.from_json(obj["beam_center"]), - pixel_size=Vec3.from_json(obj["pixel_size"]), - slit_length=from_json_quantity(obj["slit_length"]), - ) - - def as_h5(self, group: h5py.Group): - """Export data onto an HDF5 group""" - if self.name is not None: - group.create_dataset("name", data=[self.name]) - if self.distance: - self.distance.as_h5(group, "SDD") - if self.offset: - self.offset.as_h5(group.create_group("offset")) - if self.orientation: - self.orientation.as_h5(group.create_group("orientation")) - if self.beam_center: - self.beam_center.as_h5(group.create_group("beam_center")) - if self.pixel_size: - self.pixel_size.as_h5(group.create_group("pixel_size")) - if self.slit_length: - self.slit_length.as_h5(group, "slit_length") - -@dataclass(kw_only=True) -class Aperture: - distance: Quantity[float] | None - size: Vec3 | None - size_name: str | None - name: str | None - type_: str | None - - def summary(self): - return ( - f" Aperture:\n" - f" Name: {self.name}\n" - f" Aperture size: {self.size}\n" - f" Aperture distance: {self.distance}\n" - ) - - @staticmethod - def from_json(obj): - return Aperture( - distance=from_json_quantity(obj["distance"]), - size=Vec3.from_json(obj["size"]), - size_name=obj["size_name"], - name=obj["name"], - type_=obj["type"], - ) - - - def as_h5(self, group: h5py.Group): - """Export data onto an HDF5 group""" - if self.distance is not None: - self.distance.as_h5(group, "distance") - if self.name is not None: - group.attrs["name"] = self.name - if self.type_ is not None: - group.attrs["type"] = self.type_ - if self.size: - size_group = group.create_group("size") - self.size.as_h5(size_group) - if self.size_name is not None: - size_group.attrs["name"] = self.size_name - - - -@dataclass(kw_only=True) -class Collimation: - """ - Class to hold collimation information - """ - - length: Quantity[float] | None - apertures: list[Aperture] - - def summary(self): - return f"Collimation:\n Length: {self.length}\n" + "".join([a.summary() for a in self.apertures]) - - @staticmethod - def from_json(obj): - return Collimation( - length=from_json_quantity(obj["length"]) if obj["length"] else None, - apertures=list(map(Aperture.from_json, obj["apertures"])), - ) - - def as_h5(self, group: h5py.Group): - """Export data onto an HDF5 group""" - if self.length: - self.length.as_h5(group, "length") - for idx, a in enumerate(self.apertures): - a.as_h5(group.create_group(f"sasaperture{idx:02d}")) - - -@dataclass(kw_only=True) -class BeamSize: - name: str | None - size: Vec3 | None - - @staticmethod - def from_json(obj): - return BeamSize(name=obj["name"], size=Vec3.from_json(obj["size"])) - - def as_h5(self, group: h5py.Group): - """Export data onto an HDF5 group""" - if self.name: - group.attrs["name"] = self.name - if self.size: - self.size.as_h5(group) - - -@dataclass(kw_only=True) -class Source: - radiation: str | None - beam_shape: str | None - beam_size: BeamSize | None - wavelength: Quantity[float] | None - wavelength_min: Quantity[float] | None - wavelength_max: Quantity[float] | None - wavelength_spread: Quantity[float] | None - - def summary(self) -> str: - return ( - f"Source:\n" - f" Radiation: {self.radiation}\n" - f" Shape: {self.beam_shape}\n" - f" Wavelength: {self.wavelength}\n" - f" Min. Wavelength: {self.wavelength_min}\n" - f" Max. Wavelength: {self.wavelength_max}\n" - f" Wavelength Spread: {self.wavelength_spread}\n" - f" Beam Size: {self.beam_size}\n" - ) - - @staticmethod - def from_json(obj): - return Source( - radiation=obj["radiation"], - beam_shape=obj["beam_shape"], - beam_size=BeamSize.from_json(obj["beam_size"]) if obj["beam_size"] else None, - wavelength=obj["wavelength"], - wavelength_min=obj["wavelength_min"], - wavelength_max=obj["wavelength_max"], - wavelength_spread=obj["wavelength_spread"], - ) - - def as_h5(self, group: h5py.Group): - """Export data onto an HDF5 group""" - if self.radiation: - group.create_dataset("radiation", data=[self.radiation]) - if self.beam_shape: - group.create_dataset("beam_shape", data=[self.beam_shape]) - if self.beam_size: - self.beam_size.as_h5(group.create_group("beam_size")) - if self.wavelength: - self.wavelength.as_h5(group, "wavelength") - if self.wavelength_min: - self.wavelength_min.as_h5(group, "wavelength_min") - if self.wavelength_max: - self.wavelength_max.as_h5(group, "wavelength_max") - if self.wavelength_spread: - self.wavelength_spread.as_h5(group, "wavelength_spread") - - - - - -@dataclass(kw_only=True) -class Sample: - """ - Class to hold the sample description - """ - - name: str | None - sample_id: str | None - thickness: Quantity[float] | None - transmission: float | None - temperature: Quantity[float] | None - position: Vec3 | None - orientation: Rot3 | None - details: list[str] - - def summary(self) -> str: - return ( - f"Sample:\n" - f" ID: {self.sample_id}\n" - f" Transmission: {self.transmission}\n" - f" Thickness: {self.thickness}\n" - f" Temperature: {self.temperature}\n" - f" Position: {self.position}\n" - f" Orientation: {self.orientation}\n" - ) - - @staticmethod - def from_json(obj): - return Sample( - name=obj["name"], - sample_id=obj["sample_id"], - thickness=obj["thickness"], - transmission=obj["transmission"], - temperature=obj["temperature"], - position=obj["position"], - orientation=obj["orientation"], - details=obj["details"], - ) - - def as_h5(self, f: h5py.Group): - """Export data onto an HDF5 group""" - if self.name is not None: - f.attrs["name"] = self.name - if self.sample_id is not None: - f.create_dataset("ID", data=[self.sample_id]) - if self.thickness: - self.thickness.as_h5(f, "thickness") - if self.transmission is not None: - f.create_dataset("transmission", data=[self.transmission]) - if self.temperature: - self.temperature.as_h5(f, "temperature") - if self.position: - self.position.as_h5(f.create_group("position")) - if self.orientation: - self.orientation.as_h5(f.create_group("orientation")) - if self.details: - f.create_dataset("details", data=self.details) - - -@dataclass(kw_only=True) -class Process: - """ - Class that holds information about the processes - performed on the data. - """ - - name: str | None - date: str | None - description: str | None - terms: dict[str, str | Quantity[float]] - notes: list[str] - - def single_line_desc(self): - """ - Return a single line string representing the process - """ - return f"{self.name.value} {self.date.value} {self.description.value}" - - def summary(self): - if self.terms: - termInfo = " Terms:\n" + "\n".join([f" {k}: {v}" for k, v in self.terms.items()]) + "\n" - else: - termInfo = "" - - if self.notes: - noteInfo = " Notes:\n" + "\n".join([f" {note}" for note in self.notes]) + "\n" - else: - noteInfo = "" - - return ( - f"Process:\n" - f" Name: {self.name}\n" - f" Date: {self.date}\n" - f" Description: {self.description}\n" - f"{termInfo}" - f"{noteInfo}" - ) - - @staticmethod - def from_json(obj): - return Process( - name=obj["name"], - date=obj["date"], - description=obj["description"], - terms=obj["terms"], - notes=obj["notes"], - ) - - def as_h5(self, group: h5py.Group): - """Export data onto an HDF5 group""" - if self.name is not None: - group.create_dataset("name", data=[self.name]) - if self.date is not None: - group.create_dataset("date", data=[self.date]) - if self.description is not None: - group.create_dataset("description", data=[self.description]) - if self.terms: - for idx, (term, value) in enumerate(self.terms.items()): - node = group.create_group(f"term{idx:02d}") - node.attrs["name"] = term - if type(value) is Quantity: - node.attrs["value"] = value.value - node.attrs["unit"] = value.units.symbol - else: - node.attrs["value"] = value - for idx, note in enumerate(self.notes): - group.create_dataset(f"note{idx:02d}", data=[note]) - - -@dataclass -class Instrument: - collimations: list[Collimation] - source: Source | None - detector: list[Detector] - - def summary(self): - return ( - "\n".join([c.summary() for c in self.collimations]) - + "".join([d.summary() for d in self.detector]) - + (self.source.summary() if self.source is not None else "") - ) - - @staticmethod - def from_json(obj): - return Instrument( - collimations=list(map(Collimation.from_json, obj["collimations"])), - source=Source.from_json(obj["source"]), - detector=list(map(Detector.from_json, obj["detector"])), - ) - - def as_h5(self, group: h5py.Group): - """Export data onto an HDF5 group""" - if self.source: - self.source.as_h5(group.create_group("sassource")) - for idx, c in enumerate(self.collimations): - c.as_h5(group.create_group(f"sascollimation{idx:02d}")) - for idx, d in enumerate(self.detector): - d.as_h5(group.create_group(f"sasdetector{idx:02d}")) - - -@dataclass(kw_only=True) -class MetaNode: - name: str - attrs: dict[str, str] - contents: str | Quantity | ndarray | list["MetaNode"] - - def to_string(self, header=""): - """Convert node to pretty printer string""" - if self.attrs: - attributes = f"\n{header} Attributes:\n" + "\n".join( - [f"{header} {k}: {v}" for k, v in self.attrs.items()] - ) - else: - attributes = "" - if self.contents: - if type(self.contents) is str: - children = f"\n{header} {self.contents}" - else: - children = "".join([n.to_string(header + " ") for n in self.contents]) - else: - children = "" - - return f"\n{header}{self.name}:{attributes}{children}" - - def filter(self, name: str) -> list[ndarray | Quantity | str]: - match self.contents: - case str() | ndarray() | Quantity(): - if name == self.name: - return [self.contents] - case list(): - return [y for x in self.contents for y in x.filter(name)] - case _: - raise RuntimeError(f"Cannot filter contents of type {type(self.contents)}: {self.contents}") - return [] - - def __eq__(self, other) -> bool: - """Custom equality overload needed since numpy arrays don't - play nicely with equality""" - match self.contents: - case ndarray(): - if not np.all(self.contents == other.contents): - return False - case Quantity(): - result = self.contents == other.contents - if type(result) is ndarray and not np.all(result): - return False - if type(result) is bool and not result: - return False - case _: - if self.contents != other.contents: - return False - for k, v in self.attrs.items(): - if k not in other.attrs: - return False - if type(v) is np.ndarray and np.any(v != other.attrs[k]): - return False - if type(v) is not np.ndarray and v != other.attrs[k]: - return False - return self.name == other.name - - @staticmethod - def from_json(obj): - def from_content(con): - match con: - case list(): - return list(map(MetaNode.from_json, con)) - case { - "type": "ndarray", - "dtype": dtype, - "encoding": "base64", - "contents": contents, - "shape": shape, - }: - return np.frombuffer(base64.b64decode(contents), dtype=dtype).reshape(shape) - case {"value": value, "units": units}: - return from_json_quantity({"value": from_content(value), "units": from_content(units)}) - case _: - return con - - return MetaNode( - name=obj["name"], - attrs={k: from_content(v) for k, v in obj["attrs"].items()}, - contents=from_content(obj["contents"]), - ) - - -@dataclass(kw_only=True, eq=True) -class Metadata: - title: str | None - run: list[str] - definition: str | None - process: list[Process] - sample: Sample | None - instrument: Instrument | None - raw: MetaNode | None - - def summary(self): - run_string = str(self.run[0] if len(self.run) == 1 else self.run) - return ( - f" {self.title}, Run: {run_string}\n" - + " " - + "=" * len(str(self.title)) - + "=======" - + "=" * len(run_string) - + "\n\n" - + f"Definition: {self.title}\n" - + "".join([p.summary() for p in self.process]) - + (self.sample.summary() if self.sample else "") - + (self.instrument.summary() if self.instrument else "") - ) - - @staticmethod - def from_json(obj): - return Metadata( - title=obj["title"] if obj["title"] else None, - run=obj["run"], - definition=obj["definition"] if obj["definition"] else None, - process=[Process.from_json(p) for p in obj["process"]], - sample=Sample.from_json(obj["sample"]) if obj["sample"] else None, - instrument=Instrument.from_json(obj["instrument"]) if obj["instrument"] else None, - raw=MetaNode.from_json(obj["raw"]), - ) + def _raw_values(self) -> FieldDataType: + raise NotImplementedError("not implemented in base class") @property - def id_header(self): - """Generate a header for used in the unique_id for datasets""" - title = "" - if self.title is not None: - title = self.title - return f"{title}:{",".join(self.run)}" - - def as_h5(self, f: h5py.Group): - """Export data onto an HDF5 group""" - for idx, run in enumerate(self.run): - f.create_dataset(f"run{idx:02d}", data=[run]) - if self.title is not None: - f.create_dataset("title", data=[self.title]) - if self.definition is not None: - f.create_dataset("definition", data=[self.definition]) - if self.process: - for idx, process in enumerate(self.process): - name = f"sasprocess{idx:02d}" - process.as_h5(f.create_group(name)) - if self.sample: - self.sample.as_h5(f.create_group("sassample")) - if self.instrument: - self.instrument.as_h5(f.create_group("sasinstrument")) - # self.raw.as_h5(meta) if self.raw else None - - -class MetadataEncoder(json.JSONEncoder): - def default(self, obj): - match obj: - case None: - return None - case bytes(): - return obj.decode("utf-8") - case NamedUnit(): - return obj.name - case Quantity(): - return {"value": obj.value, "units": obj.units.ascii_symbol} - case ndarray(): - return { - "type": "ndarray", - "encoding": "base64", - "contents": base64.b64encode(obj.tobytes()).decode("utf-8"), - "dtype": obj.dtype.str, - "shape": obj.shape, - } - case Vec3(): - return { - "x": obj.x, - "y": obj.y, - "z": obj.z, - } - case Rot3(): - return { - "roll": obj.roll, - "pitch": obj.pitch, - "yaw": obj.yaw, - } - case Sample(): - return { - "name": obj.name, - "sample_id": obj.sample_id, - "thickness": obj.thickness, - "transmission": obj.transmission, - "temperature": obj.temperature, - "position": obj.position, - "orientation": obj.orientation, - "details": obj.details, - } - case Process(): - return { - "name": obj.name, - "date": obj.date, - "description": obj.description, - "terms": {k: obj.terms[k] for k in obj.terms}, - "notes": obj.notes, - } - case Aperture(): - return { - "distance": obj.distance, - "size": obj.size, - "size_name": obj.size_name, - "name": obj.name, - "type": obj.type_, - } - case Collimation(): - return { - "length": obj.length, - "apertures": [a for a in obj.apertures], - } - case BeamSize(): - return {"name": obj.name, "size": obj.size} - case Source(): - return { - "radiation": obj.radiation, - "beam_shape": obj.beam_shape, - "beam_size": obj.beam_size, - "wavelength": obj.wavelength, - "wavelength_min": obj.wavelength_min, - "wavelength_max": obj.wavelength_max, - "wavelength_spread": obj.wavelength_spread, - } - case Detector(): - return { - "name": obj.name, - "distance": obj.distance, - "offset": obj.offset, - "orientation": obj.orientation, - "beam_center": obj.beam_center, - "pixel_size": obj.pixel_size, - "slit_length": obj.slit_length, - } - case Instrument(): - return { - "collimations": [c for c in obj.collimations], - "source": obj.source, - "detector": [d for d in obj.detector], - } - case MetaNode(): - return {"name": obj.name, "attrs": obj.attrs, "contents": obj.contents} - case Metadata(): - return { - "title": obj.title, - "run": obj.run, - "definition": obj.definition, - "process": [p for p in obj.process], - "sample": obj.sample, - "instrument": obj.instrument, - "raw": obj.raw, - } - case _: - return super().default(obj) - - -def access_meta(obj: dataclass, key: str) -> Any | None: - """Use a string accessor to locate a key from within the data - object. - - The basic grammar of these accessors explicitly match the python - syntax for accessing the data. For example, to access the `name` - field within the object `person`, you would call - `access_meta(person, ".name")`. Similarly, lists and dicts are - access with square brackets. - - > assert access_meta(person, '.name') == person.name - > assert access_meta(person, '.phone.home') == person.phone.home - > assert access_meta(person, '.addresses[0].postal_code') == person.address[0].postal_code - > assert access_meta(person, '.children["Taylor"]') == person.children["Taylor"] + def value(self) -> OutputDataType: + raise NotImplementedError("value not implemented in base class") - Obviously, when the accessor is know ahead of time, `access_meta` - provides no benefit over directly retrieving the data. However, - when a data structure is loaded at runtime (e.g. the metadata of a - neutron scattering file), then it isn't possible to know in - advance the location of the specific value that the user desires. - `access_meta` allows the user to provide the location at runtime. - This function returns `None` when the key is not a valid address - for any data within the structure. Since the leaf could be any - type that is not a list, dict, or dataclass, the return type of - the function is `Any | None`. - The list of locations within a structure is given by the - `meta_tags` function. +class QuantityAccessor(Accessor[ArrayLike, Quantity[ArrayLike]]): + def __init__(self, target_field: str, units_field: str | None = None): + super().__init__(target_field) + self._units_field = units_field - """ - result = obj - while key != "": - match key: - case accessor if accessor.startswith("."): - for fld in fields(result): - field_string = f".{fld.name}" - if accessor.startswith(field_string): - key = accessor[len(field_string) :] - result = getattr(result, fld.name) - break - case index if (type(result) is list) and (matches := re.match(r"\[(\d+?)\](.*)", index)): - result = result[int(matches[1])] - key = matches[2] - case name if (type(result) is dict) and (matches := re.match(r'\["(.+)"\](.*)', name)): - result = result[matches[1]] - key = matches[2] - case _: - return None - return result + def _get_units(self) -> Unit: + pass + def _raw_values(self) -> ArrayLike: + pass -def meta_tags(obj: dataclass) -> list[str]: - """Find all leaf accessors from a data object. - - The function treats the passed in object as a tree. Lists, dicts, - and dataclasses are all treated as branches on the tree and any - other type is treated as a leaf. The function then returns a list - of strings, where each string is a "path" from the root of the - tree to one leaf. The structure of the path is designed to mimic - the python code to access that specific leaf value. - - These accessors allow us to treat accessing entries within a - structure as first class values. This list can then be presented - to the user to allow them to select specific information within - the larger structure. This is particularly important when plotting - against a specific date value within the structure. - - Example: - - >@dataclass - class Thermometer: - temperature: float - units: str - params: list - > item = Example() - > item.temperature = 273 - > item.units = "K" - > item.old_values = [{'date': '2025-08-12', 'temperature': 300'}] - > assert meta_tags(item) = ['.temperature', '.units', '.old_values[0]["date"]', '.old_values[0]["temperature"]'] - - The actual value of the leaf object specified by a path can be - retrieved with the `access_meta` function. - - """ - result = [] - items = [("", obj)] - while items: - path, item = items.pop() - match item: - case list(xs): - for idx, x in enumerate(xs): - items.append((f"{path}[{idx}]", x)) - case dict(xs): - for k, v in xs.items(): - items.append((f'{path}["{k}"]', v)) - case n if is_dataclass(n): - for fld in fields(item): - items.append((f"{path}.{fld.name}", getattr(item, fld.name))) - case _: - result.append(path) - return result - - -@dataclass(kw_only=True) -class TagCollection: - """The collected tags and their variability.""" - - singular: set[str] = field(default_factory=set) - variable: set[str] = field(default_factory=set) +class StringAccessor(Accessor[str]): + @property + def value(self) -> str: + return self._raw_values() -def collect_tags(objs: list[dataclass]) -> TagCollection: - """Identify uniform and varying data within a groups of data objects - The resulting TagCollection contains every accessor string that is - valid for every object in the `objs` list. For example, if - `obj.name` is a string for every `obj` in `objs`, then the string - ".name" will be present in one of the two sets in the tags - collection. +class LengthAccessor(QuantityAccessor): + @property + def m(self): + return self.value.in_units_of("m") - To be more specific, if `obj.name` exists and has the same value - for every `obj` in `objs`, the string ".name" will be included in - the `singular` set. If there are at least two distinct values for - `obj.name`, then ".name" will be in the `variable` set. - """ - if not objs: - return ([], []) - first = objs.pop() - terms = set(meta_tags(first)) - for obj in objs: - terms = terms.intersection(set(meta_tags(obj))) +class TimeAccessor(QuantityAccessor): + pass - objs.append(first) - result = TagCollection() +class TemperatureAccessor(QuantityAccessor): + pass - for term in terms: - values = set([access_meta(obj, term) for obj in objs]) - if len(values) == 1: - result.singular.add(term) - else: - result.variable.add(term) - return result +class AbsoluteTemperatureAccessor(QuantityAccessor): + pass diff --git a/sasdata/model_requirements.py b/sasdata/model_requirements.py index 3773c86ee..a35759bd2 100644 --- a/sasdata/model_requirements.py +++ b/sasdata/model_requirements.py @@ -1,581 +1,22 @@ -from abc import ABC, abstractmethod -from functools import singledispatch -from typing import Self +from dataclasses import dataclass import numpy as np -from scipy.special import erf, j0 -from sasdata import dataset_types -from sasdata.data import SasData -from sasdata.quantities import units -from sasdata.quantities.quantity import Operation, Quantity +from transforms.operation import Operation -class ModellingRequirements(ABC): - """Requirements that need to be passed to any modelling step""" - +@dataclass +class ModellingRequirements: + """ Requirements that need to be passed to any modelling step """ dimensionality: int operation: Operation - def __add__(self, other: Self) -> Self: - return self.compose(other) - - @singledispatch - def compose(self, other: Self) -> Self: - # Compose uses the reversed order - return compose(other, self) - @abstractmethod - def preprocess_q(self, data: np.ndarray, full_data: SasData) -> np.ndarray: - """Transform the Q values before processing in the model""" + def from_qi_transformation(self, data: np.ndarray) -> np.ndaarray: pass - @abstractmethod - def postprocess_iq(self, data: np.ndarray, full_data: SasData) -> np.ndarray: - """Transform the I(Q) values after running the model""" - pass - - -class ComposeRequirements(ModellingRequirements): - """Composition of two models""" - - first: ModellingRequirements - second: ModellingRequirements - - def __init__(self, fst, snd): - self.first = fst - self.second = snd - - def preprocess_q(self, data: np.ndarray, full_data: SasData) -> np.ndarray: - """Perform both transformations in order""" - return self.second.preprocess_q( - self.first.preprocess_q(data, full_data), full_data - ) - - def postprocess_iq(self, data: np.ndarray, full_data: SasData) -> np.ndarray: - """Perform both transformations in order""" - return self.second.postprocess_iq( - self.first.postprocess_iq(data, full_data), full_data - ) - - -class SesansModel(ModellingRequirements): - """Perform Hankel transform for SESANS""" - - def preprocess_q( - self, spin_echo_length: np.ndarray, full_data: SasData - ) -> np.ndarray: - """Calculate the q values needed to perform the Hankel transform - - Note: this is undefined for the case when spin_echo_lengths contains - exactly one element and that values is zero. - - """ - if len(spin_echo_length) == 1: - q_min, q_max = ( - 0.01 * 2 * np.pi / spin_echo_length[-1], - 10 * 2 * np.pi / spin_echo_length[0], - ) - else: - # TODO: Why does q_min depend on the number of correlation lengths? - # TODO: Why does q_max depend on the correlation step size? - q_min = 0.1 * 2 * np.pi / (np.size(spin_echo_length) * spin_echo_length[-1]) - q_max = 2 * np.pi / (spin_echo_length[1] - spin_echo_length[0]) - - # TODO: Possibly make this adjustable - log_spacing = 1.0003 - self.q = np.exp(np.arange(np.log(q_min), np.log(q_max), np.log(log_spacing))) - - dq = np.diff(self.q) - dq = np.insert(dq, 0, dq[0]) - - self.H0 = dq / (2 * np.pi) * self.q - - self.H = np.outer(self.q, spin_echo_length) - j0(self.H, out=self.H) - self.H *= (dq * self.q / (2 * np.pi)).reshape((-1, 1)) - - reptheta = np.outer( - self.q, - full_data._data_contents["Wavelength"].in_units_of(units.angstroms) - / (2 * np.pi), - ) - # Note: Using inplace update with reptheta => arcsin(reptheta). - # When q L / 2 pi > 1 that means wavelength is too large to - # reach that q value at any angle. These should produce theta = NaN - # without any warnings. - # - # Reverse the condition to protect against NaN. We can't use - # theta > zaccept since all comparisons with NaN return False. - zaccept = [ - x.terms["zmax"] for x in full_data.metadata.process if "zmax" in x.terms - ][0] - with np.errstate(invalid="ignore"): - mask = ~(np.arcsin(reptheta) <= zaccept.in_units_of(units.radians)) - self.H[mask] = 0 - - return self.q - - def postprocess_iq(self, data: np.ndarray, full_data: SasData) -> np.ndarray: - """ - Apply the SESANS transform to the computed I(q) - """ - G0 = self.H0 @ data - G = self.H.T @ data - P = G - G0 - - return P - - -MINIMUM_RESOLUTION = 1e-8 -MINIMUM_ABSOLUTE_Q = 0.02 # relative to the minimum q in the data -# According to (Barker & Pedersen 1995 JAC), 2.5 sigma is a good limit. -# According to simulations with github.com:scattering/sansresolution.git -# it is better to use asymmetric bounds (2.5, 3.0) -PINHOLE_N_SIGMA = (2.5, 3.0) - - -class PinholeModel(ModellingRequirements): - """Perform a pin hole smearing""" - - def __init__(self, q_width: np.ndarray, nsigma: (float, float) = PINHOLE_N_SIGMA): - self.q_width = q_width - self.nsigma_low, self.nsigma_high = nsigma - - def preprocess_q(self, q: np.ndarray, full_data: SasData) -> np.ndarray: - """Perform smearing transform""" - self.q = q - q_min = np.min(self.q - self.nsigma_low * self.q_width) - q_max = np.max(self.q + self.nsigma_high * self.q_width) - - self.q_calc = linear_extrapolation(self.q, q_min, q_max) - - # Protect against models which are not defined for very low q. Limit - # the smallest q value evaluated (in absolute) to 0.02*min - cutoff = MINIMUM_ABSOLUTE_Q * np.min(self.q) - self.q_calc = self.q_calc[abs(self.q_calc) >= cutoff] - - # Build weight matrix from calculated q values - self.weight_matrix = pinhole_resolution( - self.q_calc, - self.q, - np.maximum(self.q_width, MINIMUM_RESOLUTION), - nsigma=(self.nsigma_low, self.nsigma_high), - ) - - return np.abs(self.q_calc) - - def postprocess_iq(self, data: np.ndarray, full_data: SasData) -> np.ndarray: - """Perform smearing transform""" - return self.weight_matrix.T @ data - - -class SlitModel(ModellingRequirements): - """Perform a slit smearing""" - - def __init__( - self, - q_length: np.ndarray, - q_width: np.ndarray, - nsigma: (float, float) = PINHOLE_N_SIGMA, - ): - self.q_length = q_length - self.q_width = q_width - self.nsigma_low, self.nsigma_high = nsigma - - def preprocess_q(self, q: np.ndarray, full_data: SasData) -> np.ndarray: - """Perform smearing transform""" - self.q = q - q_min = np.min(self.q - self.nsigma_low * self.q_width) - q_max = np.max(self.q + self.nsigma_high * self.q_width) - - self.q_calc = slit_extend_q(self.q, self.q_width, self.q_length) - - # Protect against models which are not defined for very low q. Limit - # the smallest q value evaluated (in absolute) to 0.02*min - cutoff = MINIMUM_ABSOLUTE_Q * np.min(self.q) - self.q_calc = self.q_calc[abs(self.q_calc) >= cutoff] - - # Build weight matrix from calculated q values - self.weight_matrix = slit_resolution( - self.q_calc, self.q, self.q_length, self.q_width - ) - - return np.abs(self.q_calc) - - def postprocess_iq(self, data: np.ndarray, full_data: SasData) -> np.ndarray: - """Perform smearing transform""" - return self.weight_matrix.T @ data - - -class NullModel(ModellingRequirements): - """A model that does nothing""" - - def compose(self, other: ModellingRequirements) -> ModellingRequirements: - return other - - def preprocess_q( - self, data: Quantity[np.ndarray], _full_data: SasData - ) -> np.ndarray: - """Do nothing""" - return data - - def postprocess_iq(self, data: np.ndarray, _full_data: SasData) -> np.ndarray: - """Do nothing""" - return data - - -def guess_requirements(data: SasData) -> ModellingRequirements: - """Use names of axes and units to guess what kind of processing needs to be done""" - if data.dataset_type == dataset_types.sesans: - return SesansModel() - pass - - -@singledispatch -def compose( - second: ModellingRequirements, first: ModellingRequirements -) -> ModellingRequirements: - """Compose to models together - - This function uses a reverse order so that it can perform dispatch on - the *second* term, since the classes already had a chance to dispatch - on the first parameter - - """ - return ComposeRequirements(first, second) - - -@compose.register -def _(second: NullModel, first: ModellingRequirements) -> ModellingRequirements: - """Null model is the identity element of composition""" - return first - - -@compose.register -def _(second: SesansModel, first: ModellingRequirements) -> ModellingRequirements: - match first: - case PinholeModel() | SlitModel(): - # To the first approximation, there is no slit smearing in SESANS data - return second - case _: - return ComposeRequirements(first, second) - - -def linear_extrapolation(q, q_min, q_max): - """ - Extrapolate *q* out to [*q_min*, *q_max*] using the step size in *q* as - a guide. Extrapolation below uses about the same size as the first - interval. Extrapolation above uses about the same size as the final - interval. - - Note that extrapolated values may be negative. - """ - q = np.sort(q) - delta_low = q[1] - q[0] if len(q) > 1 else 0 - n_low = int(np.ceil((q[0] - q_min) / delta_low)) if delta_low > 0 else 15 - q_low = np.linspace(q_min, q[0], n_low + 1)[:-1] if q_min + 2 * MINIMUM_RESOLUTION >= q[0] else [] - - delta_high = q[-1] - q[-2] if len(q) > 1 else 0 - n_high = int(np.ceil((q_max - q[-1]) / delta_high)) if delta_high > 0 else 15 - q_high = np.linspace(q[-1], q_max, n_high + 1)[1:] if q_max - 2 * MINIMUM_RESOLUTION <= q[-1] else [] - return np.concatenate([q_low, q, q_high]) - - -def pinhole_resolution( - q_calc: np.ndarray, q: np.ndarray, q_width: np.ndarray, nsigma=PINHOLE_N_SIGMA -) -> np.ndarray: - r""" - Compute the convolution matrix *W* for pinhole resolution 1-D data. - - Each row *W[i]* determines the normalized weight that the corresponding - points *q_calc* contribute to the resolution smeared point *q[i]*. Given - *W*, the resolution smearing can be computed using *dot(W,q)*. - - Note that resolution is limited to $\pm 2.5 \sigma$.[1] The true resolution - function is a broadened triangle, and does not extend over the entire - range $(-\infty, +\infty)$. It is important to impose this limitation - since some models fall so steeply that the weighted value in gaussian - tails would otherwise dominate the integral. - - *q_calc* must be increasing. *q_width* must be greater than zero. - - [1] Barker, J. G., and J. S. Pedersen. 1995. Instrumental Smearing Effects - in Radially Symmetric Small-Angle Neutron Scattering by Numerical and - Analytical Methods. Journal of Applied Crystallography 28 (2): 105--14. - https://doi.org/10.1107/S0021889894010095. - """ - # The current algorithm is a midpoint rectangle rule. In the test case, - # neither trapezoid nor Simpson's rule improved the accuracy. - edges = bin_edges(q_calc) - # edges[edges < 0.0] = 0.0 # clip edges below zero - cdf = erf((edges[:, None] - q[None, :]) / (np.sqrt(2.0) * q_width)[None, :]) - weights = cdf[1:] - cdf[:-1] - # Limit q range to (-2.5,+3) sigma - try: - nsigma_low, nsigma_high = nsigma - except TypeError: - nsigma_low = nsigma_high = nsigma - qhigh = q + nsigma_high * q_width - qlow = q - nsigma_low * q_width # linear limits - ##qlow = q*q/qhigh # log limits - weights[q_calc[:, None] < qlow[None, :]] = 0.0 - weights[q_calc[:, None] > qhigh[None, :]] = 0.0 - weights /= np.sum(weights, axis=0)[None, :] - return weights - - -def bin_edges(x): - """ - Determine bin edges from bin centers, assuming that edges are centered - between the bins. - - Note: this uses the arithmetic mean, which may not be appropriate for - log-scaled data. - """ - if len(x) < 2 or (np.diff(x) < 0).any(): - raise ValueError("Expected bins to be an increasing set") - edges = np.hstack( - [ - x[0] - 0.5 * (x[1] - x[0]), # first point minus half first interval - 0.5 * (x[1:] + x[:-1]), # mid points of all central intervals - x[-1] + 0.5 * (x[-1] - x[-2]), # last point plus half last interval - ] - ) - return edges - - -def slit_resolution(q_calc, q, width, length, n_length=30): - r""" - Build a weight matrix to compute *I_s(q)* from *I(q_calc)*, given - $q_\perp$ = *width* (in the high-resolution axis) and $q_\parallel$ - = *length* (in the low resolution axis). *n_length* is the number - of steps to use in the integration over $q_\parallel$ when both - $q_\perp$ and $q_\parallel$ are non-zero. - - Each $q$ can have an independent width and length value even though - current instruments use the same slit setting for all measured points. - - If slit length is large relative to width, use: - - .. math:: - - I_s(q_i) = \frac{1}{\Delta q_\perp} - \int_0^{\Delta q_\perp} - I\left(\sqrt{q_i^2 + q_\perp^2}\right) \,dq_\perp - - If slit width is large relative to length, use: - - .. math:: - - I_s(q_i) = \frac{1}{2 \Delta q_\parallel} - \int_{-\Delta q_\parallel}^{\Delta q_\parallel} - I\left(|q_i + q_\parallel|\right) \,dq_\parallel - - For a mixture of slit width and length use: - - .. math:: - - I_s(q_i) = \frac{1}{2 \Delta q_\parallel \Delta q_\perp} - \int_{-\Delta q_\parallel}^{\Delta q_\parallel} - \int_0^{\Delta q_\perp} - I\left(\sqrt{(q_i + q_\parallel)^2 + q_\perp^2}\right) - \,dq_\perp dq_\parallel - - **Definition** - - We are using the mid-point integration rule to assign weights to each - element of a weight matrix $W$ so that - - .. math:: - - I_s(q) = W\,I(q_\text{calc}) - - If *q_calc* is at the mid-point, we can infer the bin edges from the - pairwise averages of *q_calc*, adding the missing edges before - *q_calc[0]* and after *q_calc[-1]*. - - For $q_\parallel = 0$, the smeared value can be computed numerically - using the $u$ substitution - - .. math:: - - u_j = \sqrt{q_j^2 - q^2} - - This gives - - .. math:: - - I_s(q) \approx \sum_j I(u_j) \Delta u_j - - where $I(u_j)$ is the value at the mid-point, and $\Delta u_j$ is the - difference between consecutive edges which have been first converted - to $u$. Only $u_j \in [0, \Delta q_\perp]$ are used, which corresponds - to $q_j \in \left[q, \sqrt{q^2 + \Delta q_\perp}\right]$, so - - .. math:: - - W_{ij} = \frac{1}{\Delta q_\perp} \Delta u_j - = \frac{1}{\Delta q_\perp} \left( - \sqrt{q_{j+1}^2 - q_i^2} - \sqrt{q_j^2 - q_i^2} \right) - \ \text{if}\ q_j \in \left[q_i, \sqrt{q_i^2 + q_\perp^2}\right] - - where $I_s(q_i)$ is the theory function being computed and $q_j$ are the - mid-points between the calculated values in *q_calc*. We tweak the - edges of the initial and final intervals so that they lie on integration - limits. - - (To be precise, the transformed midpoint $u(q_j)$ is not necessarily the - midpoint of the edges $u((q_{j-1}+q_j)/2)$ and $u((q_j + q_{j+1})/2)$, - but it is at least in the interval, so the approximation is going to be - a little better than the left or right Riemann sum, and should be - good enough for our purposes.) - - For $q_\perp = 0$, the $u$ substitution is simpler: - - .. math:: - - u_j = \left|q_j - q\right| - - so - - .. math:: - - W_{ij} = \frac{1}{2 \Delta q_\parallel} \Delta u_j - = \frac{1}{2 \Delta q_\parallel} (q_{j+1} - q_j) - \ \text{if}\ q_j \in - \left[q-\Delta q_\parallel, q+\Delta q_\parallel\right] - - However, we need to support cases were $u_j < 0$, which means using - $2 (q_{j+1} - q_j)$ when $q_j \in \left[0, q_\parallel-q_i\right]$. - This is not an issue for $q_i > q_\parallel$. - - For both $q_\perp > 0$ and $q_\parallel > 0$ we perform a 2 dimensional - integration with - - .. math:: - - u_{jk} = \sqrt{q_j^2 - (q + (k\Delta q_\parallel/L))^2} - \ \text{for}\ k = -L \ldots L - - for $L$ = *n_length*. This gives - - .. math:: - - W_{ij} = \frac{1}{2 \Delta q_\perp q_\parallel} - \sum_{k=-L}^L \Delta u_{jk} - \left(\frac{\Delta q_\parallel}{2 L + 1}\right) - - - """ - - # The current algorithm is a midpoint rectangle rule. - q_edges = bin_edges(q_calc) # Note: requires q > 0 - weights = np.zeros((len(q), len(q_calc)), "d") - - for i, (qi, w, l) in enumerate(zip(q, width, length)): - if w == 0.0 and l == 0.0: - # Perfect resolution, so return the theory value directly. - # Note: assumes that q is a subset of q_calc. If qi need not be - # in q_calc, then we can do a weighted interpolation by looking - # up qi in q_calc, then weighting the result by the relative - # distance to the neighbouring points. - weights[i, :] = q_calc == qi - elif l == 0: - weights[i, :] = _q_perp_weights(q_edges, qi, w) - elif w == 0 and qi >= l: - in_x = 1.0 * ((q_calc >= qi - l) & (q_calc <= qi + l)) - weights[i, :] = in_x * np.diff(q_edges) / (2 * l) - elif w == 0: - in_x = 1.0 * ((q_calc >= qi - l) & (q_calc <= qi + l)) - abs_x = 1.0 * (q_calc < abs(qi - l)) - weights[i, :] = (in_x + abs_x) * np.diff(q_edges) / (2 * l) - else: - weights[i, :] = _q_perp_weights( - q_edges, qi + np.arange(-n_length, n_length + 1) * l / n_length, w - ) - weights[i, :] /= 2 * n_length + 1 - - return weights.T - - -def _q_perp_weights(q_edges, qi, w): - q_edges = np.reshape(q_edges, (1, -1)) - qi = np.reshape(qi, (-1, 1)) - # Convert bin edges from q to u - u_limit = np.sqrt(qi**2 + w**2) - u_edges = q_edges**2 - qi**2 - u_edges[q_edges < abs(qi)] = 0.0 - u_edges[q_edges > u_limit] = np.repeat( - u_limit**2 - qi**2, u_edges.shape[1], axis=1 - )[q_edges > u_limit] - return (np.diff(np.sqrt(u_edges), axis=1) / w).sum(axis=0) - - -def slit_extend_q(q, width, length): - """ - Given *q*, *width* and *length*, find a set of sampling points *q_calc* so - that each point I(q) has sufficient support from the underlying - function. - """ - q_min, q_max = np.min(q - length), np.max(np.sqrt((q + length) ** 2 + width**2)) - - return geometric_extrapolation(q, q_min, q_max) - - -def geometric_extrapolation(q, q_min, q_max, points_per_decade=None): - r""" - Extrapolate *q* to [*q_min*, *q_max*] using geometric steps, with the - average geometric step size in *q* as the step size. - - if *q_min* is zero or less then *q[0]/10* is used instead. - - *points_per_decade* sets the ratio between consecutive steps such - that there will be $n$ points used for every factor of 10 increase - in *q*. - - If *points_per_decade* is not given, it will be estimated as follows. - Starting at $q_1$ and stepping geometrically by $\Delta q$ to $q_n$ - in $n$ points gives a geometric average of: - - .. math:: - - \log \Delta q = (\log q_n - \log q_1) / (n - 1) - - From this we can compute the number of steps required to extend $q$ - from $q_n$ to $q_\text{max}$ by $\Delta q$ as: - - .. math:: - - n_\text{extend} = (\log q_\text{max} - \log q_n) / \log \Delta q - Substituting: - .. math:: - n_\text{extend} = (n-1) (\log q_\text{max} - \log q_n) - / (\log q_n - \log q_1) - """ - DEFAULT_POINTS_PER_DECADE = 10 - q = np.sort(q) - data_min, data_max = q[0], q[-1] - if points_per_decade is None: - if data_max > data_min: - log_delta_q = (len(q) - 1) / (np.log(data_max) - np.log(data_min)) - else: - log_delta_q = np.log(10.0) / DEFAULT_POINTS_PER_DECADE - else: - log_delta_q = np.log(10.0) / points_per_decade - if q_min <= 0: - q_min = data_min * MINIMUM_ABSOLUTE_Q - if q_min < data_min: - n_low = int(np.ceil(log_delta_q * (np.log(data_min) - np.log(q_min)))) - q_low = np.logspace(np.log10(q_min), np.log10(data_min), n_low + 1)[:-1] - else: - q_low = [] - if q_max > data_max: - n_high = int(np.ceil(log_delta_q * (np.log(q_max) - np.log(data_max)))) - q_high = np.logspace(np.log10(data_max), np.log10(q_max), n_high + 1)[1:] - else: - q_high = [] - return np.concatenate([q_low, q, q_high]) +def guess_requirements(abscissae, ordinate) -> ModellingRequirements: + """ Use names of axes and units to guess what kind of processing needs to be done """ diff --git a/sasdata/quantities/quantities.py b/sasdata/quantities/quantities.py new file mode 100644 index 000000000..05ee51cef --- /dev/null +++ b/sasdata/quantities/quantities.py @@ -0,0 +1,146 @@ +from typing import Collection, Sequence, TypeVar, Generic +from dataclasses import dataclass + +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, other: "Dimensions"): + + 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, other: "Dimensions"): + + 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) + + +@dataclass +class UnitName: + ascii_name: str + unicode_name: str | None = None + + @property + def best_name(self): + if self.unicode_name is None: + return self.ascii_name + else: + return self.unicode_name + +class Unit: + def __init__(self, + si_scaling_factor: float, + dimensions: Dimensions, + name: UnitName | None = None): + + self.scale = si_scaling_factor + self.dimensions = dimensions + self.name = name + + def _components(self, tokens: Sequence["UnitToken"]): + pass + + def __mul__(self, other: "Unit"): + if not isinstance(other, Unit): + return NotImplemented + + return Unit(self.scale * other.scale, self.dimensions * other.dimensions) + + def __truediv__(self, other: "Unit"): + if not isinstance(other, Unit): + return NotImplemented + + return Unit(self.scale / other.scale, self.dimensions / other.dimensions) + + def __pow__(self, power: int): + if not isinstance(power, int): + return NotImplemented + + return Unit(self.scale**power, self.dimensions**power) + + +QuantityType = TypeVar("QuantityType") +class Quantity(Generic[QuantityType]): + def __init__(self, value: QuantityType, units: Unit): + self.value = value + self.units = units + + def in_units_of(self, units: Unit) -> QuantityType: + pass + +class ExpressionMethod: + pass + + +class SetExpressionMethod(ExpressionMethod): + pass + + +class AnyExpressionMethod(ExpressionMethod): + pass + + +class ForceExpressionMethod(ExpressionMethod): + pass + + +class UnitToken: + def __init__(self, unit: Collection[NamedUnit], method: ExpressionMethod): + pass + +unit_dictionary = { + "Amps": Unit(1, Dimensions(current=1), UnitName("A")), + "Coulombs": Unit(1, Dimensions(current=1, time=1), UnitName("C")) +} + +@dataclass +class Disambiguator: + A: Unit = unit_dictionary["Amps"] + C: Unit = unit_dictionary["Coulombs"] + +def parse_units(unit_string: str, disambiguator: Disambiguator = Disambiguator()) -> Unit: + pass diff --git a/sasdata/transforms/operation.py b/sasdata/transforms/operation.py new file mode 100644 index 000000000..e8bf15cfd --- /dev/null +++ b/sasdata/transforms/operation.py @@ -0,0 +1,19 @@ +import numpy as np +from sasdata.quantities.quantities import Quantity + +class Operation: + """ Sketch of what model post-processing classes might look like """ + + children: list["Operation"] + named_children: dict[str, "Operation"] + + @property + def name(self) -> str: + raise NotImplementedError("No name for transform") + + def evaluate(self) -> Quantity[np.ndarray]: + pass + + def __call__(self, *children, **named_children): + self.children = children + self.named_children = named_children \ No newline at end of file From 51541321b4c50c37d5bcf2f34a0a168b4cf30881 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 5 Aug 2024 09:14:59 +0100 Subject: [PATCH 003/675] Some units --- sasdata/data.py | 3 +- sasdata/metadata.py | 33 ++++++++++--- sasdata/quantities/quantities.py | 49 ++++++++++++++++++- sasdata/quantities/units_table.py | 78 +++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 10 deletions(-) create mode 100644 sasdata/quantities/units_table.py diff --git a/sasdata/data.py b/sasdata/data.py index 8c2921a3c..e4919948a 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -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 diff --git a/sasdata/metadata.py b/sasdata/metadata.py index e3d05abba..6713c452f 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -1,4 +1,4 @@ -from typing import Generic, TypeVar +from typing import TypeVar from numpy._typing import ArrayLike @@ -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 @@ -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 @@ -62,3 +70,14 @@ class TemperatureAccessor(QuantityAccessor): class AbsoluteTemperatureAccessor(QuantityAccessor): 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 diff --git a/sasdata/quantities/quantities.py b/sasdata/quantities/quantities.py index 05ee51cef..464f3c6e1 100644 --- a/sasdata/quantities/quantities.py +++ b/sasdata/quantities/quantities.py @@ -1,6 +1,9 @@ from typing import Collection, Sequence, TypeVar, Generic from dataclasses import dataclass +from numpy._typing import ArrayLike + + class Dimensions: """ @@ -58,6 +61,17 @@ def __pow__(self, power: int): self.current * power, self.temperature * power) + def __eq__(self, other: "Dimensions"): + 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 + + @dataclass class UnitName: @@ -102,9 +116,12 @@ def __pow__(self, power: int): return Unit(self.scale**power, self.dimensions**power) + def equivalent(self, other: "Unit"): + return self.dimensions == other.dimensions + -QuantityType = TypeVar("QuantityType") -class Quantity(Generic[QuantityType]): +# QuantityType = TypeVar("QuantityType") +class Quantity[QuantityType]: def __init__(self, value: QuantityType, units: Unit): self.value = value self.units = units @@ -112,6 +129,34 @@ def __init__(self, value: QuantityType, units: Unit): def in_units_of(self, units: Unit) -> QuantityType: pass + def __mul__(self, other: ArrayLike | "Quantity" ) -> "Quantity": + if isinstance(other, Quantity): + pass + + else: + pass + + def __truediv__(self, other: float | "Quantity") -> "Quantity": + if isinstance(other, Quantity): + pass + + else: + pass + + def __rdiv__(self, other: float | "Quantity") -> "Quantity": + if isinstance(other, Quantity): + pass + + else: + pass + def __add__(self, other: "Quantity") -> "Quantity": + if isinstance(other, Quantity): + pass + + def __sub__(self, other: "Quantity") -> "Quantity": + if isinstance(other, Quantity): + pass + class ExpressionMethod: pass diff --git a/sasdata/quantities/units_table.py b/sasdata/quantities/units_table.py new file mode 100644 index 000000000..5a2d6b5c4 --- /dev/null +++ b/sasdata/quantities/units_table.py @@ -0,0 +1,78 @@ +""" +Builds a data file containing details of units +""" + +import numpy as np + +bigger_magnitudes = [ + ("E", None, "exa", 1e18), + ("P", None, "peta", 1e15), + ("T", None, "tera", 1e12), + ("G", None, "giga", 1e9), + ("M", None, "mega", 1e6), + ("k", None, "kilo", 1e3) ] + +smaller_magnitudes = [ + ("m", None, "milli", 1e-3), + ("u", "µ", "micro", 1e-6), + ("n", None, "nano", 1e-9), + ("p", None, "pico", 1e-12), + ("f", None, "femto", 1e-15), + ("a", None, "atto", 1e-18)] + +all_magnitudes = bigger_magnitudes + smaller_magnitudes + +# Length, time, mass, current, temperature +base_si_units = [ + ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, all_magnitudes), + ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, smaller_magnitudes), + ("g", None, "gram", "grams", 1, 0, 0, 1, 0, 0, all_magnitudes), + ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, all_magnitudes), + ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, all_magnitudes) ] + +derived_si_units = [ + ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, all_magnitudes), + ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, all_magnitudes), + ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, all_magnitudes), + ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, all_magnitudes), + ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, all_magnitudes), + ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, all_magnitudes), + ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, all_magnitudes), + ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, all_magnitudes), + ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, all_magnitudes), + ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, all_magnitudes), + ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, all_magnitudes), + ("T", None, "tesla", "tesla", 1, 2, -2, 1, -1, 0, all_magnitudes), + ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, all_magnitudes), + ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, []) +] + +non_si_units = [ + ("A", None, "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), + ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, []), + ("hr", None, "hour", "hours", 360, 0, 1, 0, 0, 0, []), + ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), + ("day", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), + ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), + ("yr", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), + ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, []), + ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, []), + ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []) +] + +all_units = base_si_units + derived_si_units + non_si_units + +encoding = "utf-8" + +with open("unit_data.txt", mode='w', encoding=encoding) as fid: + for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, magnitudes in all_units: + fid.write(f"'{symbol}', '{special_symbol}', '{singular}', '{plural}', ") + fid.write(f"{scale}, {length}, {time}, {mass}, {current}, {temperature}\n") + + for mag_symbol, mag_special_symbol, name, mag_scale in magnitudes: + + combined_symbol = (mag_symbol if mag_special_symbol is None else mag_special_symbol) + \ + (symbol if special_symbol is None else special_symbol) + + fid.write(f"'{mag_symbol}{symbol}', '{combined_symbol}', '{name}{singular}', '{name}{plural}', ") + fid.write(f"{scale * mag_scale}, {length}, {time}, {mass}, {current}, {temperature}\n") From 4d513d5ccdb02fd8d903fdabcab869463ca8de5c Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 6 Aug 2024 14:34:38 +0100 Subject: [PATCH 004/675] Units now available and grouped --- sasdata/quantities/_units_base.py | 297 +-- sasdata/quantities/_units_table.py | 268 +++ sasdata/quantities/quantities.py | 1 + sasdata/quantities/units.py | 3436 +++++++--------------------- sasdata/quantities/units_table.py | 78 - 5 files changed, 1205 insertions(+), 2875 deletions(-) create mode 100644 sasdata/quantities/_units_table.py delete mode 100644 sasdata/quantities/units_table.py diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 5543f1d4e..11db5272d 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -1,23 +1,17 @@ -from collections.abc import Sequence from dataclasses import dataclass -from fractions import Fraction -from typing import Self +from typing import Sequence, Self, TypeVar import numpy as np -from unicode_superscript import int_as_unicode_superscript +from sasdata.quantities.unicode_superscript import int_as_unicode_superscript -class DimensionError(Exception): - pass class Dimensions: """ - Note that some SI Base units are not useful from the perspecive of the sasview project, and make things - behave badly. In particular: moles and angular measures are dimensionless, and candelas are really a weighted - measure of power. + Note that some SI Base units are - We do however track angle and amount, because its really useful for formatting units + For example, moles and angular measures are dimensionless from this perspective, and candelas are """ def __init__(self, @@ -25,22 +19,13 @@ def __init__(self, time: int = 0, mass: int = 0, current: int = 0, - temperature: int = 0, - moles_hint: int = 0, - angle_hint: int = 0): + temperature: int = 0): self.length = length self.time = time self.mass = mass self.current = current self.temperature = temperature - self.moles_hint = moles_hint - self.angle_hint = angle_hint - - @property - def is_dimensionless(self): - """ Is this dimension dimensionless (ignores moles_hint and angle_hint) """ - return self.length == 0 and self.time == 0 and self.mass == 0 and self.current == 0 and self.temperature == 0 def __mul__(self: Self, other: Self): @@ -52,9 +37,7 @@ def __mul__(self: Self, other: Self): self.time + other.time, self.mass + other.mass, self.current + other.current, - self.temperature + other.temperature, - self.moles_hint + other.moles_hint, - self.angle_hint + other.angle_hint) + self.temperature + other.temperature) def __truediv__(self: Self, other: Self): @@ -66,50 +49,19 @@ def __truediv__(self: Self, other: Self): self.time - other.time, self.mass - other.mass, self.current - other.current, - self.temperature - other.temperature, - self.moles_hint - other.moles_hint, - self.angle_hint - other.angle_hint) + self.temperature - other.temperature) - def __pow__(self, power: int | float): + def __pow__(self, power: int): - if not isinstance(power, (int, float)): + if not isinstance(power, int): return NotImplemented - frac = Fraction(power).limit_denominator(500) # Probably way bigger than needed, 10 would probably be fine - denominator = frac.denominator - numerator = frac.numerator - - # Throw errors if dimension is not a multiple of the denominator - - if self.length % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with length dimensionality {self.length}") - - if self.time % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with time dimensionality {self.time}") - - if self.mass % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with mass dimensionality {self.mass}") - - if self.current % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with current dimensionality {self.current}") - - if self.temperature % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with temperature dimensionality {self.temperature}") - - if self.moles_hint % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with moles hint dimensionality of {self.moles_hint}") - - if self.angle_hint % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with angle hint dimensionality of {self.angle_hint}") - return Dimensions( - (self.length * numerator) // denominator, - (self.time * numerator) // denominator, - (self.mass * numerator) // denominator, - (self.current * numerator) // denominator, - (self.temperature * numerator) // denominator, - (self.moles_hint * numerator) // denominator, - (self.angle_hint * numerator) // denominator) + 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): @@ -117,9 +69,7 @@ def __eq__(self: Self, other: Self): self.time == other.time and self.mass == other.mass and self.current == other.current and - self.temperature == other.temperature and - self.moles_hint == other.moles_hint and - self.angle_hint == other.angle_hint) + self.temperature == other.temperature) return NotImplemented @@ -142,93 +92,60 @@ def __hash__(self): if self.temperature < 0: two_powers += 16 - if self.moles_hint < 0: - two_powers += 32 - - if self.angle_hint < 0: - two_powers += 64 - return 2**two_powers * 3**abs(self.length) * 5**abs(self.time) * \ - 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) * \ - 17**abs(self.moles_hint) * 19**abs(self.angle_hint) + 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) def __repr__(self): - tokens = [] + s = "" for name, size in [ - ("length", self.length), - ("time", self.time), - ("mass", self.mass), - ("current", self.current), - ("temperature", self.temperature), - ("amount", self.moles_hint), - ("angle", self.angle_hint)]: + ("L", self.length), + ("T", self.time), + ("M", self.mass), + ("C", self.current), + ("K", self.temperature)]: if size == 0: pass elif size == 1: - tokens.append(f"{name}") + s += f"{name}" else: - tokens.append(f"{name}{int_as_unicode_superscript(size)}") - - return ' '.join(tokens) - - def si_repr(self): - tokens = [] - for name, size in [ - ("kg", self.mass), - ("m", self.length), - ("s", self.time), - ("A", self.current), - ("K", self.temperature), - ("mol", self.moles_hint)]: - - if size == 0: - pass - elif size == 1: - tokens.append(f"{name}") - else: - tokens.append(f"{name}{int_as_unicode_superscript(size)}") - - match self.angle_hint: - case 0: - pass - case 2: - tokens.append("sr") - case -2: - tokens.append("sr" + int_as_unicode_superscript(-1)) - case _: - tokens.append("rad" + int_as_unicode_superscript(self.angle_hint)) - - return ''.join(tokens) + s += f"{name}{int_as_unicode_superscript(size)}" + return s class Unit: def __init__(self, si_scaling_factor: float, - dimensions: Dimensions): + 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: "Unit"): - if isinstance(other, Unit): - return Unit(self.scale * other.scale, self.dimensions * other.dimensions) - elif isinstance(other, (int, float)): - return Unit(other * self.scale, self.dimensions) - return NotImplemented +class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): + pass - def __truediv__(self: Self, other: "Unit"): - if isinstance(other, Unit): - return Unit(self.scale / other.scale, self.dimensions / other.dimensions) - elif isinstance(other, (int, float)): - return Unit(self.scale / other, self.dimensions) - else: + 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 - def __rtruediv__(self: Self, other: "Unit"): + 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)): @@ -236,133 +153,19 @@ def __rtruediv__(self: Self, other: "Unit"): else: return NotImplemented - def __pow__(self, power: int | float): - if not isinstance(power, int | float): + 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: "Unit"): + def equivalent(self: Self, other: Self): return self.dimensions == other.dimensions - def __eq__(self: Self, other: "Unit"): + def __eq__(self: Self, other: Self): return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 - def si_equivalent(self): - """ Get the SI unit corresponding to this unit""" - return Unit(1, self.dimensions) - - def _format_unit(self, format_process: list["UnitFormatProcessor"]): - for processor in format_process: - pass - - def __repr__(self): - if self.scale == 1: - # We're in SI - return self.dimensions.si_repr() - - else: - return f"Unit[{self.scale}, {self.dimensions}]" - - @staticmethod - def parse(unit_string: str) -> "Unit": - pass - -class NamedUnit(Unit): - """ Units, but they have a name, and a symbol - - :si_scaling_factor: Number of these units per SI equivalent - :param dimensions: Dimensions object representing the dimensionality of these units - :param name: Name of unit - string without unicode - :param ascii_symbol: Symbol for unit without unicode - :param symbol: Unicode symbol - """ - def __init__(self, - si_scaling_factor: float, - dimensions: Dimensions, - name: str | None = None, - ascii_symbol: str | None = None, - latex_symbol: str | None = None, - symbol: str | None = None): - - super().__init__(si_scaling_factor, dimensions) - self.name = name - self.ascii_symbol = ascii_symbol - self.symbol = symbol - self.latex_symbol = latex_symbol if latex_symbol is not None else ascii_symbol - - def __repr__(self): - return self.name - - def __eq__(self, other): - """Match other units exactly or match strings against ANY of our names""" - match other: - case str(): - return self.name == other or self.name == f"{other}s" or self.ascii_symbol == other or self.symbol == other - case NamedUnit(): - return self.name == other.name \ - and self.ascii_symbol == other.ascii_symbol and self.symbol == other.symbol - case Unit(): - return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 - case _: - return False - - - def startswith(self, prefix: str) -> bool: - """Check if any representation of the unit begins with the prefix string""" - prefix = prefix.lower() - return (self.name is not None and self.name.lower().startswith(prefix)) \ - or (self.ascii_symbol is not None and self.ascii_symbol.lower().startswith(prefix)) \ - or (self.symbol is not None and self.symbol.lower().startswith(prefix)) - -# -# Parsing plan: -# Require unknown amounts of units to be explicitly positive or negative? -# -# - - - -@dataclass -class ProcessedUnitToken: - """ Mid processing representation of formatted units """ - base_string: str - exponent_string: str - latex_exponent_string: str - exponent: int - -class UnitFormatProcessor: - """ Represents a step in the unit processing pipeline""" - def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: - """ This will be called to deal with each processing stage""" - -class RequiredUnitFormatProcessor(UnitFormatProcessor): - """ This unit is required to exist in the formatting """ - def __init__(self, unit: Unit, power: int = 1): - self.unit = unit - self.power = power - def apply(self, scale, dimensions) -> tuple[float, Dimensions, ProcessedUnitToken]: - new_scale = scale / (self.unit.scale * self.power) - new_dimensions = self.unit.dimensions / (dimensions**self.power) - token = ProcessedUnitToken(self.unit, self.power) - - return new_scale, new_dimensions, token -class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): - """ This processor minimises the dimensionality of the unit by multiplying by as many - units of the specified type as needed """ - def __init__(self, unit: Unit): - self.unit = unit - - def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: - pass - -class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): - pass - class UnitGroup: - """ A group of units that all have the same dimensionality """ - def __init__(self, name: str, units: list[NamedUnit]): + def __init__(self, name: str, units: list[Unit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) - diff --git a/sasdata/quantities/_units_table.py b/sasdata/quantities/_units_table.py new file mode 100644 index 000000000..9b88cab42 --- /dev/null +++ b/sasdata/quantities/_units_table.py @@ -0,0 +1,268 @@ +""" +Builds a data file containing details of units +""" + +import numpy as np +from collections import defaultdict +from _units_base import Dimensions, Unit + +bigger_magnitudes = [ + ("E", None, "exa", 1e18), + ("P", None, "peta", 1e15), + ("T", None, "tera", 1e12), + ("G", None, "giga", 1e9), + ("M", None, "mega", 1e6), + ("k", None, "kilo", 1e3) ] + +smaller_magnitudes = [ + ("m", None, "milli", 1e-3), + ("u", "µ", "micro", 1e-6), + ("n", None, "nano", 1e-9), + ("p", None, "pico", 1e-12), + ("f", None, "femto", 1e-15), + ("a", None, "atto", 1e-18)] + +all_magnitudes = bigger_magnitudes + smaller_magnitudes + +# Length, time, mass, current, temperature +base_si_units = [ + ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, all_magnitudes), + ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, smaller_magnitudes), + ("g", None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, all_magnitudes), + ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, all_magnitudes), + ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, all_magnitudes) ] + +derived_si_units = [ + ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, all_magnitudes), + ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, all_magnitudes), + ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, all_magnitudes), + ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, all_magnitudes), + ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, all_magnitudes), + ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, all_magnitudes), + ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, all_magnitudes), + ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, all_magnitudes), + ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, all_magnitudes), + ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, all_magnitudes), + ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, all_magnitudes), + ("T", None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, all_magnitudes), + ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, all_magnitudes), + ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, []) +] + +non_si_units = [ + ("A", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), + ("Ang", None, "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), + ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, []), + ("hr", None, "hour", "hours", 360, 0, 1, 0, 0, 0, []), + ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), + ("day", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), + ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), + ("yr", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), + ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, []), + ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, []), + ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []) +] + + + +all_units = base_si_units + derived_si_units + non_si_units + +encoding = "utf-8" + +def format_name(name: str): + return name.lower().replace(" ", "_") + +header = """ + +Autogenerated file by _units_table.py + + + + ******** DO NOT EDIT BY HAND ******** + + + +""" + +with open("units.py", 'w', encoding=encoding) as fid: + + # Write warning header + fid.write('"""'+header+'"""') + + # Write in class definitions + fid.write("\n\n" + "#\n" + "# Included from _units_base.py\n" + "#\n\n") + + with open("_units_base.py", 'r') as base: + for line in base: + fid.write(line) + + # Write in unit definitions + fid.write("\n\n" + "#\n" + "# Specific units \n" + "#\n\n") + + symbol_lookup = {} + unit_types_temp = defaultdict(list) # Keep track of unit types + unit_types = defaultdict(list) + + for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, magnitudes in all_units: + + formatted_plural = format_name(plural) + formatted_singular = format_name(singular) + + dimensions = Dimensions(length, time, mass, current, temperature) + fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature})," + f"name='{formatted_plural}'," + f"ascii_symbol='{symbol}'," + f"symbol='{symbol if special_symbol is None else special_symbol}')\n") + + symbol_lookup[symbol] = formatted_plural + if special_symbol is not None: + symbol_lookup[special_symbol] = formatted_plural + + unit_types_temp[hash(dimensions)].append( + (symbol, special_symbol, formatted_singular, formatted_plural, scale, dimensions)) + + unit_types[hash(dimensions)].append(formatted_plural) + + for mag_symbol, mag_special_symbol, name, mag_scale in magnitudes: + + # Work out the combined symbol, accounts for unicode or not + combined_special_symbol = (mag_symbol if mag_special_symbol is None else mag_special_symbol) + \ + (symbol if special_symbol is None else special_symbol) + + combined_symbol = mag_symbol + symbol + + # Combined unit name + combined_name_singular = f"{name}{formatted_singular}" + combined_name_plural = f"{name}{formatted_plural}" + + combined_scale = scale * mag_scale + + # Units + dimensions = Dimensions(length, time, mass, current, temperature) + fid.write(f"{combined_name_plural} = Unit({combined_scale}, " + f"Dimensions({length}, {time}, {mass}, {current}, {temperature})," + f"name='{combined_name_plural}'," + f"ascii_symbol='{combined_symbol}'," + f"symbol='{combined_special_symbol}')\n") + + symbol_lookup[combined_symbol] = combined_name_plural + symbol_lookup[combined_special_symbol] = combined_name_plural + + unit_types_temp[hash(dimensions)].append( + (combined_symbol, combined_special_symbol, combined_name_singular, + combined_name_plural, combined_scale, dimensions)) + + unit_types[hash(dimensions)].append(combined_name_plural) + + # + # Higher dimensioned types + # + + length_units = unit_types_temp[hash(Dimensions(length=1))] + time_units = unit_types_temp[hash(Dimensions(time=1))] + + # Length based + for symbol, special_symbol, singular, plural, scale, _ in length_units: + for prefix, power, name, unicode_suffix in [ + ("square_", 2, plural, '²'), + ("cubic_", 3, plural, '³'), + ("per_", -1, singular, '⁻¹'), + ("per_square_", -2, singular,'⁻²'), + ("per_cubic_", -3, singular,'⁻³')]: + + dimensions = Dimensions(length=power) + unit_name = prefix + name + unit_special_symbol = (symbol if special_symbol is None else special_symbol) + unicode_suffix + unit_symbol = symbol + f"^{power}" + fid.write(f"{unit_name} = Unit({scale**power}, Dimensions({power}, 0, 0, 0, 0), " + f"name='{unit_name}', " + f"ascii_symbol='{unit_symbol}', " + f"symbol='{unit_special_symbol}')\n") + + unit_types[hash(dimensions)].append(unit_name) + + # Speed and acceleration + for length_symbol, length_special_symbol, _, length_name, length_scale, _ in length_units: + for time_symbol, time_special_symbol, time_name, _, time_scale, _ in time_units: + speed_name = length_name + "_per_" + time_name + accel_name = length_name + "_per_square_" + time_name + + speed_dimensions = Dimensions(length=1, time=-1) + accel_dimensions = Dimensions(length=1, time=-2) + + fid.write(f"{speed_name} " + f"= Unit({length_scale / time_scale}, " + f"Dimensions(1, -1, 0, 0, 0), " + f"name='{speed_name}', " + f"ascii_symbol='{length_symbol}/{time_symbol}', " + f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") + + fid.write(f"{accel_name} = Unit({length_scale / time_scale}, " + f"Dimensions(1, -2, 0, 0, 0), " + f"name='{accel_name}', " + f"ascii_symbol='{length_symbol}/{time_symbol}^2', " + f"symbol='{length_special_symbol}{time_special_symbol}⁻²')\n") + + unit_types[hash(speed_dimensions)].append(speed_name) + unit_types[hash(accel_dimensions)].append(accel_name) + + # + # Write out the symbol lookup table + # + fid.write("\n#\n# Lookup table from symbols to units\n#\n\n") + fid.write("symbol_lookup = {\n") + for k in symbol_lookup: + fid.write(f' "{k}": {symbol_lookup[k]},\n') + fid.write("}\n\n") + + # + # Collections of units by type + # + + dimension_names = [ + ("length", Dimensions(length=1)), + ("area", Dimensions(length=2)), + ("volume", Dimensions(length=3)), + ("inverse_length", Dimensions(length=-1)), + ("inverse_area", Dimensions(length=-2)), + ("inverse_volume", Dimensions(length=-3)), + ("time", Dimensions(time=1)), + ("rate", Dimensions(time=-1)), + ("speed", Dimensions(length=1, time=-1)), + ("acceleration", Dimensions(length=1, time=-2)), + ("force", Dimensions(1, -2, 1, 0, 0)), + ("pressure", Dimensions(-1, -2, 1, 0, 0)), + ("energy", Dimensions(2, -2, 1, 0, 0)), + ("power", Dimensions(2, -3, 1, 0, 0)), + ("charge", Dimensions(0, 1, 0, 1, 0)), + ("potential", Dimensions(2, -3, 1, -1, 0)), + ("resistance", Dimensions(2, -3, 1, -2, 0)), + ("capacitance", Dimensions(-2, 4, -1, 2, 0)), + ("conductance", Dimensions(-2, 3, -1, 2, 0)), + ("magnetic_flux", Dimensions(2, -2, 1, -1, 0)), + ("magnetic_flux_density", Dimensions(0, -2, 1, -1, 0)), + ("inductance", Dimensions(2, -2, 1, -2, 0)), + ("temperature", Dimensions(temperature=1)) + ] + + fid.write("\n#\n# Units by type \n#\n\n") + + for dimension_name, dimensions in dimension_names: + + print(dimensions, hash(dimensions)) + + fid.write(f"\n" + f"{dimension_name} = UnitGroup(\n" + f" name = '{dimension_name}', \n" + f" units = [\n") + + for unit_name in unit_types[hash(dimensions)]: + fid.write(" " + unit_name + ",\n") + + fid.write("])\n") \ No newline at end of file diff --git a/sasdata/quantities/quantities.py b/sasdata/quantities/quantities.py index 464f3c6e1..e2ac0ea67 100644 --- a/sasdata/quantities/quantities.py +++ b/sasdata/quantities/quantities.py @@ -3,6 +3,7 @@ from numpy._typing import ArrayLike +from sasdata.quantities.units import Unit class Dimensions: """ diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index fe840ab85..6105e27f5 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -1,78 +1,10 @@ """ -This file is autogenerated! +Autogenerated file by _units_table.py -Do not edit by hand, instead edit the files that build it (_build_tables.py, _units_base.py) - - -DDDDDDDDDDDDD NNNNNNNN NNNNNNNN tttt -D::::::::::::DDD N:::::::N N::::::N ttt:::t -D:::::::::::::::DD N::::::::N N::::::N t:::::t -DDD:::::DDDDD:::::D N:::::::::N N::::::N t:::::t - D:::::D D:::::D ooooooooooo N::::::::::N N::::::N ooooooooooo ttttttt:::::ttttttt - D:::::D D:::::D oo:::::::::::oo N:::::::::::N N::::::N oo:::::::::::oo t:::::::::::::::::t - D:::::D D:::::Do:::::::::::::::o N:::::::N::::N N::::::No:::::::::::::::ot:::::::::::::::::t - D:::::D D:::::Do:::::ooooo:::::o N::::::N N::::N N::::::No:::::ooooo:::::otttttt:::::::tttttt - D:::::D D:::::Do::::o o::::o N::::::N N::::N:::::::No::::o o::::o t:::::t - D:::::D D:::::Do::::o o::::o N::::::N N:::::::::::No::::o o::::o t:::::t - D:::::D D:::::Do::::o o::::o N::::::N N::::::::::No::::o o::::o t:::::t - D:::::D D:::::D o::::o o::::o N::::::N N:::::::::No::::o o::::o t:::::t tttttt -DDD:::::DDDDD:::::D o:::::ooooo:::::o N::::::N N::::::::No:::::ooooo:::::o t::::::tttt:::::t -D:::::::::::::::DD o:::::::::::::::o N::::::N N:::::::No:::::::::::::::o tt::::::::::::::t -D::::::::::::DDD oo:::::::::::oo N::::::N N::::::N oo:::::::::::oo tt:::::::::::tt -DDDDDDDDDDDDD ooooooooooo NNNNNNNN NNNNNNN ooooooooooo ttttttttttt - - - - - - - - - dddddddd -EEEEEEEEEEEEEEEEEEEEEE d::::::d iiii tttt BBBBBBBBBBBBBBBBB -E::::::::::::::::::::E d::::::d i::::i ttt:::t B::::::::::::::::B -E::::::::::::::::::::E d::::::d iiii t:::::t B::::::BBBBBB:::::B -EE::::::EEEEEEEEE::::E d:::::d t:::::t BB:::::B B:::::B - E:::::E EEEEEE ddddddddd:::::d iiiiiiittttttt:::::ttttttt B::::B B:::::Byyyyyyy yyyyyyy - E:::::E dd::::::::::::::d i:::::it:::::::::::::::::t B::::B B:::::B y:::::y y:::::y - E::::::EEEEEEEEEE d::::::::::::::::d i::::it:::::::::::::::::t B::::BBBBBB:::::B y:::::y y:::::y - E:::::::::::::::E d:::::::ddddd:::::d i::::itttttt:::::::tttttt B:::::::::::::BB y:::::y y:::::y - E:::::::::::::::E d::::::d d:::::d i::::i t:::::t B::::BBBBBB:::::B y:::::y y:::::y - E::::::EEEEEEEEEE d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y y:::::y - E:::::E d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y:::::y - E:::::E EEEEEEd:::::d d:::::d i::::i t:::::t tttttt B::::B B:::::B y:::::::::y -EE::::::EEEEEEEE:::::Ed::::::ddddd::::::ddi::::::i t::::::tttt:::::t BB:::::BBBBBB::::::B y:::::::y -E::::::::::::::::::::E d:::::::::::::::::di::::::i tt::::::::::::::t B:::::::::::::::::B y:::::y -E::::::::::::::::::::E d:::::::::ddd::::di::::::i tt:::::::::::tt B::::::::::::::::B y:::::y -EEEEEEEEEEEEEEEEEEEEEE ddddddddd dddddiiiiiiii ttttttttttt BBBBBBBBBBBBBBBBB y:::::y - y:::::y - y:::::y - y:::::y - y:::::y - yyyyyyy - - - - dddddddd -HHHHHHHHH HHHHHHHHH d::::::d -H:::::::H H:::::::H d::::::d -H:::::::H H:::::::H d::::::d -HH::::::H H::::::HH d:::::d - H:::::H H:::::H aaaaaaaaaaaaa nnnn nnnnnnnn ddddddddd:::::d - H:::::H H:::::H a::::::::::::a n:::nn::::::::nn dd::::::::::::::d - H::::::HHHHH::::::H aaaaaaaaa:::::an::::::::::::::nn d::::::::::::::::d - H:::::::::::::::::H a::::ann:::::::::::::::nd:::::::ddddd:::::d - H:::::::::::::::::H aaaaaaa:::::a n:::::nnnn:::::nd::::::d d:::::d - H::::::HHHHH::::::H aa::::::::::::a n::::n n::::nd:::::d d:::::d - H:::::H H:::::H a::::aaaa::::::a n::::n n::::nd:::::d d:::::d - H:::::H H:::::H a::::a a:::::a n::::n n::::nd:::::d d:::::d -HH::::::H H::::::HHa::::a a:::::a n::::n n::::nd::::::ddddd::::::dd -H:::::::H H:::::::Ha:::::aaaa::::::a n::::n n::::n d:::::::::::::::::d -H:::::::H H:::::::H a::::::::::aa:::a n::::n n::::n d:::::::::ddd::::d -HHHHHHHHH HHHHHHHHH aaaaaaaaaa aaaa nnnnnn nnnnnn ddddddddd ddddd + ******** DO NOT EDIT BY HAND ******** @@ -82,27 +14,20 @@ # Included from _units_base.py # -from collections.abc import Sequence from dataclasses import dataclass -from fractions import Fraction -from typing import Self +from typing import Sequence, Self, TypeVar import numpy as np from sasdata.quantities.unicode_superscript import int_as_unicode_superscript -class DimensionError(Exception): - pass - class Dimensions: """ - Note that some SI Base units are not useful from the perspecive of the sasview project, and make things - behave badly. In particular: moles and angular measures are dimensionless, and candelas are really a weighted - measure of power. + Note that some SI Base units are - We do however track angle and amount, because its really useful for formatting units + For example, moles and angular measures are dimensionless from this perspective, and candelas are """ def __init__(self, @@ -110,22 +35,13 @@ def __init__(self, time: int = 0, mass: int = 0, current: int = 0, - temperature: int = 0, - moles_hint: int = 0, - angle_hint: int = 0): + temperature: int = 0): self.length = length self.time = time self.mass = mass self.current = current self.temperature = temperature - self.moles_hint = moles_hint - self.angle_hint = angle_hint - - @property - def is_dimensionless(self): - """ Is this dimension dimensionless (ignores moles_hint and angle_hint) """ - return self.length == 0 and self.time == 0 and self.mass == 0 and self.current == 0 and self.temperature == 0 def __mul__(self: Self, other: Self): @@ -137,9 +53,7 @@ def __mul__(self: Self, other: Self): self.time + other.time, self.mass + other.mass, self.current + other.current, - self.temperature + other.temperature, - self.moles_hint + other.moles_hint, - self.angle_hint + other.angle_hint) + self.temperature + other.temperature) def __truediv__(self: Self, other: Self): @@ -151,50 +65,19 @@ def __truediv__(self: Self, other: Self): self.time - other.time, self.mass - other.mass, self.current - other.current, - self.temperature - other.temperature, - self.moles_hint - other.moles_hint, - self.angle_hint - other.angle_hint) + self.temperature - other.temperature) - def __pow__(self, power: int | float): + def __pow__(self, power: int): - if not isinstance(power, (int, float)): + if not isinstance(power, int): return NotImplemented - frac = Fraction(power).limit_denominator(500) # Probably way bigger than needed, 10 would probably be fine - denominator = frac.denominator - numerator = frac.numerator - - # Throw errors if dimension is not a multiple of the denominator - - if self.length % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with length dimensionality {self.length}") - - if self.time % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with time dimensionality {self.time}") - - if self.mass % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with mass dimensionality {self.mass}") - - if self.current % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with current dimensionality {self.current}") - - if self.temperature % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with temperature dimensionality {self.temperature}") - - if self.moles_hint % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with moles hint dimensionality of {self.moles_hint}") - - if self.angle_hint % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with angle hint dimensionality of {self.angle_hint}") - return Dimensions( - (self.length * numerator) // denominator, - (self.time * numerator) // denominator, - (self.mass * numerator) // denominator, - (self.current * numerator) // denominator, - (self.temperature * numerator) // denominator, - (self.moles_hint * numerator) // denominator, - (self.angle_hint * numerator) // denominator) + 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): @@ -202,9 +85,7 @@ def __eq__(self: Self, other: Self): self.time == other.time and self.mass == other.mass and self.current == other.current and - self.temperature == other.temperature and - self.moles_hint == other.moles_hint and - self.angle_hint == other.angle_hint) + self.temperature == other.temperature) return NotImplemented @@ -227,93 +108,57 @@ def __hash__(self): if self.temperature < 0: two_powers += 16 - if self.moles_hint < 0: - two_powers += 32 - - if self.angle_hint < 0: - two_powers += 64 - return 2**two_powers * 3**abs(self.length) * 5**abs(self.time) * \ - 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) * \ - 17**abs(self.moles_hint) * 19**abs(self.angle_hint) + 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) def __repr__(self): - tokens = [] + s = "" for name, size in [ - ("length", self.length), - ("time", self.time), - ("mass", self.mass), - ("current", self.current), - ("temperature", self.temperature), - ("amount", self.moles_hint), - ("angle", self.angle_hint)]: + ("L", self.length), + ("T", self.time), + ("M", self.mass), + ("C", self.current), + ("K", self.temperature)]: if size == 0: pass elif size == 1: - tokens.append(f"{name}") + s += f"{name}" else: - tokens.append(f"{name}{int_as_unicode_superscript(size)}") - - return ' '.join(tokens) - - def si_repr(self): - tokens = [] - for name, size in [ - ("kg", self.mass), - ("m", self.length), - ("s", self.time), - ("A", self.current), - ("K", self.temperature), - ("mol", self.moles_hint)]: - - if size == 0: - pass - elif size == 1: - tokens.append(f"{name}") - else: - tokens.append(f"{name}{int_as_unicode_superscript(size)}") - - match self.angle_hint: - case 0: - pass - case 2: - tokens.append("sr") - case -2: - tokens.append("sr" + int_as_unicode_superscript(-1)) - case _: - tokens.append("rad" + int_as_unicode_superscript(self.angle_hint)) - - return ''.join(tokens) + s += f"{name}{int_as_unicode_superscript(size)}" + return s class Unit: def __init__(self, si_scaling_factor: float, - dimensions: Dimensions): + 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: "Unit"): - if isinstance(other, Unit): - return Unit(self.scale * other.scale, self.dimensions * other.dimensions) - elif isinstance(other, (int, float)): - return Unit(other * self.scale, self.dimensions) - return NotImplemented + def __mul__(self: Self, other: Self): + if not isinstance(other, Unit): + return NotImplemented - def __truediv__(self: Self, other: "Unit"): - if isinstance(other, Unit): - return Unit(self.scale / other.scale, self.dimensions / other.dimensions) - elif isinstance(other, (int, float)): - return Unit(self.scale / other, self.dimensions) - else: + return Unit(self.scale * other.scale, self.dimensions * other.dimensions) + + def __truediv__(self: Self, other: Self): + if not isinstance(other, Unit): return NotImplemented - def __rtruediv__(self: Self, other: "Unit"): + 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)): @@ -321,1467 +166,733 @@ def __rtruediv__(self: Self, other: "Unit"): else: return NotImplemented - def __pow__(self, power: int | float): - if not isinstance(power, int | float): + 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: "Unit"): + def equivalent(self: Self, other: Self): return self.dimensions == other.dimensions - def __eq__(self: Self, other: "Unit"): + def __eq__(self: Self, other: Self): return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 - def si_equivalent(self): - """ Get the SI unit corresponding to this unit""" - return Unit(1, self.dimensions) - - def _format_unit(self, format_process: list["UnitFormatProcessor"]): - for processor in format_process: - pass - - def __repr__(self): - if self.scale == 1: - # We're in SI - return self.dimensions.si_repr() - - else: - return f"Unit[{self.scale}, {self.dimensions}]" - - @staticmethod - def parse(unit_string: str) -> "Unit": - pass - -class NamedUnit(Unit): - """ Units, but they have a name, and a symbol - - :si_scaling_factor: Number of these units per SI equivalent - :param dimensions: Dimensions object representing the dimensionality of these units - :param name: Name of unit - string without unicode - :param ascii_symbol: Symbol for unit without unicode - :param symbol: Unicode symbol - """ - def __init__(self, - si_scaling_factor: float, - dimensions: Dimensions, - name: str | None = None, - ascii_symbol: str | None = None, - latex_symbol: str | None = None, - symbol: str | None = None): - - super().__init__(si_scaling_factor, dimensions) - self.name = name - self.ascii_symbol = ascii_symbol - self.symbol = symbol - self.latex_symbol = latex_symbol if latex_symbol is not None else ascii_symbol - - def __repr__(self): - return self.name - - def __eq__(self, other): - """Match other units exactly or match strings against ANY of our names""" - match other: - case str(): - return self.name == other or self.name == f"{other}s" or self.ascii_symbol == other or self.symbol == other - case NamedUnit(): - return self.name == other.name \ - and self.ascii_symbol == other.ascii_symbol and self.symbol == other.symbol - case Unit(): - return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 - case _: - return False - - - def startswith(self, prefix: str) -> bool: - """Check if any representation of the unit begins with the prefix string""" - prefix = prefix.lower() - return (self.name is not None and self.name.lower().startswith(prefix)) \ - or (self.ascii_symbol is not None and self.ascii_symbol.lower().startswith(prefix)) \ - or (self.symbol is not None and self.symbol.lower().startswith(prefix)) - -# -# Parsing plan: -# Require unknown amounts of units to be explicitly positive or negative? -# -# - - - -@dataclass -class ProcessedUnitToken: - """ Mid processing representation of formatted units """ - base_string: str - exponent_string: str - latex_exponent_string: str - exponent: int - -class UnitFormatProcessor: - """ Represents a step in the unit processing pipeline""" - def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: - """ This will be called to deal with each processing stage""" - -class RequiredUnitFormatProcessor(UnitFormatProcessor): - """ This unit is required to exist in the formatting """ - def __init__(self, unit: Unit, power: int = 1): - self.unit = unit - self.power = power - def apply(self, scale, dimensions) -> tuple[float, Dimensions, ProcessedUnitToken]: - new_scale = scale / (self.unit.scale * self.power) - new_dimensions = self.unit.dimensions / (dimensions**self.power) - token = ProcessedUnitToken(self.unit, self.power) - - return new_scale, new_dimensions, token -class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): - """ This processor minimises the dimensionality of the unit by multiplying by as many - units of the specified type as needed """ - def __init__(self, unit: Unit): - self.unit = unit - - def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: - pass - -class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): - pass - class UnitGroup: - """ A group of units that all have the same dimensionality """ - def __init__(self, name: str, units: list[NamedUnit]): + def __init__(self, name: str, units: list[Unit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) - # -# Specific units +# Specific units # -meters = NamedUnit(1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') -exameters = NamedUnit(1e+18, Dimensions(1, 0, 0, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') -petameters = NamedUnit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') -terameters = NamedUnit(1000000000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') -gigameters = NamedUnit(1000000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='gigameters',ascii_symbol='Gm',symbol='Gm') -megameters = NamedUnit(1000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='megameters',ascii_symbol='Mm',symbol='Mm') -kilometers = NamedUnit(1000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='kilometers',ascii_symbol='km',symbol='km') -millimeters = NamedUnit(0.001, Dimensions(1, 0, 0, 0, 0, 0, 0),name='millimeters',ascii_symbol='mm',symbol='mm') -micrometers = NamedUnit(1e-06, Dimensions(1, 0, 0, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',latex_symbol=r'{\mu}m',symbol='µm') -nanometers = NamedUnit(1e-09, Dimensions(1, 0, 0, 0, 0, 0, 0),name='nanometers',ascii_symbol='nm',symbol='nm') -picometers = NamedUnit(1e-12, Dimensions(1, 0, 0, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') -femtometers = NamedUnit(1e-15, Dimensions(1, 0, 0, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') -attometers = NamedUnit(1e-18, Dimensions(1, 0, 0, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') -decimeters = NamedUnit(0.1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='decimeters',ascii_symbol='dm',symbol='dm') -centimeters = NamedUnit(0.01, Dimensions(1, 0, 0, 0, 0, 0, 0),name='centimeters',ascii_symbol='cm',symbol='cm') -seconds = NamedUnit(1, Dimensions(0, 1, 0, 0, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') -milliseconds = NamedUnit(0.001, Dimensions(0, 1, 0, 0, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') -microseconds = NamedUnit(1e-06, Dimensions(0, 1, 0, 0, 0, 0, 0),name='microseconds',ascii_symbol='us',latex_symbol=r'{\mu}s',symbol='µs') -nanoseconds = NamedUnit(1e-09, Dimensions(0, 1, 0, 0, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') -picoseconds = NamedUnit(1e-12, Dimensions(0, 1, 0, 0, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') -femtoseconds = NamedUnit(1e-15, Dimensions(0, 1, 0, 0, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') -attoseconds = NamedUnit(1e-18, Dimensions(0, 1, 0, 0, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') -grams = NamedUnit(0.001, Dimensions(0, 0, 1, 0, 0, 0, 0),name='grams',ascii_symbol='g',symbol='g') -exagrams = NamedUnit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') -petagrams = NamedUnit(1000000000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') -teragrams = NamedUnit(1000000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') -gigagrams = NamedUnit(1000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='gigagrams',ascii_symbol='Gg',symbol='Gg') -megagrams = NamedUnit(1000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='megagrams',ascii_symbol='Mg',symbol='Mg') -kilograms = NamedUnit(1.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='kilograms',ascii_symbol='kg',symbol='kg') -milligrams = NamedUnit(1e-06, Dimensions(0, 0, 1, 0, 0, 0, 0),name='milligrams',ascii_symbol='mg',symbol='mg') -micrograms = NamedUnit(1e-09, Dimensions(0, 0, 1, 0, 0, 0, 0),name='micrograms',ascii_symbol='ug',latex_symbol=r'{\mu}g',symbol='µg') -nanograms = NamedUnit(1.0000000000000002e-12, Dimensions(0, 0, 1, 0, 0, 0, 0),name='nanograms',ascii_symbol='ng',symbol='ng') -picograms = NamedUnit(1e-15, Dimensions(0, 0, 1, 0, 0, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') -femtograms = NamedUnit(1e-18, Dimensions(0, 0, 1, 0, 0, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') -attograms = NamedUnit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') -amperes = NamedUnit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amperes',ascii_symbol='A',symbol='A') -exaamperes = NamedUnit(1e+18, Dimensions(0, 0, 0, 1, 0, 0, 0),name='exaamperes',ascii_symbol='EA',symbol='EA') -petaamperes = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='petaamperes',ascii_symbol='PA',symbol='PA') -teraamperes = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='teraamperes',ascii_symbol='TA',symbol='TA') -gigaamperes = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='gigaamperes',ascii_symbol='GA',symbol='GA') -megaamperes = NamedUnit(1000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='megaamperes',ascii_symbol='MA',symbol='MA') -kiloamperes = NamedUnit(1000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='kiloamperes',ascii_symbol='kA',symbol='kA') -milliamperes = NamedUnit(0.001, Dimensions(0, 0, 0, 1, 0, 0, 0),name='milliamperes',ascii_symbol='mA',symbol='mA') -microamperes = NamedUnit(1e-06, Dimensions(0, 0, 0, 1, 0, 0, 0),name='microamperes',ascii_symbol='uA',latex_symbol=r'{\mu}A',symbol='µA') -nanoamperes = NamedUnit(1e-09, Dimensions(0, 0, 0, 1, 0, 0, 0),name='nanoamperes',ascii_symbol='nA',symbol='nA') -picoamperes = NamedUnit(1e-12, Dimensions(0, 0, 0, 1, 0, 0, 0),name='picoamperes',ascii_symbol='pA',symbol='pA') -femtoamperes = NamedUnit(1e-15, Dimensions(0, 0, 0, 1, 0, 0, 0),name='femtoamperes',ascii_symbol='fA',symbol='fA') -attoamperes = NamedUnit(1e-18, Dimensions(0, 0, 0, 1, 0, 0, 0),name='attoamperes',ascii_symbol='aA',symbol='aA') -kelvin = NamedUnit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kelvin',ascii_symbol='K',symbol='K') -exakelvin = NamedUnit(1e+18, Dimensions(0, 0, 0, 0, 1, 0, 0),name='exakelvin',ascii_symbol='EK',symbol='EK') -petakelvin = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='petakelvin',ascii_symbol='PK',symbol='PK') -terakelvin = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='terakelvin',ascii_symbol='TK',symbol='TK') -gigakelvin = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='gigakelvin',ascii_symbol='GK',symbol='GK') -megakelvin = NamedUnit(1000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='megakelvin',ascii_symbol='MK',symbol='MK') -kilokelvin = NamedUnit(1000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kilokelvin',ascii_symbol='kK',symbol='kK') -millikelvin = NamedUnit(0.001, Dimensions(0, 0, 0, 0, 1, 0, 0),name='millikelvin',ascii_symbol='mK',symbol='mK') -microkelvin = NamedUnit(1e-06, Dimensions(0, 0, 0, 0, 1, 0, 0),name='microkelvin',ascii_symbol='uK',latex_symbol=r'{\mu}K',symbol='µK') -nanokelvin = NamedUnit(1e-09, Dimensions(0, 0, 0, 0, 1, 0, 0),name='nanokelvin',ascii_symbol='nK',symbol='nK') -picokelvin = NamedUnit(1e-12, Dimensions(0, 0, 0, 0, 1, 0, 0),name='picokelvin',ascii_symbol='pK',symbol='pK') -femtokelvin = NamedUnit(1e-15, Dimensions(0, 0, 0, 0, 1, 0, 0),name='femtokelvin',ascii_symbol='fK',symbol='fK') -attokelvin = NamedUnit(1e-18, Dimensions(0, 0, 0, 0, 1, 0, 0),name='attokelvin',ascii_symbol='aK',symbol='aK') -hertz = NamedUnit(1, Dimensions(0, -1, 0, 0, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') -exahertz = NamedUnit(1e+18, Dimensions(0, -1, 0, 0, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') -petahertz = NamedUnit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') -terahertz = NamedUnit(1000000000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') -gigahertz = NamedUnit(1000000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='gigahertz',ascii_symbol='GHz',symbol='GHz') -megahertz = NamedUnit(1000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='megahertz',ascii_symbol='MHz',symbol='MHz') -kilohertz = NamedUnit(1000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='kilohertz',ascii_symbol='kHz',symbol='kHz') -millihertz = NamedUnit(0.001, Dimensions(0, -1, 0, 0, 0, 0, 0),name='millihertz',ascii_symbol='mHz',symbol='mHz') -microhertz = NamedUnit(1e-06, Dimensions(0, -1, 0, 0, 0, 0, 0),name='microhertz',ascii_symbol='uHz',latex_symbol=r'{\mu}Hz',symbol='µHz') -nanohertz = NamedUnit(1e-09, Dimensions(0, -1, 0, 0, 0, 0, 0),name='nanohertz',ascii_symbol='nHz',symbol='nHz') -picohertz = NamedUnit(1e-12, Dimensions(0, -1, 0, 0, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') -femtohertz = NamedUnit(1e-15, Dimensions(0, -1, 0, 0, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') -attohertz = NamedUnit(1e-18, Dimensions(0, -1, 0, 0, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') -newtons = NamedUnit(1, Dimensions(1, -2, 1, 0, 0, 0, 0),name='newtons',ascii_symbol='N',symbol='N') -exanewtons = NamedUnit(1e+18, Dimensions(1, -2, 1, 0, 0, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') -petanewtons = NamedUnit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') -teranewtons = NamedUnit(1000000000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') -giganewtons = NamedUnit(1000000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='giganewtons',ascii_symbol='GN',symbol='GN') -meganewtons = NamedUnit(1000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='meganewtons',ascii_symbol='MN',symbol='MN') -kilonewtons = NamedUnit(1000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kilonewtons',ascii_symbol='kN',symbol='kN') -millinewtons = NamedUnit(0.001, Dimensions(1, -2, 1, 0, 0, 0, 0),name='millinewtons',ascii_symbol='mN',symbol='mN') -micronewtons = NamedUnit(1e-06, Dimensions(1, -2, 1, 0, 0, 0, 0),name='micronewtons',ascii_symbol='uN',latex_symbol=r'{\mu}N',symbol='µN') -nanonewtons = NamedUnit(1e-09, Dimensions(1, -2, 1, 0, 0, 0, 0),name='nanonewtons',ascii_symbol='nN',symbol='nN') -piconewtons = NamedUnit(1e-12, Dimensions(1, -2, 1, 0, 0, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') -femtonewtons = NamedUnit(1e-15, Dimensions(1, -2, 1, 0, 0, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') -attonewtons = NamedUnit(1e-18, Dimensions(1, -2, 1, 0, 0, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') -pascals = NamedUnit(1, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') -exapascals = NamedUnit(1e+18, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') -petapascals = NamedUnit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') -terapascals = NamedUnit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') -gigapascals = NamedUnit(1000000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='gigapascals',ascii_symbol='GPa',symbol='GPa') -megapascals = NamedUnit(1000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='megapascals',ascii_symbol='MPa',symbol='MPa') -kilopascals = NamedUnit(1000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='kilopascals',ascii_symbol='kPa',symbol='kPa') -millipascals = NamedUnit(0.001, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='millipascals',ascii_symbol='mPa',symbol='mPa') -micropascals = NamedUnit(1e-06, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='micropascals',ascii_symbol='uPa',latex_symbol=r'{\mu}Pa',symbol='µPa') -nanopascals = NamedUnit(1e-09, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='nanopascals',ascii_symbol='nPa',symbol='nPa') -picopascals = NamedUnit(1e-12, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') -femtopascals = NamedUnit(1e-15, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') -attopascals = NamedUnit(1e-18, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') -joules = NamedUnit(1, Dimensions(2, -2, 1, 0, 0, 0, 0),name='joules',ascii_symbol='J',symbol='J') -exajoules = NamedUnit(1e+18, Dimensions(2, -2, 1, 0, 0, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') -petajoules = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') -terajoules = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') -gigajoules = NamedUnit(1000000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='gigajoules',ascii_symbol='GJ',symbol='GJ') -megajoules = NamedUnit(1000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='megajoules',ascii_symbol='MJ',symbol='MJ') -kilojoules = NamedUnit(1000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='kilojoules',ascii_symbol='kJ',symbol='kJ') -millijoules = NamedUnit(0.001, Dimensions(2, -2, 1, 0, 0, 0, 0),name='millijoules',ascii_symbol='mJ',symbol='mJ') -microjoules = NamedUnit(1e-06, Dimensions(2, -2, 1, 0, 0, 0, 0),name='microjoules',ascii_symbol='uJ',latex_symbol=r'{\mu}J',symbol='µJ') -nanojoules = NamedUnit(1e-09, Dimensions(2, -2, 1, 0, 0, 0, 0),name='nanojoules',ascii_symbol='nJ',symbol='nJ') -picojoules = NamedUnit(1e-12, Dimensions(2, -2, 1, 0, 0, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') -femtojoules = NamedUnit(1e-15, Dimensions(2, -2, 1, 0, 0, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') -attojoules = NamedUnit(1e-18, Dimensions(2, -2, 1, 0, 0, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') -watts = NamedUnit(1, Dimensions(2, -3, 1, 0, 0, 0, 0),name='watts',ascii_symbol='W',symbol='W') -exawatts = NamedUnit(1e+18, Dimensions(2, -3, 1, 0, 0, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') -petawatts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') -terawatts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') -gigawatts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='gigawatts',ascii_symbol='GW',symbol='GW') -megawatts = NamedUnit(1000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='megawatts',ascii_symbol='MW',symbol='MW') -kilowatts = NamedUnit(1000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='kilowatts',ascii_symbol='kW',symbol='kW') -milliwatts = NamedUnit(0.001, Dimensions(2, -3, 1, 0, 0, 0, 0),name='milliwatts',ascii_symbol='mW',symbol='mW') -microwatts = NamedUnit(1e-06, Dimensions(2, -3, 1, 0, 0, 0, 0),name='microwatts',ascii_symbol='uW',latex_symbol=r'{\mu}W',symbol='µW') -nanowatts = NamedUnit(1e-09, Dimensions(2, -3, 1, 0, 0, 0, 0),name='nanowatts',ascii_symbol='nW',symbol='nW') -picowatts = NamedUnit(1e-12, Dimensions(2, -3, 1, 0, 0, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') -femtowatts = NamedUnit(1e-15, Dimensions(2, -3, 1, 0, 0, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') -attowatts = NamedUnit(1e-18, Dimensions(2, -3, 1, 0, 0, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') -coulombs = NamedUnit(1, Dimensions(0, 1, 0, 1, 0, 0, 0),name='coulombs',ascii_symbol='C',symbol='C') -exacoulombs = NamedUnit(1e+18, Dimensions(0, 1, 0, 1, 0, 0, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') -petacoulombs = NamedUnit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') -teracoulombs = NamedUnit(1000000000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') -gigacoulombs = NamedUnit(1000000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='gigacoulombs',ascii_symbol='GC',symbol='GC') -megacoulombs = NamedUnit(1000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='megacoulombs',ascii_symbol='MC',symbol='MC') -kilocoulombs = NamedUnit(1000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='kilocoulombs',ascii_symbol='kC',symbol='kC') -millicoulombs = NamedUnit(0.001, Dimensions(0, 1, 0, 1, 0, 0, 0),name='millicoulombs',ascii_symbol='mC',symbol='mC') -microcoulombs = NamedUnit(1e-06, Dimensions(0, 1, 0, 1, 0, 0, 0),name='microcoulombs',ascii_symbol='uC',latex_symbol=r'{\mu}C',symbol='µC') -nanocoulombs = NamedUnit(1e-09, Dimensions(0, 1, 0, 1, 0, 0, 0),name='nanocoulombs',ascii_symbol='nC',symbol='nC') -picocoulombs = NamedUnit(1e-12, Dimensions(0, 1, 0, 1, 0, 0, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') -femtocoulombs = NamedUnit(1e-15, Dimensions(0, 1, 0, 1, 0, 0, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') -attocoulombs = NamedUnit(1e-18, Dimensions(0, 1, 0, 1, 0, 0, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') -volts = NamedUnit(1, Dimensions(2, -3, 1, -1, 0, 0, 0),name='volts',ascii_symbol='V',symbol='V') -exavolts = NamedUnit(1e+18, Dimensions(2, -3, 1, -1, 0, 0, 0),name='exavolts',ascii_symbol='EV',symbol='EV') -petavolts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='petavolts',ascii_symbol='PV',symbol='PV') -teravolts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='teravolts',ascii_symbol='TV',symbol='TV') -gigavolts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='gigavolts',ascii_symbol='GV',symbol='GV') -megavolts = NamedUnit(1000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='megavolts',ascii_symbol='MV',symbol='MV') -kilovolts = NamedUnit(1000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='kilovolts',ascii_symbol='kV',symbol='kV') -millivolts = NamedUnit(0.001, Dimensions(2, -3, 1, -1, 0, 0, 0),name='millivolts',ascii_symbol='mV',symbol='mV') -microvolts = NamedUnit(1e-06, Dimensions(2, -3, 1, -1, 0, 0, 0),name='microvolts',ascii_symbol='uV',latex_symbol=r'{\mu}V',symbol='µV') -nanovolts = NamedUnit(1e-09, Dimensions(2, -3, 1, -1, 0, 0, 0),name='nanovolts',ascii_symbol='nV',symbol='nV') -picovolts = NamedUnit(1e-12, Dimensions(2, -3, 1, -1, 0, 0, 0),name='picovolts',ascii_symbol='pV',symbol='pV') -femtovolts = NamedUnit(1e-15, Dimensions(2, -3, 1, -1, 0, 0, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') -attovolts = NamedUnit(1e-18, Dimensions(2, -3, 1, -1, 0, 0, 0),name='attovolts',ascii_symbol='aV',symbol='aV') -ohms = NamedUnit(1, Dimensions(2, -3, 1, -2, 0, 0, 0),name='ohms',ascii_symbol='Ohm',latex_symbol=r'\Omega',symbol='Ω') -exaohms = NamedUnit(1e+18, Dimensions(2, -3, 1, -2, 0, 0, 0),name='exaohms',ascii_symbol='EOhm',latex_symbol=r'E\Omega',symbol='EΩ') -petaohms = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='petaohms',ascii_symbol='POhm',latex_symbol=r'P\Omega',symbol='PΩ') -teraohms = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='teraohms',ascii_symbol='TOhm',latex_symbol=r'T\Omega',symbol='TΩ') -gigaohms = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='gigaohms',ascii_symbol='GOhm',latex_symbol=r'G\Omega',symbol='GΩ') -megaohms = NamedUnit(1000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='megaohms',ascii_symbol='MOhm',latex_symbol=r'M\Omega',symbol='MΩ') -kiloohms = NamedUnit(1000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='kiloohms',ascii_symbol='kOhm',latex_symbol=r'k\Omega',symbol='kΩ') -milliohms = NamedUnit(0.001, Dimensions(2, -3, 1, -2, 0, 0, 0),name='milliohms',ascii_symbol='mOhm',latex_symbol=r'm\Omega',symbol='mΩ') -microohms = NamedUnit(1e-06, Dimensions(2, -3, 1, -2, 0, 0, 0),name='microohms',ascii_symbol='uOhm',latex_symbol=r'{\mu}\Omega',symbol='µΩ') -nanoohms = NamedUnit(1e-09, Dimensions(2, -3, 1, -2, 0, 0, 0),name='nanoohms',ascii_symbol='nOhm',latex_symbol=r'n\Omega',symbol='nΩ') -picoohms = NamedUnit(1e-12, Dimensions(2, -3, 1, -2, 0, 0, 0),name='picoohms',ascii_symbol='pOhm',latex_symbol=r'p\Omega',symbol='pΩ') -femtoohms = NamedUnit(1e-15, Dimensions(2, -3, 1, -2, 0, 0, 0),name='femtoohms',ascii_symbol='fOhm',latex_symbol=r'f\Omega',symbol='fΩ') -attoohms = NamedUnit(1e-18, Dimensions(2, -3, 1, -2, 0, 0, 0),name='attoohms',ascii_symbol='aOhm',latex_symbol=r'a\Omega',symbol='aΩ') -farads = NamedUnit(1, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='farads',ascii_symbol='F',symbol='F') -exafarads = NamedUnit(1e+18, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='exafarads',ascii_symbol='EF',symbol='EF') -petafarads = NamedUnit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='petafarads',ascii_symbol='PF',symbol='PF') -terafarads = NamedUnit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='terafarads',ascii_symbol='TF',symbol='TF') -gigafarads = NamedUnit(1000000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='gigafarads',ascii_symbol='GF',symbol='GF') -megafarads = NamedUnit(1000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='megafarads',ascii_symbol='MF',symbol='MF') -kilofarads = NamedUnit(1000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='kilofarads',ascii_symbol='kF',symbol='kF') -millifarads = NamedUnit(0.001, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='millifarads',ascii_symbol='mF',symbol='mF') -microfarads = NamedUnit(1e-06, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='microfarads',ascii_symbol='uF',latex_symbol=r'{\mu}F',symbol='µF') -nanofarads = NamedUnit(1e-09, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='nanofarads',ascii_symbol='nF',symbol='nF') -picofarads = NamedUnit(1e-12, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='picofarads',ascii_symbol='pF',symbol='pF') -femtofarads = NamedUnit(1e-15, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') -attofarads = NamedUnit(1e-18, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='attofarads',ascii_symbol='aF',symbol='aF') -siemens = NamedUnit(1, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='siemens',ascii_symbol='S',symbol='S') -exasiemens = NamedUnit(1e+18, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') -petasiemens = NamedUnit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') -terasiemens = NamedUnit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') -gigasiemens = NamedUnit(1000000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='gigasiemens',ascii_symbol='GS',symbol='GS') -megasiemens = NamedUnit(1000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='megasiemens',ascii_symbol='MS',symbol='MS') -kilosiemens = NamedUnit(1000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='kilosiemens',ascii_symbol='kS',symbol='kS') -millisiemens = NamedUnit(0.001, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='millisiemens',ascii_symbol='mS',symbol='mS') -microsiemens = NamedUnit(1e-06, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='microsiemens',ascii_symbol='uS',latex_symbol=r'{\mu}S',symbol='µS') -nanosiemens = NamedUnit(1e-09, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='nanosiemens',ascii_symbol='nS',symbol='nS') -picosiemens = NamedUnit(1e-12, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') -femtosiemens = NamedUnit(1e-15, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') -attosiemens = NamedUnit(1e-18, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') -webers = NamedUnit(1, Dimensions(2, -2, 1, -1, 0, 0, 0),name='webers',ascii_symbol='Wb',symbol='Wb') -exawebers = NamedUnit(1e+18, Dimensions(2, -2, 1, -1, 0, 0, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') -petawebers = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') -terawebers = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') -gigawebers = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='gigawebers',ascii_symbol='GWb',symbol='GWb') -megawebers = NamedUnit(1000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='megawebers',ascii_symbol='MWb',symbol='MWb') -kilowebers = NamedUnit(1000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='kilowebers',ascii_symbol='kWb',symbol='kWb') -milliwebers = NamedUnit(0.001, Dimensions(2, -2, 1, -1, 0, 0, 0),name='milliwebers',ascii_symbol='mWb',symbol='mWb') -microwebers = NamedUnit(1e-06, Dimensions(2, -2, 1, -1, 0, 0, 0),name='microwebers',ascii_symbol='uWb',latex_symbol=r'{\mu}Wb',symbol='µWb') -nanowebers = NamedUnit(1e-09, Dimensions(2, -2, 1, -1, 0, 0, 0),name='nanowebers',ascii_symbol='nWb',symbol='nWb') -picowebers = NamedUnit(1e-12, Dimensions(2, -2, 1, -1, 0, 0, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') -femtowebers = NamedUnit(1e-15, Dimensions(2, -2, 1, -1, 0, 0, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') -attowebers = NamedUnit(1e-18, Dimensions(2, -2, 1, -1, 0, 0, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') -tesla = NamedUnit(1, Dimensions(0, -2, 1, -1, 0, 0, 0),name='tesla',ascii_symbol='T',symbol='T') -exatesla = NamedUnit(1e+18, Dimensions(0, -2, 1, -1, 0, 0, 0),name='exatesla',ascii_symbol='ET',symbol='ET') -petatesla = NamedUnit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='petatesla',ascii_symbol='PT',symbol='PT') -teratesla = NamedUnit(1000000000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='teratesla',ascii_symbol='TT',symbol='TT') -gigatesla = NamedUnit(1000000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='gigatesla',ascii_symbol='GT',symbol='GT') -megatesla = NamedUnit(1000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='megatesla',ascii_symbol='MT',symbol='MT') -kilotesla = NamedUnit(1000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='kilotesla',ascii_symbol='kT',symbol='kT') -millitesla = NamedUnit(0.001, Dimensions(0, -2, 1, -1, 0, 0, 0),name='millitesla',ascii_symbol='mT',symbol='mT') -microtesla = NamedUnit(1e-06, Dimensions(0, -2, 1, -1, 0, 0, 0),name='microtesla',ascii_symbol='uT',latex_symbol=r'{\mu}T',symbol='µT') -nanotesla = NamedUnit(1e-09, Dimensions(0, -2, 1, -1, 0, 0, 0),name='nanotesla',ascii_symbol='nT',symbol='nT') -picotesla = NamedUnit(1e-12, Dimensions(0, -2, 1, -1, 0, 0, 0),name='picotesla',ascii_symbol='pT',symbol='pT') -femtotesla = NamedUnit(1e-15, Dimensions(0, -2, 1, -1, 0, 0, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') -attotesla = NamedUnit(1e-18, Dimensions(0, -2, 1, -1, 0, 0, 0),name='attotesla',ascii_symbol='aT',symbol='aT') -henry = NamedUnit(1, Dimensions(2, -2, 1, -2, 0, 0, 0),name='henry',ascii_symbol='H',symbol='H') -exahenry = NamedUnit(1e+18, Dimensions(2, -2, 1, -2, 0, 0, 0),name='exahenry',ascii_symbol='EH',symbol='EH') -petahenry = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='petahenry',ascii_symbol='PH',symbol='PH') -terahenry = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='terahenry',ascii_symbol='TH',symbol='TH') -gigahenry = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='gigahenry',ascii_symbol='GH',symbol='GH') -megahenry = NamedUnit(1000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='megahenry',ascii_symbol='MH',symbol='MH') -kilohenry = NamedUnit(1000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='kilohenry',ascii_symbol='kH',symbol='kH') -millihenry = NamedUnit(0.001, Dimensions(2, -2, 1, -2, 0, 0, 0),name='millihenry',ascii_symbol='mH',symbol='mH') -microhenry = NamedUnit(1e-06, Dimensions(2, -2, 1, -2, 0, 0, 0),name='microhenry',ascii_symbol='uH',latex_symbol=r'{\mu}H',symbol='µH') -nanohenry = NamedUnit(1e-09, Dimensions(2, -2, 1, -2, 0, 0, 0),name='nanohenry',ascii_symbol='nH',symbol='nH') -picohenry = NamedUnit(1e-12, Dimensions(2, -2, 1, -2, 0, 0, 0),name='picohenry',ascii_symbol='pH',symbol='pH') -femtohenry = NamedUnit(1e-15, Dimensions(2, -2, 1, -2, 0, 0, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') -attohenry = NamedUnit(1e-18, Dimensions(2, -2, 1, -2, 0, 0, 0),name='attohenry',ascii_symbol='aH',symbol='aH') -angstroms = NamedUnit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',latex_symbol=r'\AA',symbol='Å') -microns = NamedUnit(1e-06, Dimensions(1, 0, 0, 0, 0, 0, 0),name='microns',ascii_symbol='micron',symbol='micron') -minutes = NamedUnit(60, Dimensions(0, 1, 0, 0, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') -revolutions_per_minute = NamedUnit(0.016666666666666666, Dimensions(0, -1, 0, 0, 0, 0, 0),name='revolutions_per_minute',ascii_symbol='rpm',symbol='rpm') -hours = NamedUnit(3600, Dimensions(0, 1, 0, 0, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') -days = NamedUnit(86400, Dimensions(0, 1, 0, 0, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') -years = NamedUnit(31556952.0, Dimensions(0, 1, 0, 0, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') -degrees = NamedUnit(57.29577951308232, Dimensions(0, 0, 0, 0, 0, 0, 1),name='degrees',ascii_symbol='deg',symbol='deg') -radians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 1),name='radians',ascii_symbol='rad',symbol='rad') -rotations = NamedUnit(6.283185307179586, Dimensions(0, 0, 0, 0, 0, 0, 1),name='rotations',ascii_symbol='rot',symbol='rot') -stradians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') -litres = NamedUnit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') -electronvolts = NamedUnit(1.602176634e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') -exaelectronvolts = NamedUnit(0.1602176634, Dimensions(2, -2, 1, 0, 0, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') -petaelectronvolts = NamedUnit(0.0001602176634, Dimensions(2, -2, 1, 0, 0, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') -teraelectronvolts = NamedUnit(1.602176634e-07, Dimensions(2, -2, 1, 0, 0, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') -gigaelectronvolts = NamedUnit(1.6021766339999998e-10, Dimensions(2, -2, 1, 0, 0, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') -megaelectronvolts = NamedUnit(1.6021766339999998e-13, Dimensions(2, -2, 1, 0, 0, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') -kiloelectronvolts = NamedUnit(1.602176634e-16, Dimensions(2, -2, 1, 0, 0, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') -millielectronvolts = NamedUnit(1.6021766339999998e-22, Dimensions(2, -2, 1, 0, 0, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') -microelectronvolts = NamedUnit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0, 0, 0),name='microelectronvolts',ascii_symbol='ueV',latex_symbol=r'{\mu}eV',symbol='µeV') -nanoelectronvolts = NamedUnit(1.602176634e-28, Dimensions(2, -2, 1, 0, 0, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') -picoelectronvolts = NamedUnit(1.6021766339999998e-31, Dimensions(2, -2, 1, 0, 0, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') -femtoelectronvolts = NamedUnit(1.602176634e-34, Dimensions(2, -2, 1, 0, 0, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') -attoelectronvolts = NamedUnit(1.602176634e-37, Dimensions(2, -2, 1, 0, 0, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') -atomic_mass_units = NamedUnit(1.660538921e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') -moles = NamedUnit(6.02214076e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') -millimoles = NamedUnit(6.02214076e+20, Dimensions(0, 0, 0, 0, 0, 1, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') -micromoles = NamedUnit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0, 1, 0),name='micromoles',ascii_symbol='umol',latex_symbol=r'{\mu}mol',symbol='µmol') -nanomoles = NamedUnit(602214076000000.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') -picomoles = NamedUnit(602214076000.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') -femtomoles = NamedUnit(602214076.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') -attomoles = NamedUnit(602214.076, Dimensions(0, 0, 0, 0, 0, 1, 0),name='attomoles',ascii_symbol='amol',symbol='amol') -kg_force = NamedUnit(9.80665, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kg_force',ascii_symbol='kgForce',symbol='kgForce') -degrees_celsius = NamedUnit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') -miles = NamedUnit(1609.344, Dimensions(1, 0, 0, 0, 0, 0, 0),name='miles',ascii_symbol='miles',symbol='miles') -yards = NamedUnit(0.9144000000000001, Dimensions(1, 0, 0, 0, 0, 0, 0),name='yards',ascii_symbol='yrd',symbol='yrd') -feet = NamedUnit(0.3048, Dimensions(1, 0, 0, 0, 0, 0, 0),name='feet',ascii_symbol='ft',symbol='ft') -inches = NamedUnit(0.0254, Dimensions(1, 0, 0, 0, 0, 0, 0),name='inches',ascii_symbol='in',symbol='in') -pounds = NamedUnit(0.45359237, Dimensions(0, 0, 1, 0, 0, 0, 0),name='pounds',ascii_symbol='lb',symbol='lb') -pounds_force = NamedUnit(4.448222, Dimensions(1, -2, 1, 0, 0, 0, 0),name='pounds_force',ascii_symbol='lbf',symbol='lbf') -ounces = NamedUnit(0.028349523125, Dimensions(0, 0, 1, 0, 0, 0, 0),name='ounces',ascii_symbol='oz',symbol='oz') -pounds_force_per_square_inch = NamedUnit(6894.757889515779, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pounds_force_per_square_inch',ascii_symbol='psi',symbol='psi') -none = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') -percent = NamedUnit(0.01, Dimensions(0, 0, 0, 0, 0, 0, 0),name='percent',ascii_symbol='percent',latex_symbol=r'\%',symbol='%') -square_meters = NamedUnit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') -cubic_meters = NamedUnit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') -per_meter = NamedUnit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') -per_square_meter = NamedUnit(1.0, Dimensions(length=-2), name='per_square_meter', ascii_symbol='m^-2', symbol='m⁻²') -per_cubic_meter = NamedUnit(1.0, Dimensions(length=-3), name='per_cubic_meter', ascii_symbol='m^-3', symbol='m⁻³') -square_exameters = NamedUnit(1e+36, Dimensions(length=2), name='square_exameters', ascii_symbol='Em^2', symbol='Em²') -cubic_exameters = NamedUnit(1e+54, Dimensions(length=3), name='cubic_exameters', ascii_symbol='Em^3', symbol='Em³') -per_exameter = NamedUnit(1e-18, Dimensions(length=-1), name='per_exameter', ascii_symbol='Em^-1', symbol='Em⁻¹') -per_square_exameter = NamedUnit(1e-36, Dimensions(length=-2), name='per_square_exameter', ascii_symbol='Em^-2', symbol='Em⁻²') -per_cubic_exameter = NamedUnit(1e-54, Dimensions(length=-3), name='per_cubic_exameter', ascii_symbol='Em^-3', symbol='Em⁻³') -square_petameters = NamedUnit(1e+30, Dimensions(length=2), name='square_petameters', ascii_symbol='Pm^2', symbol='Pm²') -cubic_petameters = NamedUnit(1e+45, Dimensions(length=3), name='cubic_petameters', ascii_symbol='Pm^3', symbol='Pm³') -per_petameter = NamedUnit(1e-15, Dimensions(length=-1), name='per_petameter', ascii_symbol='Pm^-1', symbol='Pm⁻¹') -per_square_petameter = NamedUnit(1e-30, Dimensions(length=-2), name='per_square_petameter', ascii_symbol='Pm^-2', symbol='Pm⁻²') -per_cubic_petameter = NamedUnit(1e-45, Dimensions(length=-3), name='per_cubic_petameter', ascii_symbol='Pm^-3', symbol='Pm⁻³') -square_terameters = NamedUnit(1e+24, Dimensions(length=2), name='square_terameters', ascii_symbol='Tm^2', symbol='Tm²') -cubic_terameters = NamedUnit(1e+36, Dimensions(length=3), name='cubic_terameters', ascii_symbol='Tm^3', symbol='Tm³') -per_terameter = NamedUnit(1e-12, Dimensions(length=-1), name='per_terameter', ascii_symbol='Tm^-1', symbol='Tm⁻¹') -per_square_terameter = NamedUnit(1e-24, Dimensions(length=-2), name='per_square_terameter', ascii_symbol='Tm^-2', symbol='Tm⁻²') -per_cubic_terameter = NamedUnit(1e-36, Dimensions(length=-3), name='per_cubic_terameter', ascii_symbol='Tm^-3', symbol='Tm⁻³') -square_gigameters = NamedUnit(1e+18, Dimensions(length=2), name='square_gigameters', ascii_symbol='Gm^2', symbol='Gm²') -cubic_gigameters = NamedUnit(1e+27, Dimensions(length=3), name='cubic_gigameters', ascii_symbol='Gm^3', symbol='Gm³') -per_gigameter = NamedUnit(1e-09, Dimensions(length=-1), name='per_gigameter', ascii_symbol='Gm^-1', symbol='Gm⁻¹') -per_square_gigameter = NamedUnit(1e-18, Dimensions(length=-2), name='per_square_gigameter', ascii_symbol='Gm^-2', symbol='Gm⁻²') -per_cubic_gigameter = NamedUnit(1e-27, Dimensions(length=-3), name='per_cubic_gigameter', ascii_symbol='Gm^-3', symbol='Gm⁻³') -square_megameters = NamedUnit(1000000000000.0, Dimensions(length=2), name='square_megameters', ascii_symbol='Mm^2', symbol='Mm²') -cubic_megameters = NamedUnit(1e+18, Dimensions(length=3), name='cubic_megameters', ascii_symbol='Mm^3', symbol='Mm³') -per_megameter = NamedUnit(1e-06, Dimensions(length=-1), name='per_megameter', ascii_symbol='Mm^-1', symbol='Mm⁻¹') -per_square_megameter = NamedUnit(1e-12, Dimensions(length=-2), name='per_square_megameter', ascii_symbol='Mm^-2', symbol='Mm⁻²') -per_cubic_megameter = NamedUnit(1e-18, Dimensions(length=-3), name='per_cubic_megameter', ascii_symbol='Mm^-3', symbol='Mm⁻³') -square_kilometers = NamedUnit(1000000.0, Dimensions(length=2), name='square_kilometers', ascii_symbol='km^2', symbol='km²') -cubic_kilometers = NamedUnit(1000000000.0, Dimensions(length=3), name='cubic_kilometers', ascii_symbol='km^3', symbol='km³') -per_kilometer = NamedUnit(0.001, Dimensions(length=-1), name='per_kilometer', ascii_symbol='km^-1', symbol='km⁻¹') -per_square_kilometer = NamedUnit(1e-06, Dimensions(length=-2), name='per_square_kilometer', ascii_symbol='km^-2', symbol='km⁻²') -per_cubic_kilometer = NamedUnit(1e-09, Dimensions(length=-3), name='per_cubic_kilometer', ascii_symbol='km^-3', symbol='km⁻³') -square_millimeters = NamedUnit(1e-06, Dimensions(length=2), name='square_millimeters', ascii_symbol='mm^2', symbol='mm²') -cubic_millimeters = NamedUnit(1e-09, Dimensions(length=3), name='cubic_millimeters', ascii_symbol='mm^3', symbol='mm³') -per_millimeter = NamedUnit(1000.0, Dimensions(length=-1), name='per_millimeter', ascii_symbol='mm^-1', symbol='mm⁻¹') -per_square_millimeter = NamedUnit(1000000.0, Dimensions(length=-2), name='per_square_millimeter', ascii_symbol='mm^-2', symbol='mm⁻²') -per_cubic_millimeter = NamedUnit(999999999.9999999, Dimensions(length=-3), name='per_cubic_millimeter', ascii_symbol='mm^-3', symbol='mm⁻³') -square_micrometers = NamedUnit(1e-12, Dimensions(length=2), name='square_micrometers', ascii_symbol='um^2', symbol='µm²') -cubic_micrometers = NamedUnit(9.999999999999999e-19, Dimensions(length=3), name='cubic_micrometers', ascii_symbol='um^3', symbol='µm³') -per_micrometer = NamedUnit(1000000.0, Dimensions(length=-1), name='per_micrometer', ascii_symbol='um^-1', symbol='µm⁻¹') -per_square_micrometer = NamedUnit(1000000000000.0001, Dimensions(length=-2), name='per_square_micrometer', ascii_symbol='um^-2', symbol='µm⁻²') -per_cubic_micrometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3), name='per_cubic_micrometer', ascii_symbol='um^-3', symbol='µm⁻³') -square_nanometers = NamedUnit(1e-18, Dimensions(length=2), name='square_nanometers', ascii_symbol='nm^2', symbol='nm²') -cubic_nanometers = NamedUnit(1.0000000000000002e-27, Dimensions(length=3), name='cubic_nanometers', ascii_symbol='nm^3', symbol='nm³') -per_nanometer = NamedUnit(999999999.9999999, Dimensions(length=-1), name='per_nanometer', ascii_symbol='nm^-1', symbol='nm⁻¹') -per_square_nanometer = NamedUnit(9.999999999999999e+17, Dimensions(length=-2), name='per_square_nanometer', ascii_symbol='nm^-2', symbol='nm⁻²') -per_cubic_nanometer = NamedUnit(9.999999999999999e+26, Dimensions(length=-3), name='per_cubic_nanometer', ascii_symbol='nm^-3', symbol='nm⁻³') -square_picometers = NamedUnit(1e-24, Dimensions(length=2), name='square_picometers', ascii_symbol='pm^2', symbol='pm²') -cubic_picometers = NamedUnit(1e-36, Dimensions(length=3), name='cubic_picometers', ascii_symbol='pm^3', symbol='pm³') -per_picometer = NamedUnit(1000000000000.0, Dimensions(length=-1), name='per_picometer', ascii_symbol='pm^-1', symbol='pm⁻¹') -per_square_picometer = NamedUnit(1e+24, Dimensions(length=-2), name='per_square_picometer', ascii_symbol='pm^-2', symbol='pm⁻²') -per_cubic_picometer = NamedUnit(1e+36, Dimensions(length=-3), name='per_cubic_picometer', ascii_symbol='pm^-3', symbol='pm⁻³') -square_femtometers = NamedUnit(1e-30, Dimensions(length=2), name='square_femtometers', ascii_symbol='fm^2', symbol='fm²') -cubic_femtometers = NamedUnit(1.0000000000000003e-45, Dimensions(length=3), name='cubic_femtometers', ascii_symbol='fm^3', symbol='fm³') -per_femtometer = NamedUnit(999999999999999.9, Dimensions(length=-1), name='per_femtometer', ascii_symbol='fm^-1', symbol='fm⁻¹') -per_square_femtometer = NamedUnit(9.999999999999999e+29, Dimensions(length=-2), name='per_square_femtometer', ascii_symbol='fm^-2', symbol='fm⁻²') -per_cubic_femtometer = NamedUnit(9.999999999999998e+44, Dimensions(length=-3), name='per_cubic_femtometer', ascii_symbol='fm^-3', symbol='fm⁻³') -square_attometers = NamedUnit(1.0000000000000001e-36, Dimensions(length=2), name='square_attometers', ascii_symbol='am^2', symbol='am²') -cubic_attometers = NamedUnit(1.0000000000000002e-54, Dimensions(length=3), name='cubic_attometers', ascii_symbol='am^3', symbol='am³') -per_attometer = NamedUnit(9.999999999999999e+17, Dimensions(length=-1), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') -per_square_attometer = NamedUnit(9.999999999999999e+35, Dimensions(length=-2), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') -per_cubic_attometer = NamedUnit(9.999999999999997e+53, Dimensions(length=-3), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') -square_decimeters = NamedUnit(0.010000000000000002, Dimensions(length=2), name='square_decimeters', ascii_symbol='dm^2', symbol='dm²') -cubic_decimeters = NamedUnit(0.0010000000000000002, Dimensions(length=3), name='cubic_decimeters', ascii_symbol='dm^3', symbol='dm³') -per_decimeter = NamedUnit(10.0, Dimensions(length=-1), name='per_decimeter', ascii_symbol='dm^-1', symbol='dm⁻¹') -per_square_decimeter = NamedUnit(99.99999999999999, Dimensions(length=-2), name='per_square_decimeter', ascii_symbol='dm^-2', symbol='dm⁻²') -per_cubic_decimeter = NamedUnit(999.9999999999999, Dimensions(length=-3), name='per_cubic_decimeter', ascii_symbol='dm^-3', symbol='dm⁻³') -square_centimeters = NamedUnit(0.0001, Dimensions(length=2), name='square_centimeters', ascii_symbol='cm^2', symbol='cm²') -cubic_centimeters = NamedUnit(1.0000000000000002e-06, Dimensions(length=3), name='cubic_centimeters', ascii_symbol='cm^3', symbol='cm³') -per_centimeter = NamedUnit(100.0, Dimensions(length=-1), name='per_centimeter', ascii_symbol='cm^-1', symbol='cm⁻¹') -per_square_centimeter = NamedUnit(10000.0, Dimensions(length=-2), name='per_square_centimeter', ascii_symbol='cm^-2', symbol='cm⁻²') -per_cubic_centimeter = NamedUnit(999999.9999999999, Dimensions(length=-3), name='per_cubic_centimeter', ascii_symbol='cm^-3', symbol='cm⁻³') -square_angstroms = NamedUnit(1.0000000000000001e-20, Dimensions(length=2), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ų') -cubic_angstroms = NamedUnit(1e-30, Dimensions(length=3), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='ų') -per_angstrom = NamedUnit(10000000000.0, Dimensions(length=-1), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Å⁻¹') -per_square_angstrom = NamedUnit(1e+20, Dimensions(length=-2), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Å⁻²') -per_cubic_angstrom = NamedUnit(9.999999999999999e+29, Dimensions(length=-3), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Å⁻³') -square_microns = NamedUnit(1e-12, Dimensions(length=2), name='square_microns', ascii_symbol='micron^2', symbol='micron²') -cubic_microns = NamedUnit(9.999999999999999e-19, Dimensions(length=3), name='cubic_microns', ascii_symbol='micron^3', symbol='micron³') -per_micron = NamedUnit(1000000.0, Dimensions(length=-1), name='per_micron', ascii_symbol='micron^-1', symbol='micron⁻¹') -per_square_micron = NamedUnit(1000000000000.0001, Dimensions(length=-2), name='per_square_micron', ascii_symbol='micron^-2', symbol='micron⁻²') -per_cubic_micron = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3), name='per_cubic_micron', ascii_symbol='micron^-3', symbol='micron⁻³') -square_miles = NamedUnit(2589988.110336, Dimensions(length=2), name='square_miles', ascii_symbol='miles^2', symbol='miles²') -cubic_miles = NamedUnit(4168181825.44058, Dimensions(length=3), name='cubic_miles', ascii_symbol='miles^3', symbol='miles³') -per_mile = NamedUnit(0.0006213711922373339, Dimensions(length=-1), name='per_mile', ascii_symbol='miles^-1', symbol='miles⁻¹') -per_square_mile = NamedUnit(3.861021585424458e-07, Dimensions(length=-2), name='per_square_mile', ascii_symbol='miles^-2', symbol='miles⁻²') -per_cubic_mile = NamedUnit(2.399127585789277e-10, Dimensions(length=-3), name='per_cubic_mile', ascii_symbol='miles^-3', symbol='miles⁻³') -square_yards = NamedUnit(0.8361273600000002, Dimensions(length=2), name='square_yards', ascii_symbol='yrd^2', symbol='yrd²') -cubic_yards = NamedUnit(0.7645548579840002, Dimensions(length=3), name='cubic_yards', ascii_symbol='yrd^3', symbol='yrd³') -per_yard = NamedUnit(1.0936132983377076, Dimensions(length=-1), name='per_yard', ascii_symbol='yrd^-1', symbol='yrd⁻¹') -per_square_yard = NamedUnit(1.19599004630108, Dimensions(length=-2), name='per_square_yard', ascii_symbol='yrd^-2', symbol='yrd⁻²') -per_cubic_yard = NamedUnit(1.3079506193143917, Dimensions(length=-3), name='per_cubic_yard', ascii_symbol='yrd^-3', symbol='yrd⁻³') -square_feet = NamedUnit(0.09290304, Dimensions(length=2), name='square_feet', ascii_symbol='ft^2', symbol='ft²') -cubic_feet = NamedUnit(0.028316846592000004, Dimensions(length=3), name='cubic_feet', ascii_symbol='ft^3', symbol='ft³') -per_foot = NamedUnit(3.280839895013123, Dimensions(length=-1), name='per_foot', ascii_symbol='ft^-1', symbol='ft⁻¹') -per_square_foot = NamedUnit(10.763910416709722, Dimensions(length=-2), name='per_square_foot', ascii_symbol='ft^-2', symbol='ft⁻²') -per_cubic_foot = NamedUnit(35.314666721488585, Dimensions(length=-3), name='per_cubic_foot', ascii_symbol='ft^-3', symbol='ft⁻³') -square_inches = NamedUnit(0.00064516, Dimensions(length=2), name='square_inches', ascii_symbol='in^2', symbol='in²') -cubic_inches = NamedUnit(1.6387064e-05, Dimensions(length=3), name='cubic_inches', ascii_symbol='in^3', symbol='in³') -per_inch = NamedUnit(39.37007874015748, Dimensions(length=-1), name='per_inch', ascii_symbol='in^-1', symbol='in⁻¹') -per_square_inch = NamedUnit(1550.0031000062002, Dimensions(length=-2), name='per_square_inch', ascii_symbol='in^-2', symbol='in⁻²') -per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3), name='per_cubic_inch', ascii_symbol='in^-3', symbol='in⁻³') -meters_per_second = NamedUnit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='ms⁻¹') -meters_per_square_second = NamedUnit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='ms⁻²') -meters_per_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='mms⁻¹') -meters_per_square_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='mms⁻²') -meters_per_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='mµs⁻¹') -meters_per_square_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='mµs⁻²') -meters_per_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='mns⁻¹') -meters_per_square_nanosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='mns⁻²') -meters_per_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='mps⁻¹') -meters_per_square_picosecond = NamedUnit(1.0000000000000001e+24, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='mps⁻²') -meters_per_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='mfs⁻¹') -meters_per_square_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='mfs⁻²') -meters_per_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='mas⁻¹') -meters_per_square_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='mas⁻²') -meters_per_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='mmin⁻¹') -meters_per_square_minute = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='mmin⁻²') -meters_per_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='mh⁻¹') -meters_per_square_hour = NamedUnit(7.71604938271605e-08, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='mh⁻²') -meters_per_day = NamedUnit(1.1574074074074073e-05, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='md⁻¹') -meters_per_square_day = NamedUnit(1.3395919067215363e-10, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='md⁻²') -meters_per_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='my⁻¹') -meters_per_square_year = NamedUnit(1.0041761481530735e-15, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='my⁻²') -exameters_per_second = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='Ems⁻¹') -exameters_per_square_second = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='Ems⁻²') -exameters_per_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') -exameters_per_square_millisecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') -exameters_per_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') -exameters_per_square_microsecond = NamedUnit(1e+30, Dimensions(length=1, time=-2), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') -exameters_per_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') -exameters_per_square_nanosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') -exameters_per_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-1), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') -exameters_per_square_picosecond = NamedUnit(1e+42, Dimensions(length=1, time=-2), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') -exameters_per_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') -exameters_per_square_femtosecond = NamedUnit(9.999999999999999e+47, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') -exameters_per_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-1), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') -exameters_per_square_attosecond = NamedUnit(9.999999999999999e+53, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') -exameters_per_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='Emmin⁻¹') -exameters_per_square_minute = NamedUnit(277777777777777.78, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='Emmin⁻²') -exameters_per_hour = NamedUnit(277777777777777.78, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='Emh⁻¹') -exameters_per_square_hour = NamedUnit(77160493827.16049, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='Emh⁻²') -exameters_per_day = NamedUnit(11574074074074.074, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='Emd⁻¹') -exameters_per_square_day = NamedUnit(133959190.67215364, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='Emd⁻²') -exameters_per_year = NamedUnit(31688738506.81143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='Emy⁻¹') -exameters_per_square_year = NamedUnit(1004.1761481530735, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='Emy⁻²') -petameters_per_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='Pms⁻¹') -petameters_per_square_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='Pms⁻²') -petameters_per_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') -petameters_per_square_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') -petameters_per_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') -petameters_per_square_microsecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') -petameters_per_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') -petameters_per_square_nanosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') -petameters_per_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-1), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') -petameters_per_square_picosecond = NamedUnit(1.0000000000000001e+39, Dimensions(length=1, time=-2), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') -petameters_per_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') -petameters_per_square_femtosecond = NamedUnit(1e+45, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') -petameters_per_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') -petameters_per_square_attosecond = NamedUnit(9.999999999999998e+50, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') -petameters_per_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='Pmmin⁻¹') -petameters_per_square_minute = NamedUnit(277777777777.7778, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='Pmmin⁻²') -petameters_per_hour = NamedUnit(277777777777.7778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='Pmh⁻¹') -petameters_per_square_hour = NamedUnit(77160493.82716049, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='Pmh⁻²') -petameters_per_day = NamedUnit(11574074074.074074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='Pmd⁻¹') -petameters_per_square_day = NamedUnit(133959.19067215364, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='Pmd⁻²') -petameters_per_year = NamedUnit(31688738.506811433, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='Pmy⁻¹') -petameters_per_square_year = NamedUnit(1.0041761481530735, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='Pmy⁻²') -terameters_per_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='Tms⁻¹') -terameters_per_square_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='Tms⁻²') -terameters_per_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') -terameters_per_square_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') -terameters_per_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') -terameters_per_square_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') -terameters_per_nanosecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') -terameters_per_square_nanosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') -terameters_per_picosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') -terameters_per_square_picosecond = NamedUnit(1e+36, Dimensions(length=1, time=-2), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') -terameters_per_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') -terameters_per_square_femtosecond = NamedUnit(9.999999999999999e+41, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') -terameters_per_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') -terameters_per_square_attosecond = NamedUnit(9.999999999999999e+47, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') -terameters_per_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='Tmmin⁻¹') -terameters_per_square_minute = NamedUnit(277777777.7777778, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='Tmmin⁻²') -terameters_per_hour = NamedUnit(277777777.7777778, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='Tmh⁻¹') -terameters_per_square_hour = NamedUnit(77160.49382716049, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='Tmh⁻²') -terameters_per_day = NamedUnit(11574074.074074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='Tmd⁻¹') -terameters_per_square_day = NamedUnit(133.95919067215362, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='Tmd⁻²') -terameters_per_year = NamedUnit(31688.73850681143, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='Tmy⁻¹') -terameters_per_square_year = NamedUnit(0.0010041761481530736, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='Tmy⁻²') -gigameters_per_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='Gms⁻¹') -gigameters_per_square_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='Gms⁻²') -gigameters_per_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') -gigameters_per_square_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') -gigameters_per_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') -gigameters_per_square_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') -gigameters_per_nanosecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') -gigameters_per_square_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') -gigameters_per_picosecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') -gigameters_per_square_picosecond = NamedUnit(1.0000000000000001e+33, Dimensions(length=1, time=-2), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') -gigameters_per_femtosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') -gigameters_per_square_femtosecond = NamedUnit(1e+39, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') -gigameters_per_attosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') -gigameters_per_square_attosecond = NamedUnit(1e+45, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') -gigameters_per_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='Gmmin⁻¹') -gigameters_per_square_minute = NamedUnit(277777.77777777775, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='Gmmin⁻²') -gigameters_per_hour = NamedUnit(277777.77777777775, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='Gmh⁻¹') -gigameters_per_square_hour = NamedUnit(77.1604938271605, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='Gmh⁻²') -gigameters_per_day = NamedUnit(11574.074074074075, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='Gmd⁻¹') -gigameters_per_square_day = NamedUnit(0.13395919067215364, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='Gmd⁻²') -gigameters_per_year = NamedUnit(31.688738506811433, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='Gmy⁻¹') -gigameters_per_square_year = NamedUnit(1.0041761481530736e-06, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='Gmy⁻²') -megameters_per_second = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='Mms⁻¹') -megameters_per_square_second = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='Mms⁻²') -megameters_per_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') -megameters_per_square_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') -megameters_per_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') -megameters_per_square_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') -megameters_per_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') -megameters_per_square_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') -megameters_per_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') -megameters_per_square_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-2), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') -megameters_per_femtosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') -megameters_per_square_femtosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') -megameters_per_attosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') -megameters_per_square_attosecond = NamedUnit(9.999999999999999e+41, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') -megameters_per_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='Mmmin⁻¹') -megameters_per_square_minute = NamedUnit(277.77777777777777, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='Mmmin⁻²') -megameters_per_hour = NamedUnit(277.77777777777777, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='Mmh⁻¹') -megameters_per_square_hour = NamedUnit(0.07716049382716049, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='Mmh⁻²') -megameters_per_day = NamedUnit(11.574074074074074, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='Mmd⁻¹') -megameters_per_square_day = NamedUnit(0.00013395919067215364, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='Mmd⁻²') -megameters_per_year = NamedUnit(0.031688738506811434, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='Mmy⁻¹') -megameters_per_square_year = NamedUnit(1.0041761481530736e-09, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='Mmy⁻²') -kilometers_per_second = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kms⁻¹') -kilometers_per_square_second = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kms⁻²') -kilometers_per_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') -kilometers_per_square_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') -kilometers_per_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') -kilometers_per_square_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') -kilometers_per_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') -kilometers_per_square_nanosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') -kilometers_per_picosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') -kilometers_per_square_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') -kilometers_per_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') -kilometers_per_square_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') -kilometers_per_attosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') -kilometers_per_square_attosecond = NamedUnit(1e+39, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') -kilometers_per_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmmin⁻¹') -kilometers_per_square_minute = NamedUnit(0.2777777777777778, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmmin⁻²') -kilometers_per_hour = NamedUnit(0.2777777777777778, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmh⁻¹') -kilometers_per_square_hour = NamedUnit(7.716049382716049e-05, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmh⁻²') -kilometers_per_day = NamedUnit(0.011574074074074073, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmd⁻¹') -kilometers_per_square_day = NamedUnit(1.3395919067215364e-07, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmd⁻²') -kilometers_per_year = NamedUnit(3.168873850681143e-05, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmy⁻¹') -kilometers_per_square_year = NamedUnit(1.0041761481530736e-12, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmy⁻²') -millimeters_per_second = NamedUnit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mms⁻¹') -millimeters_per_square_second = NamedUnit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mms⁻²') -millimeters_per_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') -millimeters_per_square_millisecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') -millimeters_per_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') -millimeters_per_square_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') -millimeters_per_nanosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') -millimeters_per_square_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') -millimeters_per_picosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') -millimeters_per_square_picosecond = NamedUnit(1.0000000000000001e+21, Dimensions(length=1, time=-2), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') -millimeters_per_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') -millimeters_per_square_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') -millimeters_per_attosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') -millimeters_per_square_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') -millimeters_per_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmmin⁻¹') -millimeters_per_square_minute = NamedUnit(2.7777777777777776e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmmin⁻²') -millimeters_per_hour = NamedUnit(2.7777777777777776e-07, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmh⁻¹') -millimeters_per_square_hour = NamedUnit(7.716049382716049e-11, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmh⁻²') -millimeters_per_day = NamedUnit(1.1574074074074074e-08, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmd⁻¹') -millimeters_per_square_day = NamedUnit(1.3395919067215364e-13, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmd⁻²') -millimeters_per_year = NamedUnit(3.168873850681143e-11, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmy⁻¹') -millimeters_per_square_year = NamedUnit(1.0041761481530737e-18, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmy⁻²') -micrometers_per_second = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µms⁻¹') -micrometers_per_square_second = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µms⁻²') -micrometers_per_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') -micrometers_per_square_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') -micrometers_per_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') -micrometers_per_square_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') -micrometers_per_nanosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-1), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') -micrometers_per_square_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-2), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') -micrometers_per_picosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') -micrometers_per_square_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') -micrometers_per_femtosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') -micrometers_per_square_femtosecond = NamedUnit(9.999999999999998e+23, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') -micrometers_per_attosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') -micrometers_per_square_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') -micrometers_per_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmmin⁻¹') -micrometers_per_square_minute = NamedUnit(2.7777777777777777e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmmin⁻²') -micrometers_per_hour = NamedUnit(2.7777777777777777e-10, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmh⁻¹') -micrometers_per_square_hour = NamedUnit(7.71604938271605e-14, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmh⁻²') -micrometers_per_day = NamedUnit(1.1574074074074074e-11, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmd⁻¹') -micrometers_per_square_day = NamedUnit(1.3395919067215363e-16, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmd⁻²') -micrometers_per_year = NamedUnit(3.168873850681143e-14, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmy⁻¹') -micrometers_per_square_year = NamedUnit(1.0041761481530736e-21, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmy⁻²') -nanometers_per_second = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nms⁻¹') -nanometers_per_square_second = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nms⁻²') -nanometers_per_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') -nanometers_per_square_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') -nanometers_per_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') -nanometers_per_square_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') -nanometers_per_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') -nanometers_per_square_nanosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') -nanometers_per_picosecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') -nanometers_per_square_picosecond = NamedUnit(1000000000000000.1, Dimensions(length=1, time=-2), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') -nanometers_per_femtosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') -nanometers_per_square_femtosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') -nanometers_per_attosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') -nanometers_per_square_attosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') -nanometers_per_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmmin⁻¹') -nanometers_per_square_minute = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmmin⁻²') -nanometers_per_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmh⁻¹') -nanometers_per_square_hour = NamedUnit(7.71604938271605e-17, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmh⁻²') -nanometers_per_day = NamedUnit(1.1574074074074075e-14, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmd⁻¹') -nanometers_per_square_day = NamedUnit(1.3395919067215365e-19, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmd⁻²') -nanometers_per_year = NamedUnit(3.1688738506811435e-17, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmy⁻¹') -nanometers_per_square_year = NamedUnit(1.0041761481530737e-24, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmy⁻²') -picometers_per_second = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pms⁻¹') -picometers_per_square_second = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pms⁻²') -picometers_per_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') -picometers_per_square_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') -picometers_per_microsecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') -picometers_per_square_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') -picometers_per_nanosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') -picometers_per_square_nanosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-2), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') -picometers_per_picosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') -picometers_per_square_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') -picometers_per_femtosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-1), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') -picometers_per_square_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') -picometers_per_attosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-1), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') -picometers_per_square_attosecond = NamedUnit(9.999999999999998e+23, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') -picometers_per_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmmin⁻¹') -picometers_per_square_minute = NamedUnit(2.7777777777777775e-16, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmmin⁻²') -picometers_per_hour = NamedUnit(2.7777777777777775e-16, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmh⁻¹') -picometers_per_square_hour = NamedUnit(7.716049382716049e-20, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmh⁻²') -picometers_per_day = NamedUnit(1.1574074074074074e-17, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmd⁻¹') -picometers_per_square_day = NamedUnit(1.3395919067215362e-22, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmd⁻²') -picometers_per_year = NamedUnit(3.168873850681143e-20, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmy⁻¹') -picometers_per_square_year = NamedUnit(1.0041761481530736e-27, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmy⁻²') -femtometers_per_second = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fms⁻¹') -femtometers_per_square_second = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fms⁻²') -femtometers_per_millisecond = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') -femtometers_per_square_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') -femtometers_per_microsecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') -femtometers_per_square_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') -femtometers_per_nanosecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') -femtometers_per_square_nanosecond = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') -femtometers_per_picosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') -femtometers_per_square_picosecond = NamedUnit(1000000000.0000001, Dimensions(length=1, time=-2), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') -femtometers_per_femtosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') -femtometers_per_square_femtosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') -femtometers_per_attosecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') -femtometers_per_square_attosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') -femtometers_per_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmmin⁻¹') -femtometers_per_square_minute = NamedUnit(2.777777777777778e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmmin⁻²') -femtometers_per_hour = NamedUnit(2.777777777777778e-19, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmh⁻¹') -femtometers_per_square_hour = NamedUnit(7.71604938271605e-23, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmh⁻²') -femtometers_per_day = NamedUnit(1.1574074074074075e-20, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmd⁻¹') -femtometers_per_square_day = NamedUnit(1.3395919067215364e-25, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmd⁻²') -femtometers_per_year = NamedUnit(3.1688738506811434e-23, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmy⁻¹') -femtometers_per_square_year = NamedUnit(1.0041761481530736e-30, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmy⁻²') -attometers_per_second = NamedUnit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='ams⁻¹') -attometers_per_square_second = NamedUnit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='ams⁻²') -attometers_per_millisecond = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') -attometers_per_square_millisecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') -attometers_per_microsecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-1), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') -attometers_per_square_microsecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-2), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') -attometers_per_nanosecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') -attometers_per_square_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') -attometers_per_picosecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-1), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') -attometers_per_square_picosecond = NamedUnit(1000000.0000000001, Dimensions(length=1, time=-2), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') -attometers_per_femtosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') -attometers_per_square_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') -attometers_per_attosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') -attometers_per_square_attosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') -attometers_per_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='ammin⁻¹') -attometers_per_square_minute = NamedUnit(2.777777777777778e-22, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='ammin⁻²') -attometers_per_hour = NamedUnit(2.777777777777778e-22, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amh⁻¹') -attometers_per_square_hour = NamedUnit(7.71604938271605e-26, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amh⁻²') -attometers_per_day = NamedUnit(1.1574074074074075e-23, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amd⁻¹') -attometers_per_square_day = NamedUnit(1.3395919067215364e-28, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amd⁻²') -attometers_per_year = NamedUnit(3.1688738506811435e-26, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amy⁻¹') -attometers_per_square_year = NamedUnit(1.0041761481530737e-33, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amy⁻²') -decimeters_per_second = NamedUnit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dms⁻¹') -decimeters_per_square_second = NamedUnit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dms⁻²') -decimeters_per_millisecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='decimeters_per_millisecond', ascii_symbol='dm/ms', symbol='dmms⁻¹') -decimeters_per_square_millisecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') -decimeters_per_microsecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-1), name='decimeters_per_microsecond', ascii_symbol='dm/us', symbol='dmµs⁻¹') -decimeters_per_square_microsecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_microsecond', ascii_symbol='dm/us^2', symbol='dmµs⁻²') -decimeters_per_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='decimeters_per_nanosecond', ascii_symbol='dm/ns', symbol='dmns⁻¹') -decimeters_per_square_nanosecond = NamedUnit(1e+17, Dimensions(length=1, time=-2), name='decimeters_per_square_nanosecond', ascii_symbol='dm/ns^2', symbol='dmns⁻²') -decimeters_per_picosecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_picosecond', ascii_symbol='dm/ps', symbol='dmps⁻¹') -decimeters_per_square_picosecond = NamedUnit(1.0000000000000001e+23, Dimensions(length=1, time=-2), name='decimeters_per_square_picosecond', ascii_symbol='dm/ps^2', symbol='dmps⁻²') -decimeters_per_femtosecond = NamedUnit(100000000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_femtosecond', ascii_symbol='dm/fs', symbol='dmfs⁻¹') -decimeters_per_square_femtosecond = NamedUnit(1e+29, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') -decimeters_per_attosecond = NamedUnit(1e+17, Dimensions(length=1, time=-1), name='decimeters_per_attosecond', ascii_symbol='dm/as', symbol='dmas⁻¹') -decimeters_per_square_attosecond = NamedUnit(1e+35, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') -decimeters_per_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmmin⁻¹') -decimeters_per_square_minute = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmmin⁻²') -decimeters_per_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmh⁻¹') -decimeters_per_square_hour = NamedUnit(7.71604938271605e-09, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmh⁻²') -decimeters_per_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmd⁻¹') -decimeters_per_square_day = NamedUnit(1.3395919067215364e-11, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmd⁻²') -decimeters_per_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmy⁻¹') -decimeters_per_square_year = NamedUnit(1.0041761481530736e-16, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmy⁻²') -centimeters_per_second = NamedUnit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cms⁻¹') -centimeters_per_square_second = NamedUnit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cms⁻²') -centimeters_per_millisecond = NamedUnit(10.0, Dimensions(length=1, time=-1), name='centimeters_per_millisecond', ascii_symbol='cm/ms', symbol='cmms⁻¹') -centimeters_per_square_millisecond = NamedUnit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') -centimeters_per_microsecond = NamedUnit(10000.0, Dimensions(length=1, time=-1), name='centimeters_per_microsecond', ascii_symbol='cm/us', symbol='cmµs⁻¹') -centimeters_per_square_microsecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_microsecond', ascii_symbol='cm/us^2', symbol='cmµs⁻²') -centimeters_per_nanosecond = NamedUnit(10000000.0, Dimensions(length=1, time=-1), name='centimeters_per_nanosecond', ascii_symbol='cm/ns', symbol='cmns⁻¹') -centimeters_per_square_nanosecond = NamedUnit(1e+16, Dimensions(length=1, time=-2), name='centimeters_per_square_nanosecond', ascii_symbol='cm/ns^2', symbol='cmns⁻²') -centimeters_per_picosecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_picosecond', ascii_symbol='cm/ps', symbol='cmps⁻¹') -centimeters_per_square_picosecond = NamedUnit(1e+22, Dimensions(length=1, time=-2), name='centimeters_per_square_picosecond', ascii_symbol='cm/ps^2', symbol='cmps⁻²') -centimeters_per_femtosecond = NamedUnit(10000000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_femtosecond', ascii_symbol='cm/fs', symbol='cmfs⁻¹') -centimeters_per_square_femtosecond = NamedUnit(1e+28, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') -centimeters_per_attosecond = NamedUnit(1e+16, Dimensions(length=1, time=-1), name='centimeters_per_attosecond', ascii_symbol='cm/as', symbol='cmas⁻¹') -centimeters_per_square_attosecond = NamedUnit(1e+34, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') -centimeters_per_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmmin⁻¹') -centimeters_per_square_minute = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmmin⁻²') -centimeters_per_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmh⁻¹') -centimeters_per_square_hour = NamedUnit(7.71604938271605e-10, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmh⁻²') -centimeters_per_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmd⁻¹') -centimeters_per_square_day = NamedUnit(1.3395919067215364e-12, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmd⁻²') -centimeters_per_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmy⁻¹') -centimeters_per_square_year = NamedUnit(1.0041761481530737e-17, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmy⁻²') -angstroms_per_second = NamedUnit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='Ås⁻¹') -angstroms_per_square_second = NamedUnit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='Ås⁻²') -angstroms_per_millisecond = NamedUnit(1e-07, Dimensions(length=1, time=-1), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') -angstroms_per_square_millisecond = NamedUnit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') -angstroms_per_microsecond = NamedUnit(0.0001, Dimensions(length=1, time=-1), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') -angstroms_per_square_microsecond = NamedUnit(100.0, Dimensions(length=1, time=-2), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') -angstroms_per_nanosecond = NamedUnit(0.09999999999999999, Dimensions(length=1, time=-1), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') -angstroms_per_square_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') -angstroms_per_picosecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') -angstroms_per_square_picosecond = NamedUnit(100000000000000.02, Dimensions(length=1, time=-2), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') -angstroms_per_femtosecond = NamedUnit(100000.0, Dimensions(length=1, time=-1), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') -angstroms_per_square_femtosecond = NamedUnit(1e+20, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') -angstroms_per_attosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') -angstroms_per_square_attosecond = NamedUnit(9.999999999999999e+25, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') -angstroms_per_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='Åmin⁻¹') -angstroms_per_square_minute = NamedUnit(2.7777777777777778e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='Åmin⁻²') -angstroms_per_hour = NamedUnit(2.7777777777777778e-14, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='Åh⁻¹') -angstroms_per_square_hour = NamedUnit(7.71604938271605e-18, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='Åh⁻²') -angstroms_per_day = NamedUnit(1.1574074074074075e-15, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='Åd⁻¹') -angstroms_per_square_day = NamedUnit(1.3395919067215364e-20, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='Åd⁻²') -angstroms_per_year = NamedUnit(3.168873850681143e-18, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='Åy⁻¹') -angstroms_per_square_year = NamedUnit(1.0041761481530736e-25, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='Åy⁻²') -microns_per_second = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='microns_per_second', ascii_symbol='micron/s', symbol='microns⁻¹') -microns_per_square_second = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='microns_per_square_second', ascii_symbol='micron/s^2', symbol='microns⁻²') -microns_per_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='microns_per_millisecond', ascii_symbol='micron/ms', symbol='micronms⁻¹') -microns_per_square_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='microns_per_square_millisecond', ascii_symbol='micron/ms^2', symbol='micronms⁻²') -microns_per_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='microns_per_microsecond', ascii_symbol='micron/us', symbol='micronµs⁻¹') -microns_per_square_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='microns_per_square_microsecond', ascii_symbol='micron/us^2', symbol='micronµs⁻²') -microns_per_nanosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-1), name='microns_per_nanosecond', ascii_symbol='micron/ns', symbol='micronns⁻¹') -microns_per_square_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-2), name='microns_per_square_nanosecond', ascii_symbol='micron/ns^2', symbol='micronns⁻²') -microns_per_picosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='microns_per_picosecond', ascii_symbol='micron/ps', symbol='micronps⁻¹') -microns_per_square_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='microns_per_square_picosecond', ascii_symbol='micron/ps^2', symbol='micronps⁻²') -microns_per_femtosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='microns_per_femtosecond', ascii_symbol='micron/fs', symbol='micronfs⁻¹') -microns_per_square_femtosecond = NamedUnit(9.999999999999998e+23, Dimensions(length=1, time=-2), name='microns_per_square_femtosecond', ascii_symbol='micron/fs^2', symbol='micronfs⁻²') -microns_per_attosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='microns_per_attosecond', ascii_symbol='micron/as', symbol='micronas⁻¹') -microns_per_square_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='microns_per_square_attosecond', ascii_symbol='micron/as^2', symbol='micronas⁻²') -microns_per_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='microns_per_minute', ascii_symbol='micron/min', symbol='micronmin⁻¹') -microns_per_square_minute = NamedUnit(2.7777777777777777e-10, Dimensions(length=1, time=-2), name='microns_per_square_minute', ascii_symbol='micron/min^2', symbol='micronmin⁻²') -microns_per_hour = NamedUnit(2.7777777777777777e-10, Dimensions(length=1, time=-1), name='microns_per_hour', ascii_symbol='micron/h', symbol='micronh⁻¹') -microns_per_square_hour = NamedUnit(7.71604938271605e-14, Dimensions(length=1, time=-2), name='microns_per_square_hour', ascii_symbol='micron/h^2', symbol='micronh⁻²') -microns_per_day = NamedUnit(1.1574074074074074e-11, Dimensions(length=1, time=-1), name='microns_per_day', ascii_symbol='micron/d', symbol='micrond⁻¹') -microns_per_square_day = NamedUnit(1.3395919067215363e-16, Dimensions(length=1, time=-2), name='microns_per_square_day', ascii_symbol='micron/d^2', symbol='micrond⁻²') -microns_per_year = NamedUnit(3.168873850681143e-14, Dimensions(length=1, time=-1), name='microns_per_year', ascii_symbol='micron/y', symbol='microny⁻¹') -microns_per_square_year = NamedUnit(1.0041761481530736e-21, Dimensions(length=1, time=-2), name='microns_per_square_year', ascii_symbol='micron/y^2', symbol='microny⁻²') -miles_per_second = NamedUnit(1609.344, Dimensions(length=1, time=-1), name='miles_per_second', ascii_symbol='miles/s', symbol='miless⁻¹') -miles_per_square_second = NamedUnit(1609.344, Dimensions(length=1, time=-2), name='miles_per_square_second', ascii_symbol='miles/s^2', symbol='miless⁻²') -miles_per_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-1), name='miles_per_millisecond', ascii_symbol='miles/ms', symbol='milesms⁻¹') -miles_per_square_millisecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-2), name='miles_per_square_millisecond', ascii_symbol='miles/ms^2', symbol='milesms⁻²') -miles_per_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-1), name='miles_per_microsecond', ascii_symbol='miles/us', symbol='milesµs⁻¹') -miles_per_square_microsecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-2), name='miles_per_square_microsecond', ascii_symbol='miles/us^2', symbol='milesµs⁻²') -miles_per_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-1), name='miles_per_nanosecond', ascii_symbol='miles/ns', symbol='milesns⁻¹') -miles_per_square_nanosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-2), name='miles_per_square_nanosecond', ascii_symbol='miles/ns^2', symbol='milesns⁻²') -miles_per_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-1), name='miles_per_picosecond', ascii_symbol='miles/ps', symbol='milesps⁻¹') -miles_per_square_picosecond = NamedUnit(1.609344e+27, Dimensions(length=1, time=-2), name='miles_per_square_picosecond', ascii_symbol='miles/ps^2', symbol='milesps⁻²') -miles_per_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-1), name='miles_per_femtosecond', ascii_symbol='miles/fs', symbol='milesfs⁻¹') -miles_per_square_femtosecond = NamedUnit(1.609344e+33, Dimensions(length=1, time=-2), name='miles_per_square_femtosecond', ascii_symbol='miles/fs^2', symbol='milesfs⁻²') -miles_per_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-1), name='miles_per_attosecond', ascii_symbol='miles/as', symbol='milesas⁻¹') -miles_per_square_attosecond = NamedUnit(1.609344e+39, Dimensions(length=1, time=-2), name='miles_per_square_attosecond', ascii_symbol='miles/as^2', symbol='milesas⁻²') -miles_per_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-1), name='miles_per_minute', ascii_symbol='miles/min', symbol='milesmin⁻¹') -miles_per_square_minute = NamedUnit(0.44704, Dimensions(length=1, time=-2), name='miles_per_square_minute', ascii_symbol='miles/min^2', symbol='milesmin⁻²') -miles_per_hour = NamedUnit(0.44704, Dimensions(length=1, time=-1), name='miles_per_hour', ascii_symbol='miles/h', symbol='milesh⁻¹') -miles_per_square_hour = NamedUnit(0.00012417777777777778, Dimensions(length=1, time=-2), name='miles_per_square_hour', ascii_symbol='miles/h^2', symbol='milesh⁻²') -miles_per_day = NamedUnit(0.018626666666666666, Dimensions(length=1, time=-1), name='miles_per_day', ascii_symbol='miles/d', symbol='milesd⁻¹') -miles_per_square_day = NamedUnit(2.1558641975308643e-07, Dimensions(length=1, time=-2), name='miles_per_square_day', ascii_symbol='miles/d^2', symbol='milesd⁻²') -miles_per_year = NamedUnit(5.099808118350594e-05, Dimensions(length=1, time=-1), name='miles_per_year', ascii_symbol='miles/y', symbol='milesy⁻¹') -miles_per_square_year = NamedUnit(1.61606485897326e-12, Dimensions(length=1, time=-2), name='miles_per_square_year', ascii_symbol='miles/y^2', symbol='milesy⁻²') -yards_per_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-1), name='yards_per_second', ascii_symbol='yrd/s', symbol='yrds⁻¹') -yards_per_square_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-2), name='yards_per_square_second', ascii_symbol='yrd/s^2', symbol='yrds⁻²') -yards_per_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-1), name='yards_per_millisecond', ascii_symbol='yrd/ms', symbol='yrdms⁻¹') -yards_per_square_millisecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-2), name='yards_per_square_millisecond', ascii_symbol='yrd/ms^2', symbol='yrdms⁻²') -yards_per_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-1), name='yards_per_microsecond', ascii_symbol='yrd/us', symbol='yrdµs⁻¹') -yards_per_square_microsecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-2), name='yards_per_square_microsecond', ascii_symbol='yrd/us^2', symbol='yrdµs⁻²') -yards_per_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-1), name='yards_per_nanosecond', ascii_symbol='yrd/ns', symbol='yrdns⁻¹') -yards_per_square_nanosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-2), name='yards_per_square_nanosecond', ascii_symbol='yrd/ns^2', symbol='yrdns⁻²') -yards_per_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-1), name='yards_per_picosecond', ascii_symbol='yrd/ps', symbol='yrdps⁻¹') -yards_per_square_picosecond = NamedUnit(9.144000000000002e+23, Dimensions(length=1, time=-2), name='yards_per_square_picosecond', ascii_symbol='yrd/ps^2', symbol='yrdps⁻²') -yards_per_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-1), name='yards_per_femtosecond', ascii_symbol='yrd/fs', symbol='yrdfs⁻¹') -yards_per_square_femtosecond = NamedUnit(9.144e+29, Dimensions(length=1, time=-2), name='yards_per_square_femtosecond', ascii_symbol='yrd/fs^2', symbol='yrdfs⁻²') -yards_per_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-1), name='yards_per_attosecond', ascii_symbol='yrd/as', symbol='yrdas⁻¹') -yards_per_square_attosecond = NamedUnit(9.144e+35, Dimensions(length=1, time=-2), name='yards_per_square_attosecond', ascii_symbol='yrd/as^2', symbol='yrdas⁻²') -yards_per_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-1), name='yards_per_minute', ascii_symbol='yrd/min', symbol='yrdmin⁻¹') -yards_per_square_minute = NamedUnit(0.00025400000000000005, Dimensions(length=1, time=-2), name='yards_per_square_minute', ascii_symbol='yrd/min^2', symbol='yrdmin⁻²') -yards_per_hour = NamedUnit(0.00025400000000000005, Dimensions(length=1, time=-1), name='yards_per_hour', ascii_symbol='yrd/h', symbol='yrdh⁻¹') -yards_per_square_hour = NamedUnit(7.055555555555557e-08, Dimensions(length=1, time=-2), name='yards_per_square_hour', ascii_symbol='yrd/h^2', symbol='yrdh⁻²') -yards_per_day = NamedUnit(1.0583333333333334e-05, Dimensions(length=1, time=-1), name='yards_per_day', ascii_symbol='yrd/d', symbol='yrdd⁻¹') -yards_per_square_day = NamedUnit(1.224922839506173e-10, Dimensions(length=1, time=-2), name='yards_per_square_day', ascii_symbol='yrd/d^2', symbol='yrdd⁻²') -yards_per_year = NamedUnit(2.8976182490628376e-08, Dimensions(length=1, time=-1), name='yards_per_year', ascii_symbol='yrd/y', symbol='yrdy⁻¹') -yards_per_square_year = NamedUnit(9.182186698711705e-16, Dimensions(length=1, time=-2), name='yards_per_square_year', ascii_symbol='yrd/y^2', symbol='yrdy⁻²') -feet_per_second = NamedUnit(0.3048, Dimensions(length=1, time=-1), name='feet_per_second', ascii_symbol='ft/s', symbol='fts⁻¹') -feet_per_square_second = NamedUnit(0.3048, Dimensions(length=1, time=-2), name='feet_per_square_second', ascii_symbol='ft/s^2', symbol='fts⁻²') -feet_per_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-1), name='feet_per_millisecond', ascii_symbol='ft/ms', symbol='ftms⁻¹') -feet_per_square_millisecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-2), name='feet_per_square_millisecond', ascii_symbol='ft/ms^2', symbol='ftms⁻²') -feet_per_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-1), name='feet_per_microsecond', ascii_symbol='ft/us', symbol='ftµs⁻¹') -feet_per_square_microsecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-2), name='feet_per_square_microsecond', ascii_symbol='ft/us^2', symbol='ftµs⁻²') -feet_per_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-1), name='feet_per_nanosecond', ascii_symbol='ft/ns', symbol='ftns⁻¹') -feet_per_square_nanosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-2), name='feet_per_square_nanosecond', ascii_symbol='ft/ns^2', symbol='ftns⁻²') -feet_per_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-1), name='feet_per_picosecond', ascii_symbol='ft/ps', symbol='ftps⁻¹') -feet_per_square_picosecond = NamedUnit(3.048e+23, Dimensions(length=1, time=-2), name='feet_per_square_picosecond', ascii_symbol='ft/ps^2', symbol='ftps⁻²') -feet_per_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-1), name='feet_per_femtosecond', ascii_symbol='ft/fs', symbol='ftfs⁻¹') -feet_per_square_femtosecond = NamedUnit(3.048e+29, Dimensions(length=1, time=-2), name='feet_per_square_femtosecond', ascii_symbol='ft/fs^2', symbol='ftfs⁻²') -feet_per_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-1), name='feet_per_attosecond', ascii_symbol='ft/as', symbol='ftas⁻¹') -feet_per_square_attosecond = NamedUnit(3.0479999999999997e+35, Dimensions(length=1, time=-2), name='feet_per_square_attosecond', ascii_symbol='ft/as^2', symbol='ftas⁻²') -feet_per_minute = NamedUnit(0.00508, Dimensions(length=1, time=-1), name='feet_per_minute', ascii_symbol='ft/min', symbol='ftmin⁻¹') -feet_per_square_minute = NamedUnit(8.466666666666667e-05, Dimensions(length=1, time=-2), name='feet_per_square_minute', ascii_symbol='ft/min^2', symbol='ftmin⁻²') -feet_per_hour = NamedUnit(8.466666666666667e-05, Dimensions(length=1, time=-1), name='feet_per_hour', ascii_symbol='ft/h', symbol='fth⁻¹') -feet_per_square_hour = NamedUnit(2.351851851851852e-08, Dimensions(length=1, time=-2), name='feet_per_square_hour', ascii_symbol='ft/h^2', symbol='fth⁻²') -feet_per_day = NamedUnit(3.527777777777778e-06, Dimensions(length=1, time=-1), name='feet_per_day', ascii_symbol='ft/d', symbol='ftd⁻¹') -feet_per_square_day = NamedUnit(4.083076131687243e-11, Dimensions(length=1, time=-2), name='feet_per_square_day', ascii_symbol='ft/d^2', symbol='ftd⁻²') -feet_per_year = NamedUnit(9.658727496876124e-09, Dimensions(length=1, time=-1), name='feet_per_year', ascii_symbol='ft/y', symbol='fty⁻¹') -feet_per_square_year = NamedUnit(3.060728899570568e-16, Dimensions(length=1, time=-2), name='feet_per_square_year', ascii_symbol='ft/y^2', symbol='fty⁻²') -inches_per_second = NamedUnit(0.0254, Dimensions(length=1, time=-1), name='inches_per_second', ascii_symbol='in/s', symbol='ins⁻¹') -inches_per_square_second = NamedUnit(0.0254, Dimensions(length=1, time=-2), name='inches_per_square_second', ascii_symbol='in/s^2', symbol='ins⁻²') -inches_per_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-1), name='inches_per_millisecond', ascii_symbol='in/ms', symbol='inms⁻¹') -inches_per_square_millisecond = NamedUnit(25400.0, Dimensions(length=1, time=-2), name='inches_per_square_millisecond', ascii_symbol='in/ms^2', symbol='inms⁻²') -inches_per_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-1), name='inches_per_microsecond', ascii_symbol='in/us', symbol='inµs⁻¹') -inches_per_square_microsecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-2), name='inches_per_square_microsecond', ascii_symbol='in/us^2', symbol='inµs⁻²') -inches_per_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-1), name='inches_per_nanosecond', ascii_symbol='in/ns', symbol='inns⁻¹') -inches_per_square_nanosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-2), name='inches_per_square_nanosecond', ascii_symbol='in/ns^2', symbol='inns⁻²') -inches_per_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-1), name='inches_per_picosecond', ascii_symbol='in/ps', symbol='inps⁻¹') -inches_per_square_picosecond = NamedUnit(2.54e+22, Dimensions(length=1, time=-2), name='inches_per_square_picosecond', ascii_symbol='in/ps^2', symbol='inps⁻²') -inches_per_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-1), name='inches_per_femtosecond', ascii_symbol='in/fs', symbol='infs⁻¹') -inches_per_square_femtosecond = NamedUnit(2.54e+28, Dimensions(length=1, time=-2), name='inches_per_square_femtosecond', ascii_symbol='in/fs^2', symbol='infs⁻²') -inches_per_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-1), name='inches_per_attosecond', ascii_symbol='in/as', symbol='inas⁻¹') -inches_per_square_attosecond = NamedUnit(2.5399999999999998e+34, Dimensions(length=1, time=-2), name='inches_per_square_attosecond', ascii_symbol='in/as^2', symbol='inas⁻²') -inches_per_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-1), name='inches_per_minute', ascii_symbol='in/min', symbol='inmin⁻¹') -inches_per_square_minute = NamedUnit(7.055555555555555e-06, Dimensions(length=1, time=-2), name='inches_per_square_minute', ascii_symbol='in/min^2', symbol='inmin⁻²') -inches_per_hour = NamedUnit(7.055555555555555e-06, Dimensions(length=1, time=-1), name='inches_per_hour', ascii_symbol='in/h', symbol='inh⁻¹') -inches_per_square_hour = NamedUnit(1.9598765432098764e-09, Dimensions(length=1, time=-2), name='inches_per_square_hour', ascii_symbol='in/h^2', symbol='inh⁻²') -inches_per_day = NamedUnit(2.939814814814815e-07, Dimensions(length=1, time=-1), name='inches_per_day', ascii_symbol='in/d', symbol='ind⁻¹') -inches_per_square_day = NamedUnit(3.402563443072702e-12, Dimensions(length=1, time=-2), name='inches_per_square_day', ascii_symbol='in/d^2', symbol='ind⁻²') -inches_per_year = NamedUnit(8.048939580730103e-10, Dimensions(length=1, time=-1), name='inches_per_year', ascii_symbol='in/y', symbol='iny⁻¹') -inches_per_square_year = NamedUnit(2.550607416308807e-17, Dimensions(length=1, time=-2), name='inches_per_square_year', ascii_symbol='in/y^2', symbol='iny⁻²') -grams_per_cubic_meter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='gm⁻³') -exagrams_per_cubic_meter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='Egm⁻³') -petagrams_per_cubic_meter = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='Pgm⁻³') -teragrams_per_cubic_meter = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='Tgm⁻³') -gigagrams_per_cubic_meter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='Ggm⁻³') -megagrams_per_cubic_meter = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='Mgm⁻³') -kilograms_per_cubic_meter = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgm⁻³') -milligrams_per_cubic_meter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgm⁻³') -micrograms_per_cubic_meter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgm⁻³') -nanograms_per_cubic_meter = NamedUnit(1.0000000000000002e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngm⁻³') -picograms_per_cubic_meter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgm⁻³') -femtograms_per_cubic_meter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgm⁻³') -attograms_per_cubic_meter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agm⁻³') -atomic_mass_units_per_cubic_meter = NamedUnit(1.660538921e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='aum⁻³') -pounds_per_cubic_meter = NamedUnit(0.45359237, Dimensions(length=-3, mass=1), name='pounds_per_cubic_meter', ascii_symbol='lb m^-3', symbol='lbm⁻³') -ounces_per_cubic_meter = NamedUnit(0.028349523125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_meter', ascii_symbol='oz m^-3', symbol='ozm⁻³') -grams_per_cubic_exameter = NamedUnit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='gEm⁻³') -exagrams_per_cubic_exameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') -petagrams_per_cubic_exameter = NamedUnit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') -teragrams_per_cubic_exameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') -gigagrams_per_cubic_exameter = NamedUnit(1e-48, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_exameter', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') -megagrams_per_cubic_exameter = NamedUnit(9.999999999999999e-52, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_exameter', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') -kilograms_per_cubic_exameter = NamedUnit(9.999999999999999e-55, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_exameter', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') -milligrams_per_cubic_exameter = NamedUnit(9.999999999999998e-61, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_exameter', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') -micrograms_per_cubic_exameter = NamedUnit(9.999999999999999e-64, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_exameter', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') -nanograms_per_cubic_exameter = NamedUnit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_exameter', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') -picograms_per_cubic_exameter = NamedUnit(1e-69, Dimensions(length=-3, mass=1), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') -femtograms_per_cubic_exameter = NamedUnit(1e-72, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') -attograms_per_cubic_exameter = NamedUnit(1e-75, Dimensions(length=-3, mass=1), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -atomic_mass_units_per_cubic_exameter = NamedUnit(1.6605389209999996e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='auEm⁻³') -pounds_per_cubic_exameter = NamedUnit(4.5359237e-55, Dimensions(length=-3, mass=1), name='pounds_per_cubic_exameter', ascii_symbol='lb Em^-3', symbol='lbEm⁻³') -ounces_per_cubic_exameter = NamedUnit(2.8349523125e-56, Dimensions(length=-3, mass=1), name='ounces_per_cubic_exameter', ascii_symbol='oz Em^-3', symbol='ozEm⁻³') -grams_per_cubic_petameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='gPm⁻³') -exagrams_per_cubic_petameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') -petagrams_per_cubic_petameter = NamedUnit(1e-33, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') -teragrams_per_cubic_petameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') -gigagrams_per_cubic_petameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_petameter', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') -megagrams_per_cubic_petameter = NamedUnit(1e-42, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_petameter', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') -kilograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-45, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_petameter', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') -milligrams_per_cubic_petameter = NamedUnit(1e-51, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_petameter', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') -micrograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-54, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_petameter', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') -nanograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-57, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_petameter', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') -picograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-60, Dimensions(length=-3, mass=1), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') -femtograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-63, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') -attograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -atomic_mass_units_per_cubic_petameter = NamedUnit(1.660538921e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='auPm⁻³') -pounds_per_cubic_petameter = NamedUnit(4.5359237000000005e-46, Dimensions(length=-3, mass=1), name='pounds_per_cubic_petameter', ascii_symbol='lb Pm^-3', symbol='lbPm⁻³') -ounces_per_cubic_petameter = NamedUnit(2.8349523125000003e-47, Dimensions(length=-3, mass=1), name='ounces_per_cubic_petameter', ascii_symbol='oz Pm^-3', symbol='ozPm⁻³') -grams_per_cubic_terameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='gTm⁻³') -exagrams_per_cubic_terameter = NamedUnit(1e-21, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') -petagrams_per_cubic_terameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') -teragrams_per_cubic_terameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') -gigagrams_per_cubic_terameter = NamedUnit(9.999999999999999e-31, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_terameter', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') -megagrams_per_cubic_terameter = NamedUnit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_terameter', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') -kilograms_per_cubic_terameter = NamedUnit(1e-36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_terameter', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') -milligrams_per_cubic_terameter = NamedUnit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_terameter', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') -micrograms_per_cubic_terameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_terameter', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') -nanograms_per_cubic_terameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_terameter', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') -picograms_per_cubic_terameter = NamedUnit(1e-51, Dimensions(length=-3, mass=1), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') -femtograms_per_cubic_terameter = NamedUnit(1e-54, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') -attograms_per_cubic_terameter = NamedUnit(1.0000000000000001e-57, Dimensions(length=-3, mass=1), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -atomic_mass_units_per_cubic_terameter = NamedUnit(1.6605389209999997e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='auTm⁻³') -pounds_per_cubic_terameter = NamedUnit(4.5359237e-37, Dimensions(length=-3, mass=1), name='pounds_per_cubic_terameter', ascii_symbol='lb Tm^-3', symbol='lbTm⁻³') -ounces_per_cubic_terameter = NamedUnit(2.8349523125e-38, Dimensions(length=-3, mass=1), name='ounces_per_cubic_terameter', ascii_symbol='oz Tm^-3', symbol='ozTm⁻³') -grams_per_cubic_gigameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='gGm⁻³') -exagrams_per_cubic_gigameter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') -petagrams_per_cubic_gigameter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') -teragrams_per_cubic_gigameter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') -gigagrams_per_cubic_gigameter = NamedUnit(1e-21, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_gigameter', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') -megagrams_per_cubic_gigameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_gigameter', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') -kilograms_per_cubic_gigameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_gigameter', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') -milligrams_per_cubic_gigameter = NamedUnit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_gigameter', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') -micrograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_gigameter', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') -nanograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_gigameter', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') -picograms_per_cubic_gigameter = NamedUnit(1e-42, Dimensions(length=-3, mass=1), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') -femtograms_per_cubic_gigameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') -attograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -atomic_mass_units_per_cubic_gigameter = NamedUnit(1.660538921e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='auGm⁻³') -pounds_per_cubic_gigameter = NamedUnit(4.5359237e-28, Dimensions(length=-3, mass=1), name='pounds_per_cubic_gigameter', ascii_symbol='lb Gm^-3', symbol='lbGm⁻³') -ounces_per_cubic_gigameter = NamedUnit(2.8349523125e-29, Dimensions(length=-3, mass=1), name='ounces_per_cubic_gigameter', ascii_symbol='oz Gm^-3', symbol='ozGm⁻³') -grams_per_cubic_megameter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='gMm⁻³') -exagrams_per_cubic_megameter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') -petagrams_per_cubic_megameter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') -teragrams_per_cubic_megameter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') -gigagrams_per_cubic_megameter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_megameter', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') -megagrams_per_cubic_megameter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_megameter', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') -kilograms_per_cubic_megameter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_megameter', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') -milligrams_per_cubic_megameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_megameter', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') -micrograms_per_cubic_megameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_megameter', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') -nanograms_per_cubic_megameter = NamedUnit(1.0000000000000003e-30, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_megameter', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') -picograms_per_cubic_megameter = NamedUnit(1e-33, Dimensions(length=-3, mass=1), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') -femtograms_per_cubic_megameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') -attograms_per_cubic_megameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -atomic_mass_units_per_cubic_megameter = NamedUnit(1.6605389209999997e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='auMm⁻³') -pounds_per_cubic_megameter = NamedUnit(4.535923700000001e-19, Dimensions(length=-3, mass=1), name='pounds_per_cubic_megameter', ascii_symbol='lb Mm^-3', symbol='lbMm⁻³') -ounces_per_cubic_megameter = NamedUnit(2.8349523125000004e-20, Dimensions(length=-3, mass=1), name='ounces_per_cubic_megameter', ascii_symbol='oz Mm^-3', symbol='ozMm⁻³') -grams_per_cubic_kilometer = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='gkm⁻³') -exagrams_per_cubic_kilometer = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') -petagrams_per_cubic_kilometer = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') -teragrams_per_cubic_kilometer = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') -gigagrams_per_cubic_kilometer = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_kilometer', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') -megagrams_per_cubic_kilometer = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_kilometer', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') -kilograms_per_cubic_kilometer = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_kilometer', ascii_symbol='kg km^-3', symbol='kgkm⁻³') -milligrams_per_cubic_kilometer = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_kilometer', ascii_symbol='mg km^-3', symbol='mgkm⁻³') -micrograms_per_cubic_kilometer = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_kilometer', ascii_symbol='ug km^-3', symbol='µgkm⁻³') -nanograms_per_cubic_kilometer = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_kilometer', ascii_symbol='ng km^-3', symbol='ngkm⁻³') -picograms_per_cubic_kilometer = NamedUnit(1.0000000000000001e-24, Dimensions(length=-3, mass=1), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') -femtograms_per_cubic_kilometer = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') -attograms_per_cubic_kilometer = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') -atomic_mass_units_per_cubic_kilometer = NamedUnit(1.6605389209999997e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='aukm⁻³') -pounds_per_cubic_kilometer = NamedUnit(4.5359237000000004e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_kilometer', ascii_symbol='lb km^-3', symbol='lbkm⁻³') -ounces_per_cubic_kilometer = NamedUnit(2.8349523125000003e-11, Dimensions(length=-3, mass=1), name='ounces_per_cubic_kilometer', ascii_symbol='oz km^-3', symbol='ozkm⁻³') -grams_per_cubic_millimeter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='gmm⁻³') -exagrams_per_cubic_millimeter = NamedUnit(1e+24, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') -petagrams_per_cubic_millimeter = NamedUnit(1e+21, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') -teragrams_per_cubic_millimeter = NamedUnit(1e+18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') -gigagrams_per_cubic_millimeter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_millimeter', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') -megagrams_per_cubic_millimeter = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_millimeter', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') -kilograms_per_cubic_millimeter = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_millimeter', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') -milligrams_per_cubic_millimeter = NamedUnit(999.9999999999999, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_millimeter', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') -micrograms_per_cubic_millimeter = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_millimeter', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') -nanograms_per_cubic_millimeter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_millimeter', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') -picograms_per_cubic_millimeter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') -femtograms_per_cubic_millimeter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') -attograms_per_cubic_millimeter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -atomic_mass_units_per_cubic_millimeter = NamedUnit(1.6605389209999997e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='aumm⁻³') -pounds_per_cubic_millimeter = NamedUnit(453592370.0, Dimensions(length=-3, mass=1), name='pounds_per_cubic_millimeter', ascii_symbol='lb mm^-3', symbol='lbmm⁻³') -ounces_per_cubic_millimeter = NamedUnit(28349523.125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_millimeter', ascii_symbol='oz mm^-3', symbol='ozmm⁻³') -grams_per_cubic_micrometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='gµm⁻³') -exagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') -petagrams_per_cubic_micrometer = NamedUnit(1.0000000000000002e+30, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') -teragrams_per_cubic_micrometer = NamedUnit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') -gigagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+24, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_micrometer', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') -megagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_micrometer', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') -kilograms_per_cubic_micrometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_micrometer', ascii_symbol='kg um^-3', symbol='kgµm⁻³') -milligrams_per_cubic_micrometer = NamedUnit(1000000000000.0001, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_micrometer', ascii_symbol='mg um^-3', symbol='mgµm⁻³') -micrograms_per_cubic_micrometer = NamedUnit(1000000000.0000002, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_micrometer', ascii_symbol='ug um^-3', symbol='µgµm⁻³') -nanograms_per_cubic_micrometer = NamedUnit(1000000.0000000003, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_micrometer', ascii_symbol='ng um^-3', symbol='ngµm⁻³') -picograms_per_cubic_micrometer = NamedUnit(1000.0000000000002, Dimensions(length=-3, mass=1), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') -femtograms_per_cubic_micrometer = NamedUnit(1.0000000000000002, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') -attograms_per_cubic_micrometer = NamedUnit(0.0010000000000000002, Dimensions(length=-3, mass=1), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') -atomic_mass_units_per_cubic_micrometer = NamedUnit(1.660538921e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='auµm⁻³') -pounds_per_cubic_micrometer = NamedUnit(4.5359237000000006e+17, Dimensions(length=-3, mass=1), name='pounds_per_cubic_micrometer', ascii_symbol='lb um^-3', symbol='lbµm⁻³') -ounces_per_cubic_micrometer = NamedUnit(2.8349523125000004e+16, Dimensions(length=-3, mass=1), name='ounces_per_cubic_micrometer', ascii_symbol='oz um^-3', symbol='ozµm⁻³') -grams_per_cubic_nanometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='gnm⁻³') -exagrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') -petagrams_per_cubic_nanometer = NamedUnit(9.999999999999998e+38, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') -teragrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') -gigagrams_per_cubic_nanometer = NamedUnit(9.999999999999998e+32, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_nanometer', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') -megagrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_nanometer', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') -kilograms_per_cubic_nanometer = NamedUnit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_nanometer', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') -milligrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+20, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_nanometer', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') -micrograms_per_cubic_nanometer = NamedUnit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_nanometer', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') -nanograms_per_cubic_nanometer = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_nanometer', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') -picograms_per_cubic_nanometer = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') -femtograms_per_cubic_nanometer = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') -attograms_per_cubic_nanometer = NamedUnit(999999.9999999999, Dimensions(length=-3, mass=1), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -atomic_mass_units_per_cubic_nanometer = NamedUnit(1.6605389209999994, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='aunm⁻³') -pounds_per_cubic_nanometer = NamedUnit(4.535923699999999e+26, Dimensions(length=-3, mass=1), name='pounds_per_cubic_nanometer', ascii_symbol='lb nm^-3', symbol='lbnm⁻³') -ounces_per_cubic_nanometer = NamedUnit(2.8349523124999993e+25, Dimensions(length=-3, mass=1), name='ounces_per_cubic_nanometer', ascii_symbol='oz nm^-3', symbol='oznm⁻³') -grams_per_cubic_picometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='gpm⁻³') -exagrams_per_cubic_picometer = NamedUnit(1e+51, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') -petagrams_per_cubic_picometer = NamedUnit(1e+48, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') -teragrams_per_cubic_picometer = NamedUnit(1.0000000000000001e+45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') -gigagrams_per_cubic_picometer = NamedUnit(1e+42, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_picometer', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') -megagrams_per_cubic_picometer = NamedUnit(1.0000000000000001e+39, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_picometer', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') -kilograms_per_cubic_picometer = NamedUnit(1e+36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_picometer', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') -milligrams_per_cubic_picometer = NamedUnit(1e+30, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_picometer', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') -micrograms_per_cubic_picometer = NamedUnit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_picometer', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') -nanograms_per_cubic_picometer = NamedUnit(1.0000000000000003e+24, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_picometer', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') -picograms_per_cubic_picometer = NamedUnit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') -femtograms_per_cubic_picometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') -attograms_per_cubic_picometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -atomic_mass_units_per_cubic_picometer = NamedUnit(1660538921.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='aupm⁻³') -pounds_per_cubic_picometer = NamedUnit(4.5359237000000005e+35, Dimensions(length=-3, mass=1), name='pounds_per_cubic_picometer', ascii_symbol='lb pm^-3', symbol='lbpm⁻³') -ounces_per_cubic_picometer = NamedUnit(2.8349523125000003e+34, Dimensions(length=-3, mass=1), name='ounces_per_cubic_picometer', ascii_symbol='oz pm^-3', symbol='ozpm⁻³') -grams_per_cubic_femtometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='gfm⁻³') -exagrams_per_cubic_femtometer = NamedUnit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') -petagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+56, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') -teragrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+53, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') -gigagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+50, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_femtometer', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') -megagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_femtometer', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') -kilograms_per_cubic_femtometer = NamedUnit(9.999999999999998e+44, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_femtometer', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') -milligrams_per_cubic_femtometer = NamedUnit(9.999999999999996e+38, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_femtometer', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') -micrograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_femtometer', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') -nanograms_per_cubic_femtometer = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_femtometer', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') -picograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') -femtograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') -attograms_per_cubic_femtometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -atomic_mass_units_per_cubic_femtometer = NamedUnit(1.6605389209999992e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='aufm⁻³') -pounds_per_cubic_femtometer = NamedUnit(4.5359236999999985e+44, Dimensions(length=-3, mass=1), name='pounds_per_cubic_femtometer', ascii_symbol='lb fm^-3', symbol='lbfm⁻³') -ounces_per_cubic_femtometer = NamedUnit(2.834952312499999e+43, Dimensions(length=-3, mass=1), name='ounces_per_cubic_femtometer', ascii_symbol='oz fm^-3', symbol='ozfm⁻³') -grams_per_cubic_attometer = NamedUnit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='gam⁻³') -exagrams_per_cubic_attometer = NamedUnit(9.999999999999999e+68, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') -petagrams_per_cubic_attometer = NamedUnit(9.999999999999998e+65, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') -teragrams_per_cubic_attometer = NamedUnit(9.999999999999999e+62, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') -gigagrams_per_cubic_attometer = NamedUnit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_attometer', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') -megagrams_per_cubic_attometer = NamedUnit(9.999999999999999e+56, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_attometer', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') -kilograms_per_cubic_attometer = NamedUnit(9.999999999999999e+53, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_attometer', ascii_symbol='kg am^-3', symbol='kgam⁻³') -milligrams_per_cubic_attometer = NamedUnit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_attometer', ascii_symbol='mg am^-3', symbol='mgam⁻³') -micrograms_per_cubic_attometer = NamedUnit(1e+45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_attometer', ascii_symbol='ug am^-3', symbol='µgam⁻³') -nanograms_per_cubic_attometer = NamedUnit(1e+42, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_attometer', ascii_symbol='ng am^-3', symbol='ngam⁻³') -picograms_per_cubic_attometer = NamedUnit(1e+39, Dimensions(length=-3, mass=1), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') -femtograms_per_cubic_attometer = NamedUnit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') -attograms_per_cubic_attometer = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') -atomic_mass_units_per_cubic_attometer = NamedUnit(1.6605389209999997e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='auam⁻³') -pounds_per_cubic_attometer = NamedUnit(4.5359237e+53, Dimensions(length=-3, mass=1), name='pounds_per_cubic_attometer', ascii_symbol='lb am^-3', symbol='lbam⁻³') -ounces_per_cubic_attometer = NamedUnit(2.8349523125e+52, Dimensions(length=-3, mass=1), name='ounces_per_cubic_attometer', ascii_symbol='oz am^-3', symbol='ozam⁻³') -grams_per_cubic_decimeter = NamedUnit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='gdm⁻³') -exagrams_per_cubic_decimeter = NamedUnit(9.999999999999997e+17, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_decimeter', ascii_symbol='Eg dm^-3', symbol='Egdm⁻³') -petagrams_per_cubic_decimeter = NamedUnit(999999999999999.8, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_decimeter', ascii_symbol='Pg dm^-3', symbol='Pgdm⁻³') -teragrams_per_cubic_decimeter = NamedUnit(999999999999.9998, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_decimeter', ascii_symbol='Tg dm^-3', symbol='Tgdm⁻³') -gigagrams_per_cubic_decimeter = NamedUnit(999999999.9999998, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_decimeter', ascii_symbol='Gg dm^-3', symbol='Ggdm⁻³') -megagrams_per_cubic_decimeter = NamedUnit(999999.9999999998, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_decimeter', ascii_symbol='Mg dm^-3', symbol='Mgdm⁻³') -kilograms_per_cubic_decimeter = NamedUnit(999.9999999999998, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_decimeter', ascii_symbol='kg dm^-3', symbol='kgdm⁻³') -milligrams_per_cubic_decimeter = NamedUnit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_decimeter', ascii_symbol='mg dm^-3', symbol='mgdm⁻³') -micrograms_per_cubic_decimeter = NamedUnit(9.999999999999997e-07, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_decimeter', ascii_symbol='ug dm^-3', symbol='µgdm⁻³') -nanograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_decimeter', ascii_symbol='ng dm^-3', symbol='ngdm⁻³') -picograms_per_cubic_decimeter = NamedUnit(9.999999999999998e-13, Dimensions(length=-3, mass=1), name='picograms_per_cubic_decimeter', ascii_symbol='pg dm^-3', symbol='pgdm⁻³') -femtograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_decimeter', ascii_symbol='fg dm^-3', symbol='fgdm⁻³') -attograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-19, Dimensions(length=-3, mass=1), name='attograms_per_cubic_decimeter', ascii_symbol='ag dm^-3', symbol='agdm⁻³') -atomic_mass_units_per_cubic_decimeter = NamedUnit(1.6605389209999993e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='audm⁻³') -pounds_per_cubic_decimeter = NamedUnit(453.5923699999999, Dimensions(length=-3, mass=1), name='pounds_per_cubic_decimeter', ascii_symbol='lb dm^-3', symbol='lbdm⁻³') -ounces_per_cubic_decimeter = NamedUnit(28.349523124999994, Dimensions(length=-3, mass=1), name='ounces_per_cubic_decimeter', ascii_symbol='oz dm^-3', symbol='ozdm⁻³') -grams_per_cubic_centimeter = NamedUnit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='gcm⁻³') -exagrams_per_cubic_centimeter = NamedUnit(9.999999999999999e+20, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_centimeter', ascii_symbol='Eg cm^-3', symbol='Egcm⁻³') -petagrams_per_cubic_centimeter = NamedUnit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_centimeter', ascii_symbol='Pg cm^-3', symbol='Pgcm⁻³') -teragrams_per_cubic_centimeter = NamedUnit(999999999999999.9, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_centimeter', ascii_symbol='Tg cm^-3', symbol='Tgcm⁻³') -gigagrams_per_cubic_centimeter = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_centimeter', ascii_symbol='Gg cm^-3', symbol='Ggcm⁻³') -megagrams_per_cubic_centimeter = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_centimeter', ascii_symbol='Mg cm^-3', symbol='Mgcm⁻³') -kilograms_per_cubic_centimeter = NamedUnit(999999.9999999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_centimeter', ascii_symbol='kg cm^-3', symbol='kgcm⁻³') -milligrams_per_cubic_centimeter = NamedUnit(0.9999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_centimeter', ascii_symbol='mg cm^-3', symbol='mgcm⁻³') -micrograms_per_cubic_centimeter = NamedUnit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_centimeter', ascii_symbol='ug cm^-3', symbol='µgcm⁻³') -nanograms_per_cubic_centimeter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_centimeter', ascii_symbol='ng cm^-3', symbol='ngcm⁻³') -picograms_per_cubic_centimeter = NamedUnit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='picograms_per_cubic_centimeter', ascii_symbol='pg cm^-3', symbol='pgcm⁻³') -femtograms_per_cubic_centimeter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_centimeter', ascii_symbol='fg cm^-3', symbol='fgcm⁻³') -attograms_per_cubic_centimeter = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='attograms_per_cubic_centimeter', ascii_symbol='ag cm^-3', symbol='agcm⁻³') -atomic_mass_units_per_cubic_centimeter = NamedUnit(1.6605389209999996e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='aucm⁻³') -pounds_per_cubic_centimeter = NamedUnit(453592.36999999994, Dimensions(length=-3, mass=1), name='pounds_per_cubic_centimeter', ascii_symbol='lb cm^-3', symbol='lbcm⁻³') -ounces_per_cubic_centimeter = NamedUnit(28349.523124999996, Dimensions(length=-3, mass=1), name='ounces_per_cubic_centimeter', ascii_symbol='oz cm^-3', symbol='ozcm⁻³') -grams_per_cubic_angstrom = NamedUnit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='gÅ⁻³') -exagrams_per_cubic_angstrom = NamedUnit(1e+45, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') -petagrams_per_cubic_angstrom = NamedUnit(9.999999999999999e+41, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') -teragrams_per_cubic_angstrom = NamedUnit(1e+39, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') -gigagrams_per_cubic_angstrom = NamedUnit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_angstrom', ascii_symbol='Gg Ang^-3', symbol='GgÅ⁻³') -megagrams_per_cubic_angstrom = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_angstrom', ascii_symbol='Mg Ang^-3', symbol='MgÅ⁻³') -kilograms_per_cubic_angstrom = NamedUnit(9.999999999999999e+29, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_angstrom', ascii_symbol='kg Ang^-3', symbol='kgÅ⁻³') -milligrams_per_cubic_angstrom = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_angstrom', ascii_symbol='mg Ang^-3', symbol='mgÅ⁻³') -micrograms_per_cubic_angstrom = NamedUnit(1e+21, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_angstrom', ascii_symbol='ug Ang^-3', symbol='µgÅ⁻³') -nanograms_per_cubic_angstrom = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_angstrom', ascii_symbol='ng Ang^-3', symbol='ngÅ⁻³') -picograms_per_cubic_angstrom = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') -femtograms_per_cubic_angstrom = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') -attograms_per_cubic_angstrom = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') -atomic_mass_units_per_cubic_angstrom = NamedUnit(1660.5389209999996, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='auÅ⁻³') -pounds_per_cubic_angstrom = NamedUnit(4.5359237e+29, Dimensions(length=-3, mass=1), name='pounds_per_cubic_angstrom', ascii_symbol='lb Ang^-3', symbol='lbÅ⁻³') -ounces_per_cubic_angstrom = NamedUnit(2.8349523125e+28, Dimensions(length=-3, mass=1), name='ounces_per_cubic_angstrom', ascii_symbol='oz Ang^-3', symbol='ozÅ⁻³') -grams_per_cubic_micron = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micron', ascii_symbol='g micron^-3', symbol='gmicron⁻³') -exagrams_per_cubic_micron = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_micron', ascii_symbol='Eg micron^-3', symbol='Egmicron⁻³') -petagrams_per_cubic_micron = NamedUnit(1.0000000000000002e+30, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_micron', ascii_symbol='Pg micron^-3', symbol='Pgmicron⁻³') -teragrams_per_cubic_micron = NamedUnit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_micron', ascii_symbol='Tg micron^-3', symbol='Tgmicron⁻³') -gigagrams_per_cubic_micron = NamedUnit(1.0000000000000001e+24, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_micron', ascii_symbol='Gg micron^-3', symbol='Ggmicron⁻³') -megagrams_per_cubic_micron = NamedUnit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_micron', ascii_symbol='Mg micron^-3', symbol='Mgmicron⁻³') -kilograms_per_cubic_micron = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_micron', ascii_symbol='kg micron^-3', symbol='kgmicron⁻³') -milligrams_per_cubic_micron = NamedUnit(1000000000000.0001, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_micron', ascii_symbol='mg micron^-3', symbol='mgmicron⁻³') -micrograms_per_cubic_micron = NamedUnit(1000000000.0000002, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_micron', ascii_symbol='ug micron^-3', symbol='µgmicron⁻³') -nanograms_per_cubic_micron = NamedUnit(1000000.0000000003, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_micron', ascii_symbol='ng micron^-3', symbol='ngmicron⁻³') -picograms_per_cubic_micron = NamedUnit(1000.0000000000002, Dimensions(length=-3, mass=1), name='picograms_per_cubic_micron', ascii_symbol='pg micron^-3', symbol='pgmicron⁻³') -femtograms_per_cubic_micron = NamedUnit(1.0000000000000002, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_micron', ascii_symbol='fg micron^-3', symbol='fgmicron⁻³') -attograms_per_cubic_micron = NamedUnit(0.0010000000000000002, Dimensions(length=-3, mass=1), name='attograms_per_cubic_micron', ascii_symbol='ag micron^-3', symbol='agmicron⁻³') -atomic_mass_units_per_cubic_micron = NamedUnit(1.660538921e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micron', ascii_symbol='au micron^-3', symbol='aumicron⁻³') -pounds_per_cubic_micron = NamedUnit(4.5359237000000006e+17, Dimensions(length=-3, mass=1), name='pounds_per_cubic_micron', ascii_symbol='lb micron^-3', symbol='lbmicron⁻³') -ounces_per_cubic_micron = NamedUnit(2.8349523125000004e+16, Dimensions(length=-3, mass=1), name='ounces_per_cubic_micron', ascii_symbol='oz micron^-3', symbol='ozmicron⁻³') -grams_per_cubic_mile = NamedUnit(2.399127585789277e-13, Dimensions(length=-3, mass=1), name='grams_per_cubic_mile', ascii_symbol='g miles^-3', symbol='gmiles⁻³') -exagrams_per_cubic_mile = NamedUnit(239912.7585789277, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_mile', ascii_symbol='Eg miles^-3', symbol='Egmiles⁻³') -petagrams_per_cubic_mile = NamedUnit(239.9127585789277, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_mile', ascii_symbol='Pg miles^-3', symbol='Pgmiles⁻³') -teragrams_per_cubic_mile = NamedUnit(0.2399127585789277, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_mile', ascii_symbol='Tg miles^-3', symbol='Tgmiles⁻³') -gigagrams_per_cubic_mile = NamedUnit(0.0002399127585789277, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_mile', ascii_symbol='Gg miles^-3', symbol='Ggmiles⁻³') -megagrams_per_cubic_mile = NamedUnit(2.399127585789277e-07, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_mile', ascii_symbol='Mg miles^-3', symbol='Mgmiles⁻³') -kilograms_per_cubic_mile = NamedUnit(2.399127585789277e-10, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_mile', ascii_symbol='kg miles^-3', symbol='kgmiles⁻³') -milligrams_per_cubic_mile = NamedUnit(2.399127585789277e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_mile', ascii_symbol='mg miles^-3', symbol='mgmiles⁻³') -micrograms_per_cubic_mile = NamedUnit(2.3991275857892774e-19, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_mile', ascii_symbol='ug miles^-3', symbol='µgmiles⁻³') -nanograms_per_cubic_mile = NamedUnit(2.3991275857892774e-22, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_mile', ascii_symbol='ng miles^-3', symbol='ngmiles⁻³') -picograms_per_cubic_mile = NamedUnit(2.399127585789277e-25, Dimensions(length=-3, mass=1), name='picograms_per_cubic_mile', ascii_symbol='pg miles^-3', symbol='pgmiles⁻³') -femtograms_per_cubic_mile = NamedUnit(2.3991275857892772e-28, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_mile', ascii_symbol='fg miles^-3', symbol='fgmiles⁻³') -attograms_per_cubic_mile = NamedUnit(2.399127585789277e-31, Dimensions(length=-3, mass=1), name='attograms_per_cubic_mile', ascii_symbol='ag miles^-3', symbol='agmiles⁻³') -atomic_mass_units_per_cubic_mile = NamedUnit(3.98384473264786e-37, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_mile', ascii_symbol='au miles^-3', symbol='aumiles⁻³') -pounds_per_cubic_mile = NamedUnit(1.0882259675705365e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_mile', ascii_symbol='lb miles^-3', symbol='lbmiles⁻³') -ounces_per_cubic_mile = NamedUnit(6.801412297315853e-12, Dimensions(length=-3, mass=1), name='ounces_per_cubic_mile', ascii_symbol='oz miles^-3', symbol='ozmiles⁻³') -grams_per_cubic_yard = NamedUnit(0.0013079506193143919, Dimensions(length=-3, mass=1), name='grams_per_cubic_yard', ascii_symbol='g yrd^-3', symbol='gyrd⁻³') -exagrams_per_cubic_yard = NamedUnit(1307950619314391.8, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_yard', ascii_symbol='Eg yrd^-3', symbol='Egyrd⁻³') -petagrams_per_cubic_yard = NamedUnit(1307950619314.3918, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_yard', ascii_symbol='Pg yrd^-3', symbol='Pgyrd⁻³') -teragrams_per_cubic_yard = NamedUnit(1307950619.3143919, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_yard', ascii_symbol='Tg yrd^-3', symbol='Tgyrd⁻³') -gigagrams_per_cubic_yard = NamedUnit(1307950.6193143919, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_yard', ascii_symbol='Gg yrd^-3', symbol='Ggyrd⁻³') -megagrams_per_cubic_yard = NamedUnit(1307.9506193143918, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_yard', ascii_symbol='Mg yrd^-3', symbol='Mgyrd⁻³') -kilograms_per_cubic_yard = NamedUnit(1.3079506193143917, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_yard', ascii_symbol='kg yrd^-3', symbol='kgyrd⁻³') -milligrams_per_cubic_yard = NamedUnit(1.3079506193143917e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_yard', ascii_symbol='mg yrd^-3', symbol='mgyrd⁻³') -micrograms_per_cubic_yard = NamedUnit(1.3079506193143919e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_yard', ascii_symbol='ug yrd^-3', symbol='µgyrd⁻³') -nanograms_per_cubic_yard = NamedUnit(1.307950619314392e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_yard', ascii_symbol='ng yrd^-3', symbol='ngyrd⁻³') -picograms_per_cubic_yard = NamedUnit(1.3079506193143919e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_yard', ascii_symbol='pg yrd^-3', symbol='pgyrd⁻³') -femtograms_per_cubic_yard = NamedUnit(1.3079506193143918e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_yard', ascii_symbol='fg yrd^-3', symbol='fgyrd⁻³') -attograms_per_cubic_yard = NamedUnit(1.307950619314392e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_yard', ascii_symbol='ag yrd^-3', symbol='agyrd⁻³') -atomic_mass_units_per_cubic_yard = NamedUnit(2.1719029101176016e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_yard', ascii_symbol='au yrd^-3', symbol='auyrd⁻³') -pounds_per_cubic_yard = NamedUnit(0.5932764212577828, Dimensions(length=-3, mass=1), name='pounds_per_cubic_yard', ascii_symbol='lb yrd^-3', symbol='lbyrd⁻³') -ounces_per_cubic_yard = NamedUnit(0.037079776328611425, Dimensions(length=-3, mass=1), name='ounces_per_cubic_yard', ascii_symbol='oz yrd^-3', symbol='ozyrd⁻³') -grams_per_cubic_foot = NamedUnit(0.035314666721488586, Dimensions(length=-3, mass=1), name='grams_per_cubic_foot', ascii_symbol='g ft^-3', symbol='gft⁻³') -exagrams_per_cubic_foot = NamedUnit(3.5314666721488584e+16, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_foot', ascii_symbol='Eg ft^-3', symbol='Egft⁻³') -petagrams_per_cubic_foot = NamedUnit(35314666721488.586, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_foot', ascii_symbol='Pg ft^-3', symbol='Pgft⁻³') -teragrams_per_cubic_foot = NamedUnit(35314666721.48859, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_foot', ascii_symbol='Tg ft^-3', symbol='Tgft⁻³') -gigagrams_per_cubic_foot = NamedUnit(35314666.72148859, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_foot', ascii_symbol='Gg ft^-3', symbol='Ggft⁻³') -megagrams_per_cubic_foot = NamedUnit(35314.66672148858, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_foot', ascii_symbol='Mg ft^-3', symbol='Mgft⁻³') -kilograms_per_cubic_foot = NamedUnit(35.314666721488585, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_foot', ascii_symbol='kg ft^-3', symbol='kgft⁻³') -milligrams_per_cubic_foot = NamedUnit(3.5314666721488586e-05, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_foot', ascii_symbol='mg ft^-3', symbol='mgft⁻³') -micrograms_per_cubic_foot = NamedUnit(3.5314666721488584e-08, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_foot', ascii_symbol='ug ft^-3', symbol='µgft⁻³') -nanograms_per_cubic_foot = NamedUnit(3.531466672148859e-11, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_foot', ascii_symbol='ng ft^-3', symbol='ngft⁻³') -picograms_per_cubic_foot = NamedUnit(3.531466672148859e-14, Dimensions(length=-3, mass=1), name='picograms_per_cubic_foot', ascii_symbol='pg ft^-3', symbol='pgft⁻³') -femtograms_per_cubic_foot = NamedUnit(3.5314666721488585e-17, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_foot', ascii_symbol='fg ft^-3', symbol='fgft⁻³') -attograms_per_cubic_foot = NamedUnit(3.531466672148859e-20, Dimensions(length=-3, mass=1), name='attograms_per_cubic_foot', ascii_symbol='ag ft^-3', symbol='agft⁻³') -atomic_mass_units_per_cubic_foot = NamedUnit(5.864137857317526e-26, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_foot', ascii_symbol='au ft^-3', symbol='auft⁻³') -pounds_per_cubic_foot = NamedUnit(16.018463373960138, Dimensions(length=-3, mass=1), name='pounds_per_cubic_foot', ascii_symbol='lb ft^-3', symbol='lbft⁻³') -ounces_per_cubic_foot = NamedUnit(1.0011539608725086, Dimensions(length=-3, mass=1), name='ounces_per_cubic_foot', ascii_symbol='oz ft^-3', symbol='ozft⁻³') -grams_per_cubic_inch = NamedUnit(61.02374409473229, Dimensions(length=-3, mass=1), name='grams_per_cubic_inch', ascii_symbol='g in^-3', symbol='gin⁻³') -exagrams_per_cubic_inch = NamedUnit(6.102374409473229e+19, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_inch', ascii_symbol='Eg in^-3', symbol='Egin⁻³') -petagrams_per_cubic_inch = NamedUnit(6.102374409473229e+16, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_inch', ascii_symbol='Pg in^-3', symbol='Pgin⁻³') -teragrams_per_cubic_inch = NamedUnit(61023744094732.29, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_inch', ascii_symbol='Tg in^-3', symbol='Tgin⁻³') -gigagrams_per_cubic_inch = NamedUnit(61023744094.732285, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_inch', ascii_symbol='Gg in^-3', symbol='Ggin⁻³') -megagrams_per_cubic_inch = NamedUnit(61023744.094732285, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_inch', ascii_symbol='Mg in^-3', symbol='Mgin⁻³') -kilograms_per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_inch', ascii_symbol='kg in^-3', symbol='kgin⁻³') -milligrams_per_cubic_inch = NamedUnit(0.06102374409473228, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_inch', ascii_symbol='mg in^-3', symbol='mgin⁻³') -micrograms_per_cubic_inch = NamedUnit(6.102374409473229e-05, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_inch', ascii_symbol='ug in^-3', symbol='µgin⁻³') -nanograms_per_cubic_inch = NamedUnit(6.10237440947323e-08, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_inch', ascii_symbol='ng in^-3', symbol='ngin⁻³') -picograms_per_cubic_inch = NamedUnit(6.102374409473229e-11, Dimensions(length=-3, mass=1), name='picograms_per_cubic_inch', ascii_symbol='pg in^-3', symbol='pgin⁻³') -femtograms_per_cubic_inch = NamedUnit(6.10237440947323e-14, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_inch', ascii_symbol='fg in^-3', symbol='fgin⁻³') -attograms_per_cubic_inch = NamedUnit(6.10237440947323e-17, Dimensions(length=-3, mass=1), name='attograms_per_cubic_inch', ascii_symbol='ag in^-3', symbol='agin⁻³') -atomic_mass_units_per_cubic_inch = NamedUnit(1.0133230217444687e-22, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_inch', ascii_symbol='au in^-3', symbol='auin⁻³') -pounds_per_cubic_inch = NamedUnit(27679.904710203125, Dimensions(length=-3, mass=1), name='pounds_per_cubic_inch', ascii_symbol='lb in^-3', symbol='lbin⁻³') -ounces_per_cubic_inch = NamedUnit(1729.9940443876953, Dimensions(length=-3, mass=1), name='ounces_per_cubic_inch', ascii_symbol='oz in^-3', symbol='ozin⁻³') -moles_per_cubic_meter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='molm⁻³') -millimoles_per_cubic_meter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolm⁻³') -micromoles_per_cubic_meter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolm⁻³') -nanomoles_per_cubic_meter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolm⁻³') -picomoles_per_cubic_meter = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolm⁻³') -femtomoles_per_cubic_meter = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolm⁻³') -attomoles_per_cubic_meter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolm⁻³') -moles_per_cubic_exameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='molEm⁻³') -millimoles_per_cubic_exameter = NamedUnit(6.02214076e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') -micromoles_per_cubic_exameter = NamedUnit(6.02214076e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') -nanomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') -picomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') -femtomoles_per_cubic_exameter = NamedUnit(6.02214076e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') -attomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') -moles_per_cubic_petameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='molPm⁻³') -millimoles_per_cubic_petameter = NamedUnit(6.0221407600000005e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') -micromoles_per_cubic_petameter = NamedUnit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') -nanomoles_per_cubic_petameter = NamedUnit(6.02214076e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') -picomoles_per_cubic_petameter = NamedUnit(6.0221407600000005e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') -femtomoles_per_cubic_petameter = NamedUnit(6.022140760000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') -attomoles_per_cubic_petameter = NamedUnit(6.022140760000001e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') -moles_per_cubic_terameter = NamedUnit(6.022140759999999e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='molTm⁻³') -millimoles_per_cubic_terameter = NamedUnit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') -micromoles_per_cubic_terameter = NamedUnit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') -nanomoles_per_cubic_terameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') -picomoles_per_cubic_terameter = NamedUnit(6.02214076e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') -femtomoles_per_cubic_terameter = NamedUnit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') -attomoles_per_cubic_terameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') -moles_per_cubic_gigameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='molGm⁻³') -millimoles_per_cubic_gigameter = NamedUnit(6.022140760000001e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') -micromoles_per_cubic_gigameter = NamedUnit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') -nanomoles_per_cubic_gigameter = NamedUnit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') -picomoles_per_cubic_gigameter = NamedUnit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') -femtomoles_per_cubic_gigameter = NamedUnit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') -attomoles_per_cubic_gigameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') -moles_per_cubic_megameter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='molMm⁻³') -millimoles_per_cubic_megameter = NamedUnit(602.214076, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') -micromoles_per_cubic_megameter = NamedUnit(0.602214076, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') -nanomoles_per_cubic_megameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') -picomoles_per_cubic_megameter = NamedUnit(6.02214076e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') -femtomoles_per_cubic_megameter = NamedUnit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') -attomoles_per_cubic_megameter = NamedUnit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') -moles_per_cubic_kilometer = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='molkm⁻³') -millimoles_per_cubic_kilometer = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') -micromoles_per_cubic_kilometer = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') -nanomoles_per_cubic_kilometer = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') -picomoles_per_cubic_kilometer = NamedUnit(602.214076, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') -femtomoles_per_cubic_kilometer = NamedUnit(0.602214076, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') -attomoles_per_cubic_kilometer = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') -moles_per_cubic_millimeter = NamedUnit(6.0221407599999995e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='molmm⁻³') -millimoles_per_cubic_millimeter = NamedUnit(6.02214076e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') -micromoles_per_cubic_millimeter = NamedUnit(6.0221407599999996e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') -nanomoles_per_cubic_millimeter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') -picomoles_per_cubic_millimeter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') -femtomoles_per_cubic_millimeter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') -attomoles_per_cubic_millimeter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') -moles_per_cubic_micrometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='molµm⁻³') -millimoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') -micromoles_per_cubic_micrometer = NamedUnit(6.0221407600000004e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') -nanomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') -picomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') -femtomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') -attomoles_per_cubic_micrometer = NamedUnit(6.0221407600000005e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') -moles_per_cubic_nanometer = NamedUnit(6.022140759999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='molnm⁻³') -millimoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') -micromoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') -nanomoles_per_cubic_nanometer = NamedUnit(6.022140759999998e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') -picomoles_per_cubic_nanometer = NamedUnit(6.0221407599999985e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') -femtomoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') -attomoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') -moles_per_cubic_picometer = NamedUnit(6.0221407600000005e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='molpm⁻³') -millimoles_per_cubic_picometer = NamedUnit(6.0221407600000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') -micromoles_per_cubic_picometer = NamedUnit(6.022140760000001e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') -nanomoles_per_cubic_picometer = NamedUnit(6.0221407600000005e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') -picomoles_per_cubic_picometer = NamedUnit(6.02214076e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') -femtomoles_per_cubic_picometer = NamedUnit(6.022140760000001e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') -attomoles_per_cubic_picometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') -moles_per_cubic_femtometer = NamedUnit(6.022140759999998e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='molfm⁻³') -millimoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') -micromoles_per_cubic_femtometer = NamedUnit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') -nanomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') -picomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') -femtomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') -attomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') -moles_per_cubic_attometer = NamedUnit(6.022140759999998e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='molam⁻³') -millimoles_per_cubic_attometer = NamedUnit(6.022140759999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') -micromoles_per_cubic_attometer = NamedUnit(6.022140759999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') -nanomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') -picomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') -femtomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') -attomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') -moles_per_cubic_decimeter = NamedUnit(6.022140759999998e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='moldm⁻³') -millimoles_per_cubic_decimeter = NamedUnit(6.0221407599999985e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') -micromoles_per_cubic_decimeter = NamedUnit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') -nanomoles_per_cubic_decimeter = NamedUnit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') -picomoles_per_cubic_decimeter = NamedUnit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') -femtomoles_per_cubic_decimeter = NamedUnit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') -attomoles_per_cubic_decimeter = NamedUnit(602214075.9999999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') -moles_per_cubic_centimeter = NamedUnit(6.022140759999999e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='molcm⁻³') -millimoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') -micromoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') -nanomoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') -picomoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') -femtomoles_per_cubic_centimeter = NamedUnit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') -attomoles_per_cubic_centimeter = NamedUnit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') -moles_per_cubic_angstrom = NamedUnit(6.022140759999999e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='molÅ⁻³') -millimoles_per_cubic_angstrom = NamedUnit(6.02214076e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') -micromoles_per_cubic_angstrom = NamedUnit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') -nanomoles_per_cubic_angstrom = NamedUnit(6.02214076e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') -picomoles_per_cubic_angstrom = NamedUnit(6.02214076e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') -femtomoles_per_cubic_angstrom = NamedUnit(6.022140759999999e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') -attomoles_per_cubic_angstrom = NamedUnit(6.02214076e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') -moles_per_cubic_micron = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micron', ascii_symbol='mol micron^-3', symbol='molmicron⁻³') -millimoles_per_cubic_micron = NamedUnit(6.022140760000001e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micron', ascii_symbol='mmol micron^-3', symbol='mmolmicron⁻³') -micromoles_per_cubic_micron = NamedUnit(6.0221407600000004e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micron', ascii_symbol='umol micron^-3', symbol='µmolmicron⁻³') -nanomoles_per_cubic_micron = NamedUnit(6.022140760000001e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micron', ascii_symbol='nmol micron^-3', symbol='nmolmicron⁻³') -picomoles_per_cubic_micron = NamedUnit(6.022140760000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micron', ascii_symbol='pmol micron^-3', symbol='pmolmicron⁻³') -femtomoles_per_cubic_micron = NamedUnit(6.022140760000001e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micron', ascii_symbol='fmol micron^-3', symbol='fmolmicron⁻³') -attomoles_per_cubic_micron = NamedUnit(6.0221407600000005e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micron', ascii_symbol='amol micron^-3', symbol='amolmicron⁻³') -moles_per_cubic_mile = NamedUnit(144478840228220.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_mile', ascii_symbol='mol miles^-3', symbol='molmiles⁻³') -millimoles_per_cubic_mile = NamedUnit(144478840228.22003, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_mile', ascii_symbol='mmol miles^-3', symbol='mmolmiles⁻³') -micromoles_per_cubic_mile = NamedUnit(144478840.22822002, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_mile', ascii_symbol='umol miles^-3', symbol='µmolmiles⁻³') -nanomoles_per_cubic_mile = NamedUnit(144478.84022822, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_mile', ascii_symbol='nmol miles^-3', symbol='nmolmiles⁻³') -picomoles_per_cubic_mile = NamedUnit(144.47884022822, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_mile', ascii_symbol='pmol miles^-3', symbol='pmolmiles⁻³') -femtomoles_per_cubic_mile = NamedUnit(0.14447884022822002, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_mile', ascii_symbol='fmol miles^-3', symbol='fmolmiles⁻³') -attomoles_per_cubic_mile = NamedUnit(0.00014447884022822003, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_mile', ascii_symbol='amol miles^-3', symbol='amolmiles⁻³') -moles_per_cubic_yard = NamedUnit(7.876662736640442e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_yard', ascii_symbol='mol yrd^-3', symbol='molyrd⁻³') -millimoles_per_cubic_yard = NamedUnit(7.876662736640442e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_yard', ascii_symbol='mmol yrd^-3', symbol='mmolyrd⁻³') -micromoles_per_cubic_yard = NamedUnit(7.876662736640442e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_yard', ascii_symbol='umol yrd^-3', symbol='µmolyrd⁻³') -nanomoles_per_cubic_yard = NamedUnit(787666273664044.2, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_yard', ascii_symbol='nmol yrd^-3', symbol='nmolyrd⁻³') -picomoles_per_cubic_yard = NamedUnit(787666273664.0442, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_yard', ascii_symbol='pmol yrd^-3', symbol='pmolyrd⁻³') -femtomoles_per_cubic_yard = NamedUnit(787666273.6640443, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_yard', ascii_symbol='fmol yrd^-3', symbol='fmolyrd⁻³') -attomoles_per_cubic_yard = NamedUnit(787666.2736640442, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_yard', ascii_symbol='amol yrd^-3', symbol='amolyrd⁻³') -moles_per_cubic_foot = NamedUnit(2.1266989388929195e+25, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_foot', ascii_symbol='mol ft^-3', symbol='molft⁻³') -millimoles_per_cubic_foot = NamedUnit(2.1266989388929197e+22, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_foot', ascii_symbol='mmol ft^-3', symbol='mmolft⁻³') -micromoles_per_cubic_foot = NamedUnit(2.1266989388929196e+19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_foot', ascii_symbol='umol ft^-3', symbol='µmolft⁻³') -nanomoles_per_cubic_foot = NamedUnit(2.1266989388929196e+16, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_foot', ascii_symbol='nmol ft^-3', symbol='nmolft⁻³') -picomoles_per_cubic_foot = NamedUnit(21266989388929.2, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_foot', ascii_symbol='pmol ft^-3', symbol='pmolft⁻³') -femtomoles_per_cubic_foot = NamedUnit(21266989388.9292, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_foot', ascii_symbol='fmol ft^-3', symbol='fmolft⁻³') -attomoles_per_cubic_foot = NamedUnit(21266989.388929196, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_foot', ascii_symbol='amol ft^-3', symbol='amolft⁻³') -moles_per_cubic_inch = NamedUnit(3.6749357664069658e+28, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_inch', ascii_symbol='mol in^-3', symbol='molin⁻³') -millimoles_per_cubic_inch = NamedUnit(3.674935766406966e+25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_inch', ascii_symbol='mmol in^-3', symbol='mmolin⁻³') -micromoles_per_cubic_inch = NamedUnit(3.674935766406966e+22, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_inch', ascii_symbol='umol in^-3', symbol='µmolin⁻³') -nanomoles_per_cubic_inch = NamedUnit(3.674935766406966e+19, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_inch', ascii_symbol='nmol in^-3', symbol='nmolin⁻³') -picomoles_per_cubic_inch = NamedUnit(3.674935766406966e+16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_inch', ascii_symbol='pmol in^-3', symbol='pmolin⁻³') -femtomoles_per_cubic_inch = NamedUnit(36749357664069.664, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_inch', ascii_symbol='fmol in^-3', symbol='fmolin⁻³') -attomoles_per_cubic_inch = NamedUnit(36749357664.069664, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_inch', ascii_symbol='amol in^-3', symbol='amolin⁻³') +meters = Unit(1, Dimensions(1, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') +exameters = Unit(1e+18, Dimensions(1, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') +petameters = Unit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') +terameters = Unit(1000000000000.0, Dimensions(1, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') +gigameters = Unit(1000000000.0, Dimensions(1, 0, 0, 0, 0),name='gigameters',ascii_symbol='Gm',symbol='Gm') +megameters = Unit(1000000.0, Dimensions(1, 0, 0, 0, 0),name='megameters',ascii_symbol='Mm',symbol='Mm') +kilometers = Unit(1000.0, Dimensions(1, 0, 0, 0, 0),name='kilometers',ascii_symbol='km',symbol='km') +millimeters = Unit(0.001, Dimensions(1, 0, 0, 0, 0),name='millimeters',ascii_symbol='mm',symbol='mm') +micrometers = Unit(1e-06, Dimensions(1, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',symbol='µm') +nanometers = Unit(1e-09, Dimensions(1, 0, 0, 0, 0),name='nanometers',ascii_symbol='nm',symbol='nm') +picometers = Unit(1e-12, Dimensions(1, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') +femtometers = Unit(1e-15, Dimensions(1, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') +attometers = Unit(1e-18, Dimensions(1, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') +seconds = Unit(1, Dimensions(0, 1, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') +milliseconds = Unit(0.001, Dimensions(0, 1, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') +microseconds = Unit(1e-06, Dimensions(0, 1, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') +nanoseconds = Unit(1e-09, Dimensions(0, 1, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') +picoseconds = Unit(1e-12, Dimensions(0, 1, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') +femtoseconds = Unit(1e-15, Dimensions(0, 1, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') +attoseconds = Unit(1e-18, Dimensions(0, 1, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') +grams = Unit(0.001, Dimensions(0, 0, 1, 0, 0),name='grams',ascii_symbol='g',symbol='g') +exagrams = Unit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') +petagrams = Unit(1000000000000.0, Dimensions(0, 0, 1, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') +teragrams = Unit(1000000000.0, Dimensions(0, 0, 1, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') +gigagrams = Unit(1000000.0, Dimensions(0, 0, 1, 0, 0),name='gigagrams',ascii_symbol='Gg',symbol='Gg') +megagrams = Unit(1000.0, Dimensions(0, 0, 1, 0, 0),name='megagrams',ascii_symbol='Mg',symbol='Mg') +kilograms = Unit(1.0, Dimensions(0, 0, 1, 0, 0),name='kilograms',ascii_symbol='kg',symbol='kg') +milligrams = Unit(1e-06, Dimensions(0, 0, 1, 0, 0),name='milligrams',ascii_symbol='mg',symbol='mg') +micrograms = Unit(1e-09, Dimensions(0, 0, 1, 0, 0),name='micrograms',ascii_symbol='ug',symbol='µg') +nanograms = Unit(1.0000000000000002e-12, Dimensions(0, 0, 1, 0, 0),name='nanograms',ascii_symbol='ng',symbol='ng') +picograms = Unit(1e-15, Dimensions(0, 0, 1, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') +femtograms = Unit(1e-18, Dimensions(0, 0, 1, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') +attograms = Unit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') +amps = Unit(1, Dimensions(0, 0, 0, 1, 0),name='amps',ascii_symbol='A',symbol='A') +exaamps = Unit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamps',ascii_symbol='EA',symbol='EA') +petaamps = Unit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamps',ascii_symbol='PA',symbol='PA') +teraamps = Unit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamps',ascii_symbol='TA',symbol='TA') +gigaamps = Unit(1000000000.0, Dimensions(0, 0, 0, 1, 0),name='gigaamps',ascii_symbol='GA',symbol='GA') +megaamps = Unit(1000000.0, Dimensions(0, 0, 0, 1, 0),name='megaamps',ascii_symbol='MA',symbol='MA') +kiloamps = Unit(1000.0, Dimensions(0, 0, 0, 1, 0),name='kiloamps',ascii_symbol='kA',symbol='kA') +milliamps = Unit(0.001, Dimensions(0, 0, 0, 1, 0),name='milliamps',ascii_symbol='mA',symbol='mA') +microamps = Unit(1e-06, Dimensions(0, 0, 0, 1, 0),name='microamps',ascii_symbol='uA',symbol='µA') +nanoamps = Unit(1e-09, Dimensions(0, 0, 0, 1, 0),name='nanoamps',ascii_symbol='nA',symbol='nA') +picoamps = Unit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamps',ascii_symbol='pA',symbol='pA') +femtoamps = Unit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamps',ascii_symbol='fA',symbol='fA') +attoamps = Unit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamps',ascii_symbol='aA',symbol='aA') +kelvin = Unit(1, Dimensions(0, 0, 0, 0, 1),name='kelvin',ascii_symbol='K',symbol='K') +exakelvin = Unit(1e+18, Dimensions(0, 0, 0, 0, 1),name='exakelvin',ascii_symbol='EK',symbol='EK') +petakelvin = Unit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1),name='petakelvin',ascii_symbol='PK',symbol='PK') +terakelvin = Unit(1000000000000.0, Dimensions(0, 0, 0, 0, 1),name='terakelvin',ascii_symbol='TK',symbol='TK') +gigakelvin = Unit(1000000000.0, Dimensions(0, 0, 0, 0, 1),name='gigakelvin',ascii_symbol='GK',symbol='GK') +megakelvin = Unit(1000000.0, Dimensions(0, 0, 0, 0, 1),name='megakelvin',ascii_symbol='MK',symbol='MK') +kilokelvin = Unit(1000.0, Dimensions(0, 0, 0, 0, 1),name='kilokelvin',ascii_symbol='kK',symbol='kK') +millikelvin = Unit(0.001, Dimensions(0, 0, 0, 0, 1),name='millikelvin',ascii_symbol='mK',symbol='mK') +microkelvin = Unit(1e-06, Dimensions(0, 0, 0, 0, 1),name='microkelvin',ascii_symbol='uK',symbol='µK') +nanokelvin = Unit(1e-09, Dimensions(0, 0, 0, 0, 1),name='nanokelvin',ascii_symbol='nK',symbol='nK') +picokelvin = Unit(1e-12, Dimensions(0, 0, 0, 0, 1),name='picokelvin',ascii_symbol='pK',symbol='pK') +femtokelvin = Unit(1e-15, Dimensions(0, 0, 0, 0, 1),name='femtokelvin',ascii_symbol='fK',symbol='fK') +attokelvin = Unit(1e-18, Dimensions(0, 0, 0, 0, 1),name='attokelvin',ascii_symbol='aK',symbol='aK') +hertz = Unit(1, Dimensions(0, -1, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') +exahertz = Unit(1e+18, Dimensions(0, -1, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') +petahertz = Unit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') +terahertz = Unit(1000000000000.0, Dimensions(0, -1, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') +gigahertz = Unit(1000000000.0, Dimensions(0, -1, 0, 0, 0),name='gigahertz',ascii_symbol='GHz',symbol='GHz') +megahertz = Unit(1000000.0, Dimensions(0, -1, 0, 0, 0),name='megahertz',ascii_symbol='MHz',symbol='MHz') +kilohertz = Unit(1000.0, Dimensions(0, -1, 0, 0, 0),name='kilohertz',ascii_symbol='kHz',symbol='kHz') +millihertz = Unit(0.001, Dimensions(0, -1, 0, 0, 0),name='millihertz',ascii_symbol='mHz',symbol='mHz') +microhertz = Unit(1e-06, Dimensions(0, -1, 0, 0, 0),name='microhertz',ascii_symbol='uHz',symbol='µHz') +nanohertz = Unit(1e-09, Dimensions(0, -1, 0, 0, 0),name='nanohertz',ascii_symbol='nHz',symbol='nHz') +picohertz = Unit(1e-12, Dimensions(0, -1, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') +femtohertz = Unit(1e-15, Dimensions(0, -1, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') +attohertz = Unit(1e-18, Dimensions(0, -1, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') +newtons = Unit(1, Dimensions(1, -2, 1, 0, 0),name='newtons',ascii_symbol='N',symbol='N') +exanewtons = Unit(1e+18, Dimensions(1, -2, 1, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') +petanewtons = Unit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') +teranewtons = Unit(1000000000000.0, Dimensions(1, -2, 1, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') +giganewtons = Unit(1000000000.0, Dimensions(1, -2, 1, 0, 0),name='giganewtons',ascii_symbol='GN',symbol='GN') +meganewtons = Unit(1000000.0, Dimensions(1, -2, 1, 0, 0),name='meganewtons',ascii_symbol='MN',symbol='MN') +kilonewtons = Unit(1000.0, Dimensions(1, -2, 1, 0, 0),name='kilonewtons',ascii_symbol='kN',symbol='kN') +millinewtons = Unit(0.001, Dimensions(1, -2, 1, 0, 0),name='millinewtons',ascii_symbol='mN',symbol='mN') +micronewtons = Unit(1e-06, Dimensions(1, -2, 1, 0, 0),name='micronewtons',ascii_symbol='uN',symbol='µN') +nanonewtons = Unit(1e-09, Dimensions(1, -2, 1, 0, 0),name='nanonewtons',ascii_symbol='nN',symbol='nN') +piconewtons = Unit(1e-12, Dimensions(1, -2, 1, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') +femtonewtons = Unit(1e-15, Dimensions(1, -2, 1, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') +attonewtons = Unit(1e-18, Dimensions(1, -2, 1, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') +pascals = Unit(1, Dimensions(-1, -2, 1, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') +exapascals = Unit(1e+18, Dimensions(-1, -2, 1, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') +petapascals = Unit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') +terapascals = Unit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') +gigapascals = Unit(1000000000.0, Dimensions(-1, -2, 1, 0, 0),name='gigapascals',ascii_symbol='GPa',symbol='GPa') +megapascals = Unit(1000000.0, Dimensions(-1, -2, 1, 0, 0),name='megapascals',ascii_symbol='MPa',symbol='MPa') +kilopascals = Unit(1000.0, Dimensions(-1, -2, 1, 0, 0),name='kilopascals',ascii_symbol='kPa',symbol='kPa') +millipascals = Unit(0.001, Dimensions(-1, -2, 1, 0, 0),name='millipascals',ascii_symbol='mPa',symbol='mPa') +micropascals = Unit(1e-06, Dimensions(-1, -2, 1, 0, 0),name='micropascals',ascii_symbol='uPa',symbol='µPa') +nanopascals = Unit(1e-09, Dimensions(-1, -2, 1, 0, 0),name='nanopascals',ascii_symbol='nPa',symbol='nPa') +picopascals = Unit(1e-12, Dimensions(-1, -2, 1, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') +femtopascals = Unit(1e-15, Dimensions(-1, -2, 1, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') +attopascals = Unit(1e-18, Dimensions(-1, -2, 1, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') +joules = Unit(1, Dimensions(2, -2, 1, 0, 0),name='joules',ascii_symbol='J',symbol='J') +exajoules = Unit(1e+18, Dimensions(2, -2, 1, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') +petajoules = Unit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') +terajoules = Unit(1000000000000.0, Dimensions(2, -2, 1, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') +gigajoules = Unit(1000000000.0, Dimensions(2, -2, 1, 0, 0),name='gigajoules',ascii_symbol='GJ',symbol='GJ') +megajoules = Unit(1000000.0, Dimensions(2, -2, 1, 0, 0),name='megajoules',ascii_symbol='MJ',symbol='MJ') +kilojoules = Unit(1000.0, Dimensions(2, -2, 1, 0, 0),name='kilojoules',ascii_symbol='kJ',symbol='kJ') +millijoules = Unit(0.001, Dimensions(2, -2, 1, 0, 0),name='millijoules',ascii_symbol='mJ',symbol='mJ') +microjoules = Unit(1e-06, Dimensions(2, -2, 1, 0, 0),name='microjoules',ascii_symbol='uJ',symbol='µJ') +nanojoules = Unit(1e-09, Dimensions(2, -2, 1, 0, 0),name='nanojoules',ascii_symbol='nJ',symbol='nJ') +picojoules = Unit(1e-12, Dimensions(2, -2, 1, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') +femtojoules = Unit(1e-15, Dimensions(2, -2, 1, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') +attojoules = Unit(1e-18, Dimensions(2, -2, 1, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') +watts = Unit(1, Dimensions(2, -3, 1, 0, 0),name='watts',ascii_symbol='W',symbol='W') +exawatts = Unit(1e+18, Dimensions(2, -3, 1, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') +petawatts = Unit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') +terawatts = Unit(1000000000000.0, Dimensions(2, -3, 1, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') +gigawatts = Unit(1000000000.0, Dimensions(2, -3, 1, 0, 0),name='gigawatts',ascii_symbol='GW',symbol='GW') +megawatts = Unit(1000000.0, Dimensions(2, -3, 1, 0, 0),name='megawatts',ascii_symbol='MW',symbol='MW') +kilowatts = Unit(1000.0, Dimensions(2, -3, 1, 0, 0),name='kilowatts',ascii_symbol='kW',symbol='kW') +milliwatts = Unit(0.001, Dimensions(2, -3, 1, 0, 0),name='milliwatts',ascii_symbol='mW',symbol='mW') +microwatts = Unit(1e-06, Dimensions(2, -3, 1, 0, 0),name='microwatts',ascii_symbol='uW',symbol='µW') +nanowatts = Unit(1e-09, Dimensions(2, -3, 1, 0, 0),name='nanowatts',ascii_symbol='nW',symbol='nW') +picowatts = Unit(1e-12, Dimensions(2, -3, 1, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') +femtowatts = Unit(1e-15, Dimensions(2, -3, 1, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') +attowatts = Unit(1e-18, Dimensions(2, -3, 1, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') +coulombs = Unit(1, Dimensions(0, 1, 0, 1, 0),name='coulombs',ascii_symbol='C',symbol='C') +exacoulombs = Unit(1e+18, Dimensions(0, 1, 0, 1, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') +petacoulombs = Unit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') +teracoulombs = Unit(1000000000000.0, Dimensions(0, 1, 0, 1, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') +gigacoulombs = Unit(1000000000.0, Dimensions(0, 1, 0, 1, 0),name='gigacoulombs',ascii_symbol='GC',symbol='GC') +megacoulombs = Unit(1000000.0, Dimensions(0, 1, 0, 1, 0),name='megacoulombs',ascii_symbol='MC',symbol='MC') +kilocoulombs = Unit(1000.0, Dimensions(0, 1, 0, 1, 0),name='kilocoulombs',ascii_symbol='kC',symbol='kC') +millicoulombs = Unit(0.001, Dimensions(0, 1, 0, 1, 0),name='millicoulombs',ascii_symbol='mC',symbol='mC') +microcoulombs = Unit(1e-06, Dimensions(0, 1, 0, 1, 0),name='microcoulombs',ascii_symbol='uC',symbol='µC') +nanocoulombs = Unit(1e-09, Dimensions(0, 1, 0, 1, 0),name='nanocoulombs',ascii_symbol='nC',symbol='nC') +picocoulombs = Unit(1e-12, Dimensions(0, 1, 0, 1, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') +femtocoulombs = Unit(1e-15, Dimensions(0, 1, 0, 1, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') +attocoulombs = Unit(1e-18, Dimensions(0, 1, 0, 1, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') +volts = Unit(1, Dimensions(2, -3, 1, -1, 0),name='volts',ascii_symbol='V',symbol='V') +exavolts = Unit(1e+18, Dimensions(2, -3, 1, -1, 0),name='exavolts',ascii_symbol='EV',symbol='EV') +petavolts = Unit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0),name='petavolts',ascii_symbol='PV',symbol='PV') +teravolts = Unit(1000000000000.0, Dimensions(2, -3, 1, -1, 0),name='teravolts',ascii_symbol='TV',symbol='TV') +gigavolts = Unit(1000000000.0, Dimensions(2, -3, 1, -1, 0),name='gigavolts',ascii_symbol='GV',symbol='GV') +megavolts = Unit(1000000.0, Dimensions(2, -3, 1, -1, 0),name='megavolts',ascii_symbol='MV',symbol='MV') +kilovolts = Unit(1000.0, Dimensions(2, -3, 1, -1, 0),name='kilovolts',ascii_symbol='kV',symbol='kV') +millivolts = Unit(0.001, Dimensions(2, -3, 1, -1, 0),name='millivolts',ascii_symbol='mV',symbol='mV') +microvolts = Unit(1e-06, Dimensions(2, -3, 1, -1, 0),name='microvolts',ascii_symbol='uV',symbol='µV') +nanovolts = Unit(1e-09, Dimensions(2, -3, 1, -1, 0),name='nanovolts',ascii_symbol='nV',symbol='nV') +picovolts = Unit(1e-12, Dimensions(2, -3, 1, -1, 0),name='picovolts',ascii_symbol='pV',symbol='pV') +femtovolts = Unit(1e-15, Dimensions(2, -3, 1, -1, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') +attovolts = Unit(1e-18, Dimensions(2, -3, 1, -1, 0),name='attovolts',ascii_symbol='aV',symbol='aV') +ohms = Unit(1, Dimensions(2, -3, 1, -2, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') +exaohms = Unit(1e+18, Dimensions(2, -3, 1, -2, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') +petaohms = Unit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') +teraohms = Unit(1000000000000.0, Dimensions(2, -3, 1, -2, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') +gigaohms = Unit(1000000000.0, Dimensions(2, -3, 1, -2, 0),name='gigaohms',ascii_symbol='GOhm',symbol='GΩ') +megaohms = Unit(1000000.0, Dimensions(2, -3, 1, -2, 0),name='megaohms',ascii_symbol='MOhm',symbol='MΩ') +kiloohms = Unit(1000.0, Dimensions(2, -3, 1, -2, 0),name='kiloohms',ascii_symbol='kOhm',symbol='kΩ') +milliohms = Unit(0.001, Dimensions(2, -3, 1, -2, 0),name='milliohms',ascii_symbol='mOhm',symbol='mΩ') +microohms = Unit(1e-06, Dimensions(2, -3, 1, -2, 0),name='microohms',ascii_symbol='uOhm',symbol='µΩ') +nanoohms = Unit(1e-09, Dimensions(2, -3, 1, -2, 0),name='nanoohms',ascii_symbol='nOhm',symbol='nΩ') +picoohms = Unit(1e-12, Dimensions(2, -3, 1, -2, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') +femtoohms = Unit(1e-15, Dimensions(2, -3, 1, -2, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') +attoohms = Unit(1e-18, Dimensions(2, -3, 1, -2, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') +farads = Unit(1, Dimensions(-2, 4, -1, 2, 0),name='farads',ascii_symbol='F',symbol='F') +exafarads = Unit(1e+18, Dimensions(-2, 4, -1, 2, 0),name='exafarads',ascii_symbol='EF',symbol='EF') +petafarads = Unit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='petafarads',ascii_symbol='PF',symbol='PF') +terafarads = Unit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='terafarads',ascii_symbol='TF',symbol='TF') +gigafarads = Unit(1000000000.0, Dimensions(-2, 4, -1, 2, 0),name='gigafarads',ascii_symbol='GF',symbol='GF') +megafarads = Unit(1000000.0, Dimensions(-2, 4, -1, 2, 0),name='megafarads',ascii_symbol='MF',symbol='MF') +kilofarads = Unit(1000.0, Dimensions(-2, 4, -1, 2, 0),name='kilofarads',ascii_symbol='kF',symbol='kF') +millifarads = Unit(0.001, Dimensions(-2, 4, -1, 2, 0),name='millifarads',ascii_symbol='mF',symbol='mF') +microfarads = Unit(1e-06, Dimensions(-2, 4, -1, 2, 0),name='microfarads',ascii_symbol='uF',symbol='µF') +nanofarads = Unit(1e-09, Dimensions(-2, 4, -1, 2, 0),name='nanofarads',ascii_symbol='nF',symbol='nF') +picofarads = Unit(1e-12, Dimensions(-2, 4, -1, 2, 0),name='picofarads',ascii_symbol='pF',symbol='pF') +femtofarads = Unit(1e-15, Dimensions(-2, 4, -1, 2, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') +attofarads = Unit(1e-18, Dimensions(-2, 4, -1, 2, 0),name='attofarads',ascii_symbol='aF',symbol='aF') +siemens = Unit(1, Dimensions(-2, 3, -1, 2, 0),name='siemens',ascii_symbol='S',symbol='S') +exasiemens = Unit(1e+18, Dimensions(-2, 3, -1, 2, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') +petasiemens = Unit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') +terasiemens = Unit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') +gigasiemens = Unit(1000000000.0, Dimensions(-2, 3, -1, 2, 0),name='gigasiemens',ascii_symbol='GS',symbol='GS') +megasiemens = Unit(1000000.0, Dimensions(-2, 3, -1, 2, 0),name='megasiemens',ascii_symbol='MS',symbol='MS') +kilosiemens = Unit(1000.0, Dimensions(-2, 3, -1, 2, 0),name='kilosiemens',ascii_symbol='kS',symbol='kS') +millisiemens = Unit(0.001, Dimensions(-2, 3, -1, 2, 0),name='millisiemens',ascii_symbol='mS',symbol='mS') +microsiemens = Unit(1e-06, Dimensions(-2, 3, -1, 2, 0),name='microsiemens',ascii_symbol='uS',symbol='µS') +nanosiemens = Unit(1e-09, Dimensions(-2, 3, -1, 2, 0),name='nanosiemens',ascii_symbol='nS',symbol='nS') +picosiemens = Unit(1e-12, Dimensions(-2, 3, -1, 2, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') +femtosiemens = Unit(1e-15, Dimensions(-2, 3, -1, 2, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') +attosiemens = Unit(1e-18, Dimensions(-2, 3, -1, 2, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') +webers = Unit(1, Dimensions(2, -2, 1, -1, 0),name='webers',ascii_symbol='Wb',symbol='Wb') +exawebers = Unit(1e+18, Dimensions(2, -2, 1, -1, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') +petawebers = Unit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') +terawebers = Unit(1000000000000.0, Dimensions(2, -2, 1, -1, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') +gigawebers = Unit(1000000000.0, Dimensions(2, -2, 1, -1, 0),name='gigawebers',ascii_symbol='GWb',symbol='GWb') +megawebers = Unit(1000000.0, Dimensions(2, -2, 1, -1, 0),name='megawebers',ascii_symbol='MWb',symbol='MWb') +kilowebers = Unit(1000.0, Dimensions(2, -2, 1, -1, 0),name='kilowebers',ascii_symbol='kWb',symbol='kWb') +milliwebers = Unit(0.001, Dimensions(2, -2, 1, -1, 0),name='milliwebers',ascii_symbol='mWb',symbol='mWb') +microwebers = Unit(1e-06, Dimensions(2, -2, 1, -1, 0),name='microwebers',ascii_symbol='uWb',symbol='µWb') +nanowebers = Unit(1e-09, Dimensions(2, -2, 1, -1, 0),name='nanowebers',ascii_symbol='nWb',symbol='nWb') +picowebers = Unit(1e-12, Dimensions(2, -2, 1, -1, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') +femtowebers = Unit(1e-15, Dimensions(2, -2, 1, -1, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') +attowebers = Unit(1e-18, Dimensions(2, -2, 1, -1, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') +tesla = Unit(1, Dimensions(0, -2, 1, -1, 0),name='tesla',ascii_symbol='T',symbol='T') +exatesla = Unit(1e+18, Dimensions(0, -2, 1, -1, 0),name='exatesla',ascii_symbol='ET',symbol='ET') +petatesla = Unit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0),name='petatesla',ascii_symbol='PT',symbol='PT') +teratesla = Unit(1000000000000.0, Dimensions(0, -2, 1, -1, 0),name='teratesla',ascii_symbol='TT',symbol='TT') +gigatesla = Unit(1000000000.0, Dimensions(0, -2, 1, -1, 0),name='gigatesla',ascii_symbol='GT',symbol='GT') +megatesla = Unit(1000000.0, Dimensions(0, -2, 1, -1, 0),name='megatesla',ascii_symbol='MT',symbol='MT') +kilotesla = Unit(1000.0, Dimensions(0, -2, 1, -1, 0),name='kilotesla',ascii_symbol='kT',symbol='kT') +millitesla = Unit(0.001, Dimensions(0, -2, 1, -1, 0),name='millitesla',ascii_symbol='mT',symbol='mT') +microtesla = Unit(1e-06, Dimensions(0, -2, 1, -1, 0),name='microtesla',ascii_symbol='uT',symbol='µT') +nanotesla = Unit(1e-09, Dimensions(0, -2, 1, -1, 0),name='nanotesla',ascii_symbol='nT',symbol='nT') +picotesla = Unit(1e-12, Dimensions(0, -2, 1, -1, 0),name='picotesla',ascii_symbol='pT',symbol='pT') +femtotesla = Unit(1e-15, Dimensions(0, -2, 1, -1, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') +attotesla = Unit(1e-18, Dimensions(0, -2, 1, -1, 0),name='attotesla',ascii_symbol='aT',symbol='aT') +henry = Unit(1, Dimensions(2, -2, 1, -2, 0),name='henry',ascii_symbol='H',symbol='H') +exahenry = Unit(1e+18, Dimensions(2, -2, 1, -2, 0),name='exahenry',ascii_symbol='EH',symbol='EH') +petahenry = Unit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0),name='petahenry',ascii_symbol='PH',symbol='PH') +terahenry = Unit(1000000000000.0, Dimensions(2, -2, 1, -2, 0),name='terahenry',ascii_symbol='TH',symbol='TH') +gigahenry = Unit(1000000000.0, Dimensions(2, -2, 1, -2, 0),name='gigahenry',ascii_symbol='GH',symbol='GH') +megahenry = Unit(1000000.0, Dimensions(2, -2, 1, -2, 0),name='megahenry',ascii_symbol='MH',symbol='MH') +kilohenry = Unit(1000.0, Dimensions(2, -2, 1, -2, 0),name='kilohenry',ascii_symbol='kH',symbol='kH') +millihenry = Unit(0.001, Dimensions(2, -2, 1, -2, 0),name='millihenry',ascii_symbol='mH',symbol='mH') +microhenry = Unit(1e-06, Dimensions(2, -2, 1, -2, 0),name='microhenry',ascii_symbol='uH',symbol='µH') +nanohenry = Unit(1e-09, Dimensions(2, -2, 1, -2, 0),name='nanohenry',ascii_symbol='nH',symbol='nH') +picohenry = Unit(1e-12, Dimensions(2, -2, 1, -2, 0),name='picohenry',ascii_symbol='pH',symbol='pH') +femtohenry = Unit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') +attohenry = Unit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') +degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1),name='degrees_celsius',ascii_symbol='C',symbol='C') +angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0),name='angstroms',ascii_symbol='A',symbol='Å') +angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Ang') +minutes = Unit(60, Dimensions(0, 1, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') +hours = Unit(360, Dimensions(0, 1, 0, 0, 0),name='hours',ascii_symbol='hr',symbol='hr') +days = Unit(8640, Dimensions(0, 1, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') +days = Unit(8640, Dimensions(0, 1, 0, 0, 0),name='days',ascii_symbol='day',symbol='day') +years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') +years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0),name='years',ascii_symbol='yr',symbol='yr') +degrees = Unit(57.29577951308232, Dimensions(0, 0, 0, 0, 0),name='degrees',ascii_symbol='deg',symbol='deg') +radians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='radians',ascii_symbol='rad',symbol='rad') +stradians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='stradians',ascii_symbol='sr',symbol='sr') +square_meters = Unit(1, Dimensions(2, 0, 0, 0, 0), name='square_meters', ascii_symbol='m^2', symbol='m²') +cubic_meters = Unit(1, Dimensions(3, 0, 0, 0, 0), name='cubic_meters', ascii_symbol='m^3', symbol='m³') +per_meter = Unit(1.0, Dimensions(-1, 0, 0, 0, 0), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') +per_square_meter = Unit(1.0, Dimensions(-2, 0, 0, 0, 0), name='per_square_meter', ascii_symbol='m^-2', symbol='m⁻²') +per_cubic_meter = Unit(1.0, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_meter', ascii_symbol='m^-3', symbol='m⁻³') +square_exameters = Unit(1e+36, Dimensions(2, 0, 0, 0, 0), name='square_exameters', ascii_symbol='Em^2', symbol='Em²') +cubic_exameters = Unit(1e+54, Dimensions(3, 0, 0, 0, 0), name='cubic_exameters', ascii_symbol='Em^3', symbol='Em³') +per_exameter = Unit(1e-18, Dimensions(-1, 0, 0, 0, 0), name='per_exameter', ascii_symbol='Em^-1', symbol='Em⁻¹') +per_square_exameter = Unit(1e-36, Dimensions(-2, 0, 0, 0, 0), name='per_square_exameter', ascii_symbol='Em^-2', symbol='Em⁻²') +per_cubic_exameter = Unit(1e-54, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_exameter', ascii_symbol='Em^-3', symbol='Em⁻³') +square_petameters = Unit(1e+30, Dimensions(2, 0, 0, 0, 0), name='square_petameters', ascii_symbol='Pm^2', symbol='Pm²') +cubic_petameters = Unit(1e+45, Dimensions(3, 0, 0, 0, 0), name='cubic_petameters', ascii_symbol='Pm^3', symbol='Pm³') +per_petameter = Unit(1e-15, Dimensions(-1, 0, 0, 0, 0), name='per_petameter', ascii_symbol='Pm^-1', symbol='Pm⁻¹') +per_square_petameter = Unit(1e-30, Dimensions(-2, 0, 0, 0, 0), name='per_square_petameter', ascii_symbol='Pm^-2', symbol='Pm⁻²') +per_cubic_petameter = Unit(1e-45, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_petameter', ascii_symbol='Pm^-3', symbol='Pm⁻³') +square_terameters = Unit(1e+24, Dimensions(2, 0, 0, 0, 0), name='square_terameters', ascii_symbol='Tm^2', symbol='Tm²') +cubic_terameters = Unit(1e+36, Dimensions(3, 0, 0, 0, 0), name='cubic_terameters', ascii_symbol='Tm^3', symbol='Tm³') +per_terameter = Unit(1e-12, Dimensions(-1, 0, 0, 0, 0), name='per_terameter', ascii_symbol='Tm^-1', symbol='Tm⁻¹') +per_square_terameter = Unit(1e-24, Dimensions(-2, 0, 0, 0, 0), name='per_square_terameter', ascii_symbol='Tm^-2', symbol='Tm⁻²') +per_cubic_terameter = Unit(1e-36, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_terameter', ascii_symbol='Tm^-3', symbol='Tm⁻³') +square_gigameters = Unit(1e+18, Dimensions(2, 0, 0, 0, 0), name='square_gigameters', ascii_symbol='Gm^2', symbol='Gm²') +cubic_gigameters = Unit(1e+27, Dimensions(3, 0, 0, 0, 0), name='cubic_gigameters', ascii_symbol='Gm^3', symbol='Gm³') +per_gigameter = Unit(1e-09, Dimensions(-1, 0, 0, 0, 0), name='per_gigameter', ascii_symbol='Gm^-1', symbol='Gm⁻¹') +per_square_gigameter = Unit(1e-18, Dimensions(-2, 0, 0, 0, 0), name='per_square_gigameter', ascii_symbol='Gm^-2', symbol='Gm⁻²') +per_cubic_gigameter = Unit(1e-27, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_gigameter', ascii_symbol='Gm^-3', symbol='Gm⁻³') +square_megameters = Unit(1000000000000.0, Dimensions(2, 0, 0, 0, 0), name='square_megameters', ascii_symbol='Mm^2', symbol='Mm²') +cubic_megameters = Unit(1e+18, Dimensions(3, 0, 0, 0, 0), name='cubic_megameters', ascii_symbol='Mm^3', symbol='Mm³') +per_megameter = Unit(1e-06, Dimensions(-1, 0, 0, 0, 0), name='per_megameter', ascii_symbol='Mm^-1', symbol='Mm⁻¹') +per_square_megameter = Unit(1e-12, Dimensions(-2, 0, 0, 0, 0), name='per_square_megameter', ascii_symbol='Mm^-2', symbol='Mm⁻²') +per_cubic_megameter = Unit(1e-18, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_megameter', ascii_symbol='Mm^-3', symbol='Mm⁻³') +square_kilometers = Unit(1000000.0, Dimensions(2, 0, 0, 0, 0), name='square_kilometers', ascii_symbol='km^2', symbol='km²') +cubic_kilometers = Unit(1000000000.0, Dimensions(3, 0, 0, 0, 0), name='cubic_kilometers', ascii_symbol='km^3', symbol='km³') +per_kilometer = Unit(0.001, Dimensions(-1, 0, 0, 0, 0), name='per_kilometer', ascii_symbol='km^-1', symbol='km⁻¹') +per_square_kilometer = Unit(1e-06, Dimensions(-2, 0, 0, 0, 0), name='per_square_kilometer', ascii_symbol='km^-2', symbol='km⁻²') +per_cubic_kilometer = Unit(1e-09, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_kilometer', ascii_symbol='km^-3', symbol='km⁻³') +square_millimeters = Unit(1e-06, Dimensions(2, 0, 0, 0, 0), name='square_millimeters', ascii_symbol='mm^2', symbol='mm²') +cubic_millimeters = Unit(1e-09, Dimensions(3, 0, 0, 0, 0), name='cubic_millimeters', ascii_symbol='mm^3', symbol='mm³') +per_millimeter = Unit(1000.0, Dimensions(-1, 0, 0, 0, 0), name='per_millimeter', ascii_symbol='mm^-1', symbol='mm⁻¹') +per_square_millimeter = Unit(1000000.0, Dimensions(-2, 0, 0, 0, 0), name='per_square_millimeter', ascii_symbol='mm^-2', symbol='mm⁻²') +per_cubic_millimeter = Unit(999999999.9999999, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_millimeter', ascii_symbol='mm^-3', symbol='mm⁻³') +square_micrometers = Unit(1e-12, Dimensions(2, 0, 0, 0, 0), name='square_micrometers', ascii_symbol='um^2', symbol='µm²') +cubic_micrometers = Unit(9.999999999999999e-19, Dimensions(3, 0, 0, 0, 0), name='cubic_micrometers', ascii_symbol='um^3', symbol='µm³') +per_micrometer = Unit(1000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_micrometer', ascii_symbol='um^-1', symbol='µm⁻¹') +per_square_micrometer = Unit(1000000000000.0001, Dimensions(-2, 0, 0, 0, 0), name='per_square_micrometer', ascii_symbol='um^-2', symbol='µm⁻²') +per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_micrometer', ascii_symbol='um^-3', symbol='µm⁻³') +square_nanometers = Unit(1e-18, Dimensions(2, 0, 0, 0, 0), name='square_nanometers', ascii_symbol='nm^2', symbol='nm²') +cubic_nanometers = Unit(1.0000000000000002e-27, Dimensions(3, 0, 0, 0, 0), name='cubic_nanometers', ascii_symbol='nm^3', symbol='nm³') +per_nanometer = Unit(999999999.9999999, Dimensions(-1, 0, 0, 0, 0), name='per_nanometer', ascii_symbol='nm^-1', symbol='nm⁻¹') +per_square_nanometer = Unit(9.999999999999999e+17, Dimensions(-2, 0, 0, 0, 0), name='per_square_nanometer', ascii_symbol='nm^-2', symbol='nm⁻²') +per_cubic_nanometer = Unit(9.999999999999999e+26, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_nanometer', ascii_symbol='nm^-3', symbol='nm⁻³') +square_picometers = Unit(1e-24, Dimensions(2, 0, 0, 0, 0), name='square_picometers', ascii_symbol='pm^2', symbol='pm²') +cubic_picometers = Unit(1e-36, Dimensions(3, 0, 0, 0, 0), name='cubic_picometers', ascii_symbol='pm^3', symbol='pm³') +per_picometer = Unit(1000000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_picometer', ascii_symbol='pm^-1', symbol='pm⁻¹') +per_square_picometer = Unit(1e+24, Dimensions(-2, 0, 0, 0, 0), name='per_square_picometer', ascii_symbol='pm^-2', symbol='pm⁻²') +per_cubic_picometer = Unit(1e+36, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_picometer', ascii_symbol='pm^-3', symbol='pm⁻³') +square_femtometers = Unit(1e-30, Dimensions(2, 0, 0, 0, 0), name='square_femtometers', ascii_symbol='fm^2', symbol='fm²') +cubic_femtometers = Unit(1.0000000000000003e-45, Dimensions(3, 0, 0, 0, 0), name='cubic_femtometers', ascii_symbol='fm^3', symbol='fm³') +per_femtometer = Unit(999999999999999.9, Dimensions(-1, 0, 0, 0, 0), name='per_femtometer', ascii_symbol='fm^-1', symbol='fm⁻¹') +per_square_femtometer = Unit(9.999999999999999e+29, Dimensions(-2, 0, 0, 0, 0), name='per_square_femtometer', ascii_symbol='fm^-2', symbol='fm⁻²') +per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_femtometer', ascii_symbol='fm^-3', symbol='fm⁻³') +square_attometers = Unit(1.0000000000000001e-36, Dimensions(2, 0, 0, 0, 0), name='square_attometers', ascii_symbol='am^2', symbol='am²') +cubic_attometers = Unit(1.0000000000000002e-54, Dimensions(3, 0, 0, 0, 0), name='cubic_attometers', ascii_symbol='am^3', symbol='am³') +per_attometer = Unit(9.999999999999999e+17, Dimensions(-1, 0, 0, 0, 0), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') +per_square_attometer = Unit(9.999999999999999e+35, Dimensions(-2, 0, 0, 0, 0), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') +per_cubic_attometer = Unit(9.999999999999997e+53, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') +square_angstroms = Unit(1.0000000000000001e-20, Dimensions(2, 0, 0, 0, 0), name='square_angstroms', ascii_symbol='A^2', symbol='Ų') +cubic_angstroms = Unit(1e-30, Dimensions(3, 0, 0, 0, 0), name='cubic_angstroms', ascii_symbol='A^3', symbol='ų') +per_angstrom = Unit(10000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_angstrom', ascii_symbol='A^-1', symbol='Å⁻¹') +per_square_angstrom = Unit(1e+20, Dimensions(-2, 0, 0, 0, 0), name='per_square_angstrom', ascii_symbol='A^-2', symbol='Å⁻²') +per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_angstrom', ascii_symbol='A^-3', symbol='Å⁻³') +square_angstroms = Unit(1.0000000000000001e-20, Dimensions(2, 0, 0, 0, 0), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ang²') +cubic_angstroms = Unit(1e-30, Dimensions(3, 0, 0, 0, 0), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='Ang³') +per_angstrom = Unit(10000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Ang⁻¹') +per_square_angstrom = Unit(1e+20, Dimensions(-2, 0, 0, 0, 0), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Ang⁻²') +per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Ang⁻³') +meters_per_second = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') +meters_per_square_second = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') +meters_per_millisecond = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') +meters_per_square_millisecond = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') +meters_per_microsecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') +meters_per_square_microsecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') +meters_per_nanosecond = Unit(999999999.9999999, Dimensions(1, -1, 0, 0, 0), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') +meters_per_square_nanosecond = Unit(999999999.9999999, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') +meters_per_picosecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') +meters_per_square_picosecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') +meters_per_femtosecond = Unit(999999999999999.9, Dimensions(1, -1, 0, 0, 0), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') +meters_per_square_femtosecond = Unit(999999999999999.9, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') +meters_per_attosecond = Unit(9.999999999999999e+17, Dimensions(1, -1, 0, 0, 0), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') +meters_per_square_attosecond = Unit(9.999999999999999e+17, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') +meters_per_minute = Unit(0.016666666666666666, Dimensions(1, -1, 0, 0, 0), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') +meters_per_square_minute = Unit(0.016666666666666666, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') +meters_per_hour = Unit(0.002777777777777778, Dimensions(1, -1, 0, 0, 0), name='meters_per_hour', ascii_symbol='m/hr', symbol='NoneNone⁻¹') +meters_per_square_hour = Unit(0.002777777777777778, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_hour', ascii_symbol='m/hr^2', symbol='NoneNone⁻²') +meters_per_day = Unit(0.00011574074074074075, Dimensions(1, -1, 0, 0, 0), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') +meters_per_square_day = Unit(0.00011574074074074075, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') +meters_per_day = Unit(0.00011574074074074075, Dimensions(1, -1, 0, 0, 0), name='meters_per_day', ascii_symbol='m/day', symbol='NoneNone⁻¹') +meters_per_square_day = Unit(0.00011574074074074075, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_day', ascii_symbol='m/day^2', symbol='NoneNone⁻²') +meters_per_year = Unit(3.168873850681143e-07, Dimensions(1, -1, 0, 0, 0), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') +meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') +meters_per_year = Unit(3.168873850681143e-07, Dimensions(1, -1, 0, 0, 0), name='meters_per_year', ascii_symbol='m/yr', symbol='NoneNone⁻¹') +meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_year', ascii_symbol='m/yr^2', symbol='NoneNone⁻²') +exameters_per_second = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') +exameters_per_square_second = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') +exameters_per_millisecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') +exameters_per_square_millisecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') +exameters_per_microsecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') +exameters_per_square_microsecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') +exameters_per_nanosecond = Unit(9.999999999999999e+26, Dimensions(1, -1, 0, 0, 0), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') +exameters_per_square_nanosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') +exameters_per_picosecond = Unit(1e+30, Dimensions(1, -1, 0, 0, 0), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') +exameters_per_square_picosecond = Unit(1e+30, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') +exameters_per_femtosecond = Unit(1e+33, Dimensions(1, -1, 0, 0, 0), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') +exameters_per_square_femtosecond = Unit(1e+33, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') +exameters_per_attosecond = Unit(9.999999999999999e+35, Dimensions(1, -1, 0, 0, 0), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') +exameters_per_square_attosecond = Unit(9.999999999999999e+35, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') +exameters_per_minute = Unit(1.6666666666666666e+16, Dimensions(1, -1, 0, 0, 0), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') +exameters_per_square_minute = Unit(1.6666666666666666e+16, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') +exameters_per_hour = Unit(2777777777777778.0, Dimensions(1, -1, 0, 0, 0), name='exameters_per_hour', ascii_symbol='Em/hr', symbol='EmNone⁻¹') +exameters_per_square_hour = Unit(2777777777777778.0, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_hour', ascii_symbol='Em/hr^2', symbol='EmNone⁻²') +exameters_per_day = Unit(115740740740740.73, Dimensions(1, -1, 0, 0, 0), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') +exameters_per_square_day = Unit(115740740740740.73, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') +exameters_per_day = Unit(115740740740740.73, Dimensions(1, -1, 0, 0, 0), name='exameters_per_day', ascii_symbol='Em/day', symbol='EmNone⁻¹') +exameters_per_square_day = Unit(115740740740740.73, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_day', ascii_symbol='Em/day^2', symbol='EmNone⁻²') +exameters_per_year = Unit(316887385068.1143, Dimensions(1, -1, 0, 0, 0), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') +exameters_per_square_year = Unit(316887385068.1143, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') +exameters_per_year = Unit(316887385068.1143, Dimensions(1, -1, 0, 0, 0), name='exameters_per_year', ascii_symbol='Em/yr', symbol='EmNone⁻¹') +exameters_per_square_year = Unit(316887385068.1143, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_year', ascii_symbol='Em/yr^2', symbol='EmNone⁻²') +petameters_per_second = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') +petameters_per_square_second = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') +petameters_per_millisecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') +petameters_per_square_millisecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') +petameters_per_microsecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') +petameters_per_square_microsecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') +petameters_per_nanosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') +petameters_per_square_nanosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') +petameters_per_picosecond = Unit(1e+27, Dimensions(1, -1, 0, 0, 0), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') +petameters_per_square_picosecond = Unit(1e+27, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') +petameters_per_femtosecond = Unit(9.999999999999999e+29, Dimensions(1, -1, 0, 0, 0), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') +petameters_per_square_femtosecond = Unit(9.999999999999999e+29, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') +petameters_per_attosecond = Unit(1e+33, Dimensions(1, -1, 0, 0, 0), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') +petameters_per_square_attosecond = Unit(1e+33, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') +petameters_per_minute = Unit(16666666666666.666, Dimensions(1, -1, 0, 0, 0), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') +petameters_per_square_minute = Unit(16666666666666.666, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') +petameters_per_hour = Unit(2777777777777.778, Dimensions(1, -1, 0, 0, 0), name='petameters_per_hour', ascii_symbol='Pm/hr', symbol='PmNone⁻¹') +petameters_per_square_hour = Unit(2777777777777.778, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_hour', ascii_symbol='Pm/hr^2', symbol='PmNone⁻²') +petameters_per_day = Unit(115740740740.74074, Dimensions(1, -1, 0, 0, 0), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') +petameters_per_square_day = Unit(115740740740.74074, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') +petameters_per_day = Unit(115740740740.74074, Dimensions(1, -1, 0, 0, 0), name='petameters_per_day', ascii_symbol='Pm/day', symbol='PmNone⁻¹') +petameters_per_square_day = Unit(115740740740.74074, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_day', ascii_symbol='Pm/day^2', symbol='PmNone⁻²') +petameters_per_year = Unit(316887385.0681143, Dimensions(1, -1, 0, 0, 0), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') +petameters_per_square_year = Unit(316887385.0681143, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') +petameters_per_year = Unit(316887385.0681143, Dimensions(1, -1, 0, 0, 0), name='petameters_per_year', ascii_symbol='Pm/yr', symbol='PmNone⁻¹') +petameters_per_square_year = Unit(316887385.0681143, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_year', ascii_symbol='Pm/yr^2', symbol='PmNone⁻²') +terameters_per_second = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') +terameters_per_square_second = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') +terameters_per_millisecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') +terameters_per_square_millisecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') +terameters_per_microsecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') +terameters_per_square_microsecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') +terameters_per_nanosecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') +terameters_per_square_nanosecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') +terameters_per_picosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') +terameters_per_square_picosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') +terameters_per_femtosecond = Unit(9.999999999999999e+26, Dimensions(1, -1, 0, 0, 0), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') +terameters_per_square_femtosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') +terameters_per_attosecond = Unit(9.999999999999999e+29, Dimensions(1, -1, 0, 0, 0), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') +terameters_per_square_attosecond = Unit(9.999999999999999e+29, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') +terameters_per_minute = Unit(16666666666.666666, Dimensions(1, -1, 0, 0, 0), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') +terameters_per_square_minute = Unit(16666666666.666666, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') +terameters_per_hour = Unit(2777777777.7777777, Dimensions(1, -1, 0, 0, 0), name='terameters_per_hour', ascii_symbol='Tm/hr', symbol='TmNone⁻¹') +terameters_per_square_hour = Unit(2777777777.7777777, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_hour', ascii_symbol='Tm/hr^2', symbol='TmNone⁻²') +terameters_per_day = Unit(115740740.74074075, Dimensions(1, -1, 0, 0, 0), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') +terameters_per_square_day = Unit(115740740.74074075, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') +terameters_per_day = Unit(115740740.74074075, Dimensions(1, -1, 0, 0, 0), name='terameters_per_day', ascii_symbol='Tm/day', symbol='TmNone⁻¹') +terameters_per_square_day = Unit(115740740.74074075, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_day', ascii_symbol='Tm/day^2', symbol='TmNone⁻²') +terameters_per_year = Unit(316887.38506811426, Dimensions(1, -1, 0, 0, 0), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') +terameters_per_square_year = Unit(316887.38506811426, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') +terameters_per_year = Unit(316887.38506811426, Dimensions(1, -1, 0, 0, 0), name='terameters_per_year', ascii_symbol='Tm/yr', symbol='TmNone⁻¹') +terameters_per_square_year = Unit(316887.38506811426, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_year', ascii_symbol='Tm/yr^2', symbol='TmNone⁻²') +gigameters_per_second = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') +gigameters_per_square_second = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') +gigameters_per_millisecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') +gigameters_per_square_millisecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') +gigameters_per_microsecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') +gigameters_per_square_microsecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') +gigameters_per_nanosecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') +gigameters_per_square_nanosecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') +gigameters_per_picosecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') +gigameters_per_square_picosecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') +gigameters_per_femtosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') +gigameters_per_square_femtosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') +gigameters_per_attosecond = Unit(9.999999999999999e+26, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') +gigameters_per_square_attosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') +gigameters_per_minute = Unit(16666666.666666666, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') +gigameters_per_square_minute = Unit(16666666.666666666, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') +gigameters_per_hour = Unit(2777777.777777778, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_hour', ascii_symbol='Gm/hr', symbol='GmNone⁻¹') +gigameters_per_square_hour = Unit(2777777.777777778, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_hour', ascii_symbol='Gm/hr^2', symbol='GmNone⁻²') +gigameters_per_day = Unit(115740.74074074074, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') +gigameters_per_square_day = Unit(115740.74074074074, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') +gigameters_per_day = Unit(115740.74074074074, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_day', ascii_symbol='Gm/day', symbol='GmNone⁻¹') +gigameters_per_square_day = Unit(115740.74074074074, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_day', ascii_symbol='Gm/day^2', symbol='GmNone⁻²') +gigameters_per_year = Unit(316.88738506811427, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') +gigameters_per_square_year = Unit(316.88738506811427, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') +gigameters_per_year = Unit(316.88738506811427, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_year', ascii_symbol='Gm/yr', symbol='GmNone⁻¹') +gigameters_per_square_year = Unit(316.88738506811427, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_year', ascii_symbol='Gm/yr^2', symbol='GmNone⁻²') +megameters_per_second = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') +megameters_per_square_second = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') +megameters_per_millisecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') +megameters_per_square_millisecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') +megameters_per_microsecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') +megameters_per_square_microsecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') +megameters_per_nanosecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') +megameters_per_square_nanosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') +megameters_per_picosecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') +megameters_per_square_picosecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') +megameters_per_femtosecond = Unit(9.999999999999999e+20, Dimensions(1, -1, 0, 0, 0), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') +megameters_per_square_femtosecond = Unit(9.999999999999999e+20, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') +megameters_per_attosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') +megameters_per_square_attosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') +megameters_per_minute = Unit(16666.666666666668, Dimensions(1, -1, 0, 0, 0), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') +megameters_per_square_minute = Unit(16666.666666666668, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') +megameters_per_hour = Unit(2777.777777777778, Dimensions(1, -1, 0, 0, 0), name='megameters_per_hour', ascii_symbol='Mm/hr', symbol='MmNone⁻¹') +megameters_per_square_hour = Unit(2777.777777777778, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_hour', ascii_symbol='Mm/hr^2', symbol='MmNone⁻²') +megameters_per_day = Unit(115.74074074074075, Dimensions(1, -1, 0, 0, 0), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') +megameters_per_square_day = Unit(115.74074074074075, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') +megameters_per_day = Unit(115.74074074074075, Dimensions(1, -1, 0, 0, 0), name='megameters_per_day', ascii_symbol='Mm/day', symbol='MmNone⁻¹') +megameters_per_square_day = Unit(115.74074074074075, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_day', ascii_symbol='Mm/day^2', symbol='MmNone⁻²') +megameters_per_year = Unit(0.3168873850681143, Dimensions(1, -1, 0, 0, 0), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') +megameters_per_square_year = Unit(0.3168873850681143, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') +megameters_per_year = Unit(0.3168873850681143, Dimensions(1, -1, 0, 0, 0), name='megameters_per_year', ascii_symbol='Mm/yr', symbol='MmNone⁻¹') +megameters_per_square_year = Unit(0.3168873850681143, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_year', ascii_symbol='Mm/yr^2', symbol='MmNone⁻²') +kilometers_per_second = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') +kilometers_per_square_second = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') +kilometers_per_millisecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') +kilometers_per_square_millisecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') +kilometers_per_microsecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') +kilometers_per_square_microsecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') +kilometers_per_nanosecond = Unit(999999999999.9999, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') +kilometers_per_square_nanosecond = Unit(999999999999.9999, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') +kilometers_per_picosecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') +kilometers_per_square_picosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') +kilometers_per_femtosecond = Unit(9.999999999999999e+17, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') +kilometers_per_square_femtosecond = Unit(9.999999999999999e+17, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') +kilometers_per_attosecond = Unit(9.999999999999999e+20, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') +kilometers_per_square_attosecond = Unit(9.999999999999999e+20, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') +kilometers_per_minute = Unit(16.666666666666668, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') +kilometers_per_square_minute = Unit(16.666666666666668, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') +kilometers_per_hour = Unit(2.7777777777777777, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_hour', ascii_symbol='km/hr', symbol='kmNone⁻¹') +kilometers_per_square_hour = Unit(2.7777777777777777, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_hour', ascii_symbol='km/hr^2', symbol='kmNone⁻²') +kilometers_per_day = Unit(0.11574074074074074, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') +kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') +kilometers_per_day = Unit(0.11574074074074074, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_day', ascii_symbol='km/day', symbol='kmNone⁻¹') +kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_day', ascii_symbol='km/day^2', symbol='kmNone⁻²') +kilometers_per_year = Unit(0.0003168873850681143, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') +kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') +kilometers_per_year = Unit(0.0003168873850681143, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_year', ascii_symbol='km/yr', symbol='kmNone⁻¹') +kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_year', ascii_symbol='km/yr^2', symbol='kmNone⁻²') +millimeters_per_second = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') +millimeters_per_square_second = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') +millimeters_per_millisecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') +millimeters_per_square_millisecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') +millimeters_per_microsecond = Unit(1000.0000000000001, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') +millimeters_per_square_microsecond = Unit(1000.0000000000001, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') +millimeters_per_nanosecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') +millimeters_per_square_nanosecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') +millimeters_per_picosecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') +millimeters_per_square_picosecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') +millimeters_per_femtosecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') +millimeters_per_square_femtosecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') +millimeters_per_attosecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') +millimeters_per_square_attosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') +millimeters_per_minute = Unit(1.6666666666666667e-05, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') +millimeters_per_square_minute = Unit(1.6666666666666667e-05, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') +millimeters_per_hour = Unit(2.777777777777778e-06, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_hour', ascii_symbol='mm/hr', symbol='mmNone⁻¹') +millimeters_per_square_hour = Unit(2.777777777777778e-06, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_hour', ascii_symbol='mm/hr^2', symbol='mmNone⁻²') +millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') +millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') +millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_day', ascii_symbol='mm/day', symbol='mmNone⁻¹') +millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_day', ascii_symbol='mm/day^2', symbol='mmNone⁻²') +millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') +millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') +millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_year', ascii_symbol='mm/yr', symbol='mmNone⁻¹') +millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_year', ascii_symbol='mm/yr^2', symbol='mmNone⁻²') +micrometers_per_second = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') +micrometers_per_square_second = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') +micrometers_per_millisecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') +micrometers_per_square_millisecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') +micrometers_per_microsecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') +micrometers_per_square_microsecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') +micrometers_per_nanosecond = Unit(999.9999999999999, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') +micrometers_per_square_nanosecond = Unit(999.9999999999999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') +micrometers_per_picosecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') +micrometers_per_square_picosecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') +micrometers_per_femtosecond = Unit(999999999.9999999, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') +micrometers_per_square_femtosecond = Unit(999999999.9999999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') +micrometers_per_attosecond = Unit(999999999999.9999, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') +micrometers_per_square_attosecond = Unit(999999999999.9999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') +micrometers_per_minute = Unit(1.6666666666666667e-08, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') +micrometers_per_square_minute = Unit(1.6666666666666667e-08, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') +micrometers_per_hour = Unit(2.7777777777777776e-09, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_hour', ascii_symbol='um/hr', symbol='µmNone⁻¹') +micrometers_per_square_hour = Unit(2.7777777777777776e-09, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_hour', ascii_symbol='um/hr^2', symbol='µmNone⁻²') +micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') +micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') +micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_day', ascii_symbol='um/day', symbol='µmNone⁻¹') +micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_day', ascii_symbol='um/day^2', symbol='µmNone⁻²') +micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') +micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') +micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_year', ascii_symbol='um/yr', symbol='µmNone⁻¹') +micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_year', ascii_symbol='um/yr^2', symbol='µmNone⁻²') +nanometers_per_second = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') +nanometers_per_square_second = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') +nanometers_per_millisecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') +nanometers_per_square_millisecond = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') +nanometers_per_microsecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') +nanometers_per_square_microsecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') +nanometers_per_nanosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') +nanometers_per_square_nanosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') +nanometers_per_picosecond = Unit(1000.0000000000001, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') +nanometers_per_square_picosecond = Unit(1000.0000000000001, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') +nanometers_per_femtosecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') +nanometers_per_square_femtosecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') +nanometers_per_attosecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') +nanometers_per_square_attosecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') +nanometers_per_minute = Unit(1.6666666666666667e-11, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') +nanometers_per_square_minute = Unit(1.6666666666666667e-11, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') +nanometers_per_hour = Unit(2.777777777777778e-12, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_hour', ascii_symbol='nm/hr', symbol='nmNone⁻¹') +nanometers_per_square_hour = Unit(2.777777777777778e-12, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_hour', ascii_symbol='nm/hr^2', symbol='nmNone⁻²') +nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') +nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') +nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_day', ascii_symbol='nm/day', symbol='nmNone⁻¹') +nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_day', ascii_symbol='nm/day^2', symbol='nmNone⁻²') +nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') +nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') +nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_year', ascii_symbol='nm/yr', symbol='nmNone⁻¹') +nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_year', ascii_symbol='nm/yr^2', symbol='nmNone⁻²') +picometers_per_second = Unit(1e-12, Dimensions(1, -1, 0, 0, 0), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') +picometers_per_square_second = Unit(1e-12, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') +picometers_per_millisecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') +picometers_per_square_millisecond = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') +picometers_per_microsecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') +picometers_per_square_microsecond = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') +picometers_per_nanosecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') +picometers_per_square_nanosecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') +picometers_per_picosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') +picometers_per_square_picosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') +picometers_per_femtosecond = Unit(999.9999999999999, Dimensions(1, -1, 0, 0, 0), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') +picometers_per_square_femtosecond = Unit(999.9999999999999, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') +picometers_per_attosecond = Unit(999999.9999999999, Dimensions(1, -1, 0, 0, 0), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') +picometers_per_square_attosecond = Unit(999999.9999999999, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') +picometers_per_minute = Unit(1.6666666666666667e-14, Dimensions(1, -1, 0, 0, 0), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') +picometers_per_square_minute = Unit(1.6666666666666667e-14, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') +picometers_per_hour = Unit(2.7777777777777776e-15, Dimensions(1, -1, 0, 0, 0), name='picometers_per_hour', ascii_symbol='pm/hr', symbol='pmNone⁻¹') +picometers_per_square_hour = Unit(2.7777777777777776e-15, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_hour', ascii_symbol='pm/hr^2', symbol='pmNone⁻²') +picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(1, -1, 0, 0, 0), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') +picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') +picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(1, -1, 0, 0, 0), name='picometers_per_day', ascii_symbol='pm/day', symbol='pmNone⁻¹') +picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_day', ascii_symbol='pm/day^2', symbol='pmNone⁻²') +picometers_per_year = Unit(3.168873850681143e-19, Dimensions(1, -1, 0, 0, 0), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') +picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') +picometers_per_year = Unit(3.168873850681143e-19, Dimensions(1, -1, 0, 0, 0), name='picometers_per_year', ascii_symbol='pm/yr', symbol='pmNone⁻¹') +picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_year', ascii_symbol='pm/yr^2', symbol='pmNone⁻²') +femtometers_per_second = Unit(1e-15, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') +femtometers_per_square_second = Unit(1e-15, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') +femtometers_per_millisecond = Unit(1e-12, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') +femtometers_per_square_millisecond = Unit(1e-12, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') +femtometers_per_microsecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') +femtometers_per_square_microsecond = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') +femtometers_per_nanosecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') +femtometers_per_square_nanosecond = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') +femtometers_per_picosecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') +femtometers_per_square_picosecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') +femtometers_per_femtosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') +femtometers_per_square_femtosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') +femtometers_per_attosecond = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') +femtometers_per_square_attosecond = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') +femtometers_per_minute = Unit(1.6666666666666667e-17, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') +femtometers_per_square_minute = Unit(1.6666666666666667e-17, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') +femtometers_per_hour = Unit(2.777777777777778e-18, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_hour', ascii_symbol='fm/hr', symbol='fmNone⁻¹') +femtometers_per_square_hour = Unit(2.777777777777778e-18, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_hour', ascii_symbol='fm/hr^2', symbol='fmNone⁻²') +femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') +femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') +femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_day', ascii_symbol='fm/day', symbol='fmNone⁻¹') +femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_day', ascii_symbol='fm/day^2', symbol='fmNone⁻²') +femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') +femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') +femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_year', ascii_symbol='fm/yr', symbol='fmNone⁻¹') +femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_year', ascii_symbol='fm/yr^2', symbol='fmNone⁻²') +attometers_per_second = Unit(1e-18, Dimensions(1, -1, 0, 0, 0), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') +attometers_per_square_second = Unit(1e-18, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') +attometers_per_millisecond = Unit(1e-15, Dimensions(1, -1, 0, 0, 0), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') +attometers_per_square_millisecond = Unit(1e-15, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') +attometers_per_microsecond = Unit(1.0000000000000002e-12, Dimensions(1, -1, 0, 0, 0), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') +attometers_per_square_microsecond = Unit(1.0000000000000002e-12, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') +attometers_per_nanosecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') +attometers_per_square_nanosecond = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') +attometers_per_picosecond = Unit(1.0000000000000002e-06, Dimensions(1, -1, 0, 0, 0), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') +attometers_per_square_picosecond = Unit(1.0000000000000002e-06, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') +attometers_per_femtosecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') +attometers_per_square_femtosecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') +attometers_per_attosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') +attometers_per_square_attosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') +attometers_per_minute = Unit(1.6666666666666668e-20, Dimensions(1, -1, 0, 0, 0), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') +attometers_per_square_minute = Unit(1.6666666666666668e-20, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') +attometers_per_hour = Unit(2.7777777777777778e-21, Dimensions(1, -1, 0, 0, 0), name='attometers_per_hour', ascii_symbol='am/hr', symbol='amNone⁻¹') +attometers_per_square_hour = Unit(2.7777777777777778e-21, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_hour', ascii_symbol='am/hr^2', symbol='amNone⁻²') +attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(1, -1, 0, 0, 0), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') +attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') +attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(1, -1, 0, 0, 0), name='attometers_per_day', ascii_symbol='am/day', symbol='amNone⁻¹') +attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_day', ascii_symbol='am/day^2', symbol='amNone⁻²') +attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(1, -1, 0, 0, 0), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') +attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') +attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(1, -1, 0, 0, 0), name='attometers_per_year', ascii_symbol='am/yr', symbol='amNone⁻¹') +attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_year', ascii_symbol='am/yr^2', symbol='amNone⁻²') +angstroms_per_second = Unit(1e-10, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_second', ascii_symbol='A/s', symbol='ÅNone⁻¹') +angstroms_per_square_second = Unit(1e-10, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_second', ascii_symbol='A/s^2', symbol='ÅNone⁻²') +angstroms_per_millisecond = Unit(1e-07, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_millisecond', ascii_symbol='A/ms', symbol='Åms⁻¹') +angstroms_per_square_millisecond = Unit(1e-07, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_millisecond', ascii_symbol='A/ms^2', symbol='Åms⁻²') +angstroms_per_microsecond = Unit(0.0001, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_microsecond', ascii_symbol='A/us', symbol='ŵs⁻¹') +angstroms_per_square_microsecond = Unit(0.0001, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_microsecond', ascii_symbol='A/us^2', symbol='ŵs⁻²') +angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_nanosecond', ascii_symbol='A/ns', symbol='Åns⁻¹') +angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_nanosecond', ascii_symbol='A/ns^2', symbol='Åns⁻²') +angstroms_per_picosecond = Unit(100.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_picosecond', ascii_symbol='A/ps', symbol='Åps⁻¹') +angstroms_per_square_picosecond = Unit(100.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_picosecond', ascii_symbol='A/ps^2', symbol='Åps⁻²') +angstroms_per_femtosecond = Unit(100000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_femtosecond', ascii_symbol='A/fs', symbol='Åfs⁻¹') +angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_femtosecond', ascii_symbol='A/fs^2', symbol='Åfs⁻²') +angstroms_per_attosecond = Unit(100000000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_attosecond', ascii_symbol='A/as', symbol='Åas⁻¹') +angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_attosecond', ascii_symbol='A/as^2', symbol='Åas⁻²') +angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_minute', ascii_symbol='A/min', symbol='ÅNone⁻¹') +angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_minute', ascii_symbol='A/min^2', symbol='ÅNone⁻²') +angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_hour', ascii_symbol='A/hr', symbol='ÅNone⁻¹') +angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_hour', ascii_symbol='A/hr^2', symbol='ÅNone⁻²') +angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='A/d', symbol='ÅNone⁻¹') +angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='A/d^2', symbol='ÅNone⁻²') +angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='A/day', symbol='ÅNone⁻¹') +angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='A/day^2', symbol='ÅNone⁻²') +angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='A/y', symbol='ÅNone⁻¹') +angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='A/y^2', symbol='ÅNone⁻²') +angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='A/yr', symbol='ÅNone⁻¹') +angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='A/yr^2', symbol='ÅNone⁻²') +angstroms_per_second = Unit(1e-10, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='NoneNone⁻¹') +angstroms_per_square_second = Unit(1e-10, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='NoneNone⁻²') +angstroms_per_millisecond = Unit(1e-07, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Nonems⁻¹') +angstroms_per_square_millisecond = Unit(1e-07, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Nonems⁻²') +angstroms_per_microsecond = Unit(0.0001, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='Noneµs⁻¹') +angstroms_per_square_microsecond = Unit(0.0001, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='Noneµs⁻²') +angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Nonens⁻¹') +angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Nonens⁻²') +angstroms_per_picosecond = Unit(100.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Noneps⁻¹') +angstroms_per_square_picosecond = Unit(100.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Noneps⁻²') +angstroms_per_femtosecond = Unit(100000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Nonefs⁻¹') +angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Nonefs⁻²') +angstroms_per_attosecond = Unit(100000000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Noneas⁻¹') +angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Noneas⁻²') +angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='NoneNone⁻¹') +angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='NoneNone⁻²') +angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_hour', ascii_symbol='Ang/hr', symbol='NoneNone⁻¹') +angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_hour', ascii_symbol='Ang/hr^2', symbol='NoneNone⁻²') +angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='NoneNone⁻¹') +angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='NoneNone⁻²') +angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='Ang/day', symbol='NoneNone⁻¹') +angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='Ang/day^2', symbol='NoneNone⁻²') +angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='NoneNone⁻¹') +angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='NoneNone⁻²') +angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/yr', symbol='NoneNone⁻¹') +angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/yr^2', symbol='NoneNone⁻²') # # Lookup table from symbols to units @@ -1802,8 +913,6 @@ def __init__(self, name: str, units: list[NamedUnit]): "pm": picometers, "fm": femtometers, "am": attometers, - "dm": decimeters, - "cm": centimeters, "s": seconds, "ms": milliseconds, "us": microseconds, @@ -1827,19 +936,19 @@ def __init__(self, name: str, units: list[NamedUnit]): "fg": femtograms, "ag": attograms, "A": angstroms, - "EA": exaamperes, - "PA": petaamperes, - "TA": teraamperes, - "GA": gigaamperes, - "MA": megaamperes, - "kA": kiloamperes, - "mA": milliamperes, - "uA": microamperes, - "µA": microamperes, - "nA": nanoamperes, - "pA": picoamperes, - "fA": femtoamperes, - "aA": attoamperes, + "EA": exaamps, + "PA": petaamps, + "TA": teraamps, + "GA": gigaamps, + "MA": megaamps, + "kA": kiloamps, + "mA": milliamps, + "uA": microamps, + "µA": microamps, + "nA": nanoamps, + "pA": picoamps, + "fA": femtoamps, + "aA": attoamps, "K": kelvin, "EK": exakelvin, "PK": petakelvin, @@ -1924,7 +1033,7 @@ def __init__(self, name: str, units: list[NamedUnit]): "pW": picowatts, "fW": femtowatts, "aW": attowatts, - "C": kelvin, + "C": degrees_celsius, "EC": exacoulombs, "PC": petacoulombs, "TC": teracoulombs, @@ -2048,85 +1157,27 @@ def __init__(self, name: str, units: list[NamedUnit]): "pH": picohenry, "fH": femtohenry, "aH": attohenry, - "Ang": angstroms, "Å": angstroms, - "micron": microns, + "Ang": angstroms, "min": minutes, - "rpm": revolutions_per_minute, - "h": hours, + "hr": hours, "d": days, + "day": days, "y": years, + "yr": years, "deg": degrees, "rad": radians, - "rot": rotations, "sr": stradians, - "l": litres, - "eV": electronvolts, - "EeV": exaelectronvolts, - "PeV": petaelectronvolts, - "TeV": teraelectronvolts, - "GeV": gigaelectronvolts, - "MeV": megaelectronvolts, - "keV": kiloelectronvolts, - "meV": millielectronvolts, - "ueV": microelectronvolts, - "µeV": microelectronvolts, - "neV": nanoelectronvolts, - "peV": picoelectronvolts, - "feV": femtoelectronvolts, - "aeV": attoelectronvolts, - "au": atomic_mass_units, - "mol": moles, - "mmol": millimoles, - "umol": micromoles, - "µmol": micromoles, - "nmol": nanomoles, - "pmol": picomoles, - "fmol": femtomoles, - "amol": attomoles, - "kgForce": kg_force, - "miles": miles, - "yrd": yards, - "ft": feet, - "in": inches, - "lb": pounds, - "lbf": pounds_force, - "oz": ounces, - "psi": pounds_force_per_square_inch, - "percent": percent, - "%": percent, - "Amps": amperes, - "amps": amperes, - "Coulombs": degrees_celsius, - "coulombs": degrees_celsius, - "yr": years, - "year": years, - "day": days, - "hr": hours, - "hour": hours, - "amu": atomic_mass_units, - "degr": degrees, - "Deg": degrees, - "degree": degrees, - "degrees": degrees, - "Degrees": degrees, - "Counts": none, - "counts": none, - "cnts": none, - "Cnts": none, - "a.u.": none, - "fraction": none, - "Fraction": none, } # -# Units by type +# Units by type # length = UnitGroup( - name = 'length', + name = 'length', units = [ meters, exameters, @@ -2141,18 +1192,12 @@ def __init__(self, name: str, units: list[NamedUnit]): picometers, femtometers, attometers, - decimeters, - centimeters, angstroms, - microns, - miles, - yards, - feet, - inches, + angstroms, ]) area = UnitGroup( - name = 'area', + name = 'area', units = [ square_meters, square_exameters, @@ -2167,20 +1212,13 @@ def __init__(self, name: str, units: list[NamedUnit]): square_picometers, square_femtometers, square_attometers, - square_decimeters, - square_centimeters, square_angstroms, - square_microns, - square_miles, - square_yards, - square_feet, - square_inches, + square_angstroms, ]) volume = UnitGroup( - name = 'volume', + name = 'volume', units = [ - litres, cubic_meters, cubic_exameters, cubic_petameters, @@ -2194,18 +1232,12 @@ def __init__(self, name: str, units: list[NamedUnit]): cubic_picometers, cubic_femtometers, cubic_attometers, - cubic_decimeters, - cubic_centimeters, cubic_angstroms, - cubic_microns, - cubic_miles, - cubic_yards, - cubic_feet, - cubic_inches, + cubic_angstroms, ]) inverse_length = UnitGroup( - name = 'inverse_length', + name = 'inverse_length', units = [ per_meter, per_exameter, @@ -2220,18 +1252,12 @@ def __init__(self, name: str, units: list[NamedUnit]): per_picometer, per_femtometer, per_attometer, - per_decimeter, - per_centimeter, per_angstrom, - per_micron, - per_mile, - per_yard, - per_foot, - per_inch, + per_angstrom, ]) inverse_area = UnitGroup( - name = 'inverse_area', + name = 'inverse_area', units = [ per_square_meter, per_square_exameter, @@ -2246,18 +1272,12 @@ def __init__(self, name: str, units: list[NamedUnit]): per_square_picometer, per_square_femtometer, per_square_attometer, - per_square_decimeter, - per_square_centimeter, per_square_angstrom, - per_square_micron, - per_square_mile, - per_square_yard, - per_square_foot, - per_square_inch, + per_square_angstrom, ]) inverse_volume = UnitGroup( - name = 'inverse_volume', + name = 'inverse_volume', units = [ per_cubic_meter, per_cubic_exameter, @@ -2272,18 +1292,12 @@ def __init__(self, name: str, units: list[NamedUnit]): per_cubic_picometer, per_cubic_femtometer, per_cubic_attometer, - per_cubic_decimeter, - per_cubic_centimeter, per_cubic_angstrom, - per_cubic_micron, - per_cubic_mile, - per_cubic_yard, - per_cubic_foot, - per_cubic_inch, + per_cubic_angstrom, ]) time = UnitGroup( - name = 'time', + name = 'time', units = [ seconds, milliseconds, @@ -2295,11 +1309,13 @@ def __init__(self, name: str, units: list[NamedUnit]): minutes, hours, days, + days, + years, years, ]) rate = UnitGroup( - name = 'rate', + name = 'rate', units = [ hertz, exahertz, @@ -2318,7 +1334,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) speed = UnitGroup( - name = 'speed', + name = 'speed', units = [ meters_per_second, meters_per_millisecond, @@ -2330,6 +1346,8 @@ def __init__(self, name: str, units: list[NamedUnit]): meters_per_minute, meters_per_hour, meters_per_day, + meters_per_day, + meters_per_year, meters_per_year, exameters_per_second, exameters_per_millisecond, @@ -2341,6 +1359,8 @@ def __init__(self, name: str, units: list[NamedUnit]): exameters_per_minute, exameters_per_hour, exameters_per_day, + exameters_per_day, + exameters_per_year, exameters_per_year, petameters_per_second, petameters_per_millisecond, @@ -2352,6 +1372,8 @@ def __init__(self, name: str, units: list[NamedUnit]): petameters_per_minute, petameters_per_hour, petameters_per_day, + petameters_per_day, + petameters_per_year, petameters_per_year, terameters_per_second, terameters_per_millisecond, @@ -2363,6 +1385,8 @@ def __init__(self, name: str, units: list[NamedUnit]): terameters_per_minute, terameters_per_hour, terameters_per_day, + terameters_per_day, + terameters_per_year, terameters_per_year, gigameters_per_second, gigameters_per_millisecond, @@ -2374,6 +1398,8 @@ def __init__(self, name: str, units: list[NamedUnit]): gigameters_per_minute, gigameters_per_hour, gigameters_per_day, + gigameters_per_day, + gigameters_per_year, gigameters_per_year, megameters_per_second, megameters_per_millisecond, @@ -2385,6 +1411,8 @@ def __init__(self, name: str, units: list[NamedUnit]): megameters_per_minute, megameters_per_hour, megameters_per_day, + megameters_per_day, + megameters_per_year, megameters_per_year, kilometers_per_second, kilometers_per_millisecond, @@ -2396,6 +1424,8 @@ def __init__(self, name: str, units: list[NamedUnit]): kilometers_per_minute, kilometers_per_hour, kilometers_per_day, + kilometers_per_day, + kilometers_per_year, kilometers_per_year, millimeters_per_second, millimeters_per_millisecond, @@ -2407,6 +1437,8 @@ def __init__(self, name: str, units: list[NamedUnit]): millimeters_per_minute, millimeters_per_hour, millimeters_per_day, + millimeters_per_day, + millimeters_per_year, millimeters_per_year, micrometers_per_second, micrometers_per_millisecond, @@ -2418,6 +1450,8 @@ def __init__(self, name: str, units: list[NamedUnit]): micrometers_per_minute, micrometers_per_hour, micrometers_per_day, + micrometers_per_day, + micrometers_per_year, micrometers_per_year, nanometers_per_second, nanometers_per_millisecond, @@ -2429,6 +1463,8 @@ def __init__(self, name: str, units: list[NamedUnit]): nanometers_per_minute, nanometers_per_hour, nanometers_per_day, + nanometers_per_day, + nanometers_per_year, nanometers_per_year, picometers_per_second, picometers_per_millisecond, @@ -2440,6 +1476,8 @@ def __init__(self, name: str, units: list[NamedUnit]): picometers_per_minute, picometers_per_hour, picometers_per_day, + picometers_per_day, + picometers_per_year, picometers_per_year, femtometers_per_second, femtometers_per_millisecond, @@ -2451,6 +1489,8 @@ def __init__(self, name: str, units: list[NamedUnit]): femtometers_per_minute, femtometers_per_hour, femtometers_per_day, + femtometers_per_day, + femtometers_per_year, femtometers_per_year, attometers_per_second, attometers_per_millisecond, @@ -2462,29 +1502,9 @@ def __init__(self, name: str, units: list[NamedUnit]): attometers_per_minute, attometers_per_hour, attometers_per_day, + attometers_per_day, + attometers_per_year, attometers_per_year, - decimeters_per_second, - decimeters_per_millisecond, - decimeters_per_microsecond, - decimeters_per_nanosecond, - decimeters_per_picosecond, - decimeters_per_femtosecond, - decimeters_per_attosecond, - decimeters_per_minute, - decimeters_per_hour, - decimeters_per_day, - decimeters_per_year, - centimeters_per_second, - centimeters_per_millisecond, - centimeters_per_microsecond, - centimeters_per_nanosecond, - centimeters_per_picosecond, - centimeters_per_femtosecond, - centimeters_per_attosecond, - centimeters_per_minute, - centimeters_per_hour, - centimeters_per_day, - centimeters_per_year, angstroms_per_second, angstroms_per_millisecond, angstroms_per_microsecond, @@ -2495,66 +1515,26 @@ def __init__(self, name: str, units: list[NamedUnit]): angstroms_per_minute, angstroms_per_hour, angstroms_per_day, + angstroms_per_day, + angstroms_per_year, + angstroms_per_year, + angstroms_per_second, + angstroms_per_millisecond, + angstroms_per_microsecond, + angstroms_per_nanosecond, + angstroms_per_picosecond, + angstroms_per_femtosecond, + angstroms_per_attosecond, + angstroms_per_minute, + angstroms_per_hour, + angstroms_per_day, + angstroms_per_day, + angstroms_per_year, angstroms_per_year, - microns_per_second, - microns_per_millisecond, - microns_per_microsecond, - microns_per_nanosecond, - microns_per_picosecond, - microns_per_femtosecond, - microns_per_attosecond, - microns_per_minute, - microns_per_hour, - microns_per_day, - microns_per_year, - miles_per_second, - miles_per_millisecond, - miles_per_microsecond, - miles_per_nanosecond, - miles_per_picosecond, - miles_per_femtosecond, - miles_per_attosecond, - miles_per_minute, - miles_per_hour, - miles_per_day, - miles_per_year, - yards_per_second, - yards_per_millisecond, - yards_per_microsecond, - yards_per_nanosecond, - yards_per_picosecond, - yards_per_femtosecond, - yards_per_attosecond, - yards_per_minute, - yards_per_hour, - yards_per_day, - yards_per_year, - feet_per_second, - feet_per_millisecond, - feet_per_microsecond, - feet_per_nanosecond, - feet_per_picosecond, - feet_per_femtosecond, - feet_per_attosecond, - feet_per_minute, - feet_per_hour, - feet_per_day, - feet_per_year, - inches_per_second, - inches_per_millisecond, - inches_per_microsecond, - inches_per_nanosecond, - inches_per_picosecond, - inches_per_femtosecond, - inches_per_attosecond, - inches_per_minute, - inches_per_hour, - inches_per_day, - inches_per_year, ]) acceleration = UnitGroup( - name = 'acceleration', + name = 'acceleration', units = [ meters_per_square_second, meters_per_square_millisecond, @@ -2566,6 +1546,8 @@ def __init__(self, name: str, units: list[NamedUnit]): meters_per_square_minute, meters_per_square_hour, meters_per_square_day, + meters_per_square_day, + meters_per_square_year, meters_per_square_year, exameters_per_square_second, exameters_per_square_millisecond, @@ -2577,6 +1559,8 @@ def __init__(self, name: str, units: list[NamedUnit]): exameters_per_square_minute, exameters_per_square_hour, exameters_per_square_day, + exameters_per_square_day, + exameters_per_square_year, exameters_per_square_year, petameters_per_square_second, petameters_per_square_millisecond, @@ -2588,6 +1572,8 @@ def __init__(self, name: str, units: list[NamedUnit]): petameters_per_square_minute, petameters_per_square_hour, petameters_per_square_day, + petameters_per_square_day, + petameters_per_square_year, petameters_per_square_year, terameters_per_square_second, terameters_per_square_millisecond, @@ -2599,6 +1585,8 @@ def __init__(self, name: str, units: list[NamedUnit]): terameters_per_square_minute, terameters_per_square_hour, terameters_per_square_day, + terameters_per_square_day, + terameters_per_square_year, terameters_per_square_year, gigameters_per_square_second, gigameters_per_square_millisecond, @@ -2610,6 +1598,8 @@ def __init__(self, name: str, units: list[NamedUnit]): gigameters_per_square_minute, gigameters_per_square_hour, gigameters_per_square_day, + gigameters_per_square_day, + gigameters_per_square_year, gigameters_per_square_year, megameters_per_square_second, megameters_per_square_millisecond, @@ -2621,6 +1611,8 @@ def __init__(self, name: str, units: list[NamedUnit]): megameters_per_square_minute, megameters_per_square_hour, megameters_per_square_day, + megameters_per_square_day, + megameters_per_square_year, megameters_per_square_year, kilometers_per_square_second, kilometers_per_square_millisecond, @@ -2632,6 +1624,8 @@ def __init__(self, name: str, units: list[NamedUnit]): kilometers_per_square_minute, kilometers_per_square_hour, kilometers_per_square_day, + kilometers_per_square_day, + kilometers_per_square_year, kilometers_per_square_year, millimeters_per_square_second, millimeters_per_square_millisecond, @@ -2643,6 +1637,8 @@ def __init__(self, name: str, units: list[NamedUnit]): millimeters_per_square_minute, millimeters_per_square_hour, millimeters_per_square_day, + millimeters_per_square_day, + millimeters_per_square_year, millimeters_per_square_year, micrometers_per_square_second, micrometers_per_square_millisecond, @@ -2654,6 +1650,8 @@ def __init__(self, name: str, units: list[NamedUnit]): micrometers_per_square_minute, micrometers_per_square_hour, micrometers_per_square_day, + micrometers_per_square_day, + micrometers_per_square_year, micrometers_per_square_year, nanometers_per_square_second, nanometers_per_square_millisecond, @@ -2665,6 +1663,8 @@ def __init__(self, name: str, units: list[NamedUnit]): nanometers_per_square_minute, nanometers_per_square_hour, nanometers_per_square_day, + nanometers_per_square_day, + nanometers_per_square_year, nanometers_per_square_year, picometers_per_square_second, picometers_per_square_millisecond, @@ -2676,6 +1676,8 @@ def __init__(self, name: str, units: list[NamedUnit]): picometers_per_square_minute, picometers_per_square_hour, picometers_per_square_day, + picometers_per_square_day, + picometers_per_square_year, picometers_per_square_year, femtometers_per_square_second, femtometers_per_square_millisecond, @@ -2687,6 +1689,8 @@ def __init__(self, name: str, units: list[NamedUnit]): femtometers_per_square_minute, femtometers_per_square_hour, femtometers_per_square_day, + femtometers_per_square_day, + femtometers_per_square_year, femtometers_per_square_year, attometers_per_square_second, attometers_per_square_millisecond, @@ -2698,29 +1702,9 @@ def __init__(self, name: str, units: list[NamedUnit]): attometers_per_square_minute, attometers_per_square_hour, attometers_per_square_day, + attometers_per_square_day, + attometers_per_square_year, attometers_per_square_year, - decimeters_per_square_second, - decimeters_per_square_millisecond, - decimeters_per_square_microsecond, - decimeters_per_square_nanosecond, - decimeters_per_square_picosecond, - decimeters_per_square_femtosecond, - decimeters_per_square_attosecond, - decimeters_per_square_minute, - decimeters_per_square_hour, - decimeters_per_square_day, - decimeters_per_square_year, - centimeters_per_square_second, - centimeters_per_square_millisecond, - centimeters_per_square_microsecond, - centimeters_per_square_nanosecond, - centimeters_per_square_picosecond, - centimeters_per_square_femtosecond, - centimeters_per_square_attosecond, - centimeters_per_square_minute, - centimeters_per_square_hour, - centimeters_per_square_day, - centimeters_per_square_year, angstroms_per_square_second, angstroms_per_square_millisecond, angstroms_per_square_microsecond, @@ -2731,407 +1715,26 @@ def __init__(self, name: str, units: list[NamedUnit]): angstroms_per_square_minute, angstroms_per_square_hour, angstroms_per_square_day, + angstroms_per_square_day, + angstroms_per_square_year, + angstroms_per_square_year, + angstroms_per_square_second, + angstroms_per_square_millisecond, + angstroms_per_square_microsecond, + angstroms_per_square_nanosecond, + angstroms_per_square_picosecond, + angstroms_per_square_femtosecond, + angstroms_per_square_attosecond, + angstroms_per_square_minute, + angstroms_per_square_hour, + angstroms_per_square_day, + angstroms_per_square_day, + angstroms_per_square_year, angstroms_per_square_year, - microns_per_square_second, - microns_per_square_millisecond, - microns_per_square_microsecond, - microns_per_square_nanosecond, - microns_per_square_picosecond, - microns_per_square_femtosecond, - microns_per_square_attosecond, - microns_per_square_minute, - microns_per_square_hour, - microns_per_square_day, - microns_per_square_year, - miles_per_square_second, - miles_per_square_millisecond, - miles_per_square_microsecond, - miles_per_square_nanosecond, - miles_per_square_picosecond, - miles_per_square_femtosecond, - miles_per_square_attosecond, - miles_per_square_minute, - miles_per_square_hour, - miles_per_square_day, - miles_per_square_year, - yards_per_square_second, - yards_per_square_millisecond, - yards_per_square_microsecond, - yards_per_square_nanosecond, - yards_per_square_picosecond, - yards_per_square_femtosecond, - yards_per_square_attosecond, - yards_per_square_minute, - yards_per_square_hour, - yards_per_square_day, - yards_per_square_year, - feet_per_square_second, - feet_per_square_millisecond, - feet_per_square_microsecond, - feet_per_square_nanosecond, - feet_per_square_picosecond, - feet_per_square_femtosecond, - feet_per_square_attosecond, - feet_per_square_minute, - feet_per_square_hour, - feet_per_square_day, - feet_per_square_year, - inches_per_square_second, - inches_per_square_millisecond, - inches_per_square_microsecond, - inches_per_square_nanosecond, - inches_per_square_picosecond, - inches_per_square_femtosecond, - inches_per_square_attosecond, - inches_per_square_minute, - inches_per_square_hour, - inches_per_square_day, - inches_per_square_year, -]) - -density = UnitGroup( - name = 'density', - units = [ - grams_per_cubic_meter, - exagrams_per_cubic_meter, - petagrams_per_cubic_meter, - teragrams_per_cubic_meter, - gigagrams_per_cubic_meter, - megagrams_per_cubic_meter, - kilograms_per_cubic_meter, - milligrams_per_cubic_meter, - micrograms_per_cubic_meter, - nanograms_per_cubic_meter, - picograms_per_cubic_meter, - femtograms_per_cubic_meter, - attograms_per_cubic_meter, - atomic_mass_units_per_cubic_meter, - pounds_per_cubic_meter, - ounces_per_cubic_meter, - grams_per_cubic_exameter, - exagrams_per_cubic_exameter, - petagrams_per_cubic_exameter, - teragrams_per_cubic_exameter, - gigagrams_per_cubic_exameter, - megagrams_per_cubic_exameter, - kilograms_per_cubic_exameter, - milligrams_per_cubic_exameter, - micrograms_per_cubic_exameter, - nanograms_per_cubic_exameter, - picograms_per_cubic_exameter, - femtograms_per_cubic_exameter, - attograms_per_cubic_exameter, - atomic_mass_units_per_cubic_exameter, - pounds_per_cubic_exameter, - ounces_per_cubic_exameter, - grams_per_cubic_petameter, - exagrams_per_cubic_petameter, - petagrams_per_cubic_petameter, - teragrams_per_cubic_petameter, - gigagrams_per_cubic_petameter, - megagrams_per_cubic_petameter, - kilograms_per_cubic_petameter, - milligrams_per_cubic_petameter, - micrograms_per_cubic_petameter, - nanograms_per_cubic_petameter, - picograms_per_cubic_petameter, - femtograms_per_cubic_petameter, - attograms_per_cubic_petameter, - atomic_mass_units_per_cubic_petameter, - pounds_per_cubic_petameter, - ounces_per_cubic_petameter, - grams_per_cubic_terameter, - exagrams_per_cubic_terameter, - petagrams_per_cubic_terameter, - teragrams_per_cubic_terameter, - gigagrams_per_cubic_terameter, - megagrams_per_cubic_terameter, - kilograms_per_cubic_terameter, - milligrams_per_cubic_terameter, - micrograms_per_cubic_terameter, - nanograms_per_cubic_terameter, - picograms_per_cubic_terameter, - femtograms_per_cubic_terameter, - attograms_per_cubic_terameter, - atomic_mass_units_per_cubic_terameter, - pounds_per_cubic_terameter, - ounces_per_cubic_terameter, - grams_per_cubic_gigameter, - exagrams_per_cubic_gigameter, - petagrams_per_cubic_gigameter, - teragrams_per_cubic_gigameter, - gigagrams_per_cubic_gigameter, - megagrams_per_cubic_gigameter, - kilograms_per_cubic_gigameter, - milligrams_per_cubic_gigameter, - micrograms_per_cubic_gigameter, - nanograms_per_cubic_gigameter, - picograms_per_cubic_gigameter, - femtograms_per_cubic_gigameter, - attograms_per_cubic_gigameter, - atomic_mass_units_per_cubic_gigameter, - pounds_per_cubic_gigameter, - ounces_per_cubic_gigameter, - grams_per_cubic_megameter, - exagrams_per_cubic_megameter, - petagrams_per_cubic_megameter, - teragrams_per_cubic_megameter, - gigagrams_per_cubic_megameter, - megagrams_per_cubic_megameter, - kilograms_per_cubic_megameter, - milligrams_per_cubic_megameter, - micrograms_per_cubic_megameter, - nanograms_per_cubic_megameter, - picograms_per_cubic_megameter, - femtograms_per_cubic_megameter, - attograms_per_cubic_megameter, - atomic_mass_units_per_cubic_megameter, - pounds_per_cubic_megameter, - ounces_per_cubic_megameter, - grams_per_cubic_kilometer, - exagrams_per_cubic_kilometer, - petagrams_per_cubic_kilometer, - teragrams_per_cubic_kilometer, - gigagrams_per_cubic_kilometer, - megagrams_per_cubic_kilometer, - kilograms_per_cubic_kilometer, - milligrams_per_cubic_kilometer, - micrograms_per_cubic_kilometer, - nanograms_per_cubic_kilometer, - picograms_per_cubic_kilometer, - femtograms_per_cubic_kilometer, - attograms_per_cubic_kilometer, - atomic_mass_units_per_cubic_kilometer, - pounds_per_cubic_kilometer, - ounces_per_cubic_kilometer, - grams_per_cubic_millimeter, - exagrams_per_cubic_millimeter, - petagrams_per_cubic_millimeter, - teragrams_per_cubic_millimeter, - gigagrams_per_cubic_millimeter, - megagrams_per_cubic_millimeter, - kilograms_per_cubic_millimeter, - milligrams_per_cubic_millimeter, - micrograms_per_cubic_millimeter, - nanograms_per_cubic_millimeter, - picograms_per_cubic_millimeter, - femtograms_per_cubic_millimeter, - attograms_per_cubic_millimeter, - atomic_mass_units_per_cubic_millimeter, - pounds_per_cubic_millimeter, - ounces_per_cubic_millimeter, - grams_per_cubic_micrometer, - exagrams_per_cubic_micrometer, - petagrams_per_cubic_micrometer, - teragrams_per_cubic_micrometer, - gigagrams_per_cubic_micrometer, - megagrams_per_cubic_micrometer, - kilograms_per_cubic_micrometer, - milligrams_per_cubic_micrometer, - micrograms_per_cubic_micrometer, - nanograms_per_cubic_micrometer, - picograms_per_cubic_micrometer, - femtograms_per_cubic_micrometer, - attograms_per_cubic_micrometer, - atomic_mass_units_per_cubic_micrometer, - pounds_per_cubic_micrometer, - ounces_per_cubic_micrometer, - grams_per_cubic_nanometer, - exagrams_per_cubic_nanometer, - petagrams_per_cubic_nanometer, - teragrams_per_cubic_nanometer, - gigagrams_per_cubic_nanometer, - megagrams_per_cubic_nanometer, - kilograms_per_cubic_nanometer, - milligrams_per_cubic_nanometer, - micrograms_per_cubic_nanometer, - nanograms_per_cubic_nanometer, - picograms_per_cubic_nanometer, - femtograms_per_cubic_nanometer, - attograms_per_cubic_nanometer, - atomic_mass_units_per_cubic_nanometer, - pounds_per_cubic_nanometer, - ounces_per_cubic_nanometer, - grams_per_cubic_picometer, - exagrams_per_cubic_picometer, - petagrams_per_cubic_picometer, - teragrams_per_cubic_picometer, - gigagrams_per_cubic_picometer, - megagrams_per_cubic_picometer, - kilograms_per_cubic_picometer, - milligrams_per_cubic_picometer, - micrograms_per_cubic_picometer, - nanograms_per_cubic_picometer, - picograms_per_cubic_picometer, - femtograms_per_cubic_picometer, - attograms_per_cubic_picometer, - atomic_mass_units_per_cubic_picometer, - pounds_per_cubic_picometer, - ounces_per_cubic_picometer, - grams_per_cubic_femtometer, - exagrams_per_cubic_femtometer, - petagrams_per_cubic_femtometer, - teragrams_per_cubic_femtometer, - gigagrams_per_cubic_femtometer, - megagrams_per_cubic_femtometer, - kilograms_per_cubic_femtometer, - milligrams_per_cubic_femtometer, - micrograms_per_cubic_femtometer, - nanograms_per_cubic_femtometer, - picograms_per_cubic_femtometer, - femtograms_per_cubic_femtometer, - attograms_per_cubic_femtometer, - atomic_mass_units_per_cubic_femtometer, - pounds_per_cubic_femtometer, - ounces_per_cubic_femtometer, - grams_per_cubic_attometer, - exagrams_per_cubic_attometer, - petagrams_per_cubic_attometer, - teragrams_per_cubic_attometer, - gigagrams_per_cubic_attometer, - megagrams_per_cubic_attometer, - kilograms_per_cubic_attometer, - milligrams_per_cubic_attometer, - micrograms_per_cubic_attometer, - nanograms_per_cubic_attometer, - picograms_per_cubic_attometer, - femtograms_per_cubic_attometer, - attograms_per_cubic_attometer, - atomic_mass_units_per_cubic_attometer, - pounds_per_cubic_attometer, - ounces_per_cubic_attometer, - grams_per_cubic_decimeter, - exagrams_per_cubic_decimeter, - petagrams_per_cubic_decimeter, - teragrams_per_cubic_decimeter, - gigagrams_per_cubic_decimeter, - megagrams_per_cubic_decimeter, - kilograms_per_cubic_decimeter, - milligrams_per_cubic_decimeter, - micrograms_per_cubic_decimeter, - nanograms_per_cubic_decimeter, - picograms_per_cubic_decimeter, - femtograms_per_cubic_decimeter, - attograms_per_cubic_decimeter, - atomic_mass_units_per_cubic_decimeter, - pounds_per_cubic_decimeter, - ounces_per_cubic_decimeter, - grams_per_cubic_centimeter, - exagrams_per_cubic_centimeter, - petagrams_per_cubic_centimeter, - teragrams_per_cubic_centimeter, - gigagrams_per_cubic_centimeter, - megagrams_per_cubic_centimeter, - kilograms_per_cubic_centimeter, - milligrams_per_cubic_centimeter, - micrograms_per_cubic_centimeter, - nanograms_per_cubic_centimeter, - picograms_per_cubic_centimeter, - femtograms_per_cubic_centimeter, - attograms_per_cubic_centimeter, - atomic_mass_units_per_cubic_centimeter, - pounds_per_cubic_centimeter, - ounces_per_cubic_centimeter, - grams_per_cubic_angstrom, - exagrams_per_cubic_angstrom, - petagrams_per_cubic_angstrom, - teragrams_per_cubic_angstrom, - gigagrams_per_cubic_angstrom, - megagrams_per_cubic_angstrom, - kilograms_per_cubic_angstrom, - milligrams_per_cubic_angstrom, - micrograms_per_cubic_angstrom, - nanograms_per_cubic_angstrom, - picograms_per_cubic_angstrom, - femtograms_per_cubic_angstrom, - attograms_per_cubic_angstrom, - atomic_mass_units_per_cubic_angstrom, - pounds_per_cubic_angstrom, - ounces_per_cubic_angstrom, - grams_per_cubic_micron, - exagrams_per_cubic_micron, - petagrams_per_cubic_micron, - teragrams_per_cubic_micron, - gigagrams_per_cubic_micron, - megagrams_per_cubic_micron, - kilograms_per_cubic_micron, - milligrams_per_cubic_micron, - micrograms_per_cubic_micron, - nanograms_per_cubic_micron, - picograms_per_cubic_micron, - femtograms_per_cubic_micron, - attograms_per_cubic_micron, - atomic_mass_units_per_cubic_micron, - pounds_per_cubic_micron, - ounces_per_cubic_micron, - grams_per_cubic_mile, - exagrams_per_cubic_mile, - petagrams_per_cubic_mile, - teragrams_per_cubic_mile, - gigagrams_per_cubic_mile, - megagrams_per_cubic_mile, - kilograms_per_cubic_mile, - milligrams_per_cubic_mile, - micrograms_per_cubic_mile, - nanograms_per_cubic_mile, - picograms_per_cubic_mile, - femtograms_per_cubic_mile, - attograms_per_cubic_mile, - atomic_mass_units_per_cubic_mile, - pounds_per_cubic_mile, - ounces_per_cubic_mile, - grams_per_cubic_yard, - exagrams_per_cubic_yard, - petagrams_per_cubic_yard, - teragrams_per_cubic_yard, - gigagrams_per_cubic_yard, - megagrams_per_cubic_yard, - kilograms_per_cubic_yard, - milligrams_per_cubic_yard, - micrograms_per_cubic_yard, - nanograms_per_cubic_yard, - picograms_per_cubic_yard, - femtograms_per_cubic_yard, - attograms_per_cubic_yard, - atomic_mass_units_per_cubic_yard, - pounds_per_cubic_yard, - ounces_per_cubic_yard, - grams_per_cubic_foot, - exagrams_per_cubic_foot, - petagrams_per_cubic_foot, - teragrams_per_cubic_foot, - gigagrams_per_cubic_foot, - megagrams_per_cubic_foot, - kilograms_per_cubic_foot, - milligrams_per_cubic_foot, - micrograms_per_cubic_foot, - nanograms_per_cubic_foot, - picograms_per_cubic_foot, - femtograms_per_cubic_foot, - attograms_per_cubic_foot, - atomic_mass_units_per_cubic_foot, - pounds_per_cubic_foot, - ounces_per_cubic_foot, - grams_per_cubic_inch, - exagrams_per_cubic_inch, - petagrams_per_cubic_inch, - teragrams_per_cubic_inch, - gigagrams_per_cubic_inch, - megagrams_per_cubic_inch, - kilograms_per_cubic_inch, - milligrams_per_cubic_inch, - micrograms_per_cubic_inch, - nanograms_per_cubic_inch, - picograms_per_cubic_inch, - femtograms_per_cubic_inch, - attograms_per_cubic_inch, - atomic_mass_units_per_cubic_inch, - pounds_per_cubic_inch, - ounces_per_cubic_inch, ]) force = UnitGroup( - name = 'force', + name = 'force', units = [ newtons, exanewtons, @@ -3146,12 +1749,10 @@ def __init__(self, name: str, units: list[NamedUnit]): piconewtons, femtonewtons, attonewtons, - kg_force, - pounds_force, ]) pressure = UnitGroup( - name = 'pressure', + name = 'pressure', units = [ pascals, exapascals, @@ -3166,11 +1767,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picopascals, femtopascals, attopascals, - pounds_force_per_square_inch, ]) energy = UnitGroup( - name = 'energy', + name = 'energy', units = [ joules, exajoules, @@ -3185,23 +1785,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picojoules, femtojoules, attojoules, - electronvolts, - exaelectronvolts, - petaelectronvolts, - teraelectronvolts, - gigaelectronvolts, - megaelectronvolts, - kiloelectronvolts, - millielectronvolts, - microelectronvolts, - nanoelectronvolts, - picoelectronvolts, - femtoelectronvolts, - attoelectronvolts, ]) power = UnitGroup( - name = 'power', + name = 'power', units = [ watts, exawatts, @@ -3219,7 +1806,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) charge = UnitGroup( - name = 'charge', + name = 'charge', units = [ coulombs, exacoulombs, @@ -3237,7 +1824,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) potential = UnitGroup( - name = 'potential', + name = 'potential', units = [ volts, exavolts, @@ -3255,7 +1842,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) resistance = UnitGroup( - name = 'resistance', + name = 'resistance', units = [ ohms, exaohms, @@ -3273,7 +1860,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) capacitance = UnitGroup( - name = 'capacitance', + name = 'capacitance', units = [ farads, exafarads, @@ -3291,7 +1878,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) conductance = UnitGroup( - name = 'conductance', + name = 'conductance', units = [ siemens, exasiemens, @@ -3309,7 +1896,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) magnetic_flux = UnitGroup( - name = 'magnetic_flux', + name = 'magnetic_flux', units = [ webers, exawebers, @@ -3327,7 +1914,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) magnetic_flux_density = UnitGroup( - name = 'magnetic_flux_density', + name = 'magnetic_flux_density', units = [ tesla, exatesla, @@ -3345,7 +1932,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) inductance = UnitGroup( - name = 'inductance', + name = 'inductance', units = [ henry, exahenry, @@ -3363,7 +1950,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) temperature = UnitGroup( - name = 'temperature', + name = 'temperature', units = [ kelvin, exakelvin, @@ -3380,254 +1967,3 @@ def __init__(self, name: str, units: list[NamedUnit]): attokelvin, degrees_celsius, ]) - -dimensionless = UnitGroup( - name = 'dimensionless', - units = [ - none, - percent, -]) - -angle = UnitGroup( - name = 'angle', - units = [ - degrees, - radians, - rotations, -]) - -solid_angle = UnitGroup( - name = 'solid_angle', - units = [ - stradians, -]) - -amount = UnitGroup( - name = 'amount', - units = [ - moles, - millimoles, - micromoles, - nanomoles, - picomoles, - femtomoles, - attomoles, -]) - -concentration = UnitGroup( - name = 'concentration', - units = [ - moles_per_cubic_meter, - millimoles_per_cubic_meter, - micromoles_per_cubic_meter, - nanomoles_per_cubic_meter, - picomoles_per_cubic_meter, - femtomoles_per_cubic_meter, - attomoles_per_cubic_meter, - moles_per_cubic_exameter, - millimoles_per_cubic_exameter, - micromoles_per_cubic_exameter, - nanomoles_per_cubic_exameter, - picomoles_per_cubic_exameter, - femtomoles_per_cubic_exameter, - attomoles_per_cubic_exameter, - moles_per_cubic_petameter, - millimoles_per_cubic_petameter, - micromoles_per_cubic_petameter, - nanomoles_per_cubic_petameter, - picomoles_per_cubic_petameter, - femtomoles_per_cubic_petameter, - attomoles_per_cubic_petameter, - moles_per_cubic_terameter, - millimoles_per_cubic_terameter, - micromoles_per_cubic_terameter, - nanomoles_per_cubic_terameter, - picomoles_per_cubic_terameter, - femtomoles_per_cubic_terameter, - attomoles_per_cubic_terameter, - moles_per_cubic_gigameter, - millimoles_per_cubic_gigameter, - micromoles_per_cubic_gigameter, - nanomoles_per_cubic_gigameter, - picomoles_per_cubic_gigameter, - femtomoles_per_cubic_gigameter, - attomoles_per_cubic_gigameter, - moles_per_cubic_megameter, - millimoles_per_cubic_megameter, - micromoles_per_cubic_megameter, - nanomoles_per_cubic_megameter, - picomoles_per_cubic_megameter, - femtomoles_per_cubic_megameter, - attomoles_per_cubic_megameter, - moles_per_cubic_kilometer, - millimoles_per_cubic_kilometer, - micromoles_per_cubic_kilometer, - nanomoles_per_cubic_kilometer, - picomoles_per_cubic_kilometer, - femtomoles_per_cubic_kilometer, - attomoles_per_cubic_kilometer, - moles_per_cubic_millimeter, - millimoles_per_cubic_millimeter, - micromoles_per_cubic_millimeter, - nanomoles_per_cubic_millimeter, - picomoles_per_cubic_millimeter, - femtomoles_per_cubic_millimeter, - attomoles_per_cubic_millimeter, - moles_per_cubic_micrometer, - millimoles_per_cubic_micrometer, - micromoles_per_cubic_micrometer, - nanomoles_per_cubic_micrometer, - picomoles_per_cubic_micrometer, - femtomoles_per_cubic_micrometer, - attomoles_per_cubic_micrometer, - moles_per_cubic_nanometer, - millimoles_per_cubic_nanometer, - micromoles_per_cubic_nanometer, - nanomoles_per_cubic_nanometer, - picomoles_per_cubic_nanometer, - femtomoles_per_cubic_nanometer, - attomoles_per_cubic_nanometer, - moles_per_cubic_picometer, - millimoles_per_cubic_picometer, - micromoles_per_cubic_picometer, - nanomoles_per_cubic_picometer, - picomoles_per_cubic_picometer, - femtomoles_per_cubic_picometer, - attomoles_per_cubic_picometer, - moles_per_cubic_femtometer, - millimoles_per_cubic_femtometer, - micromoles_per_cubic_femtometer, - nanomoles_per_cubic_femtometer, - picomoles_per_cubic_femtometer, - femtomoles_per_cubic_femtometer, - attomoles_per_cubic_femtometer, - moles_per_cubic_attometer, - millimoles_per_cubic_attometer, - micromoles_per_cubic_attometer, - nanomoles_per_cubic_attometer, - picomoles_per_cubic_attometer, - femtomoles_per_cubic_attometer, - attomoles_per_cubic_attometer, - moles_per_cubic_decimeter, - millimoles_per_cubic_decimeter, - micromoles_per_cubic_decimeter, - nanomoles_per_cubic_decimeter, - picomoles_per_cubic_decimeter, - femtomoles_per_cubic_decimeter, - attomoles_per_cubic_decimeter, - moles_per_cubic_centimeter, - millimoles_per_cubic_centimeter, - micromoles_per_cubic_centimeter, - nanomoles_per_cubic_centimeter, - picomoles_per_cubic_centimeter, - femtomoles_per_cubic_centimeter, - attomoles_per_cubic_centimeter, - moles_per_cubic_angstrom, - millimoles_per_cubic_angstrom, - micromoles_per_cubic_angstrom, - nanomoles_per_cubic_angstrom, - picomoles_per_cubic_angstrom, - femtomoles_per_cubic_angstrom, - attomoles_per_cubic_angstrom, - moles_per_cubic_micron, - millimoles_per_cubic_micron, - micromoles_per_cubic_micron, - nanomoles_per_cubic_micron, - picomoles_per_cubic_micron, - femtomoles_per_cubic_micron, - attomoles_per_cubic_micron, - moles_per_cubic_mile, - millimoles_per_cubic_mile, - micromoles_per_cubic_mile, - nanomoles_per_cubic_mile, - picomoles_per_cubic_mile, - femtomoles_per_cubic_mile, - attomoles_per_cubic_mile, - moles_per_cubic_yard, - millimoles_per_cubic_yard, - micromoles_per_cubic_yard, - nanomoles_per_cubic_yard, - picomoles_per_cubic_yard, - femtomoles_per_cubic_yard, - attomoles_per_cubic_yard, - moles_per_cubic_foot, - millimoles_per_cubic_foot, - micromoles_per_cubic_foot, - nanomoles_per_cubic_foot, - picomoles_per_cubic_foot, - femtomoles_per_cubic_foot, - attomoles_per_cubic_foot, - moles_per_cubic_inch, - millimoles_per_cubic_inch, - micromoles_per_cubic_inch, - nanomoles_per_cubic_inch, - picomoles_per_cubic_inch, - femtomoles_per_cubic_inch, - attomoles_per_cubic_inch, -]) - - -unit_group_names = [ - 'length', - 'area', - 'volume', - 'inverse_length', - 'inverse_area', - 'inverse_volume', - 'time', - 'rate', - 'speed', - 'acceleration', - 'density', - 'force', - 'pressure', - 'energy', - 'power', - 'charge', - 'potential', - 'resistance', - 'capacitance', - 'conductance', - 'magnetic_flux', - 'magnetic_flux_density', - 'inductance', - 'temperature', - 'dimensionless', - 'angle', - 'solid_angle', - 'amount', - 'concentration', -] - -unit_groups = { - 'length': length, - 'area': area, - 'volume': volume, - 'inverse_length': inverse_length, - 'inverse_area': inverse_area, - 'inverse_volume': inverse_volume, - 'time': time, - 'rate': rate, - 'speed': speed, - 'acceleration': acceleration, - 'density': density, - 'force': force, - 'pressure': pressure, - 'energy': energy, - 'power': power, - 'charge': charge, - 'potential': potential, - 'resistance': resistance, - 'capacitance': capacitance, - 'conductance': conductance, - 'magnetic_flux': magnetic_flux, - 'magnetic_flux_density': magnetic_flux_density, - 'inductance': inductance, - 'temperature': temperature, - 'dimensionless': dimensionless, - 'angle': angle, - 'solid_angle': solid_angle, - 'amount': amount, - 'concentration': concentration, -} - diff --git a/sasdata/quantities/units_table.py b/sasdata/quantities/units_table.py deleted file mode 100644 index 5a2d6b5c4..000000000 --- a/sasdata/quantities/units_table.py +++ /dev/null @@ -1,78 +0,0 @@ -""" -Builds a data file containing details of units -""" - -import numpy as np - -bigger_magnitudes = [ - ("E", None, "exa", 1e18), - ("P", None, "peta", 1e15), - ("T", None, "tera", 1e12), - ("G", None, "giga", 1e9), - ("M", None, "mega", 1e6), - ("k", None, "kilo", 1e3) ] - -smaller_magnitudes = [ - ("m", None, "milli", 1e-3), - ("u", "µ", "micro", 1e-6), - ("n", None, "nano", 1e-9), - ("p", None, "pico", 1e-12), - ("f", None, "femto", 1e-15), - ("a", None, "atto", 1e-18)] - -all_magnitudes = bigger_magnitudes + smaller_magnitudes - -# Length, time, mass, current, temperature -base_si_units = [ - ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, all_magnitudes), - ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, smaller_magnitudes), - ("g", None, "gram", "grams", 1, 0, 0, 1, 0, 0, all_magnitudes), - ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, all_magnitudes), - ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, all_magnitudes) ] - -derived_si_units = [ - ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, all_magnitudes), - ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, all_magnitudes), - ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, all_magnitudes), - ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, all_magnitudes), - ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, all_magnitudes), - ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, all_magnitudes), - ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, all_magnitudes), - ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, all_magnitudes), - ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, all_magnitudes), - ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, all_magnitudes), - ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, all_magnitudes), - ("T", None, "tesla", "tesla", 1, 2, -2, 1, -1, 0, all_magnitudes), - ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, all_magnitudes), - ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, []) -] - -non_si_units = [ - ("A", None, "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), - ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, []), - ("hr", None, "hour", "hours", 360, 0, 1, 0, 0, 0, []), - ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), - ("day", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), - ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), - ("yr", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), - ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, []), - ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, []), - ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []) -] - -all_units = base_si_units + derived_si_units + non_si_units - -encoding = "utf-8" - -with open("unit_data.txt", mode='w', encoding=encoding) as fid: - for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, magnitudes in all_units: - fid.write(f"'{symbol}', '{special_symbol}', '{singular}', '{plural}', ") - fid.write(f"{scale}, {length}, {time}, {mass}, {current}, {temperature}\n") - - for mag_symbol, mag_special_symbol, name, mag_scale in magnitudes: - - combined_symbol = (mag_symbol if mag_special_symbol is None else mag_special_symbol) + \ - (symbol if special_symbol is None else special_symbol) - - fid.write(f"'{mag_symbol}{symbol}', '{combined_symbol}', '{name}{singular}', '{name}{plural}', ") - fid.write(f"{scale * mag_scale}, {length}, {time}, {mass}, {current}, {temperature}\n") From f8e883ccae44c428c7efbed59439f52aa4f230eb Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 6 Aug 2024 14:57:06 +0100 Subject: [PATCH 005/675] More units --- sasdata/dataset_types.py | 35 ++--- sasdata/quantities/_units_table.py | 25 +++- sasdata/quantities/units.py | 211 +++++++++++++++++++++++++++++ 3 files changed, 253 insertions(+), 18 deletions(-) diff --git a/sasdata/dataset_types.py b/sasdata/dataset_types.py index 27848b3e2..407affcfc 100644 --- a/sasdata/dataset_types.py +++ b/sasdata/dataset_types.py @@ -2,6 +2,8 @@ from dataclasses import dataclass +import sasdata.quantities.units as units + # # VERY ROUGH DRAFT - FOR PROTOTYPING PURPOSES # @@ -54,22 +56,23 @@ class DatasetType: # # The unit options should only be those compatible with the field # -default_units = { - "Q": "1/A", - "I": "1/cm", - "Qx": "1/A", - "Qy": "1/A", - "Qz": "1/A", - "dI": "1/A", - "dQ": "1/A", - "dQx": "1/A", - "dQy": "1/A", - "dQz": "1/A", - "z": "A", - "G": "", - "shaddow": "", - "temperature": "K", - "magnetic field": "T" + +unit_kinds = { + "Q": units.inverse_length, + "I": units.inverse_length, + "Qx": units.inverse_length, + "Qy": units.inverse_length, + "Qz": units.inverse_length, + "dI": units.inverse_length, + "dQ": units.inverse_length, + "dQx": units.inverse_length, + "dQy": units.inverse_length, + "dQz": units.inverse_length, + "z": units.length, + "G": units.area, + "shaddow": units.dimensionless, + "temperature": units.temperature, + "magnetic field": units.magnetic_flux_density } # diff --git a/sasdata/quantities/_units_table.py b/sasdata/quantities/_units_table.py index 9b88cab42..62e38138a 100644 --- a/sasdata/quantities/_units_table.py +++ b/sasdata/quantities/_units_table.py @@ -60,7 +60,8 @@ ("yr", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, []), ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, []), - ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []) + ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []), + ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, []) ] @@ -166,6 +167,7 @@ def format_name(name: str): length_units = unit_types_temp[hash(Dimensions(length=1))] time_units = unit_types_temp[hash(Dimensions(time=1))] + mass_units = unit_types_temp[hash(Dimensions(mass=1))] # Length based for symbol, special_symbol, singular, plural, scale, _ in length_units: @@ -212,6 +214,23 @@ def format_name(name: str): unit_types[hash(speed_dimensions)].append(speed_name) unit_types[hash(accel_dimensions)].append(accel_name) + # Density + for length_symbol, length_special_symbol, _, length_name, length_scale, _ in length_units: + for mass_symbol, mass_special_symbol, mass_name, _, mass_scale, _ in mass_units: + + name = length_name + "_per_cubic_" + mass_name + + dimensions = Dimensions(length=-3, time=1) + + fid.write(f"{speed_name} " + f"= Unit({mass_scale / length_scale**3}, " + f"Dimensions(-3, 1, 0, 0, 0), " + f"name='{name}', " + f"ascii_symbol='{mass_symbol} {length_symbol}^-3', " + f"symbol='{mass_special_symbol}{length_special_symbol}⁻³')\n") + + unit_types[hash(dimensions)].append(name) + # # Write out the symbol lookup table # @@ -236,6 +255,7 @@ def format_name(name: str): ("rate", Dimensions(time=-1)), ("speed", Dimensions(length=1, time=-1)), ("acceleration", Dimensions(length=1, time=-2)), + ("density", Dimensions(length=-3, mass=1)), ("force", Dimensions(1, -2, 1, 0, 0)), ("pressure", Dimensions(-1, -2, 1, 0, 0)), ("energy", Dimensions(2, -2, 1, 0, 0)), @@ -248,7 +268,8 @@ def format_name(name: str): ("magnetic_flux", Dimensions(2, -2, 1, -1, 0)), ("magnetic_flux_density", Dimensions(0, -2, 1, -1, 0)), ("inductance", Dimensions(2, -2, 1, -2, 0)), - ("temperature", Dimensions(temperature=1)) + ("temperature", Dimensions(temperature=1)), + ("dimensionless", Dimensions()) ] fid.write("\n#\n# Units by type \n#\n\n") diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 6105e27f5..530ce41bb 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -428,6 +428,7 @@ def __init__(self, name: str, units: list[Unit]): degrees = Unit(57.29577951308232, Dimensions(0, 0, 0, 0, 0),name='degrees',ascii_symbol='deg',symbol='deg') radians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='radians',ascii_symbol='rad',symbol='rad') stradians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='stradians',ascii_symbol='sr',symbol='sr') +none = Unit(1, Dimensions(0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') square_meters = Unit(1, Dimensions(2, 0, 0, 0, 0), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = Unit(1, Dimensions(3, 0, 0, 0, 0), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = Unit(1.0, Dimensions(-1, 0, 0, 0, 0), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -893,6 +894,201 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='NoneNone⁻²') angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/yr', symbol='NoneNone⁻¹') angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/yr^2', symbol='NoneNone⁻²') +angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_gram', ascii_symbol='g m^-3', symbol='NoneNone⁻³') +angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_exagram', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') +angstroms_per_year = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_petagram', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') +angstroms_per_year = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_teragram', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') +angstroms_per_year = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_gigagram', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') +angstroms_per_year = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_megagram', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') +angstroms_per_year = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_kilogram', ascii_symbol='kg m^-3', symbol='kgNone⁻³') +angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_milligram', ascii_symbol='mg m^-3', symbol='mgNone⁻³') +angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_microgram', ascii_symbol='ug m^-3', symbol='µgNone⁻³') +angstroms_per_year = Unit(1.0000000000000002e-12, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_nanogram', ascii_symbol='ng m^-3', symbol='ngNone⁻³') +angstroms_per_year = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_picogram', ascii_symbol='pg m^-3', symbol='pgNone⁻³') +angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_femtogram', ascii_symbol='fg m^-3', symbol='fgNone⁻³') +angstroms_per_year = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_attogram', ascii_symbol='ag m^-3', symbol='agNone⁻³') +angstroms_per_year = Unit(1e-57, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_gram', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') +angstroms_per_year = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_exagram', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') +angstroms_per_year = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_petagram', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') +angstroms_per_year = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_teragram', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') +angstroms_per_year = Unit(1e-48, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_gigagram', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') +angstroms_per_year = Unit(9.999999999999999e-52, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_megagram', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') +angstroms_per_year = Unit(9.999999999999999e-55, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_kilogram', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') +angstroms_per_year = Unit(9.999999999999998e-61, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_milligram', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') +angstroms_per_year = Unit(9.999999999999999e-64, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_microgram', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_nanogram', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') +angstroms_per_year = Unit(1e-69, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_picogram', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') +angstroms_per_year = Unit(1e-72, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_femtogram', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') +angstroms_per_year = Unit(1e-75, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_attogram', ascii_symbol='ag Em^-3', symbol='agEm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_gram', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') +angstroms_per_year = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_exagram', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') +angstroms_per_year = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_petagram', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_teragram', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_gigagram', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') +angstroms_per_year = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_megagram', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-45, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_kilogram', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') +angstroms_per_year = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_milligram', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') +angstroms_per_year = Unit(1.0000000000000002e-54, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_microgram', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') +angstroms_per_year = Unit(1.0000000000000002e-57, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_nanogram', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-60, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_picogram', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') +angstroms_per_year = Unit(1.0000000000000002e-63, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_femtogram', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_attogram', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') +angstroms_per_year = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_gram', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') +angstroms_per_year = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_exagram', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') +angstroms_per_year = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_petagram', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') +angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_teragram', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') +angstroms_per_year = Unit(9.999999999999999e-31, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_gigagram', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') +angstroms_per_year = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_megagram', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') +angstroms_per_year = Unit(1e-36, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_kilogram', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') +angstroms_per_year = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_milligram', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') +angstroms_per_year = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_microgram', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_nanogram', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') +angstroms_per_year = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_picogram', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') +angstroms_per_year = Unit(1e-54, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_femtogram', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-57, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_attogram', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') +angstroms_per_year = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_gram', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') +angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_exagram', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') +angstroms_per_year = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_petagram', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') +angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_teragram', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') +angstroms_per_year = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_gigagram', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') +angstroms_per_year = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_megagram', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') +angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_kilogram', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') +angstroms_per_year = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_milligram', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_microgram', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_nanogram', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') +angstroms_per_year = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_picogram', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') +angstroms_per_year = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_femtogram', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_attogram', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_gram', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') +angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_exagram', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') +angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_petagram', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') +angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_teragram', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') +angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_gigagram', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') +angstroms_per_year = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_megagram', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') +angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_kilogram', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') +angstroms_per_year = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_milligram', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') +angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_microgram', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') +angstroms_per_year = Unit(1.0000000000000003e-30, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_nanogram', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') +angstroms_per_year = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_picogram', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_femtogram', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_attogram', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') +angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_gram', ascii_symbol='g km^-3', symbol='Nonekm⁻³') +angstroms_per_year = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_exagram', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') +angstroms_per_year = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_petagram', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') +angstroms_per_year = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_teragram', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') +angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_gigagram', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') +angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_megagram', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') +angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_kilogram', ascii_symbol='kg km^-3', symbol='kgkm⁻³') +angstroms_per_year = Unit(9.999999999999999e-16, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_milligram', ascii_symbol='mg km^-3', symbol='mgkm⁻³') +angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_microgram', ascii_symbol='ug km^-3', symbol='µgkm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_nanogram', ascii_symbol='ng km^-3', symbol='ngkm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-24, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_picogram', ascii_symbol='pg km^-3', symbol='pgkm⁻³') +angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_femtogram', ascii_symbol='fg km^-3', symbol='fgkm⁻³') +angstroms_per_year = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_attogram', ascii_symbol='ag km^-3', symbol='agkm⁻³') +angstroms_per_year = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_gram', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') +angstroms_per_year = Unit(1e+24, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_exagram', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') +angstroms_per_year = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_petagram', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') +angstroms_per_year = Unit(1e+18, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_teragram', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') +angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_gigagram', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') +angstroms_per_year = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_megagram', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') +angstroms_per_year = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_kilogram', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') +angstroms_per_year = Unit(999.9999999999999, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_milligram', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') +angstroms_per_year = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_microgram', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') +angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_nanogram', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') +angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_picogram', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') +angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_femtogram', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') +angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_attogram', ascii_symbol='ag mm^-3', symbol='agmm⁻³') +angstroms_per_year = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_gram', ascii_symbol='g um^-3', symbol='Noneµm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_exagram', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') +angstroms_per_year = Unit(1.0000000000000002e+30, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_petagram', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') +angstroms_per_year = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_teragram', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+24, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_gigagram', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_megagram', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_kilogram', ascii_symbol='kg um^-3', symbol='kgµm⁻³') +angstroms_per_year = Unit(1000000000000.0001, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_milligram', ascii_symbol='mg um^-3', symbol='mgµm⁻³') +angstroms_per_year = Unit(1000000000.0000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_microgram', ascii_symbol='ug um^-3', symbol='µgµm⁻³') +angstroms_per_year = Unit(1000000.0000000003, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_nanogram', ascii_symbol='ng um^-3', symbol='ngµm⁻³') +angstroms_per_year = Unit(1000.0000000000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_picogram', ascii_symbol='pg um^-3', symbol='pgµm⁻³') +angstroms_per_year = Unit(1.0000000000000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_femtogram', ascii_symbol='fg um^-3', symbol='fgµm⁻³') +angstroms_per_year = Unit(0.0010000000000000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_attogram', ascii_symbol='ag um^-3', symbol='agµm⁻³') +angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_gram', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') +angstroms_per_year = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_exagram', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') +angstroms_per_year = Unit(9.999999999999998e+38, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_petagram', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') +angstroms_per_year = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_teragram', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') +angstroms_per_year = Unit(9.999999999999998e+32, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_gigagram', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') +angstroms_per_year = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_megagram', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') +angstroms_per_year = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_kilogram', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') +angstroms_per_year = Unit(9.999999999999997e+20, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_milligram', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') +angstroms_per_year = Unit(9.999999999999999e+17, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_microgram', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') +angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_nanogram', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') +angstroms_per_year = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_picogram', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') +angstroms_per_year = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_femtogram', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') +angstroms_per_year = Unit(999999.9999999999, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_attogram', ascii_symbol='ag nm^-3', symbol='agnm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_gram', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') +angstroms_per_year = Unit(1e+51, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_exagram', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') +angstroms_per_year = Unit(1e+48, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_petagram', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+45, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_teragram', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') +angstroms_per_year = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_gigagram', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+39, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_megagram', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') +angstroms_per_year = Unit(1e+36, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_kilogram', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') +angstroms_per_year = Unit(1e+30, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_milligram', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') +angstroms_per_year = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_microgram', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') +angstroms_per_year = Unit(1.0000000000000003e+24, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_nanogram', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_picogram', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_femtogram', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') +angstroms_per_year = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_attogram', ascii_symbol='ag pm^-3', symbol='agpm⁻³') +angstroms_per_year = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_gram', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') +angstroms_per_year = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_exagram', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+56, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_petagram', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+53, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_teragram', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+50, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_gigagram', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_megagram', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') +angstroms_per_year = Unit(9.999999999999998e+44, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_kilogram', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') +angstroms_per_year = Unit(9.999999999999996e+38, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_milligram', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_microgram', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') +angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_nanogram', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_picogram', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_femtogram', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') +angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_attogram', ascii_symbol='ag fm^-3', symbol='agfm⁻³') +angstroms_per_year = Unit(9.999999999999998e+50, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_gram', ascii_symbol='g am^-3', symbol='Noneam⁻³') +angstroms_per_year = Unit(9.999999999999999e+68, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_exagram', ascii_symbol='Eg am^-3', symbol='Egam⁻³') +angstroms_per_year = Unit(9.999999999999998e+65, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_petagram', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') +angstroms_per_year = Unit(9.999999999999999e+62, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_teragram', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') +angstroms_per_year = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_gigagram', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') +angstroms_per_year = Unit(9.999999999999999e+56, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_megagram', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') +angstroms_per_year = Unit(9.999999999999999e+53, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_kilogram', ascii_symbol='kg am^-3', symbol='kgam⁻³') +angstroms_per_year = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_milligram', ascii_symbol='mg am^-3', symbol='mgam⁻³') +angstroms_per_year = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_microgram', ascii_symbol='ug am^-3', symbol='µgam⁻³') +angstroms_per_year = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_nanogram', ascii_symbol='ng am^-3', symbol='ngam⁻³') +angstroms_per_year = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_picogram', ascii_symbol='pg am^-3', symbol='pgam⁻³') +angstroms_per_year = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_femtogram', ascii_symbol='fg am^-3', symbol='fgam⁻³') +angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_attogram', ascii_symbol='ag am^-3', symbol='agam⁻³') +angstroms_per_year = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gram', ascii_symbol='g A^-3', symbol='NoneÅ⁻³') +angstroms_per_year = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_exagram', ascii_symbol='Eg A^-3', symbol='EgÅ⁻³') +angstroms_per_year = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_petagram', ascii_symbol='Pg A^-3', symbol='PgÅ⁻³') +angstroms_per_year = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_teragram', ascii_symbol='Tg A^-3', symbol='TgÅ⁻³') +angstroms_per_year = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gigagram', ascii_symbol='Gg A^-3', symbol='GgÅ⁻³') +angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_megagram', ascii_symbol='Mg A^-3', symbol='MgÅ⁻³') +angstroms_per_year = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_kilogram', ascii_symbol='kg A^-3', symbol='kgÅ⁻³') +angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_milligram', ascii_symbol='mg A^-3', symbol='mgÅ⁻³') +angstroms_per_year = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_microgram', ascii_symbol='ug A^-3', symbol='µgÅ⁻³') +angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_nanogram', ascii_symbol='ng A^-3', symbol='ngÅ⁻³') +angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_picogram', ascii_symbol='pg A^-3', symbol='pgÅ⁻³') +angstroms_per_year = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_femtogram', ascii_symbol='fg A^-3', symbol='fgÅ⁻³') +angstroms_per_year = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_attogram', ascii_symbol='ag A^-3', symbol='agÅ⁻³') +angstroms_per_year = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gram', ascii_symbol='g Ang^-3', symbol='NoneNone⁻³') +angstroms_per_year = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_exagram', ascii_symbol='Eg Ang^-3', symbol='EgNone⁻³') +angstroms_per_year = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_petagram', ascii_symbol='Pg Ang^-3', symbol='PgNone⁻³') +angstroms_per_year = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_teragram', ascii_symbol='Tg Ang^-3', symbol='TgNone⁻³') +angstroms_per_year = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gigagram', ascii_symbol='Gg Ang^-3', symbol='GgNone⁻³') +angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_megagram', ascii_symbol='Mg Ang^-3', symbol='MgNone⁻³') +angstroms_per_year = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_kilogram', ascii_symbol='kg Ang^-3', symbol='kgNone⁻³') +angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_milligram', ascii_symbol='mg Ang^-3', symbol='mgNone⁻³') +angstroms_per_year = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_microgram', ascii_symbol='ug Ang^-3', symbol='µgNone⁻³') +angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_nanogram', ascii_symbol='ng Ang^-3', symbol='ngNone⁻³') +angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_picogram', ascii_symbol='pg Ang^-3', symbol='pgNone⁻³') +angstroms_per_year = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_femtogram', ascii_symbol='fg Ang^-3', symbol='fgNone⁻³') +angstroms_per_year = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_attogram', ascii_symbol='ag Ang^-3', symbol='agNone⁻³') # # Lookup table from symbols to units @@ -1168,6 +1364,7 @@ def __init__(self, name: str, units: list[Unit]): "deg": degrees, "rad": radians, "sr": stradians, + "none": none, } @@ -1733,6 +1930,11 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_square_year, ]) +density = UnitGroup( + name = 'density', + units = [ +]) + force = UnitGroup( name = 'force', units = [ @@ -1967,3 +2169,12 @@ def __init__(self, name: str, units: list[Unit]): attokelvin, degrees_celsius, ]) + +dimensionless = UnitGroup( + name = 'dimensionless', + units = [ + degrees, + radians, + stradians, + none, +]) From 83f605f9fe80b6c405d443cb86194891bb804831 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 6 Aug 2024 14:59:50 +0100 Subject: [PATCH 006/675] one d in shadow --- sasdata/dataset_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/dataset_types.py b/sasdata/dataset_types.py index 407affcfc..c7d2f5927 100644 --- a/sasdata/dataset_types.py +++ b/sasdata/dataset_types.py @@ -70,7 +70,7 @@ class DatasetType: "dQz": units.inverse_length, "z": units.length, "G": units.area, - "shaddow": units.dimensionless, + "shadow": units.dimensionless, "temperature": units.temperature, "magnetic field": units.magnetic_flux_density } From b759f913548d8ae3e2c9175dab6ef61b7a215170 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 6 Aug 2024 15:35:36 +0100 Subject: [PATCH 007/675] Fixed density units --- sasdata/quantities/_units_table.py | 4 +- sasdata/quantities/units.py | 390 ++++++++++++++--------------- 2 files changed, 197 insertions(+), 197 deletions(-) diff --git a/sasdata/quantities/_units_table.py b/sasdata/quantities/_units_table.py index 62e38138a..27165bde8 100644 --- a/sasdata/quantities/_units_table.py +++ b/sasdata/quantities/_units_table.py @@ -218,11 +218,11 @@ def format_name(name: str): for length_symbol, length_special_symbol, _, length_name, length_scale, _ in length_units: for mass_symbol, mass_special_symbol, mass_name, _, mass_scale, _ in mass_units: - name = length_name + "_per_cubic_" + mass_name + name = mass_name + "_per_cubic_" + length_name dimensions = Dimensions(length=-3, time=1) - fid.write(f"{speed_name} " + fid.write(f"{name} " f"= Unit({mass_scale / length_scale**3}, " f"Dimensions(-3, 1, 0, 0, 0), " f"name='{name}', " diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 530ce41bb..60517eca8 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -894,201 +894,201 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='NoneNone⁻²') angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/yr', symbol='NoneNone⁻¹') angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/yr^2', symbol='NoneNone⁻²') -angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_gram', ascii_symbol='g m^-3', symbol='NoneNone⁻³') -angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_exagram', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') -angstroms_per_year = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_petagram', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') -angstroms_per_year = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_teragram', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') -angstroms_per_year = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_gigagram', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') -angstroms_per_year = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_megagram', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') -angstroms_per_year = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_kilogram', ascii_symbol='kg m^-3', symbol='kgNone⁻³') -angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_milligram', ascii_symbol='mg m^-3', symbol='mgNone⁻³') -angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_microgram', ascii_symbol='ug m^-3', symbol='µgNone⁻³') -angstroms_per_year = Unit(1.0000000000000002e-12, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_nanogram', ascii_symbol='ng m^-3', symbol='ngNone⁻³') -angstroms_per_year = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_picogram', ascii_symbol='pg m^-3', symbol='pgNone⁻³') -angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_femtogram', ascii_symbol='fg m^-3', symbol='fgNone⁻³') -angstroms_per_year = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_attogram', ascii_symbol='ag m^-3', symbol='agNone⁻³') -angstroms_per_year = Unit(1e-57, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_gram', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') -angstroms_per_year = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_exagram', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') -angstroms_per_year = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_petagram', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') -angstroms_per_year = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_teragram', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') -angstroms_per_year = Unit(1e-48, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_gigagram', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') -angstroms_per_year = Unit(9.999999999999999e-52, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_megagram', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') -angstroms_per_year = Unit(9.999999999999999e-55, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_kilogram', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') -angstroms_per_year = Unit(9.999999999999998e-61, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_milligram', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') -angstroms_per_year = Unit(9.999999999999999e-64, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_microgram', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_nanogram', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') -angstroms_per_year = Unit(1e-69, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_picogram', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') -angstroms_per_year = Unit(1e-72, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_femtogram', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') -angstroms_per_year = Unit(1e-75, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_attogram', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_gram', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') -angstroms_per_year = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_exagram', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') -angstroms_per_year = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_petagram', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_teragram', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_gigagram', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') -angstroms_per_year = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_megagram', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-45, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_kilogram', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') -angstroms_per_year = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_milligram', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') -angstroms_per_year = Unit(1.0000000000000002e-54, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_microgram', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') -angstroms_per_year = Unit(1.0000000000000002e-57, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_nanogram', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-60, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_picogram', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') -angstroms_per_year = Unit(1.0000000000000002e-63, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_femtogram', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_attogram', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -angstroms_per_year = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_gram', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') -angstroms_per_year = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_exagram', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') -angstroms_per_year = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_petagram', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') -angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_teragram', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') -angstroms_per_year = Unit(9.999999999999999e-31, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_gigagram', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') -angstroms_per_year = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_megagram', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') -angstroms_per_year = Unit(1e-36, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_kilogram', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') -angstroms_per_year = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_milligram', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') -angstroms_per_year = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_microgram', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_nanogram', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') -angstroms_per_year = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_picogram', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') -angstroms_per_year = Unit(1e-54, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_femtogram', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-57, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_attogram', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -angstroms_per_year = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_gram', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') -angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_exagram', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') -angstroms_per_year = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_petagram', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') -angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_teragram', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') -angstroms_per_year = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_gigagram', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') -angstroms_per_year = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_megagram', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') -angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_kilogram', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') -angstroms_per_year = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_milligram', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_microgram', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_nanogram', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') -angstroms_per_year = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_picogram', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') -angstroms_per_year = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_femtogram', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_attogram', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_gram', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') -angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_exagram', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') -angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_petagram', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') -angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_teragram', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') -angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_gigagram', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') -angstroms_per_year = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_megagram', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') -angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_kilogram', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') -angstroms_per_year = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_milligram', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') -angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_microgram', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') -angstroms_per_year = Unit(1.0000000000000003e-30, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_nanogram', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') -angstroms_per_year = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_picogram', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_femtogram', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_attogram', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_gram', ascii_symbol='g km^-3', symbol='Nonekm⁻³') -angstroms_per_year = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_exagram', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') -angstroms_per_year = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_petagram', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') -angstroms_per_year = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_teragram', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') -angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_gigagram', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') -angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_megagram', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') -angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_kilogram', ascii_symbol='kg km^-3', symbol='kgkm⁻³') -angstroms_per_year = Unit(9.999999999999999e-16, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_milligram', ascii_symbol='mg km^-3', symbol='mgkm⁻³') -angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_microgram', ascii_symbol='ug km^-3', symbol='µgkm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_nanogram', ascii_symbol='ng km^-3', symbol='ngkm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-24, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_picogram', ascii_symbol='pg km^-3', symbol='pgkm⁻³') -angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_femtogram', ascii_symbol='fg km^-3', symbol='fgkm⁻³') -angstroms_per_year = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_attogram', ascii_symbol='ag km^-3', symbol='agkm⁻³') -angstroms_per_year = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_gram', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') -angstroms_per_year = Unit(1e+24, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_exagram', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') -angstroms_per_year = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_petagram', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') -angstroms_per_year = Unit(1e+18, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_teragram', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') -angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_gigagram', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') -angstroms_per_year = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_megagram', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') -angstroms_per_year = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_kilogram', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') -angstroms_per_year = Unit(999.9999999999999, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_milligram', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') -angstroms_per_year = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_microgram', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') -angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_nanogram', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') -angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_picogram', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') -angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_femtogram', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') -angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_attogram', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -angstroms_per_year = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_gram', ascii_symbol='g um^-3', symbol='Noneµm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_exagram', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') -angstroms_per_year = Unit(1.0000000000000002e+30, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_petagram', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') -angstroms_per_year = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_teragram', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+24, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_gigagram', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_megagram', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_kilogram', ascii_symbol='kg um^-3', symbol='kgµm⁻³') -angstroms_per_year = Unit(1000000000000.0001, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_milligram', ascii_symbol='mg um^-3', symbol='mgµm⁻³') -angstroms_per_year = Unit(1000000000.0000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_microgram', ascii_symbol='ug um^-3', symbol='µgµm⁻³') -angstroms_per_year = Unit(1000000.0000000003, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_nanogram', ascii_symbol='ng um^-3', symbol='ngµm⁻³') -angstroms_per_year = Unit(1000.0000000000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_picogram', ascii_symbol='pg um^-3', symbol='pgµm⁻³') -angstroms_per_year = Unit(1.0000000000000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_femtogram', ascii_symbol='fg um^-3', symbol='fgµm⁻³') -angstroms_per_year = Unit(0.0010000000000000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_attogram', ascii_symbol='ag um^-3', symbol='agµm⁻³') -angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_gram', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') -angstroms_per_year = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_exagram', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') -angstroms_per_year = Unit(9.999999999999998e+38, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_petagram', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') -angstroms_per_year = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_teragram', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') -angstroms_per_year = Unit(9.999999999999998e+32, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_gigagram', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') -angstroms_per_year = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_megagram', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') -angstroms_per_year = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_kilogram', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') -angstroms_per_year = Unit(9.999999999999997e+20, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_milligram', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') -angstroms_per_year = Unit(9.999999999999999e+17, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_microgram', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') -angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_nanogram', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') -angstroms_per_year = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_picogram', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') -angstroms_per_year = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_femtogram', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') -angstroms_per_year = Unit(999999.9999999999, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_attogram', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_gram', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') -angstroms_per_year = Unit(1e+51, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_exagram', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') -angstroms_per_year = Unit(1e+48, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_petagram', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+45, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_teragram', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') -angstroms_per_year = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_gigagram', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+39, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_megagram', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') -angstroms_per_year = Unit(1e+36, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_kilogram', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') -angstroms_per_year = Unit(1e+30, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_milligram', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') -angstroms_per_year = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_microgram', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') -angstroms_per_year = Unit(1.0000000000000003e+24, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_nanogram', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_picogram', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_femtogram', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') -angstroms_per_year = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_attogram', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -angstroms_per_year = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_gram', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') -angstroms_per_year = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_exagram', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+56, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_petagram', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+53, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_teragram', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+50, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_gigagram', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_megagram', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') -angstroms_per_year = Unit(9.999999999999998e+44, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_kilogram', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') -angstroms_per_year = Unit(9.999999999999996e+38, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_milligram', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_microgram', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') -angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_nanogram', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_picogram', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_femtogram', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') -angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_attogram', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -angstroms_per_year = Unit(9.999999999999998e+50, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_gram', ascii_symbol='g am^-3', symbol='Noneam⁻³') -angstroms_per_year = Unit(9.999999999999999e+68, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_exagram', ascii_symbol='Eg am^-3', symbol='Egam⁻³') -angstroms_per_year = Unit(9.999999999999998e+65, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_petagram', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') -angstroms_per_year = Unit(9.999999999999999e+62, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_teragram', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') -angstroms_per_year = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_gigagram', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') -angstroms_per_year = Unit(9.999999999999999e+56, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_megagram', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') -angstroms_per_year = Unit(9.999999999999999e+53, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_kilogram', ascii_symbol='kg am^-3', symbol='kgam⁻³') -angstroms_per_year = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_milligram', ascii_symbol='mg am^-3', symbol='mgam⁻³') -angstroms_per_year = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_microgram', ascii_symbol='ug am^-3', symbol='µgam⁻³') -angstroms_per_year = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_nanogram', ascii_symbol='ng am^-3', symbol='ngam⁻³') -angstroms_per_year = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_picogram', ascii_symbol='pg am^-3', symbol='pgam⁻³') -angstroms_per_year = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_femtogram', ascii_symbol='fg am^-3', symbol='fgam⁻³') -angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_attogram', ascii_symbol='ag am^-3', symbol='agam⁻³') -angstroms_per_year = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gram', ascii_symbol='g A^-3', symbol='NoneÅ⁻³') -angstroms_per_year = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_exagram', ascii_symbol='Eg A^-3', symbol='EgÅ⁻³') -angstroms_per_year = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_petagram', ascii_symbol='Pg A^-3', symbol='PgÅ⁻³') -angstroms_per_year = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_teragram', ascii_symbol='Tg A^-3', symbol='TgÅ⁻³') -angstroms_per_year = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gigagram', ascii_symbol='Gg A^-3', symbol='GgÅ⁻³') -angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_megagram', ascii_symbol='Mg A^-3', symbol='MgÅ⁻³') -angstroms_per_year = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_kilogram', ascii_symbol='kg A^-3', symbol='kgÅ⁻³') -angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_milligram', ascii_symbol='mg A^-3', symbol='mgÅ⁻³') -angstroms_per_year = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_microgram', ascii_symbol='ug A^-3', symbol='µgÅ⁻³') -angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_nanogram', ascii_symbol='ng A^-3', symbol='ngÅ⁻³') -angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_picogram', ascii_symbol='pg A^-3', symbol='pgÅ⁻³') -angstroms_per_year = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_femtogram', ascii_symbol='fg A^-3', symbol='fgÅ⁻³') -angstroms_per_year = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_attogram', ascii_symbol='ag A^-3', symbol='agÅ⁻³') -angstroms_per_year = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gram', ascii_symbol='g Ang^-3', symbol='NoneNone⁻³') -angstroms_per_year = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_exagram', ascii_symbol='Eg Ang^-3', symbol='EgNone⁻³') -angstroms_per_year = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_petagram', ascii_symbol='Pg Ang^-3', symbol='PgNone⁻³') -angstroms_per_year = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_teragram', ascii_symbol='Tg Ang^-3', symbol='TgNone⁻³') -angstroms_per_year = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gigagram', ascii_symbol='Gg Ang^-3', symbol='GgNone⁻³') -angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_megagram', ascii_symbol='Mg Ang^-3', symbol='MgNone⁻³') -angstroms_per_year = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_kilogram', ascii_symbol='kg Ang^-3', symbol='kgNone⁻³') -angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_milligram', ascii_symbol='mg Ang^-3', symbol='mgNone⁻³') -angstroms_per_year = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_microgram', ascii_symbol='ug Ang^-3', symbol='µgNone⁻³') -angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_nanogram', ascii_symbol='ng Ang^-3', symbol='ngNone⁻³') -angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_picogram', ascii_symbol='pg Ang^-3', symbol='pgNone⁻³') -angstroms_per_year = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_femtogram', ascii_symbol='fg Ang^-3', symbol='fgNone⁻³') -angstroms_per_year = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_attogram', ascii_symbol='ag Ang^-3', symbol='agNone⁻³') +gram_per_cubic_meters = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_meters', ascii_symbol='g m^-3', symbol='NoneNone⁻³') +exagram_per_cubic_meters = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_meters', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') +petagram_per_cubic_meters = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_meters', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') +teragram_per_cubic_meters = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_meters', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') +gigagram_per_cubic_meters = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_meters', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') +megagram_per_cubic_meters = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_meters', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') +kilogram_per_cubic_meters = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_meters', ascii_symbol='kg m^-3', symbol='kgNone⁻³') +milligram_per_cubic_meters = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_meters', ascii_symbol='mg m^-3', symbol='mgNone⁻³') +microgram_per_cubic_meters = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_meters', ascii_symbol='ug m^-3', symbol='µgNone⁻³') +nanogram_per_cubic_meters = Unit(1.0000000000000002e-12, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_meters', ascii_symbol='ng m^-3', symbol='ngNone⁻³') +picogram_per_cubic_meters = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_meters', ascii_symbol='pg m^-3', symbol='pgNone⁻³') +femtogram_per_cubic_meters = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_meters', ascii_symbol='fg m^-3', symbol='fgNone⁻³') +attogram_per_cubic_meters = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_meters', ascii_symbol='ag m^-3', symbol='agNone⁻³') +gram_per_cubic_exameters = Unit(1e-57, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_exameters', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') +exagram_per_cubic_exameters = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_exameters', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') +petagram_per_cubic_exameters = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_exameters', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') +teragram_per_cubic_exameters = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_exameters', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') +gigagram_per_cubic_exameters = Unit(1e-48, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_exameters', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') +megagram_per_cubic_exameters = Unit(9.999999999999999e-52, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_exameters', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') +kilogram_per_cubic_exameters = Unit(9.999999999999999e-55, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_exameters', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') +milligram_per_cubic_exameters = Unit(9.999999999999998e-61, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_exameters', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') +microgram_per_cubic_exameters = Unit(9.999999999999999e-64, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_exameters', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') +nanogram_per_cubic_exameters = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_exameters', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') +picogram_per_cubic_exameters = Unit(1e-69, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_exameters', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') +femtogram_per_cubic_exameters = Unit(1e-72, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_exameters', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') +attogram_per_cubic_exameters = Unit(1e-75, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_exameters', ascii_symbol='ag Em^-3', symbol='agEm⁻³') +gram_per_cubic_petameters = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_petameters', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') +exagram_per_cubic_petameters = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_petameters', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') +petagram_per_cubic_petameters = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_petameters', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') +teragram_per_cubic_petameters = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_petameters', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') +gigagram_per_cubic_petameters = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_petameters', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') +megagram_per_cubic_petameters = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_petameters', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') +kilogram_per_cubic_petameters = Unit(1.0000000000000001e-45, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_petameters', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') +milligram_per_cubic_petameters = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_petameters', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') +microgram_per_cubic_petameters = Unit(1.0000000000000002e-54, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_petameters', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') +nanogram_per_cubic_petameters = Unit(1.0000000000000002e-57, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_petameters', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') +picogram_per_cubic_petameters = Unit(1.0000000000000001e-60, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_petameters', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') +femtogram_per_cubic_petameters = Unit(1.0000000000000002e-63, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_petameters', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') +attogram_per_cubic_petameters = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_petameters', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') +gram_per_cubic_terameters = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_terameters', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') +exagram_per_cubic_terameters = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_terameters', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') +petagram_per_cubic_terameters = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_terameters', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') +teragram_per_cubic_terameters = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_terameters', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') +gigagram_per_cubic_terameters = Unit(9.999999999999999e-31, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_terameters', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') +megagram_per_cubic_terameters = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_terameters', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') +kilogram_per_cubic_terameters = Unit(1e-36, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_terameters', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') +milligram_per_cubic_terameters = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_terameters', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') +microgram_per_cubic_terameters = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_terameters', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') +nanogram_per_cubic_terameters = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_terameters', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') +picogram_per_cubic_terameters = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_terameters', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') +femtogram_per_cubic_terameters = Unit(1e-54, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_terameters', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') +attogram_per_cubic_terameters = Unit(1.0000000000000001e-57, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_terameters', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') +gram_per_cubic_gigameters = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_gigameters', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') +exagram_per_cubic_gigameters = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_gigameters', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') +petagram_per_cubic_gigameters = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_gigameters', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') +teragram_per_cubic_gigameters = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_gigameters', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') +gigagram_per_cubic_gigameters = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_gigameters', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') +megagram_per_cubic_gigameters = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_gigameters', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') +kilogram_per_cubic_gigameters = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_gigameters', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') +milligram_per_cubic_gigameters = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_gigameters', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') +microgram_per_cubic_gigameters = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_gigameters', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') +nanogram_per_cubic_gigameters = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_gigameters', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') +picogram_per_cubic_gigameters = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_gigameters', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') +femtogram_per_cubic_gigameters = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_gigameters', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') +attogram_per_cubic_gigameters = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_gigameters', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') +gram_per_cubic_megameters = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_megameters', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') +exagram_per_cubic_megameters = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_megameters', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') +petagram_per_cubic_megameters = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_megameters', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') +teragram_per_cubic_megameters = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_megameters', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') +gigagram_per_cubic_megameters = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_megameters', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') +megagram_per_cubic_megameters = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_megameters', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') +kilogram_per_cubic_megameters = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_megameters', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') +milligram_per_cubic_megameters = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_megameters', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') +microgram_per_cubic_megameters = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_megameters', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') +nanogram_per_cubic_megameters = Unit(1.0000000000000003e-30, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_megameters', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') +picogram_per_cubic_megameters = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_megameters', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') +femtogram_per_cubic_megameters = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_megameters', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') +attogram_per_cubic_megameters = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_megameters', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') +gram_per_cubic_kilometers = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_kilometers', ascii_symbol='g km^-3', symbol='Nonekm⁻³') +exagram_per_cubic_kilometers = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_kilometers', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') +petagram_per_cubic_kilometers = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_kilometers', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') +teragram_per_cubic_kilometers = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_kilometers', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') +gigagram_per_cubic_kilometers = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_kilometers', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') +megagram_per_cubic_kilometers = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_kilometers', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') +kilogram_per_cubic_kilometers = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_kilometers', ascii_symbol='kg km^-3', symbol='kgkm⁻³') +milligram_per_cubic_kilometers = Unit(9.999999999999999e-16, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_kilometers', ascii_symbol='mg km^-3', symbol='mgkm⁻³') +microgram_per_cubic_kilometers = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_kilometers', ascii_symbol='ug km^-3', symbol='µgkm⁻³') +nanogram_per_cubic_kilometers = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_kilometers', ascii_symbol='ng km^-3', symbol='ngkm⁻³') +picogram_per_cubic_kilometers = Unit(1.0000000000000001e-24, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_kilometers', ascii_symbol='pg km^-3', symbol='pgkm⁻³') +femtogram_per_cubic_kilometers = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_kilometers', ascii_symbol='fg km^-3', symbol='fgkm⁻³') +attogram_per_cubic_kilometers = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_kilometers', ascii_symbol='ag km^-3', symbol='agkm⁻³') +gram_per_cubic_millimeters = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_millimeters', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') +exagram_per_cubic_millimeters = Unit(1e+24, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_millimeters', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') +petagram_per_cubic_millimeters = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_millimeters', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') +teragram_per_cubic_millimeters = Unit(1e+18, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_millimeters', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') +gigagram_per_cubic_millimeters = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_millimeters', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') +megagram_per_cubic_millimeters = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_millimeters', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') +kilogram_per_cubic_millimeters = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_millimeters', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') +milligram_per_cubic_millimeters = Unit(999.9999999999999, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_millimeters', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') +microgram_per_cubic_millimeters = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_millimeters', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') +nanogram_per_cubic_millimeters = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_millimeters', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') +picogram_per_cubic_millimeters = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_millimeters', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') +femtogram_per_cubic_millimeters = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_millimeters', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') +attogram_per_cubic_millimeters = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_millimeters', ascii_symbol='ag mm^-3', symbol='agmm⁻³') +gram_per_cubic_micrometers = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_micrometers', ascii_symbol='g um^-3', symbol='Noneµm⁻³') +exagram_per_cubic_micrometers = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_micrometers', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') +petagram_per_cubic_micrometers = Unit(1.0000000000000002e+30, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_micrometers', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') +teragram_per_cubic_micrometers = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_micrometers', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') +gigagram_per_cubic_micrometers = Unit(1.0000000000000001e+24, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_micrometers', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') +megagram_per_cubic_micrometers = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_micrometers', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') +kilogram_per_cubic_micrometers = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_micrometers', ascii_symbol='kg um^-3', symbol='kgµm⁻³') +milligram_per_cubic_micrometers = Unit(1000000000000.0001, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_micrometers', ascii_symbol='mg um^-3', symbol='mgµm⁻³') +microgram_per_cubic_micrometers = Unit(1000000000.0000002, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_micrometers', ascii_symbol='ug um^-3', symbol='µgµm⁻³') +nanogram_per_cubic_micrometers = Unit(1000000.0000000003, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_micrometers', ascii_symbol='ng um^-3', symbol='ngµm⁻³') +picogram_per_cubic_micrometers = Unit(1000.0000000000002, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_micrometers', ascii_symbol='pg um^-3', symbol='pgµm⁻³') +femtogram_per_cubic_micrometers = Unit(1.0000000000000002, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_micrometers', ascii_symbol='fg um^-3', symbol='fgµm⁻³') +attogram_per_cubic_micrometers = Unit(0.0010000000000000002, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_micrometers', ascii_symbol='ag um^-3', symbol='agµm⁻³') +gram_per_cubic_nanometers = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_nanometers', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') +exagram_per_cubic_nanometers = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_nanometers', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') +petagram_per_cubic_nanometers = Unit(9.999999999999998e+38, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_nanometers', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') +teragram_per_cubic_nanometers = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_nanometers', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') +gigagram_per_cubic_nanometers = Unit(9.999999999999998e+32, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_nanometers', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') +megagram_per_cubic_nanometers = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_nanometers', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') +kilogram_per_cubic_nanometers = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_nanometers', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') +milligram_per_cubic_nanometers = Unit(9.999999999999997e+20, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_nanometers', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') +microgram_per_cubic_nanometers = Unit(9.999999999999999e+17, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_nanometers', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') +nanogram_per_cubic_nanometers = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_nanometers', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') +picogram_per_cubic_nanometers = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_nanometers', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') +femtogram_per_cubic_nanometers = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_nanometers', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') +attogram_per_cubic_nanometers = Unit(999999.9999999999, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_nanometers', ascii_symbol='ag nm^-3', symbol='agnm⁻³') +gram_per_cubic_picometers = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_picometers', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') +exagram_per_cubic_picometers = Unit(1e+51, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_picometers', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') +petagram_per_cubic_picometers = Unit(1e+48, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_picometers', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') +teragram_per_cubic_picometers = Unit(1.0000000000000001e+45, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_picometers', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') +gigagram_per_cubic_picometers = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_picometers', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') +megagram_per_cubic_picometers = Unit(1.0000000000000001e+39, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_picometers', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') +kilogram_per_cubic_picometers = Unit(1e+36, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_picometers', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') +milligram_per_cubic_picometers = Unit(1e+30, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_picometers', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') +microgram_per_cubic_picometers = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_picometers', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') +nanogram_per_cubic_picometers = Unit(1.0000000000000003e+24, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_picometers', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') +picogram_per_cubic_picometers = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_picometers', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') +femtogram_per_cubic_picometers = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_picometers', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') +attogram_per_cubic_picometers = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_picometers', ascii_symbol='ag pm^-3', symbol='agpm⁻³') +gram_per_cubic_femtometers = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_femtometers', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') +exagram_per_cubic_femtometers = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_femtometers', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') +petagram_per_cubic_femtometers = Unit(9.999999999999997e+56, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_femtometers', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') +teragram_per_cubic_femtometers = Unit(9.999999999999997e+53, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_femtometers', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') +gigagram_per_cubic_femtometers = Unit(9.999999999999997e+50, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_femtometers', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') +megagram_per_cubic_femtometers = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_femtometers', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') +kilogram_per_cubic_femtometers = Unit(9.999999999999998e+44, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_femtometers', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') +milligram_per_cubic_femtometers = Unit(9.999999999999996e+38, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_femtometers', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') +microgram_per_cubic_femtometers = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_femtometers', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') +nanogram_per_cubic_femtometers = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_femtometers', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') +picogram_per_cubic_femtometers = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_femtometers', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') +femtogram_per_cubic_femtometers = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_femtometers', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') +attogram_per_cubic_femtometers = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_femtometers', ascii_symbol='ag fm^-3', symbol='agfm⁻³') +gram_per_cubic_attometers = Unit(9.999999999999998e+50, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_attometers', ascii_symbol='g am^-3', symbol='Noneam⁻³') +exagram_per_cubic_attometers = Unit(9.999999999999999e+68, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_attometers', ascii_symbol='Eg am^-3', symbol='Egam⁻³') +petagram_per_cubic_attometers = Unit(9.999999999999998e+65, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_attometers', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') +teragram_per_cubic_attometers = Unit(9.999999999999999e+62, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_attometers', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') +gigagram_per_cubic_attometers = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_attometers', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') +megagram_per_cubic_attometers = Unit(9.999999999999999e+56, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_attometers', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') +kilogram_per_cubic_attometers = Unit(9.999999999999999e+53, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_attometers', ascii_symbol='kg am^-3', symbol='kgam⁻³') +milligram_per_cubic_attometers = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_attometers', ascii_symbol='mg am^-3', symbol='mgam⁻³') +microgram_per_cubic_attometers = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_attometers', ascii_symbol='ug am^-3', symbol='µgam⁻³') +nanogram_per_cubic_attometers = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_attometers', ascii_symbol='ng am^-3', symbol='ngam⁻³') +picogram_per_cubic_attometers = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_attometers', ascii_symbol='pg am^-3', symbol='pgam⁻³') +femtogram_per_cubic_attometers = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_attometers', ascii_symbol='fg am^-3', symbol='fgam⁻³') +attogram_per_cubic_attometers = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_attometers', ascii_symbol='ag am^-3', symbol='agam⁻³') +gram_per_cubic_angstroms = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_angstroms', ascii_symbol='g A^-3', symbol='NoneÅ⁻³') +exagram_per_cubic_angstroms = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_angstroms', ascii_symbol='Eg A^-3', symbol='EgÅ⁻³') +petagram_per_cubic_angstroms = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_angstroms', ascii_symbol='Pg A^-3', symbol='PgÅ⁻³') +teragram_per_cubic_angstroms = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_angstroms', ascii_symbol='Tg A^-3', symbol='TgÅ⁻³') +gigagram_per_cubic_angstroms = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_angstroms', ascii_symbol='Gg A^-3', symbol='GgÅ⁻³') +megagram_per_cubic_angstroms = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_angstroms', ascii_symbol='Mg A^-3', symbol='MgÅ⁻³') +kilogram_per_cubic_angstroms = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_angstroms', ascii_symbol='kg A^-3', symbol='kgÅ⁻³') +milligram_per_cubic_angstroms = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_angstroms', ascii_symbol='mg A^-3', symbol='mgÅ⁻³') +microgram_per_cubic_angstroms = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_angstroms', ascii_symbol='ug A^-3', symbol='µgÅ⁻³') +nanogram_per_cubic_angstroms = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_angstroms', ascii_symbol='ng A^-3', symbol='ngÅ⁻³') +picogram_per_cubic_angstroms = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_angstroms', ascii_symbol='pg A^-3', symbol='pgÅ⁻³') +femtogram_per_cubic_angstroms = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_angstroms', ascii_symbol='fg A^-3', symbol='fgÅ⁻³') +attogram_per_cubic_angstroms = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_angstroms', ascii_symbol='ag A^-3', symbol='agÅ⁻³') +gram_per_cubic_angstroms = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_angstroms', ascii_symbol='g Ang^-3', symbol='NoneNone⁻³') +exagram_per_cubic_angstroms = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_angstroms', ascii_symbol='Eg Ang^-3', symbol='EgNone⁻³') +petagram_per_cubic_angstroms = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_angstroms', ascii_symbol='Pg Ang^-3', symbol='PgNone⁻³') +teragram_per_cubic_angstroms = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_angstroms', ascii_symbol='Tg Ang^-3', symbol='TgNone⁻³') +gigagram_per_cubic_angstroms = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_angstroms', ascii_symbol='Gg Ang^-3', symbol='GgNone⁻³') +megagram_per_cubic_angstroms = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_angstroms', ascii_symbol='Mg Ang^-3', symbol='MgNone⁻³') +kilogram_per_cubic_angstroms = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_angstroms', ascii_symbol='kg Ang^-3', symbol='kgNone⁻³') +milligram_per_cubic_angstroms = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_angstroms', ascii_symbol='mg Ang^-3', symbol='mgNone⁻³') +microgram_per_cubic_angstroms = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_angstroms', ascii_symbol='ug Ang^-3', symbol='µgNone⁻³') +nanogram_per_cubic_angstroms = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_angstroms', ascii_symbol='ng Ang^-3', symbol='ngNone⁻³') +picogram_per_cubic_angstroms = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_angstroms', ascii_symbol='pg Ang^-3', symbol='pgNone⁻³') +femtogram_per_cubic_angstroms = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_angstroms', ascii_symbol='fg Ang^-3', symbol='fgNone⁻³') +attogram_per_cubic_angstroms = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_angstroms', ascii_symbol='ag Ang^-3', symbol='agNone⁻³') # # Lookup table from symbols to units From 346f4211226df48ca00c3679ea05a11e6ba7be51 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 6 Aug 2024 15:43:45 +0100 Subject: [PATCH 008/675] Use alias list to remove duplicates --- sasdata/quantities/_units_table.py | 26 +- sasdata/quantities/units.py | 678 +++++++++++------------------ 2 files changed, 263 insertions(+), 441 deletions(-) diff --git a/sasdata/quantities/_units_table.py b/sasdata/quantities/_units_table.py index 27165bde8..0749461f8 100644 --- a/sasdata/quantities/_units_table.py +++ b/sasdata/quantities/_units_table.py @@ -50,20 +50,23 @@ ] non_si_units = [ - ("A", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), - ("Ang", None, "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), + ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, []), - ("hr", None, "hour", "hours", 360, 0, 1, 0, 0, 0, []), + ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, []), ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), - ("day", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), - ("yr", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, []), ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, []), ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []), ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, []) ] +aliases = { + "y": ["yr", "year"], + "d": ["day"], + "h": ["hr", "hour"], + "Ang": ["A", "Å"] +} all_units = base_si_units + derived_si_units + non_si_units @@ -215,8 +218,8 @@ def format_name(name: str): unit_types[hash(accel_dimensions)].append(accel_name) # Density - for length_symbol, length_special_symbol, _, length_name, length_scale, _ in length_units: - for mass_symbol, mass_special_symbol, mass_name, _, mass_scale, _ in mass_units: + for length_symbol, length_special_symbol, length_name, _, length_scale, _ in length_units: + for mass_symbol, mass_special_symbol, _, mass_name, mass_scale, _ in mass_units: name = mass_name + "_per_cubic_" + length_name @@ -231,6 +234,15 @@ def format_name(name: str): unit_types[hash(dimensions)].append(name) + # + # Add aliases to symbol lookup table + # + + for base_name in aliases: + alias_list = aliases[base_name] + for alias in alias_list: + symbol_lookup[alias] = symbol_lookup[base_name] + # # Write out the symbol lookup table # diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 60517eca8..cedfb353a 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -417,14 +417,11 @@ def __init__(self, name: str, units: list[Unit]): femtohenry = Unit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') attohenry = Unit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1),name='degrees_celsius',ascii_symbol='C',symbol='C') -angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0),name='angstroms',ascii_symbol='A',symbol='Å') -angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Ang') +angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') minutes = Unit(60, Dimensions(0, 1, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') -hours = Unit(360, Dimensions(0, 1, 0, 0, 0),name='hours',ascii_symbol='hr',symbol='hr') +hours = Unit(360, Dimensions(0, 1, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') days = Unit(8640, Dimensions(0, 1, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') -days = Unit(8640, Dimensions(0, 1, 0, 0, 0),name='days',ascii_symbol='day',symbol='day') years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') -years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0),name='years',ascii_symbol='yr',symbol='yr') degrees = Unit(57.29577951308232, Dimensions(0, 0, 0, 0, 0),name='degrees',ascii_symbol='deg',symbol='deg') radians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='radians',ascii_symbol='rad',symbol='rad') stradians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='stradians',ascii_symbol='sr',symbol='sr') @@ -494,16 +491,11 @@ def __init__(self, name: str, units: list[Unit]): per_attometer = Unit(9.999999999999999e+17, Dimensions(-1, 0, 0, 0, 0), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') per_square_attometer = Unit(9.999999999999999e+35, Dimensions(-2, 0, 0, 0, 0), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') per_cubic_attometer = Unit(9.999999999999997e+53, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') -square_angstroms = Unit(1.0000000000000001e-20, Dimensions(2, 0, 0, 0, 0), name='square_angstroms', ascii_symbol='A^2', symbol='Ų') -cubic_angstroms = Unit(1e-30, Dimensions(3, 0, 0, 0, 0), name='cubic_angstroms', ascii_symbol='A^3', symbol='ų') -per_angstrom = Unit(10000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_angstrom', ascii_symbol='A^-1', symbol='Å⁻¹') -per_square_angstrom = Unit(1e+20, Dimensions(-2, 0, 0, 0, 0), name='per_square_angstrom', ascii_symbol='A^-2', symbol='Å⁻²') -per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_angstrom', ascii_symbol='A^-3', symbol='Å⁻³') -square_angstroms = Unit(1.0000000000000001e-20, Dimensions(2, 0, 0, 0, 0), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ang²') -cubic_angstroms = Unit(1e-30, Dimensions(3, 0, 0, 0, 0), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='Ang³') -per_angstrom = Unit(10000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Ang⁻¹') -per_square_angstrom = Unit(1e+20, Dimensions(-2, 0, 0, 0, 0), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Ang⁻²') -per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Ang⁻³') +square_angstroms = Unit(1.0000000000000001e-20, Dimensions(2, 0, 0, 0, 0), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ų') +cubic_angstroms = Unit(1e-30, Dimensions(3, 0, 0, 0, 0), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='ų') +per_angstrom = Unit(10000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Å⁻¹') +per_square_angstrom = Unit(1e+20, Dimensions(-2, 0, 0, 0, 0), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Å⁻²') +per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Å⁻³') meters_per_second = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') meters_per_square_second = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') meters_per_millisecond = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') @@ -520,16 +512,12 @@ def __init__(self, name: str, units: list[Unit]): meters_per_square_attosecond = Unit(9.999999999999999e+17, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') meters_per_minute = Unit(0.016666666666666666, Dimensions(1, -1, 0, 0, 0), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') meters_per_square_minute = Unit(0.016666666666666666, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') -meters_per_hour = Unit(0.002777777777777778, Dimensions(1, -1, 0, 0, 0), name='meters_per_hour', ascii_symbol='m/hr', symbol='NoneNone⁻¹') -meters_per_square_hour = Unit(0.002777777777777778, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_hour', ascii_symbol='m/hr^2', symbol='NoneNone⁻²') +meters_per_hour = Unit(0.002777777777777778, Dimensions(1, -1, 0, 0, 0), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') +meters_per_square_hour = Unit(0.002777777777777778, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') meters_per_day = Unit(0.00011574074074074075, Dimensions(1, -1, 0, 0, 0), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') meters_per_square_day = Unit(0.00011574074074074075, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') -meters_per_day = Unit(0.00011574074074074075, Dimensions(1, -1, 0, 0, 0), name='meters_per_day', ascii_symbol='m/day', symbol='NoneNone⁻¹') -meters_per_square_day = Unit(0.00011574074074074075, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_day', ascii_symbol='m/day^2', symbol='NoneNone⁻²') meters_per_year = Unit(3.168873850681143e-07, Dimensions(1, -1, 0, 0, 0), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') -meters_per_year = Unit(3.168873850681143e-07, Dimensions(1, -1, 0, 0, 0), name='meters_per_year', ascii_symbol='m/yr', symbol='NoneNone⁻¹') -meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_year', ascii_symbol='m/yr^2', symbol='NoneNone⁻²') exameters_per_second = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') exameters_per_square_second = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') exameters_per_millisecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') @@ -546,16 +534,12 @@ def __init__(self, name: str, units: list[Unit]): exameters_per_square_attosecond = Unit(9.999999999999999e+35, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') exameters_per_minute = Unit(1.6666666666666666e+16, Dimensions(1, -1, 0, 0, 0), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') exameters_per_square_minute = Unit(1.6666666666666666e+16, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') -exameters_per_hour = Unit(2777777777777778.0, Dimensions(1, -1, 0, 0, 0), name='exameters_per_hour', ascii_symbol='Em/hr', symbol='EmNone⁻¹') -exameters_per_square_hour = Unit(2777777777777778.0, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_hour', ascii_symbol='Em/hr^2', symbol='EmNone⁻²') +exameters_per_hour = Unit(2777777777777778.0, Dimensions(1, -1, 0, 0, 0), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') +exameters_per_square_hour = Unit(2777777777777778.0, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') exameters_per_day = Unit(115740740740740.73, Dimensions(1, -1, 0, 0, 0), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') exameters_per_square_day = Unit(115740740740740.73, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') -exameters_per_day = Unit(115740740740740.73, Dimensions(1, -1, 0, 0, 0), name='exameters_per_day', ascii_symbol='Em/day', symbol='EmNone⁻¹') -exameters_per_square_day = Unit(115740740740740.73, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_day', ascii_symbol='Em/day^2', symbol='EmNone⁻²') exameters_per_year = Unit(316887385068.1143, Dimensions(1, -1, 0, 0, 0), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') exameters_per_square_year = Unit(316887385068.1143, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') -exameters_per_year = Unit(316887385068.1143, Dimensions(1, -1, 0, 0, 0), name='exameters_per_year', ascii_symbol='Em/yr', symbol='EmNone⁻¹') -exameters_per_square_year = Unit(316887385068.1143, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_year', ascii_symbol='Em/yr^2', symbol='EmNone⁻²') petameters_per_second = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') petameters_per_square_second = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') petameters_per_millisecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') @@ -572,16 +556,12 @@ def __init__(self, name: str, units: list[Unit]): petameters_per_square_attosecond = Unit(1e+33, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') petameters_per_minute = Unit(16666666666666.666, Dimensions(1, -1, 0, 0, 0), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') petameters_per_square_minute = Unit(16666666666666.666, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') -petameters_per_hour = Unit(2777777777777.778, Dimensions(1, -1, 0, 0, 0), name='petameters_per_hour', ascii_symbol='Pm/hr', symbol='PmNone⁻¹') -petameters_per_square_hour = Unit(2777777777777.778, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_hour', ascii_symbol='Pm/hr^2', symbol='PmNone⁻²') +petameters_per_hour = Unit(2777777777777.778, Dimensions(1, -1, 0, 0, 0), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') +petameters_per_square_hour = Unit(2777777777777.778, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') petameters_per_day = Unit(115740740740.74074, Dimensions(1, -1, 0, 0, 0), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') petameters_per_square_day = Unit(115740740740.74074, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') -petameters_per_day = Unit(115740740740.74074, Dimensions(1, -1, 0, 0, 0), name='petameters_per_day', ascii_symbol='Pm/day', symbol='PmNone⁻¹') -petameters_per_square_day = Unit(115740740740.74074, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_day', ascii_symbol='Pm/day^2', symbol='PmNone⁻²') petameters_per_year = Unit(316887385.0681143, Dimensions(1, -1, 0, 0, 0), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') petameters_per_square_year = Unit(316887385.0681143, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') -petameters_per_year = Unit(316887385.0681143, Dimensions(1, -1, 0, 0, 0), name='petameters_per_year', ascii_symbol='Pm/yr', symbol='PmNone⁻¹') -petameters_per_square_year = Unit(316887385.0681143, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_year', ascii_symbol='Pm/yr^2', symbol='PmNone⁻²') terameters_per_second = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') terameters_per_square_second = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') terameters_per_millisecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') @@ -598,16 +578,12 @@ def __init__(self, name: str, units: list[Unit]): terameters_per_square_attosecond = Unit(9.999999999999999e+29, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') terameters_per_minute = Unit(16666666666.666666, Dimensions(1, -1, 0, 0, 0), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') terameters_per_square_minute = Unit(16666666666.666666, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') -terameters_per_hour = Unit(2777777777.7777777, Dimensions(1, -1, 0, 0, 0), name='terameters_per_hour', ascii_symbol='Tm/hr', symbol='TmNone⁻¹') -terameters_per_square_hour = Unit(2777777777.7777777, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_hour', ascii_symbol='Tm/hr^2', symbol='TmNone⁻²') +terameters_per_hour = Unit(2777777777.7777777, Dimensions(1, -1, 0, 0, 0), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') +terameters_per_square_hour = Unit(2777777777.7777777, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') terameters_per_day = Unit(115740740.74074075, Dimensions(1, -1, 0, 0, 0), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') terameters_per_square_day = Unit(115740740.74074075, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') -terameters_per_day = Unit(115740740.74074075, Dimensions(1, -1, 0, 0, 0), name='terameters_per_day', ascii_symbol='Tm/day', symbol='TmNone⁻¹') -terameters_per_square_day = Unit(115740740.74074075, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_day', ascii_symbol='Tm/day^2', symbol='TmNone⁻²') terameters_per_year = Unit(316887.38506811426, Dimensions(1, -1, 0, 0, 0), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') terameters_per_square_year = Unit(316887.38506811426, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') -terameters_per_year = Unit(316887.38506811426, Dimensions(1, -1, 0, 0, 0), name='terameters_per_year', ascii_symbol='Tm/yr', symbol='TmNone⁻¹') -terameters_per_square_year = Unit(316887.38506811426, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_year', ascii_symbol='Tm/yr^2', symbol='TmNone⁻²') gigameters_per_second = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') gigameters_per_square_second = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') gigameters_per_millisecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') @@ -624,16 +600,12 @@ def __init__(self, name: str, units: list[Unit]): gigameters_per_square_attosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') gigameters_per_minute = Unit(16666666.666666666, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') gigameters_per_square_minute = Unit(16666666.666666666, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') -gigameters_per_hour = Unit(2777777.777777778, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_hour', ascii_symbol='Gm/hr', symbol='GmNone⁻¹') -gigameters_per_square_hour = Unit(2777777.777777778, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_hour', ascii_symbol='Gm/hr^2', symbol='GmNone⁻²') +gigameters_per_hour = Unit(2777777.777777778, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') +gigameters_per_square_hour = Unit(2777777.777777778, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') gigameters_per_day = Unit(115740.74074074074, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') gigameters_per_square_day = Unit(115740.74074074074, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') -gigameters_per_day = Unit(115740.74074074074, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_day', ascii_symbol='Gm/day', symbol='GmNone⁻¹') -gigameters_per_square_day = Unit(115740.74074074074, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_day', ascii_symbol='Gm/day^2', symbol='GmNone⁻²') gigameters_per_year = Unit(316.88738506811427, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') gigameters_per_square_year = Unit(316.88738506811427, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') -gigameters_per_year = Unit(316.88738506811427, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_year', ascii_symbol='Gm/yr', symbol='GmNone⁻¹') -gigameters_per_square_year = Unit(316.88738506811427, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_year', ascii_symbol='Gm/yr^2', symbol='GmNone⁻²') megameters_per_second = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') megameters_per_square_second = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') megameters_per_millisecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') @@ -650,16 +622,12 @@ def __init__(self, name: str, units: list[Unit]): megameters_per_square_attosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') megameters_per_minute = Unit(16666.666666666668, Dimensions(1, -1, 0, 0, 0), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') megameters_per_square_minute = Unit(16666.666666666668, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') -megameters_per_hour = Unit(2777.777777777778, Dimensions(1, -1, 0, 0, 0), name='megameters_per_hour', ascii_symbol='Mm/hr', symbol='MmNone⁻¹') -megameters_per_square_hour = Unit(2777.777777777778, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_hour', ascii_symbol='Mm/hr^2', symbol='MmNone⁻²') +megameters_per_hour = Unit(2777.777777777778, Dimensions(1, -1, 0, 0, 0), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') +megameters_per_square_hour = Unit(2777.777777777778, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') megameters_per_day = Unit(115.74074074074075, Dimensions(1, -1, 0, 0, 0), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') megameters_per_square_day = Unit(115.74074074074075, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') -megameters_per_day = Unit(115.74074074074075, Dimensions(1, -1, 0, 0, 0), name='megameters_per_day', ascii_symbol='Mm/day', symbol='MmNone⁻¹') -megameters_per_square_day = Unit(115.74074074074075, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_day', ascii_symbol='Mm/day^2', symbol='MmNone⁻²') megameters_per_year = Unit(0.3168873850681143, Dimensions(1, -1, 0, 0, 0), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') megameters_per_square_year = Unit(0.3168873850681143, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') -megameters_per_year = Unit(0.3168873850681143, Dimensions(1, -1, 0, 0, 0), name='megameters_per_year', ascii_symbol='Mm/yr', symbol='MmNone⁻¹') -megameters_per_square_year = Unit(0.3168873850681143, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_year', ascii_symbol='Mm/yr^2', symbol='MmNone⁻²') kilometers_per_second = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') kilometers_per_square_second = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') kilometers_per_millisecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') @@ -676,16 +644,12 @@ def __init__(self, name: str, units: list[Unit]): kilometers_per_square_attosecond = Unit(9.999999999999999e+20, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') kilometers_per_minute = Unit(16.666666666666668, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') kilometers_per_square_minute = Unit(16.666666666666668, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') -kilometers_per_hour = Unit(2.7777777777777777, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_hour', ascii_symbol='km/hr', symbol='kmNone⁻¹') -kilometers_per_square_hour = Unit(2.7777777777777777, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_hour', ascii_symbol='km/hr^2', symbol='kmNone⁻²') +kilometers_per_hour = Unit(2.7777777777777777, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') +kilometers_per_square_hour = Unit(2.7777777777777777, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') kilometers_per_day = Unit(0.11574074074074074, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') -kilometers_per_day = Unit(0.11574074074074074, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_day', ascii_symbol='km/day', symbol='kmNone⁻¹') -kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_day', ascii_symbol='km/day^2', symbol='kmNone⁻²') kilometers_per_year = Unit(0.0003168873850681143, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') -kilometers_per_year = Unit(0.0003168873850681143, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_year', ascii_symbol='km/yr', symbol='kmNone⁻¹') -kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_year', ascii_symbol='km/yr^2', symbol='kmNone⁻²') millimeters_per_second = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') millimeters_per_square_second = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') millimeters_per_millisecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') @@ -702,16 +666,12 @@ def __init__(self, name: str, units: list[Unit]): millimeters_per_square_attosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') millimeters_per_minute = Unit(1.6666666666666667e-05, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') millimeters_per_square_minute = Unit(1.6666666666666667e-05, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') -millimeters_per_hour = Unit(2.777777777777778e-06, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_hour', ascii_symbol='mm/hr', symbol='mmNone⁻¹') -millimeters_per_square_hour = Unit(2.777777777777778e-06, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_hour', ascii_symbol='mm/hr^2', symbol='mmNone⁻²') +millimeters_per_hour = Unit(2.777777777777778e-06, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') +millimeters_per_square_hour = Unit(2.777777777777778e-06, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') -millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_day', ascii_symbol='mm/day', symbol='mmNone⁻¹') -millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_day', ascii_symbol='mm/day^2', symbol='mmNone⁻²') millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') -millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_year', ascii_symbol='mm/yr', symbol='mmNone⁻¹') -millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_year', ascii_symbol='mm/yr^2', symbol='mmNone⁻²') micrometers_per_second = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') micrometers_per_square_second = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') micrometers_per_millisecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') @@ -728,16 +688,12 @@ def __init__(self, name: str, units: list[Unit]): micrometers_per_square_attosecond = Unit(999999999999.9999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') micrometers_per_minute = Unit(1.6666666666666667e-08, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') micrometers_per_square_minute = Unit(1.6666666666666667e-08, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') -micrometers_per_hour = Unit(2.7777777777777776e-09, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_hour', ascii_symbol='um/hr', symbol='µmNone⁻¹') -micrometers_per_square_hour = Unit(2.7777777777777776e-09, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_hour', ascii_symbol='um/hr^2', symbol='µmNone⁻²') +micrometers_per_hour = Unit(2.7777777777777776e-09, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') +micrometers_per_square_hour = Unit(2.7777777777777776e-09, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') -micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_day', ascii_symbol='um/day', symbol='µmNone⁻¹') -micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_day', ascii_symbol='um/day^2', symbol='µmNone⁻²') micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') -micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_year', ascii_symbol='um/yr', symbol='µmNone⁻¹') -micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_year', ascii_symbol='um/yr^2', symbol='µmNone⁻²') nanometers_per_second = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') nanometers_per_square_second = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') nanometers_per_millisecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') @@ -754,16 +710,12 @@ def __init__(self, name: str, units: list[Unit]): nanometers_per_square_attosecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') nanometers_per_minute = Unit(1.6666666666666667e-11, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') nanometers_per_square_minute = Unit(1.6666666666666667e-11, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') -nanometers_per_hour = Unit(2.777777777777778e-12, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_hour', ascii_symbol='nm/hr', symbol='nmNone⁻¹') -nanometers_per_square_hour = Unit(2.777777777777778e-12, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_hour', ascii_symbol='nm/hr^2', symbol='nmNone⁻²') +nanometers_per_hour = Unit(2.777777777777778e-12, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') +nanometers_per_square_hour = Unit(2.777777777777778e-12, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') -nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_day', ascii_symbol='nm/day', symbol='nmNone⁻¹') -nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_day', ascii_symbol='nm/day^2', symbol='nmNone⁻²') nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') -nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_year', ascii_symbol='nm/yr', symbol='nmNone⁻¹') -nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_year', ascii_symbol='nm/yr^2', symbol='nmNone⁻²') picometers_per_second = Unit(1e-12, Dimensions(1, -1, 0, 0, 0), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') picometers_per_square_second = Unit(1e-12, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') picometers_per_millisecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') @@ -780,16 +732,12 @@ def __init__(self, name: str, units: list[Unit]): picometers_per_square_attosecond = Unit(999999.9999999999, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') picometers_per_minute = Unit(1.6666666666666667e-14, Dimensions(1, -1, 0, 0, 0), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') picometers_per_square_minute = Unit(1.6666666666666667e-14, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') -picometers_per_hour = Unit(2.7777777777777776e-15, Dimensions(1, -1, 0, 0, 0), name='picometers_per_hour', ascii_symbol='pm/hr', symbol='pmNone⁻¹') -picometers_per_square_hour = Unit(2.7777777777777776e-15, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_hour', ascii_symbol='pm/hr^2', symbol='pmNone⁻²') +picometers_per_hour = Unit(2.7777777777777776e-15, Dimensions(1, -1, 0, 0, 0), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') +picometers_per_square_hour = Unit(2.7777777777777776e-15, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(1, -1, 0, 0, 0), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') -picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(1, -1, 0, 0, 0), name='picometers_per_day', ascii_symbol='pm/day', symbol='pmNone⁻¹') -picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_day', ascii_symbol='pm/day^2', symbol='pmNone⁻²') picometers_per_year = Unit(3.168873850681143e-19, Dimensions(1, -1, 0, 0, 0), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') -picometers_per_year = Unit(3.168873850681143e-19, Dimensions(1, -1, 0, 0, 0), name='picometers_per_year', ascii_symbol='pm/yr', symbol='pmNone⁻¹') -picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_year', ascii_symbol='pm/yr^2', symbol='pmNone⁻²') femtometers_per_second = Unit(1e-15, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') femtometers_per_square_second = Unit(1e-15, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') femtometers_per_millisecond = Unit(1e-12, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') @@ -806,16 +754,12 @@ def __init__(self, name: str, units: list[Unit]): femtometers_per_square_attosecond = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') femtometers_per_minute = Unit(1.6666666666666667e-17, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') femtometers_per_square_minute = Unit(1.6666666666666667e-17, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') -femtometers_per_hour = Unit(2.777777777777778e-18, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_hour', ascii_symbol='fm/hr', symbol='fmNone⁻¹') -femtometers_per_square_hour = Unit(2.777777777777778e-18, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_hour', ascii_symbol='fm/hr^2', symbol='fmNone⁻²') +femtometers_per_hour = Unit(2.777777777777778e-18, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') +femtometers_per_square_hour = Unit(2.777777777777778e-18, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') -femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_day', ascii_symbol='fm/day', symbol='fmNone⁻¹') -femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_day', ascii_symbol='fm/day^2', symbol='fmNone⁻²') femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') -femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_year', ascii_symbol='fm/yr', symbol='fmNone⁻¹') -femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_year', ascii_symbol='fm/yr^2', symbol='fmNone⁻²') attometers_per_second = Unit(1e-18, Dimensions(1, -1, 0, 0, 0), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') attometers_per_square_second = Unit(1e-18, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') attometers_per_millisecond = Unit(1e-15, Dimensions(1, -1, 0, 0, 0), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') @@ -832,263 +776,216 @@ def __init__(self, name: str, units: list[Unit]): attometers_per_square_attosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') attometers_per_minute = Unit(1.6666666666666668e-20, Dimensions(1, -1, 0, 0, 0), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') attometers_per_square_minute = Unit(1.6666666666666668e-20, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') -attometers_per_hour = Unit(2.7777777777777778e-21, Dimensions(1, -1, 0, 0, 0), name='attometers_per_hour', ascii_symbol='am/hr', symbol='amNone⁻¹') -attometers_per_square_hour = Unit(2.7777777777777778e-21, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_hour', ascii_symbol='am/hr^2', symbol='amNone⁻²') +attometers_per_hour = Unit(2.7777777777777778e-21, Dimensions(1, -1, 0, 0, 0), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') +attometers_per_square_hour = Unit(2.7777777777777778e-21, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(1, -1, 0, 0, 0), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') -attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(1, -1, 0, 0, 0), name='attometers_per_day', ascii_symbol='am/day', symbol='amNone⁻¹') -attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_day', ascii_symbol='am/day^2', symbol='amNone⁻²') attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(1, -1, 0, 0, 0), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') -attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(1, -1, 0, 0, 0), name='attometers_per_year', ascii_symbol='am/yr', symbol='amNone⁻¹') -attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_year', ascii_symbol='am/yr^2', symbol='amNone⁻²') -angstroms_per_second = Unit(1e-10, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_second', ascii_symbol='A/s', symbol='ÅNone⁻¹') -angstroms_per_square_second = Unit(1e-10, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_second', ascii_symbol='A/s^2', symbol='ÅNone⁻²') -angstroms_per_millisecond = Unit(1e-07, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_millisecond', ascii_symbol='A/ms', symbol='Åms⁻¹') -angstroms_per_square_millisecond = Unit(1e-07, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_millisecond', ascii_symbol='A/ms^2', symbol='Åms⁻²') -angstroms_per_microsecond = Unit(0.0001, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_microsecond', ascii_symbol='A/us', symbol='ŵs⁻¹') -angstroms_per_square_microsecond = Unit(0.0001, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_microsecond', ascii_symbol='A/us^2', symbol='ŵs⁻²') -angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_nanosecond', ascii_symbol='A/ns', symbol='Åns⁻¹') -angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_nanosecond', ascii_symbol='A/ns^2', symbol='Åns⁻²') -angstroms_per_picosecond = Unit(100.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_picosecond', ascii_symbol='A/ps', symbol='Åps⁻¹') -angstroms_per_square_picosecond = Unit(100.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_picosecond', ascii_symbol='A/ps^2', symbol='Åps⁻²') -angstroms_per_femtosecond = Unit(100000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_femtosecond', ascii_symbol='A/fs', symbol='Åfs⁻¹') -angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_femtosecond', ascii_symbol='A/fs^2', symbol='Åfs⁻²') -angstroms_per_attosecond = Unit(100000000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_attosecond', ascii_symbol='A/as', symbol='Åas⁻¹') -angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_attosecond', ascii_symbol='A/as^2', symbol='Åas⁻²') -angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_minute', ascii_symbol='A/min', symbol='ÅNone⁻¹') -angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_minute', ascii_symbol='A/min^2', symbol='ÅNone⁻²') -angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_hour', ascii_symbol='A/hr', symbol='ÅNone⁻¹') -angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_hour', ascii_symbol='A/hr^2', symbol='ÅNone⁻²') -angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='A/d', symbol='ÅNone⁻¹') -angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='A/d^2', symbol='ÅNone⁻²') -angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='A/day', symbol='ÅNone⁻¹') -angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='A/day^2', symbol='ÅNone⁻²') -angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='A/y', symbol='ÅNone⁻¹') -angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='A/y^2', symbol='ÅNone⁻²') -angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='A/yr', symbol='ÅNone⁻¹') -angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='A/yr^2', symbol='ÅNone⁻²') -angstroms_per_second = Unit(1e-10, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='NoneNone⁻¹') -angstroms_per_square_second = Unit(1e-10, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='NoneNone⁻²') -angstroms_per_millisecond = Unit(1e-07, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Nonems⁻¹') -angstroms_per_square_millisecond = Unit(1e-07, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Nonems⁻²') -angstroms_per_microsecond = Unit(0.0001, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='Noneµs⁻¹') -angstroms_per_square_microsecond = Unit(0.0001, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='Noneµs⁻²') -angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Nonens⁻¹') -angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Nonens⁻²') -angstroms_per_picosecond = Unit(100.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Noneps⁻¹') -angstroms_per_square_picosecond = Unit(100.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Noneps⁻²') -angstroms_per_femtosecond = Unit(100000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Nonefs⁻¹') -angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Nonefs⁻²') -angstroms_per_attosecond = Unit(100000000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Noneas⁻¹') -angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Noneas⁻²') -angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='NoneNone⁻¹') -angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='NoneNone⁻²') -angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_hour', ascii_symbol='Ang/hr', symbol='NoneNone⁻¹') -angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_hour', ascii_symbol='Ang/hr^2', symbol='NoneNone⁻²') -angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='NoneNone⁻¹') -angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='NoneNone⁻²') -angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='Ang/day', symbol='NoneNone⁻¹') -angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='Ang/day^2', symbol='NoneNone⁻²') -angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='NoneNone⁻¹') -angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='NoneNone⁻²') -angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/yr', symbol='NoneNone⁻¹') -angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/yr^2', symbol='NoneNone⁻²') -gram_per_cubic_meters = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_meters', ascii_symbol='g m^-3', symbol='NoneNone⁻³') -exagram_per_cubic_meters = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_meters', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') -petagram_per_cubic_meters = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_meters', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') -teragram_per_cubic_meters = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_meters', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') -gigagram_per_cubic_meters = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_meters', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') -megagram_per_cubic_meters = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_meters', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') -kilogram_per_cubic_meters = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_meters', ascii_symbol='kg m^-3', symbol='kgNone⁻³') -milligram_per_cubic_meters = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_meters', ascii_symbol='mg m^-3', symbol='mgNone⁻³') -microgram_per_cubic_meters = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_meters', ascii_symbol='ug m^-3', symbol='µgNone⁻³') -nanogram_per_cubic_meters = Unit(1.0000000000000002e-12, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_meters', ascii_symbol='ng m^-3', symbol='ngNone⁻³') -picogram_per_cubic_meters = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_meters', ascii_symbol='pg m^-3', symbol='pgNone⁻³') -femtogram_per_cubic_meters = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_meters', ascii_symbol='fg m^-3', symbol='fgNone⁻³') -attogram_per_cubic_meters = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_meters', ascii_symbol='ag m^-3', symbol='agNone⁻³') -gram_per_cubic_exameters = Unit(1e-57, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_exameters', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') -exagram_per_cubic_exameters = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_exameters', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') -petagram_per_cubic_exameters = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_exameters', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') -teragram_per_cubic_exameters = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_exameters', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') -gigagram_per_cubic_exameters = Unit(1e-48, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_exameters', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') -megagram_per_cubic_exameters = Unit(9.999999999999999e-52, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_exameters', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') -kilogram_per_cubic_exameters = Unit(9.999999999999999e-55, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_exameters', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') -milligram_per_cubic_exameters = Unit(9.999999999999998e-61, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_exameters', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') -microgram_per_cubic_exameters = Unit(9.999999999999999e-64, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_exameters', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') -nanogram_per_cubic_exameters = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_exameters', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') -picogram_per_cubic_exameters = Unit(1e-69, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_exameters', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') -femtogram_per_cubic_exameters = Unit(1e-72, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_exameters', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') -attogram_per_cubic_exameters = Unit(1e-75, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_exameters', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -gram_per_cubic_petameters = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_petameters', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') -exagram_per_cubic_petameters = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_petameters', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') -petagram_per_cubic_petameters = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_petameters', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') -teragram_per_cubic_petameters = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_petameters', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') -gigagram_per_cubic_petameters = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_petameters', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') -megagram_per_cubic_petameters = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_petameters', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') -kilogram_per_cubic_petameters = Unit(1.0000000000000001e-45, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_petameters', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') -milligram_per_cubic_petameters = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_petameters', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') -microgram_per_cubic_petameters = Unit(1.0000000000000002e-54, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_petameters', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') -nanogram_per_cubic_petameters = Unit(1.0000000000000002e-57, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_petameters', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') -picogram_per_cubic_petameters = Unit(1.0000000000000001e-60, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_petameters', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') -femtogram_per_cubic_petameters = Unit(1.0000000000000002e-63, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_petameters', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') -attogram_per_cubic_petameters = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_petameters', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -gram_per_cubic_terameters = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_terameters', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') -exagram_per_cubic_terameters = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_terameters', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') -petagram_per_cubic_terameters = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_terameters', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') -teragram_per_cubic_terameters = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_terameters', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') -gigagram_per_cubic_terameters = Unit(9.999999999999999e-31, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_terameters', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') -megagram_per_cubic_terameters = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_terameters', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') -kilogram_per_cubic_terameters = Unit(1e-36, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_terameters', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') -milligram_per_cubic_terameters = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_terameters', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') -microgram_per_cubic_terameters = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_terameters', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') -nanogram_per_cubic_terameters = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_terameters', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') -picogram_per_cubic_terameters = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_terameters', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') -femtogram_per_cubic_terameters = Unit(1e-54, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_terameters', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') -attogram_per_cubic_terameters = Unit(1.0000000000000001e-57, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_terameters', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -gram_per_cubic_gigameters = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_gigameters', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') -exagram_per_cubic_gigameters = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_gigameters', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') -petagram_per_cubic_gigameters = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_gigameters', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') -teragram_per_cubic_gigameters = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_gigameters', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') -gigagram_per_cubic_gigameters = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_gigameters', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') -megagram_per_cubic_gigameters = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_gigameters', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') -kilogram_per_cubic_gigameters = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_gigameters', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') -milligram_per_cubic_gigameters = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_gigameters', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') -microgram_per_cubic_gigameters = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_gigameters', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') -nanogram_per_cubic_gigameters = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_gigameters', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') -picogram_per_cubic_gigameters = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_gigameters', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') -femtogram_per_cubic_gigameters = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_gigameters', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') -attogram_per_cubic_gigameters = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_gigameters', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -gram_per_cubic_megameters = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_megameters', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') -exagram_per_cubic_megameters = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_megameters', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') -petagram_per_cubic_megameters = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_megameters', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') -teragram_per_cubic_megameters = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_megameters', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') -gigagram_per_cubic_megameters = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_megameters', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') -megagram_per_cubic_megameters = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_megameters', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') -kilogram_per_cubic_megameters = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_megameters', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') -milligram_per_cubic_megameters = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_megameters', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') -microgram_per_cubic_megameters = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_megameters', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') -nanogram_per_cubic_megameters = Unit(1.0000000000000003e-30, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_megameters', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') -picogram_per_cubic_megameters = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_megameters', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') -femtogram_per_cubic_megameters = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_megameters', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') -attogram_per_cubic_megameters = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_megameters', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -gram_per_cubic_kilometers = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_kilometers', ascii_symbol='g km^-3', symbol='Nonekm⁻³') -exagram_per_cubic_kilometers = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_kilometers', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') -petagram_per_cubic_kilometers = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_kilometers', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') -teragram_per_cubic_kilometers = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_kilometers', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') -gigagram_per_cubic_kilometers = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_kilometers', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') -megagram_per_cubic_kilometers = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_kilometers', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') -kilogram_per_cubic_kilometers = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_kilometers', ascii_symbol='kg km^-3', symbol='kgkm⁻³') -milligram_per_cubic_kilometers = Unit(9.999999999999999e-16, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_kilometers', ascii_symbol='mg km^-3', symbol='mgkm⁻³') -microgram_per_cubic_kilometers = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_kilometers', ascii_symbol='ug km^-3', symbol='µgkm⁻³') -nanogram_per_cubic_kilometers = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_kilometers', ascii_symbol='ng km^-3', symbol='ngkm⁻³') -picogram_per_cubic_kilometers = Unit(1.0000000000000001e-24, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_kilometers', ascii_symbol='pg km^-3', symbol='pgkm⁻³') -femtogram_per_cubic_kilometers = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_kilometers', ascii_symbol='fg km^-3', symbol='fgkm⁻³') -attogram_per_cubic_kilometers = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_kilometers', ascii_symbol='ag km^-3', symbol='agkm⁻³') -gram_per_cubic_millimeters = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_millimeters', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') -exagram_per_cubic_millimeters = Unit(1e+24, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_millimeters', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') -petagram_per_cubic_millimeters = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_millimeters', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') -teragram_per_cubic_millimeters = Unit(1e+18, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_millimeters', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') -gigagram_per_cubic_millimeters = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_millimeters', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') -megagram_per_cubic_millimeters = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_millimeters', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') -kilogram_per_cubic_millimeters = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_millimeters', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') -milligram_per_cubic_millimeters = Unit(999.9999999999999, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_millimeters', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') -microgram_per_cubic_millimeters = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_millimeters', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') -nanogram_per_cubic_millimeters = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_millimeters', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') -picogram_per_cubic_millimeters = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_millimeters', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') -femtogram_per_cubic_millimeters = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_millimeters', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') -attogram_per_cubic_millimeters = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_millimeters', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -gram_per_cubic_micrometers = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_micrometers', ascii_symbol='g um^-3', symbol='Noneµm⁻³') -exagram_per_cubic_micrometers = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_micrometers', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') -petagram_per_cubic_micrometers = Unit(1.0000000000000002e+30, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_micrometers', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') -teragram_per_cubic_micrometers = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_micrometers', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') -gigagram_per_cubic_micrometers = Unit(1.0000000000000001e+24, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_micrometers', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') -megagram_per_cubic_micrometers = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_micrometers', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') -kilogram_per_cubic_micrometers = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_micrometers', ascii_symbol='kg um^-3', symbol='kgµm⁻³') -milligram_per_cubic_micrometers = Unit(1000000000000.0001, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_micrometers', ascii_symbol='mg um^-3', symbol='mgµm⁻³') -microgram_per_cubic_micrometers = Unit(1000000000.0000002, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_micrometers', ascii_symbol='ug um^-3', symbol='µgµm⁻³') -nanogram_per_cubic_micrometers = Unit(1000000.0000000003, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_micrometers', ascii_symbol='ng um^-3', symbol='ngµm⁻³') -picogram_per_cubic_micrometers = Unit(1000.0000000000002, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_micrometers', ascii_symbol='pg um^-3', symbol='pgµm⁻³') -femtogram_per_cubic_micrometers = Unit(1.0000000000000002, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_micrometers', ascii_symbol='fg um^-3', symbol='fgµm⁻³') -attogram_per_cubic_micrometers = Unit(0.0010000000000000002, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_micrometers', ascii_symbol='ag um^-3', symbol='agµm⁻³') -gram_per_cubic_nanometers = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_nanometers', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') -exagram_per_cubic_nanometers = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_nanometers', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') -petagram_per_cubic_nanometers = Unit(9.999999999999998e+38, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_nanometers', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') -teragram_per_cubic_nanometers = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_nanometers', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') -gigagram_per_cubic_nanometers = Unit(9.999999999999998e+32, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_nanometers', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') -megagram_per_cubic_nanometers = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_nanometers', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') -kilogram_per_cubic_nanometers = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_nanometers', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') -milligram_per_cubic_nanometers = Unit(9.999999999999997e+20, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_nanometers', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') -microgram_per_cubic_nanometers = Unit(9.999999999999999e+17, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_nanometers', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') -nanogram_per_cubic_nanometers = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_nanometers', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') -picogram_per_cubic_nanometers = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_nanometers', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') -femtogram_per_cubic_nanometers = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_nanometers', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') -attogram_per_cubic_nanometers = Unit(999999.9999999999, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_nanometers', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -gram_per_cubic_picometers = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_picometers', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') -exagram_per_cubic_picometers = Unit(1e+51, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_picometers', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') -petagram_per_cubic_picometers = Unit(1e+48, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_picometers', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') -teragram_per_cubic_picometers = Unit(1.0000000000000001e+45, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_picometers', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') -gigagram_per_cubic_picometers = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_picometers', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') -megagram_per_cubic_picometers = Unit(1.0000000000000001e+39, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_picometers', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') -kilogram_per_cubic_picometers = Unit(1e+36, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_picometers', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') -milligram_per_cubic_picometers = Unit(1e+30, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_picometers', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') -microgram_per_cubic_picometers = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_picometers', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') -nanogram_per_cubic_picometers = Unit(1.0000000000000003e+24, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_picometers', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') -picogram_per_cubic_picometers = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_picometers', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') -femtogram_per_cubic_picometers = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_picometers', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') -attogram_per_cubic_picometers = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_picometers', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -gram_per_cubic_femtometers = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_femtometers', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') -exagram_per_cubic_femtometers = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_femtometers', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') -petagram_per_cubic_femtometers = Unit(9.999999999999997e+56, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_femtometers', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') -teragram_per_cubic_femtometers = Unit(9.999999999999997e+53, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_femtometers', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') -gigagram_per_cubic_femtometers = Unit(9.999999999999997e+50, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_femtometers', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') -megagram_per_cubic_femtometers = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_femtometers', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') -kilogram_per_cubic_femtometers = Unit(9.999999999999998e+44, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_femtometers', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') -milligram_per_cubic_femtometers = Unit(9.999999999999996e+38, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_femtometers', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') -microgram_per_cubic_femtometers = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_femtometers', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') -nanogram_per_cubic_femtometers = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_femtometers', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') -picogram_per_cubic_femtometers = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_femtometers', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') -femtogram_per_cubic_femtometers = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_femtometers', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') -attogram_per_cubic_femtometers = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_femtometers', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -gram_per_cubic_attometers = Unit(9.999999999999998e+50, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_attometers', ascii_symbol='g am^-3', symbol='Noneam⁻³') -exagram_per_cubic_attometers = Unit(9.999999999999999e+68, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_attometers', ascii_symbol='Eg am^-3', symbol='Egam⁻³') -petagram_per_cubic_attometers = Unit(9.999999999999998e+65, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_attometers', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') -teragram_per_cubic_attometers = Unit(9.999999999999999e+62, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_attometers', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') -gigagram_per_cubic_attometers = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_attometers', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') -megagram_per_cubic_attometers = Unit(9.999999999999999e+56, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_attometers', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') -kilogram_per_cubic_attometers = Unit(9.999999999999999e+53, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_attometers', ascii_symbol='kg am^-3', symbol='kgam⁻³') -milligram_per_cubic_attometers = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_attometers', ascii_symbol='mg am^-3', symbol='mgam⁻³') -microgram_per_cubic_attometers = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_attometers', ascii_symbol='ug am^-3', symbol='µgam⁻³') -nanogram_per_cubic_attometers = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_attometers', ascii_symbol='ng am^-3', symbol='ngam⁻³') -picogram_per_cubic_attometers = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_attometers', ascii_symbol='pg am^-3', symbol='pgam⁻³') -femtogram_per_cubic_attometers = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_attometers', ascii_symbol='fg am^-3', symbol='fgam⁻³') -attogram_per_cubic_attometers = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_attometers', ascii_symbol='ag am^-3', symbol='agam⁻³') -gram_per_cubic_angstroms = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_angstroms', ascii_symbol='g A^-3', symbol='NoneÅ⁻³') -exagram_per_cubic_angstroms = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_angstroms', ascii_symbol='Eg A^-3', symbol='EgÅ⁻³') -petagram_per_cubic_angstroms = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_angstroms', ascii_symbol='Pg A^-3', symbol='PgÅ⁻³') -teragram_per_cubic_angstroms = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_angstroms', ascii_symbol='Tg A^-3', symbol='TgÅ⁻³') -gigagram_per_cubic_angstroms = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_angstroms', ascii_symbol='Gg A^-3', symbol='GgÅ⁻³') -megagram_per_cubic_angstroms = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_angstroms', ascii_symbol='Mg A^-3', symbol='MgÅ⁻³') -kilogram_per_cubic_angstroms = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_angstroms', ascii_symbol='kg A^-3', symbol='kgÅ⁻³') -milligram_per_cubic_angstroms = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_angstroms', ascii_symbol='mg A^-3', symbol='mgÅ⁻³') -microgram_per_cubic_angstroms = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_angstroms', ascii_symbol='ug A^-3', symbol='µgÅ⁻³') -nanogram_per_cubic_angstroms = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_angstroms', ascii_symbol='ng A^-3', symbol='ngÅ⁻³') -picogram_per_cubic_angstroms = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_angstroms', ascii_symbol='pg A^-3', symbol='pgÅ⁻³') -femtogram_per_cubic_angstroms = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_angstroms', ascii_symbol='fg A^-3', symbol='fgÅ⁻³') -attogram_per_cubic_angstroms = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_angstroms', ascii_symbol='ag A^-3', symbol='agÅ⁻³') -gram_per_cubic_angstroms = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_angstroms', ascii_symbol='g Ang^-3', symbol='NoneNone⁻³') -exagram_per_cubic_angstroms = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_angstroms', ascii_symbol='Eg Ang^-3', symbol='EgNone⁻³') -petagram_per_cubic_angstroms = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_angstroms', ascii_symbol='Pg Ang^-3', symbol='PgNone⁻³') -teragram_per_cubic_angstroms = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_angstroms', ascii_symbol='Tg Ang^-3', symbol='TgNone⁻³') -gigagram_per_cubic_angstroms = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_angstroms', ascii_symbol='Gg Ang^-3', symbol='GgNone⁻³') -megagram_per_cubic_angstroms = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_angstroms', ascii_symbol='Mg Ang^-3', symbol='MgNone⁻³') -kilogram_per_cubic_angstroms = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_angstroms', ascii_symbol='kg Ang^-3', symbol='kgNone⁻³') -milligram_per_cubic_angstroms = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_angstroms', ascii_symbol='mg Ang^-3', symbol='mgNone⁻³') -microgram_per_cubic_angstroms = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_angstroms', ascii_symbol='ug Ang^-3', symbol='µgNone⁻³') -nanogram_per_cubic_angstroms = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_angstroms', ascii_symbol='ng Ang^-3', symbol='ngNone⁻³') -picogram_per_cubic_angstroms = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_angstroms', ascii_symbol='pg Ang^-3', symbol='pgNone⁻³') -femtogram_per_cubic_angstroms = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_angstroms', ascii_symbol='fg Ang^-3', symbol='fgNone⁻³') -attogram_per_cubic_angstroms = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_angstroms', ascii_symbol='ag Ang^-3', symbol='agNone⁻³') +angstroms_per_second = Unit(1e-10, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') +angstroms_per_square_second = Unit(1e-10, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') +angstroms_per_millisecond = Unit(1e-07, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') +angstroms_per_square_millisecond = Unit(1e-07, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') +angstroms_per_microsecond = Unit(0.0001, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') +angstroms_per_square_microsecond = Unit(0.0001, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') +angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') +angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') +angstroms_per_picosecond = Unit(100.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') +angstroms_per_square_picosecond = Unit(100.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') +angstroms_per_femtosecond = Unit(100000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') +angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') +angstroms_per_attosecond = Unit(100000000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') +angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') +angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') +angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') +angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') +angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') +angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') +angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') +angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') +angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') +grams_per_cubic_meter = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_meter = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') +petagrams_per_cubic_meter = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') +teragrams_per_cubic_meter = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_meter = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') +megagrams_per_cubic_meter = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') +kilograms_per_cubic_meter = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgNone⁻³') +milligrams_per_cubic_meter = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgNone⁻³') +micrograms_per_cubic_meter = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgNone⁻³') +nanograms_per_cubic_meter = Unit(1.0000000000000002e-12, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngNone⁻³') +picograms_per_cubic_meter = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') +femtograms_per_cubic_meter = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') +attograms_per_cubic_meter = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') +grams_per_cubic_exameter = Unit(1e-57, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') +exagrams_per_cubic_exameter = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') +petagrams_per_cubic_exameter = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') +teragrams_per_cubic_exameter = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') +gigagrams_per_cubic_exameter = Unit(1e-48, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_exameter', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') +megagrams_per_cubic_exameter = Unit(9.999999999999999e-52, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_exameter', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') +kilograms_per_cubic_exameter = Unit(9.999999999999999e-55, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_exameter', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') +milligrams_per_cubic_exameter = Unit(9.999999999999998e-61, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_exameter', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') +micrograms_per_cubic_exameter = Unit(9.999999999999999e-64, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_exameter', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') +nanograms_per_cubic_exameter = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_exameter', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') +picograms_per_cubic_exameter = Unit(1e-69, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') +femtograms_per_cubic_exameter = Unit(1e-72, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') +attograms_per_cubic_exameter = Unit(1e-75, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') +grams_per_cubic_petameter = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') +exagrams_per_cubic_petameter = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') +petagrams_per_cubic_petameter = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') +teragrams_per_cubic_petameter = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') +gigagrams_per_cubic_petameter = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_petameter', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') +megagrams_per_cubic_petameter = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_petameter', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') +kilograms_per_cubic_petameter = Unit(1.0000000000000001e-45, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_petameter', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') +milligrams_per_cubic_petameter = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_petameter', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') +micrograms_per_cubic_petameter = Unit(1.0000000000000002e-54, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_petameter', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') +nanograms_per_cubic_petameter = Unit(1.0000000000000002e-57, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_petameter', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') +picograms_per_cubic_petameter = Unit(1.0000000000000001e-60, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') +femtograms_per_cubic_petameter = Unit(1.0000000000000002e-63, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') +attograms_per_cubic_petameter = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') +grams_per_cubic_terameter = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') +exagrams_per_cubic_terameter = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') +petagrams_per_cubic_terameter = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') +teragrams_per_cubic_terameter = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') +gigagrams_per_cubic_terameter = Unit(9.999999999999999e-31, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_terameter', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') +megagrams_per_cubic_terameter = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_terameter', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') +kilograms_per_cubic_terameter = Unit(1e-36, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_terameter', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') +milligrams_per_cubic_terameter = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_terameter', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') +micrograms_per_cubic_terameter = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_terameter', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') +nanograms_per_cubic_terameter = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_terameter', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') +picograms_per_cubic_terameter = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') +femtograms_per_cubic_terameter = Unit(1e-54, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') +attograms_per_cubic_terameter = Unit(1.0000000000000001e-57, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') +grams_per_cubic_gigameter = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') +exagrams_per_cubic_gigameter = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') +petagrams_per_cubic_gigameter = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') +teragrams_per_cubic_gigameter = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') +gigagrams_per_cubic_gigameter = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_gigameter', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') +megagrams_per_cubic_gigameter = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_gigameter', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') +kilograms_per_cubic_gigameter = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_gigameter', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') +milligrams_per_cubic_gigameter = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_gigameter', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') +micrograms_per_cubic_gigameter = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_gigameter', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') +nanograms_per_cubic_gigameter = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_gigameter', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') +picograms_per_cubic_gigameter = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') +femtograms_per_cubic_gigameter = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') +attograms_per_cubic_gigameter = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') +grams_per_cubic_megameter = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') +exagrams_per_cubic_megameter = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') +petagrams_per_cubic_megameter = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') +teragrams_per_cubic_megameter = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') +gigagrams_per_cubic_megameter = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_megameter', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') +megagrams_per_cubic_megameter = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_megameter', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') +kilograms_per_cubic_megameter = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_megameter', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') +milligrams_per_cubic_megameter = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_megameter', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') +micrograms_per_cubic_megameter = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_megameter', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') +nanograms_per_cubic_megameter = Unit(1.0000000000000003e-30, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_megameter', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') +picograms_per_cubic_megameter = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') +femtograms_per_cubic_megameter = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') +attograms_per_cubic_megameter = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') +grams_per_cubic_kilometer = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') +exagrams_per_cubic_kilometer = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') +petagrams_per_cubic_kilometer = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') +teragrams_per_cubic_kilometer = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') +gigagrams_per_cubic_kilometer = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_kilometer', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') +megagrams_per_cubic_kilometer = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_kilometer', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') +kilograms_per_cubic_kilometer = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_kilometer', ascii_symbol='kg km^-3', symbol='kgkm⁻³') +milligrams_per_cubic_kilometer = Unit(9.999999999999999e-16, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_kilometer', ascii_symbol='mg km^-3', symbol='mgkm⁻³') +micrograms_per_cubic_kilometer = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_kilometer', ascii_symbol='ug km^-3', symbol='µgkm⁻³') +nanograms_per_cubic_kilometer = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_kilometer', ascii_symbol='ng km^-3', symbol='ngkm⁻³') +picograms_per_cubic_kilometer = Unit(1.0000000000000001e-24, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') +femtograms_per_cubic_kilometer = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') +attograms_per_cubic_kilometer = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') +grams_per_cubic_millimeter = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') +exagrams_per_cubic_millimeter = Unit(1e+24, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') +petagrams_per_cubic_millimeter = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') +teragrams_per_cubic_millimeter = Unit(1e+18, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') +gigagrams_per_cubic_millimeter = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_millimeter', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') +megagrams_per_cubic_millimeter = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_millimeter', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') +kilograms_per_cubic_millimeter = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_millimeter', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') +milligrams_per_cubic_millimeter = Unit(999.9999999999999, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_millimeter', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') +micrograms_per_cubic_millimeter = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_millimeter', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') +nanograms_per_cubic_millimeter = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_millimeter', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') +picograms_per_cubic_millimeter = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') +femtograms_per_cubic_millimeter = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') +attograms_per_cubic_millimeter = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') +grams_per_cubic_micrometer = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') +exagrams_per_cubic_micrometer = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') +petagrams_per_cubic_micrometer = Unit(1.0000000000000002e+30, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') +teragrams_per_cubic_micrometer = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') +gigagrams_per_cubic_micrometer = Unit(1.0000000000000001e+24, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_micrometer', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') +megagrams_per_cubic_micrometer = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_micrometer', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') +kilograms_per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_micrometer', ascii_symbol='kg um^-3', symbol='kgµm⁻³') +milligrams_per_cubic_micrometer = Unit(1000000000000.0001, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_micrometer', ascii_symbol='mg um^-3', symbol='mgµm⁻³') +micrograms_per_cubic_micrometer = Unit(1000000000.0000002, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_micrometer', ascii_symbol='ug um^-3', symbol='µgµm⁻³') +nanograms_per_cubic_micrometer = Unit(1000000.0000000003, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_micrometer', ascii_symbol='ng um^-3', symbol='ngµm⁻³') +picograms_per_cubic_micrometer = Unit(1000.0000000000002, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') +femtograms_per_cubic_micrometer = Unit(1.0000000000000002, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') +attograms_per_cubic_micrometer = Unit(0.0010000000000000002, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') +grams_per_cubic_nanometer = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') +exagrams_per_cubic_nanometer = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') +petagrams_per_cubic_nanometer = Unit(9.999999999999998e+38, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') +teragrams_per_cubic_nanometer = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') +gigagrams_per_cubic_nanometer = Unit(9.999999999999998e+32, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_nanometer', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') +megagrams_per_cubic_nanometer = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_nanometer', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') +kilograms_per_cubic_nanometer = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_nanometer', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') +milligrams_per_cubic_nanometer = Unit(9.999999999999997e+20, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_nanometer', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') +micrograms_per_cubic_nanometer = Unit(9.999999999999999e+17, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_nanometer', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') +nanograms_per_cubic_nanometer = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_nanometer', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') +picograms_per_cubic_nanometer = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') +femtograms_per_cubic_nanometer = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') +attograms_per_cubic_nanometer = Unit(999999.9999999999, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') +grams_per_cubic_picometer = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') +exagrams_per_cubic_picometer = Unit(1e+51, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') +petagrams_per_cubic_picometer = Unit(1e+48, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') +teragrams_per_cubic_picometer = Unit(1.0000000000000001e+45, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') +gigagrams_per_cubic_picometer = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_picometer', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') +megagrams_per_cubic_picometer = Unit(1.0000000000000001e+39, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_picometer', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') +kilograms_per_cubic_picometer = Unit(1e+36, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_picometer', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') +milligrams_per_cubic_picometer = Unit(1e+30, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_picometer', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') +micrograms_per_cubic_picometer = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_picometer', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') +nanograms_per_cubic_picometer = Unit(1.0000000000000003e+24, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_picometer', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') +picograms_per_cubic_picometer = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') +femtograms_per_cubic_picometer = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') +attograms_per_cubic_picometer = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') +grams_per_cubic_femtometer = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') +exagrams_per_cubic_femtometer = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') +petagrams_per_cubic_femtometer = Unit(9.999999999999997e+56, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') +teragrams_per_cubic_femtometer = Unit(9.999999999999997e+53, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') +gigagrams_per_cubic_femtometer = Unit(9.999999999999997e+50, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_femtometer', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') +megagrams_per_cubic_femtometer = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_femtometer', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') +kilograms_per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_femtometer', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') +milligrams_per_cubic_femtometer = Unit(9.999999999999996e+38, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_femtometer', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') +micrograms_per_cubic_femtometer = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_femtometer', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') +nanograms_per_cubic_femtometer = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_femtometer', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') +picograms_per_cubic_femtometer = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') +femtograms_per_cubic_femtometer = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') +attograms_per_cubic_femtometer = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') +grams_per_cubic_attometer = Unit(9.999999999999998e+50, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') +exagrams_per_cubic_attometer = Unit(9.999999999999999e+68, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') +petagrams_per_cubic_attometer = Unit(9.999999999999998e+65, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') +teragrams_per_cubic_attometer = Unit(9.999999999999999e+62, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') +gigagrams_per_cubic_attometer = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_attometer', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') +megagrams_per_cubic_attometer = Unit(9.999999999999999e+56, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_attometer', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') +kilograms_per_cubic_attometer = Unit(9.999999999999999e+53, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_attometer', ascii_symbol='kg am^-3', symbol='kgam⁻³') +milligrams_per_cubic_attometer = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_attometer', ascii_symbol='mg am^-3', symbol='mgam⁻³') +micrograms_per_cubic_attometer = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_attometer', ascii_symbol='ug am^-3', symbol='µgam⁻³') +nanograms_per_cubic_attometer = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_attometer', ascii_symbol='ng am^-3', symbol='ngam⁻³') +picograms_per_cubic_attometer = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') +femtograms_per_cubic_attometer = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') +attograms_per_cubic_attometer = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') +grams_per_cubic_angstrom = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') +exagrams_per_cubic_angstrom = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') +petagrams_per_cubic_angstrom = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') +teragrams_per_cubic_angstrom = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') +gigagrams_per_cubic_angstrom = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_angstrom', ascii_symbol='Gg Ang^-3', symbol='GgÅ⁻³') +megagrams_per_cubic_angstrom = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_angstrom', ascii_symbol='Mg Ang^-3', symbol='MgÅ⁻³') +kilograms_per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_angstrom', ascii_symbol='kg Ang^-3', symbol='kgÅ⁻³') +milligrams_per_cubic_angstrom = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_angstrom', ascii_symbol='mg Ang^-3', symbol='mgÅ⁻³') +micrograms_per_cubic_angstrom = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_angstrom', ascii_symbol='ug Ang^-3', symbol='µgÅ⁻³') +nanograms_per_cubic_angstrom = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_angstrom', ascii_symbol='ng Ang^-3', symbol='ngÅ⁻³') +picograms_per_cubic_angstrom = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') +femtograms_per_cubic_angstrom = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') +attograms_per_cubic_angstrom = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') # # Lookup table from symbols to units @@ -1353,18 +1250,21 @@ def __init__(self, name: str, units: list[Unit]): "pH": picohenry, "fH": femtohenry, "aH": attohenry, - "Å": angstroms, "Ang": angstroms, + "Å": angstroms, "min": minutes, - "hr": hours, + "h": hours, "d": days, - "day": days, "y": years, - "yr": years, "deg": degrees, "rad": radians, "sr": stradians, "none": none, + "yr": years, + "year": years, + "day": days, + "hr": hours, + "hour": hours, } @@ -1390,7 +1290,6 @@ def __init__(self, name: str, units: list[Unit]): femtometers, attometers, angstroms, - angstroms, ]) area = UnitGroup( @@ -1410,7 +1309,6 @@ def __init__(self, name: str, units: list[Unit]): square_femtometers, square_attometers, square_angstroms, - square_angstroms, ]) volume = UnitGroup( @@ -1430,7 +1328,6 @@ def __init__(self, name: str, units: list[Unit]): cubic_femtometers, cubic_attometers, cubic_angstroms, - cubic_angstroms, ]) inverse_length = UnitGroup( @@ -1450,7 +1347,6 @@ def __init__(self, name: str, units: list[Unit]): per_femtometer, per_attometer, per_angstrom, - per_angstrom, ]) inverse_area = UnitGroup( @@ -1470,7 +1366,6 @@ def __init__(self, name: str, units: list[Unit]): per_square_femtometer, per_square_attometer, per_square_angstrom, - per_square_angstrom, ]) inverse_volume = UnitGroup( @@ -1490,7 +1385,6 @@ def __init__(self, name: str, units: list[Unit]): per_cubic_femtometer, per_cubic_attometer, per_cubic_angstrom, - per_cubic_angstrom, ]) time = UnitGroup( @@ -1506,8 +1400,6 @@ def __init__(self, name: str, units: list[Unit]): minutes, hours, days, - days, - years, years, ]) @@ -1543,8 +1435,6 @@ def __init__(self, name: str, units: list[Unit]): meters_per_minute, meters_per_hour, meters_per_day, - meters_per_day, - meters_per_year, meters_per_year, exameters_per_second, exameters_per_millisecond, @@ -1556,8 +1446,6 @@ def __init__(self, name: str, units: list[Unit]): exameters_per_minute, exameters_per_hour, exameters_per_day, - exameters_per_day, - exameters_per_year, exameters_per_year, petameters_per_second, petameters_per_millisecond, @@ -1569,8 +1457,6 @@ def __init__(self, name: str, units: list[Unit]): petameters_per_minute, petameters_per_hour, petameters_per_day, - petameters_per_day, - petameters_per_year, petameters_per_year, terameters_per_second, terameters_per_millisecond, @@ -1582,8 +1468,6 @@ def __init__(self, name: str, units: list[Unit]): terameters_per_minute, terameters_per_hour, terameters_per_day, - terameters_per_day, - terameters_per_year, terameters_per_year, gigameters_per_second, gigameters_per_millisecond, @@ -1595,8 +1479,6 @@ def __init__(self, name: str, units: list[Unit]): gigameters_per_minute, gigameters_per_hour, gigameters_per_day, - gigameters_per_day, - gigameters_per_year, gigameters_per_year, megameters_per_second, megameters_per_millisecond, @@ -1608,8 +1490,6 @@ def __init__(self, name: str, units: list[Unit]): megameters_per_minute, megameters_per_hour, megameters_per_day, - megameters_per_day, - megameters_per_year, megameters_per_year, kilometers_per_second, kilometers_per_millisecond, @@ -1621,8 +1501,6 @@ def __init__(self, name: str, units: list[Unit]): kilometers_per_minute, kilometers_per_hour, kilometers_per_day, - kilometers_per_day, - kilometers_per_year, kilometers_per_year, millimeters_per_second, millimeters_per_millisecond, @@ -1634,8 +1512,6 @@ def __init__(self, name: str, units: list[Unit]): millimeters_per_minute, millimeters_per_hour, millimeters_per_day, - millimeters_per_day, - millimeters_per_year, millimeters_per_year, micrometers_per_second, micrometers_per_millisecond, @@ -1647,8 +1523,6 @@ def __init__(self, name: str, units: list[Unit]): micrometers_per_minute, micrometers_per_hour, micrometers_per_day, - micrometers_per_day, - micrometers_per_year, micrometers_per_year, nanometers_per_second, nanometers_per_millisecond, @@ -1660,8 +1534,6 @@ def __init__(self, name: str, units: list[Unit]): nanometers_per_minute, nanometers_per_hour, nanometers_per_day, - nanometers_per_day, - nanometers_per_year, nanometers_per_year, picometers_per_second, picometers_per_millisecond, @@ -1673,8 +1545,6 @@ def __init__(self, name: str, units: list[Unit]): picometers_per_minute, picometers_per_hour, picometers_per_day, - picometers_per_day, - picometers_per_year, picometers_per_year, femtometers_per_second, femtometers_per_millisecond, @@ -1686,8 +1556,6 @@ def __init__(self, name: str, units: list[Unit]): femtometers_per_minute, femtometers_per_hour, femtometers_per_day, - femtometers_per_day, - femtometers_per_year, femtometers_per_year, attometers_per_second, attometers_per_millisecond, @@ -1699,8 +1567,6 @@ def __init__(self, name: str, units: list[Unit]): attometers_per_minute, attometers_per_hour, attometers_per_day, - attometers_per_day, - attometers_per_year, attometers_per_year, angstroms_per_second, angstroms_per_millisecond, @@ -1712,21 +1578,6 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_minute, angstroms_per_hour, angstroms_per_day, - angstroms_per_day, - angstroms_per_year, - angstroms_per_year, - angstroms_per_second, - angstroms_per_millisecond, - angstroms_per_microsecond, - angstroms_per_nanosecond, - angstroms_per_picosecond, - angstroms_per_femtosecond, - angstroms_per_attosecond, - angstroms_per_minute, - angstroms_per_hour, - angstroms_per_day, - angstroms_per_day, - angstroms_per_year, angstroms_per_year, ]) @@ -1743,8 +1594,6 @@ def __init__(self, name: str, units: list[Unit]): meters_per_square_minute, meters_per_square_hour, meters_per_square_day, - meters_per_square_day, - meters_per_square_year, meters_per_square_year, exameters_per_square_second, exameters_per_square_millisecond, @@ -1756,8 +1605,6 @@ def __init__(self, name: str, units: list[Unit]): exameters_per_square_minute, exameters_per_square_hour, exameters_per_square_day, - exameters_per_square_day, - exameters_per_square_year, exameters_per_square_year, petameters_per_square_second, petameters_per_square_millisecond, @@ -1769,8 +1616,6 @@ def __init__(self, name: str, units: list[Unit]): petameters_per_square_minute, petameters_per_square_hour, petameters_per_square_day, - petameters_per_square_day, - petameters_per_square_year, petameters_per_square_year, terameters_per_square_second, terameters_per_square_millisecond, @@ -1782,8 +1627,6 @@ def __init__(self, name: str, units: list[Unit]): terameters_per_square_minute, terameters_per_square_hour, terameters_per_square_day, - terameters_per_square_day, - terameters_per_square_year, terameters_per_square_year, gigameters_per_square_second, gigameters_per_square_millisecond, @@ -1795,8 +1638,6 @@ def __init__(self, name: str, units: list[Unit]): gigameters_per_square_minute, gigameters_per_square_hour, gigameters_per_square_day, - gigameters_per_square_day, - gigameters_per_square_year, gigameters_per_square_year, megameters_per_square_second, megameters_per_square_millisecond, @@ -1808,8 +1649,6 @@ def __init__(self, name: str, units: list[Unit]): megameters_per_square_minute, megameters_per_square_hour, megameters_per_square_day, - megameters_per_square_day, - megameters_per_square_year, megameters_per_square_year, kilometers_per_square_second, kilometers_per_square_millisecond, @@ -1821,8 +1660,6 @@ def __init__(self, name: str, units: list[Unit]): kilometers_per_square_minute, kilometers_per_square_hour, kilometers_per_square_day, - kilometers_per_square_day, - kilometers_per_square_year, kilometers_per_square_year, millimeters_per_square_second, millimeters_per_square_millisecond, @@ -1834,8 +1671,6 @@ def __init__(self, name: str, units: list[Unit]): millimeters_per_square_minute, millimeters_per_square_hour, millimeters_per_square_day, - millimeters_per_square_day, - millimeters_per_square_year, millimeters_per_square_year, micrometers_per_square_second, micrometers_per_square_millisecond, @@ -1847,8 +1682,6 @@ def __init__(self, name: str, units: list[Unit]): micrometers_per_square_minute, micrometers_per_square_hour, micrometers_per_square_day, - micrometers_per_square_day, - micrometers_per_square_year, micrometers_per_square_year, nanometers_per_square_second, nanometers_per_square_millisecond, @@ -1860,8 +1693,6 @@ def __init__(self, name: str, units: list[Unit]): nanometers_per_square_minute, nanometers_per_square_hour, nanometers_per_square_day, - nanometers_per_square_day, - nanometers_per_square_year, nanometers_per_square_year, picometers_per_square_second, picometers_per_square_millisecond, @@ -1873,8 +1704,6 @@ def __init__(self, name: str, units: list[Unit]): picometers_per_square_minute, picometers_per_square_hour, picometers_per_square_day, - picometers_per_square_day, - picometers_per_square_year, picometers_per_square_year, femtometers_per_square_second, femtometers_per_square_millisecond, @@ -1886,8 +1715,6 @@ def __init__(self, name: str, units: list[Unit]): femtometers_per_square_minute, femtometers_per_square_hour, femtometers_per_square_day, - femtometers_per_square_day, - femtometers_per_square_year, femtometers_per_square_year, attometers_per_square_second, attometers_per_square_millisecond, @@ -1899,8 +1726,6 @@ def __init__(self, name: str, units: list[Unit]): attometers_per_square_minute, attometers_per_square_hour, attometers_per_square_day, - attometers_per_square_day, - attometers_per_square_year, attometers_per_square_year, angstroms_per_square_second, angstroms_per_square_millisecond, @@ -1912,21 +1737,6 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_square_minute, angstroms_per_square_hour, angstroms_per_square_day, - angstroms_per_square_day, - angstroms_per_square_year, - angstroms_per_square_year, - angstroms_per_square_second, - angstroms_per_square_millisecond, - angstroms_per_square_microsecond, - angstroms_per_square_nanosecond, - angstroms_per_square_picosecond, - angstroms_per_square_femtosecond, - angstroms_per_square_attosecond, - angstroms_per_square_minute, - angstroms_per_square_hour, - angstroms_per_square_day, - angstroms_per_square_day, - angstroms_per_square_year, angstroms_per_square_year, ]) From fa8e4cdd4add2f4a8b87d2e8479501e8738a95a2 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 6 Aug 2024 18:45:07 +0100 Subject: [PATCH 009/675] More units, towards formatting --- sasdata/quantities/_units_base.py | 125 +- sasdata/quantities/_units_table.py | 112 +- sasdata/quantities/quantities.py | 38 +- sasdata/quantities/unit_formatting.py | 1 + sasdata/quantities/units.py | 1974 +++++++++++++++++-------- 5 files changed, 1557 insertions(+), 693 deletions(-) diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 11db5272d..b8bfd24e0 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -5,13 +5,14 @@ from sasdata.quantities.unicode_superscript import int_as_unicode_superscript - class Dimensions: """ - Note that some SI Base units are + Note that some SI Base units are not useful from the perspecive of the sasview project, and make things + behave badly. In particular: moles and angular measures are dimensionless, and candelas are really a weighted + measure of power. - For example, moles and angular measures are dimensionless from this perspective, and candelas are + We do however track angle and amount, because its really useful for formatting units """ def __init__(self, @@ -19,13 +20,17 @@ def __init__(self, time: int = 0, mass: int = 0, current: int = 0, - temperature: int = 0): + temperature: int = 0, + moles_hint: int = 0, + angle_hint: int = 0): self.length = length self.time = time self.mass = mass self.current = current self.temperature = temperature + self.moles_hint = moles_hint + self.angle_hint = angle_hint def __mul__(self: Self, other: Self): @@ -37,7 +42,9 @@ def __mul__(self: Self, other: Self): self.time + other.time, self.mass + other.mass, self.current + other.current, - self.temperature + other.temperature) + self.temperature + other.temperature, + self.moles_hint + other.moles_hint, + self.angle_hint + other.angle_hint) def __truediv__(self: Self, other: Self): @@ -49,7 +56,9 @@ def __truediv__(self: Self, other: Self): self.time - other.time, self.mass - other.mass, self.current - other.current, - self.temperature - other.temperature) + self.temperature - other.temperature, + self.moles_hint - other.moles_hint, + self.angle_hint - other.angle_hint) def __pow__(self, power: int): @@ -61,7 +70,9 @@ def __pow__(self, power: int): self.time * power, self.mass * power, self.current * power, - self.temperature * power) + self.temperature * power, + self.moles_hint * power, + self.angle_hint * power) def __eq__(self: Self, other: Self): if isinstance(other, Dimensions): @@ -69,7 +80,9 @@ def __eq__(self: Self, other: Self): self.time == other.time and self.mass == other.mass and self.current == other.current and - self.temperature == other.temperature) + self.temperature == other.temperature and + self.moles_hint == other.moles_hint and + self.angle_hint == other.angle_hint) return NotImplemented @@ -92,17 +105,26 @@ def __hash__(self): if self.temperature < 0: two_powers += 16 + if self.moles_hint < 0: + two_powers += 32 + + if self.angle_hint < 0: + two_powers += 64 + return 2**two_powers * 3**abs(self.length) * 5**abs(self.time) * \ - 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) + 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) * \ + 17**abs(self.moles_hint) * 19**abs(self.angle_hint) def __repr__(self): s = "" for name, size in [ - ("L", self.length), - ("T", self.time), - ("M", self.mass), - ("C", self.current), - ("K", self.temperature)]: + ("length", self.length), + ("time", self.time), + ("mass", self.mass), + ("current", self.current), + ("temperature", self.temperature), + ("amount", self.moles_hint), + ("angle", self.angle_hint)]: if size == 0: pass @@ -130,8 +152,22 @@ def __init__(self, def _components(self, tokens: Sequence["UnitToken"]): pass -class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): - pass +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): @@ -165,7 +201,64 @@ def equivalent(self: Self, other: Self): def __eq__(self: Self, other: Self): return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 + def si_equivalent(self): + """ Get the SI unit corresponding to this unit""" + return Unit(1, self.dimensions) + + def _format_unit(self, format_process: list["UnitFormatProcessor"]): + for processor in format_process: + pass +class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): + """ This processor minimises the dimensionality of the unit by multiplying by as many + units of the specified type as needed """ + def __init__(self, unit: Unit): + self.unit = unit + + def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: + pass + +class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): + pass + + +class NamedUnit: + # TODO: Add named unit class + +# +# Parsing plan: +# Require unknown amounts of units to be explicitly positive or negative? +# +# + + + +@dataclass +class ProcessedUnitToken: + """ Mid processing representation of formatted units """ + base_string: str + exponent_string: str + latex_exponent_string: str + exponent: int + +class UnitFormatProcessor: + """ Represents a step in the unit processing pipeline""" + def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: + """ This will be called to deal with each processing stage""" + +class RequiredUnitFormatProcessor(UnitFormatProcessor): + """ This unit is required to exist in the formatting """ + def __init__(self, unit: Unit, power: int = 1): + self.unit = unit + self.power = power + def apply(self, scale, dimensions) -> tuple[float, Dimensions, ProcessedUnitToken]: + new_scale = scale / (self.unit.scale * self.power) + new_dimensions = self.unit.dimensions / (dimensions**self.power) + token = ProcessedUnitToken(self.unit, self.power) + + return new_scale, new_dimensions, token + class UnitGroup: + """ A group of units that all have the same dimensionality """ def __init__(self, name: str, units: list[Unit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) diff --git a/sasdata/quantities/_units_table.py b/sasdata/quantities/_units_table.py index 0749461f8..c69b9577a 100644 --- a/sasdata/quantities/_units_table.py +++ b/sasdata/quantities/_units_table.py @@ -22,50 +22,60 @@ ("f", None, "femto", 1e-15), ("a", None, "atto", 1e-18)] +unusual_magnitudes = [ + ("d", None, "deci", 1e-1), + ("c", None, "centi", 1e-2) +] + all_magnitudes = bigger_magnitudes + smaller_magnitudes # Length, time, mass, current, temperature base_si_units = [ - ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, all_magnitudes), - ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, smaller_magnitudes), - ("g", None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, all_magnitudes), - ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, all_magnitudes), - ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, all_magnitudes) ] + ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, 0, 0, all_magnitudes + unusual_magnitudes), + ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, 0, 0, smaller_magnitudes), + ("g", None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, 0, 0, all_magnitudes), + ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), + ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, 0, 0, all_magnitudes) ] derived_si_units = [ - ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, all_magnitudes), - ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, all_magnitudes), - ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, all_magnitudes), - ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, all_magnitudes), - ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, all_magnitudes), - ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, all_magnitudes), - ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, all_magnitudes), - ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, all_magnitudes), - ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, all_magnitudes), - ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, all_magnitudes), - ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, all_magnitudes), - ("T", None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, all_magnitudes), - ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, all_magnitudes), - ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, []) + ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, 0, 0, all_magnitudes), + ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, 0, 0, all_magnitudes), + ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, 0, 0, all_magnitudes), + ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), + ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, 0, 0, all_magnitudes), + ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, 0, 0, all_magnitudes), + ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, 0, 0, all_magnitudes), + ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, 0, 0, all_magnitudes), + ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, 0, 0, all_magnitudes), + ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, 0, 0, all_magnitudes), + ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, 0, 0, all_magnitudes), + ("T", None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, 0, 0, all_magnitudes), + ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), + ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []) ] non_si_units = [ - ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), - ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, []), - ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, []), - ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), - ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), - ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, []), - ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, []), - ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []), - ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, []) + ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, 0, 0, []), + ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, 0, 0, []), + ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, 0, 0, []), + ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, 0, 0, []), + ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, 0, 0, []), + ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, 0, 1, []), + ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, 0, 1, []), + ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, 0, 2, []), + ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), + ("l", None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), + ("eV", None, "electronvolt", "electronvolts", 1.602e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), + ("au", None, "atomic mass unit", "atomic mass units", 1.661e-27, 0, 0, 1, 0, 0, 0, 0, []), + ("mol", None, "mole", "moles", 6.022e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes) ] aliases = { "y": ["yr", "year"], "d": ["day"], "h": ["hr", "hour"], - "Ang": ["A", "Å"] + "Ang": ["A", "Å"], + "au": ["a.u.", "amu"] } @@ -113,13 +123,13 @@ def format_name(name: str): unit_types_temp = defaultdict(list) # Keep track of unit types unit_types = defaultdict(list) - for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, magnitudes in all_units: + for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, moles_hint, angle_hint, magnitudes in all_units: formatted_plural = format_name(plural) formatted_singular = format_name(singular) - dimensions = Dimensions(length, time, mass, current, temperature) - fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature})," + dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) + fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," f"name='{formatted_plural}'," f"ascii_symbol='{symbol}'," f"symbol='{symbol if special_symbol is None else special_symbol}')\n") @@ -148,7 +158,7 @@ def format_name(name: str): combined_scale = scale * mag_scale # Units - dimensions = Dimensions(length, time, mass, current, temperature) + dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) fid.write(f"{combined_name_plural} = Unit({combined_scale}, " f"Dimensions({length}, {time}, {mass}, {current}, {temperature})," f"name='{combined_name_plural}'," @@ -171,6 +181,7 @@ def format_name(name: str): length_units = unit_types_temp[hash(Dimensions(length=1))] time_units = unit_types_temp[hash(Dimensions(time=1))] mass_units = unit_types_temp[hash(Dimensions(mass=1))] + amount_units = unit_types_temp[hash(Dimensions(moles_hint=1))] # Length based for symbol, special_symbol, singular, plural, scale, _ in length_units: @@ -185,7 +196,7 @@ def format_name(name: str): unit_name = prefix + name unit_special_symbol = (symbol if special_symbol is None else special_symbol) + unicode_suffix unit_symbol = symbol + f"^{power}" - fid.write(f"{unit_name} = Unit({scale**power}, Dimensions({power}, 0, 0, 0, 0), " + fid.write(f"{unit_name} = Unit({scale**power}, Dimensions(length={power}), " f"name='{unit_name}', " f"ascii_symbol='{unit_symbol}', " f"symbol='{unit_special_symbol}')\n") @@ -203,13 +214,13 @@ def format_name(name: str): fid.write(f"{speed_name} " f"= Unit({length_scale / time_scale}, " - f"Dimensions(1, -1, 0, 0, 0), " + f"Dimensions(length=1, time=-1), " f"name='{speed_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}', " f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") fid.write(f"{accel_name} = Unit({length_scale / time_scale}, " - f"Dimensions(1, -2, 0, 0, 0), " + f"Dimensions(length=1, time=-2), " f"name='{accel_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}^2', " f"symbol='{length_special_symbol}{time_special_symbol}⁻²')\n") @@ -223,17 +234,35 @@ def format_name(name: str): name = mass_name + "_per_cubic_" + length_name - dimensions = Dimensions(length=-3, time=1) + dimensions = Dimensions(length=-3, mass=1) fid.write(f"{name} " f"= Unit({mass_scale / length_scale**3}, " - f"Dimensions(-3, 1, 0, 0, 0), " + f"Dimensions(length=-3, mass=1), " f"name='{name}', " f"ascii_symbol='{mass_symbol} {length_symbol}^-3', " f"symbol='{mass_special_symbol}{length_special_symbol}⁻³')\n") unit_types[hash(dimensions)].append(name) + # Concentration + for length_symbol, length_special_symbol, length_name, _, length_scale, _ in length_units: + for amount_symbol, amount_special_symbol, _, amount_name, amount_scale, _ in amount_units: + + name = amount_name + "_per_cubic_" + length_name + + dimensions = Dimensions(length=-3, moles_hint=1) + + fid.write(f"{name} " + f"= Unit({amount_scale / length_scale**3}, " + f"Dimensions(length=-3, moles_hint=1), " + f"name='{name}', " + f"ascii_symbol='{amount_symbol} {length_symbol}^-3', " + f"symbol='{amount_special_symbol}{length_special_symbol}⁻³')\n") + + unit_types[hash(dimensions)].append(name) + + # # Add aliases to symbol lookup table # @@ -281,14 +310,17 @@ def format_name(name: str): ("magnetic_flux_density", Dimensions(0, -2, 1, -1, 0)), ("inductance", Dimensions(2, -2, 1, -2, 0)), ("temperature", Dimensions(temperature=1)), - ("dimensionless", Dimensions()) + ("dimensionless", Dimensions()), + ("angle", Dimensions(angle_hint=1)), + ("solid_angle", Dimensions(angle_hint=2)), + ("amount", Dimensions(moles_hint=1)), + ("concentration", Dimensions(length=-3, moles_hint=1)) ] fid.write("\n#\n# Units by type \n#\n\n") for dimension_name, dimensions in dimension_names: - print(dimensions, hash(dimensions)) fid.write(f"\n" f"{dimension_name} = UnitGroup(\n" diff --git a/sasdata/quantities/quantities.py b/sasdata/quantities/quantities.py index e2ac0ea67..fb6b4e2db 100644 --- a/sasdata/quantities/quantities.py +++ b/sasdata/quantities/quantities.py @@ -128,7 +128,10 @@ def __init__(self, value: QuantityType, units: Unit): self.units = units def in_units_of(self, units: Unit) -> QuantityType: - pass + if self.units.equivalent(units): + return (units.scale / self.units.scale) * self.value + else: + raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") def __mul__(self, other: ArrayLike | "Quantity" ) -> "Quantity": if isinstance(other, Quantity): @@ -157,36 +160,3 @@ def __add__(self, other: "Quantity") -> "Quantity": def __sub__(self, other: "Quantity") -> "Quantity": if isinstance(other, Quantity): pass - -class ExpressionMethod: - pass - - -class SetExpressionMethod(ExpressionMethod): - pass - - -class AnyExpressionMethod(ExpressionMethod): - pass - - -class ForceExpressionMethod(ExpressionMethod): - pass - - -class UnitToken: - def __init__(self, unit: Collection[NamedUnit], method: ExpressionMethod): - pass - -unit_dictionary = { - "Amps": Unit(1, Dimensions(current=1), UnitName("A")), - "Coulombs": Unit(1, Dimensions(current=1, time=1), UnitName("C")) -} - -@dataclass -class Disambiguator: - A: Unit = unit_dictionary["Amps"] - C: Unit = unit_dictionary["Coulombs"] - -def parse_units(unit_string: str, disambiguator: Disambiguator = Disambiguator()) -> Unit: - pass diff --git a/sasdata/quantities/unit_formatting.py b/sasdata/quantities/unit_formatting.py index e63921329..adcc7e6ba 100644 --- a/sasdata/quantities/unit_formatting.py +++ b/sasdata/quantities/unit_formatting.py @@ -1,4 +1,5 @@ + import numpy as np diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index cedfb353a..c68f6f676 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -21,13 +21,14 @@ from sasdata.quantities.unicode_superscript import int_as_unicode_superscript - class Dimensions: """ - Note that some SI Base units are + Note that some SI Base units are not useful from the perspecive of the sasview project, and make things + behave badly. In particular: moles and angular measures are dimensionless, and candelas are really a weighted + measure of power. - For example, moles and angular measures are dimensionless from this perspective, and candelas are + We do however track angle and amount, because its really useful for formatting units """ def __init__(self, @@ -35,13 +36,17 @@ def __init__(self, time: int = 0, mass: int = 0, current: int = 0, - temperature: int = 0): + temperature: int = 0, + moles_hint: int = 0, + angle_hint: int = 0): self.length = length self.time = time self.mass = mass self.current = current self.temperature = temperature + self.moles_hint = moles_hint + self.angle_hint = angle_hint def __mul__(self: Self, other: Self): @@ -53,7 +58,9 @@ def __mul__(self: Self, other: Self): self.time + other.time, self.mass + other.mass, self.current + other.current, - self.temperature + other.temperature) + self.temperature + other.temperature, + self.moles_hint + other.moles_hint, + self.angle_hint + other.angle_hint) def __truediv__(self: Self, other: Self): @@ -65,7 +72,9 @@ def __truediv__(self: Self, other: Self): self.time - other.time, self.mass - other.mass, self.current - other.current, - self.temperature - other.temperature) + self.temperature - other.temperature, + self.moles_hint - other.moles_hint, + self.angle_hint - other.angle_hint) def __pow__(self, power: int): @@ -77,7 +86,9 @@ def __pow__(self, power: int): self.time * power, self.mass * power, self.current * power, - self.temperature * power) + self.temperature * power, + self.moles_hint * power, + self.angle_hint * power) def __eq__(self: Self, other: Self): if isinstance(other, Dimensions): @@ -85,7 +96,9 @@ def __eq__(self: Self, other: Self): self.time == other.time and self.mass == other.mass and self.current == other.current and - self.temperature == other.temperature) + self.temperature == other.temperature and + self.moles_hint == other.moles_hint and + self.angle_hint == other.angle_hint) return NotImplemented @@ -108,17 +121,26 @@ def __hash__(self): if self.temperature < 0: two_powers += 16 + if self.moles_hint < 0: + two_powers += 32 + + if self.angle_hint < 0: + two_powers += 64 + return 2**two_powers * 3**abs(self.length) * 5**abs(self.time) * \ - 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) + 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) * \ + 17**abs(self.moles_hint) * 19**abs(self.angle_hint) def __repr__(self): s = "" for name, size in [ - ("L", self.length), - ("T", self.time), - ("M", self.mass), - ("C", self.current), - ("K", self.temperature)]: + ("length", self.length), + ("time", self.time), + ("mass", self.mass), + ("current", self.current), + ("temperature", self.temperature), + ("amount", self.moles_hint), + ("angle", self.angle_hint)]: if size == 0: pass @@ -178,7 +200,60 @@ def equivalent(self: Self, other: Self): def __eq__(self: Self, other: Self): return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 + def si_equivalent(self): + """ Get the SI unit corresponding to this unit""" + return Unit(1, self.dimensions) + + def _format_unit(self, format_process: list["UnitFormatProcessor"]): + for processor in format_process: + pass + +# +# Parsing plan: +# Require unknown amounts of units to be explicitly positive or negative? +# +# + + + +@dataclass +class ProcessedUnitToken: + """ Mid processing representation of formatted units """ + base_string: str + exponent_string: str + latex_exponent_string: str + exponent: int + +class UnitFormatProcessor: + """ Represents a step in the unit processing pipeline""" + def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: + """ This will be called to deal with each processing stage""" + +class RequiredUnitFormatProcessor(UnitFormatProcessor): + """ This unit is required to exist in the formatting """ + def __init__(self, unit: Unit, power: int = 1): + self.unit = unit + self.power = power + def apply(self, scale, dimensions) -> tuple[float, Dimensions, ProcessedUnitToken]: + new_scale = scale / (self.unit.scale * self.power) + new_dimensions = self.unit.dimensions / (dimensions**self.power) + token = ProcessedUnitToken(self.unit, self.power) + + return new_scale, new_dimensions, token +class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): + """ This processor minimises the dimensionality of the unit by multiplying by as many + units of the specified type as needed """ + def __init__(self, unit: Unit): + self.unit = unit + + def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: + pass + +class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): + pass + class UnitGroup: + """ A group of units that all have the same dimensionality """ def __init__(self, name: str, units: list[Unit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) @@ -188,7 +263,7 @@ def __init__(self, name: str, units: list[Unit]): # Specific units # -meters = Unit(1, Dimensions(1, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') +meters = Unit(1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') exameters = Unit(1e+18, Dimensions(1, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') petameters = Unit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') terameters = Unit(1000000000000.0, Dimensions(1, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') @@ -201,14 +276,16 @@ def __init__(self, name: str, units: list[Unit]): picometers = Unit(1e-12, Dimensions(1, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') femtometers = Unit(1e-15, Dimensions(1, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') attometers = Unit(1e-18, Dimensions(1, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') -seconds = Unit(1, Dimensions(0, 1, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') +decimeters = Unit(0.1, Dimensions(1, 0, 0, 0, 0),name='decimeters',ascii_symbol='dm',symbol='dm') +centimeters = Unit(0.01, Dimensions(1, 0, 0, 0, 0),name='centimeters',ascii_symbol='cm',symbol='cm') +seconds = Unit(1, Dimensions(0, 1, 0, 0, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') milliseconds = Unit(0.001, Dimensions(0, 1, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') microseconds = Unit(1e-06, Dimensions(0, 1, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') nanoseconds = Unit(1e-09, Dimensions(0, 1, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') picoseconds = Unit(1e-12, Dimensions(0, 1, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') femtoseconds = Unit(1e-15, Dimensions(0, 1, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') attoseconds = Unit(1e-18, Dimensions(0, 1, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') -grams = Unit(0.001, Dimensions(0, 0, 1, 0, 0),name='grams',ascii_symbol='g',symbol='g') +grams = Unit(0.001, Dimensions(0, 0, 1, 0, 0, 0, 0),name='grams',ascii_symbol='g',symbol='g') exagrams = Unit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') petagrams = Unit(1000000000000.0, Dimensions(0, 0, 1, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') teragrams = Unit(1000000000.0, Dimensions(0, 0, 1, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') @@ -221,7 +298,7 @@ def __init__(self, name: str, units: list[Unit]): picograms = Unit(1e-15, Dimensions(0, 0, 1, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') femtograms = Unit(1e-18, Dimensions(0, 0, 1, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') attograms = Unit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') -amps = Unit(1, Dimensions(0, 0, 0, 1, 0),name='amps',ascii_symbol='A',symbol='A') +amps = Unit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amps',ascii_symbol='A',symbol='A') exaamps = Unit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamps',ascii_symbol='EA',symbol='EA') petaamps = Unit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamps',ascii_symbol='PA',symbol='PA') teraamps = Unit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamps',ascii_symbol='TA',symbol='TA') @@ -234,7 +311,7 @@ def __init__(self, name: str, units: list[Unit]): picoamps = Unit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamps',ascii_symbol='pA',symbol='pA') femtoamps = Unit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamps',ascii_symbol='fA',symbol='fA') attoamps = Unit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamps',ascii_symbol='aA',symbol='aA') -kelvin = Unit(1, Dimensions(0, 0, 0, 0, 1),name='kelvin',ascii_symbol='K',symbol='K') +kelvin = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kelvin',ascii_symbol='K',symbol='K') exakelvin = Unit(1e+18, Dimensions(0, 0, 0, 0, 1),name='exakelvin',ascii_symbol='EK',symbol='EK') petakelvin = Unit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1),name='petakelvin',ascii_symbol='PK',symbol='PK') terakelvin = Unit(1000000000000.0, Dimensions(0, 0, 0, 0, 1),name='terakelvin',ascii_symbol='TK',symbol='TK') @@ -247,7 +324,7 @@ def __init__(self, name: str, units: list[Unit]): picokelvin = Unit(1e-12, Dimensions(0, 0, 0, 0, 1),name='picokelvin',ascii_symbol='pK',symbol='pK') femtokelvin = Unit(1e-15, Dimensions(0, 0, 0, 0, 1),name='femtokelvin',ascii_symbol='fK',symbol='fK') attokelvin = Unit(1e-18, Dimensions(0, 0, 0, 0, 1),name='attokelvin',ascii_symbol='aK',symbol='aK') -hertz = Unit(1, Dimensions(0, -1, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') +hertz = Unit(1, Dimensions(0, -1, 0, 0, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') exahertz = Unit(1e+18, Dimensions(0, -1, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') petahertz = Unit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') terahertz = Unit(1000000000000.0, Dimensions(0, -1, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') @@ -260,7 +337,7 @@ def __init__(self, name: str, units: list[Unit]): picohertz = Unit(1e-12, Dimensions(0, -1, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') femtohertz = Unit(1e-15, Dimensions(0, -1, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') attohertz = Unit(1e-18, Dimensions(0, -1, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') -newtons = Unit(1, Dimensions(1, -2, 1, 0, 0),name='newtons',ascii_symbol='N',symbol='N') +newtons = Unit(1, Dimensions(1, -2, 1, 0, 0, 0, 0),name='newtons',ascii_symbol='N',symbol='N') exanewtons = Unit(1e+18, Dimensions(1, -2, 1, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') petanewtons = Unit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') teranewtons = Unit(1000000000000.0, Dimensions(1, -2, 1, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') @@ -273,7 +350,7 @@ def __init__(self, name: str, units: list[Unit]): piconewtons = Unit(1e-12, Dimensions(1, -2, 1, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') femtonewtons = Unit(1e-15, Dimensions(1, -2, 1, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') attonewtons = Unit(1e-18, Dimensions(1, -2, 1, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') -pascals = Unit(1, Dimensions(-1, -2, 1, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') +pascals = Unit(1, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') exapascals = Unit(1e+18, Dimensions(-1, -2, 1, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') petapascals = Unit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') terapascals = Unit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') @@ -286,7 +363,7 @@ def __init__(self, name: str, units: list[Unit]): picopascals = Unit(1e-12, Dimensions(-1, -2, 1, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') femtopascals = Unit(1e-15, Dimensions(-1, -2, 1, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') attopascals = Unit(1e-18, Dimensions(-1, -2, 1, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') -joules = Unit(1, Dimensions(2, -2, 1, 0, 0),name='joules',ascii_symbol='J',symbol='J') +joules = Unit(1, Dimensions(2, -2, 1, 0, 0, 0, 0),name='joules',ascii_symbol='J',symbol='J') exajoules = Unit(1e+18, Dimensions(2, -2, 1, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') petajoules = Unit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') terajoules = Unit(1000000000000.0, Dimensions(2, -2, 1, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') @@ -299,7 +376,7 @@ def __init__(self, name: str, units: list[Unit]): picojoules = Unit(1e-12, Dimensions(2, -2, 1, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') femtojoules = Unit(1e-15, Dimensions(2, -2, 1, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') attojoules = Unit(1e-18, Dimensions(2, -2, 1, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') -watts = Unit(1, Dimensions(2, -3, 1, 0, 0),name='watts',ascii_symbol='W',symbol='W') +watts = Unit(1, Dimensions(2, -3, 1, 0, 0, 0, 0),name='watts',ascii_symbol='W',symbol='W') exawatts = Unit(1e+18, Dimensions(2, -3, 1, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') petawatts = Unit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') terawatts = Unit(1000000000000.0, Dimensions(2, -3, 1, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') @@ -312,7 +389,7 @@ def __init__(self, name: str, units: list[Unit]): picowatts = Unit(1e-12, Dimensions(2, -3, 1, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') femtowatts = Unit(1e-15, Dimensions(2, -3, 1, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') attowatts = Unit(1e-18, Dimensions(2, -3, 1, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') -coulombs = Unit(1, Dimensions(0, 1, 0, 1, 0),name='coulombs',ascii_symbol='C',symbol='C') +coulombs = Unit(1, Dimensions(0, 1, 0, 1, 0, 0, 0),name='coulombs',ascii_symbol='C',symbol='C') exacoulombs = Unit(1e+18, Dimensions(0, 1, 0, 1, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') petacoulombs = Unit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') teracoulombs = Unit(1000000000000.0, Dimensions(0, 1, 0, 1, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') @@ -325,7 +402,7 @@ def __init__(self, name: str, units: list[Unit]): picocoulombs = Unit(1e-12, Dimensions(0, 1, 0, 1, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') femtocoulombs = Unit(1e-15, Dimensions(0, 1, 0, 1, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') attocoulombs = Unit(1e-18, Dimensions(0, 1, 0, 1, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') -volts = Unit(1, Dimensions(2, -3, 1, -1, 0),name='volts',ascii_symbol='V',symbol='V') +volts = Unit(1, Dimensions(2, -3, 1, -1, 0, 0, 0),name='volts',ascii_symbol='V',symbol='V') exavolts = Unit(1e+18, Dimensions(2, -3, 1, -1, 0),name='exavolts',ascii_symbol='EV',symbol='EV') petavolts = Unit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0),name='petavolts',ascii_symbol='PV',symbol='PV') teravolts = Unit(1000000000000.0, Dimensions(2, -3, 1, -1, 0),name='teravolts',ascii_symbol='TV',symbol='TV') @@ -338,7 +415,7 @@ def __init__(self, name: str, units: list[Unit]): picovolts = Unit(1e-12, Dimensions(2, -3, 1, -1, 0),name='picovolts',ascii_symbol='pV',symbol='pV') femtovolts = Unit(1e-15, Dimensions(2, -3, 1, -1, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') attovolts = Unit(1e-18, Dimensions(2, -3, 1, -1, 0),name='attovolts',ascii_symbol='aV',symbol='aV') -ohms = Unit(1, Dimensions(2, -3, 1, -2, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') +ohms = Unit(1, Dimensions(2, -3, 1, -2, 0, 0, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') exaohms = Unit(1e+18, Dimensions(2, -3, 1, -2, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') petaohms = Unit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') teraohms = Unit(1000000000000.0, Dimensions(2, -3, 1, -2, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') @@ -351,7 +428,7 @@ def __init__(self, name: str, units: list[Unit]): picoohms = Unit(1e-12, Dimensions(2, -3, 1, -2, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') femtoohms = Unit(1e-15, Dimensions(2, -3, 1, -2, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') attoohms = Unit(1e-18, Dimensions(2, -3, 1, -2, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') -farads = Unit(1, Dimensions(-2, 4, -1, 2, 0),name='farads',ascii_symbol='F',symbol='F') +farads = Unit(1, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='farads',ascii_symbol='F',symbol='F') exafarads = Unit(1e+18, Dimensions(-2, 4, -1, 2, 0),name='exafarads',ascii_symbol='EF',symbol='EF') petafarads = Unit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='petafarads',ascii_symbol='PF',symbol='PF') terafarads = Unit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='terafarads',ascii_symbol='TF',symbol='TF') @@ -364,7 +441,7 @@ def __init__(self, name: str, units: list[Unit]): picofarads = Unit(1e-12, Dimensions(-2, 4, -1, 2, 0),name='picofarads',ascii_symbol='pF',symbol='pF') femtofarads = Unit(1e-15, Dimensions(-2, 4, -1, 2, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') attofarads = Unit(1e-18, Dimensions(-2, 4, -1, 2, 0),name='attofarads',ascii_symbol='aF',symbol='aF') -siemens = Unit(1, Dimensions(-2, 3, -1, 2, 0),name='siemens',ascii_symbol='S',symbol='S') +siemens = Unit(1, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='siemens',ascii_symbol='S',symbol='S') exasiemens = Unit(1e+18, Dimensions(-2, 3, -1, 2, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') petasiemens = Unit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') terasiemens = Unit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') @@ -377,7 +454,7 @@ def __init__(self, name: str, units: list[Unit]): picosiemens = Unit(1e-12, Dimensions(-2, 3, -1, 2, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') femtosiemens = Unit(1e-15, Dimensions(-2, 3, -1, 2, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') attosiemens = Unit(1e-18, Dimensions(-2, 3, -1, 2, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') -webers = Unit(1, Dimensions(2, -2, 1, -1, 0),name='webers',ascii_symbol='Wb',symbol='Wb') +webers = Unit(1, Dimensions(2, -2, 1, -1, 0, 0, 0),name='webers',ascii_symbol='Wb',symbol='Wb') exawebers = Unit(1e+18, Dimensions(2, -2, 1, -1, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') petawebers = Unit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') terawebers = Unit(1000000000000.0, Dimensions(2, -2, 1, -1, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') @@ -390,7 +467,7 @@ def __init__(self, name: str, units: list[Unit]): picowebers = Unit(1e-12, Dimensions(2, -2, 1, -1, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') femtowebers = Unit(1e-15, Dimensions(2, -2, 1, -1, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') attowebers = Unit(1e-18, Dimensions(2, -2, 1, -1, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') -tesla = Unit(1, Dimensions(0, -2, 1, -1, 0),name='tesla',ascii_symbol='T',symbol='T') +tesla = Unit(1, Dimensions(0, -2, 1, -1, 0, 0, 0),name='tesla',ascii_symbol='T',symbol='T') exatesla = Unit(1e+18, Dimensions(0, -2, 1, -1, 0),name='exatesla',ascii_symbol='ET',symbol='ET') petatesla = Unit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0),name='petatesla',ascii_symbol='PT',symbol='PT') teratesla = Unit(1000000000000.0, Dimensions(0, -2, 1, -1, 0),name='teratesla',ascii_symbol='TT',symbol='TT') @@ -403,7 +480,7 @@ def __init__(self, name: str, units: list[Unit]): picotesla = Unit(1e-12, Dimensions(0, -2, 1, -1, 0),name='picotesla',ascii_symbol='pT',symbol='pT') femtotesla = Unit(1e-15, Dimensions(0, -2, 1, -1, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') attotesla = Unit(1e-18, Dimensions(0, -2, 1, -1, 0),name='attotesla',ascii_symbol='aT',symbol='aT') -henry = Unit(1, Dimensions(2, -2, 1, -2, 0),name='henry',ascii_symbol='H',symbol='H') +henry = Unit(1, Dimensions(2, -2, 1, -2, 0, 0, 0),name='henry',ascii_symbol='H',symbol='H') exahenry = Unit(1e+18, Dimensions(2, -2, 1, -2, 0),name='exahenry',ascii_symbol='EH',symbol='EH') petahenry = Unit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0),name='petahenry',ascii_symbol='PH',symbol='PH') terahenry = Unit(1000000000000.0, Dimensions(2, -2, 1, -2, 0),name='terahenry',ascii_symbol='TH',symbol='TH') @@ -416,576 +493,806 @@ def __init__(self, name: str, units: list[Unit]): picohenry = Unit(1e-12, Dimensions(2, -2, 1, -2, 0),name='picohenry',ascii_symbol='pH',symbol='pH') femtohenry = Unit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') attohenry = Unit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') -degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1),name='degrees_celsius',ascii_symbol='C',symbol='C') -angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') -minutes = Unit(60, Dimensions(0, 1, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') -hours = Unit(360, Dimensions(0, 1, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') -days = Unit(8640, Dimensions(0, 1, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') -years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') -degrees = Unit(57.29577951308232, Dimensions(0, 0, 0, 0, 0),name='degrees',ascii_symbol='deg',symbol='deg') -radians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='radians',ascii_symbol='rad',symbol='rad') -stradians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='stradians',ascii_symbol='sr',symbol='sr') -none = Unit(1, Dimensions(0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') -square_meters = Unit(1, Dimensions(2, 0, 0, 0, 0), name='square_meters', ascii_symbol='m^2', symbol='m²') -cubic_meters = Unit(1, Dimensions(3, 0, 0, 0, 0), name='cubic_meters', ascii_symbol='m^3', symbol='m³') -per_meter = Unit(1.0, Dimensions(-1, 0, 0, 0, 0), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') -per_square_meter = Unit(1.0, Dimensions(-2, 0, 0, 0, 0), name='per_square_meter', ascii_symbol='m^-2', symbol='m⁻²') -per_cubic_meter = Unit(1.0, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_meter', ascii_symbol='m^-3', symbol='m⁻³') -square_exameters = Unit(1e+36, Dimensions(2, 0, 0, 0, 0), name='square_exameters', ascii_symbol='Em^2', symbol='Em²') -cubic_exameters = Unit(1e+54, Dimensions(3, 0, 0, 0, 0), name='cubic_exameters', ascii_symbol='Em^3', symbol='Em³') -per_exameter = Unit(1e-18, Dimensions(-1, 0, 0, 0, 0), name='per_exameter', ascii_symbol='Em^-1', symbol='Em⁻¹') -per_square_exameter = Unit(1e-36, Dimensions(-2, 0, 0, 0, 0), name='per_square_exameter', ascii_symbol='Em^-2', symbol='Em⁻²') -per_cubic_exameter = Unit(1e-54, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_exameter', ascii_symbol='Em^-3', symbol='Em⁻³') -square_petameters = Unit(1e+30, Dimensions(2, 0, 0, 0, 0), name='square_petameters', ascii_symbol='Pm^2', symbol='Pm²') -cubic_petameters = Unit(1e+45, Dimensions(3, 0, 0, 0, 0), name='cubic_petameters', ascii_symbol='Pm^3', symbol='Pm³') -per_petameter = Unit(1e-15, Dimensions(-1, 0, 0, 0, 0), name='per_petameter', ascii_symbol='Pm^-1', symbol='Pm⁻¹') -per_square_petameter = Unit(1e-30, Dimensions(-2, 0, 0, 0, 0), name='per_square_petameter', ascii_symbol='Pm^-2', symbol='Pm⁻²') -per_cubic_petameter = Unit(1e-45, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_petameter', ascii_symbol='Pm^-3', symbol='Pm⁻³') -square_terameters = Unit(1e+24, Dimensions(2, 0, 0, 0, 0), name='square_terameters', ascii_symbol='Tm^2', symbol='Tm²') -cubic_terameters = Unit(1e+36, Dimensions(3, 0, 0, 0, 0), name='cubic_terameters', ascii_symbol='Tm^3', symbol='Tm³') -per_terameter = Unit(1e-12, Dimensions(-1, 0, 0, 0, 0), name='per_terameter', ascii_symbol='Tm^-1', symbol='Tm⁻¹') -per_square_terameter = Unit(1e-24, Dimensions(-2, 0, 0, 0, 0), name='per_square_terameter', ascii_symbol='Tm^-2', symbol='Tm⁻²') -per_cubic_terameter = Unit(1e-36, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_terameter', ascii_symbol='Tm^-3', symbol='Tm⁻³') -square_gigameters = Unit(1e+18, Dimensions(2, 0, 0, 0, 0), name='square_gigameters', ascii_symbol='Gm^2', symbol='Gm²') -cubic_gigameters = Unit(1e+27, Dimensions(3, 0, 0, 0, 0), name='cubic_gigameters', ascii_symbol='Gm^3', symbol='Gm³') -per_gigameter = Unit(1e-09, Dimensions(-1, 0, 0, 0, 0), name='per_gigameter', ascii_symbol='Gm^-1', symbol='Gm⁻¹') -per_square_gigameter = Unit(1e-18, Dimensions(-2, 0, 0, 0, 0), name='per_square_gigameter', ascii_symbol='Gm^-2', symbol='Gm⁻²') -per_cubic_gigameter = Unit(1e-27, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_gigameter', ascii_symbol='Gm^-3', symbol='Gm⁻³') -square_megameters = Unit(1000000000000.0, Dimensions(2, 0, 0, 0, 0), name='square_megameters', ascii_symbol='Mm^2', symbol='Mm²') -cubic_megameters = Unit(1e+18, Dimensions(3, 0, 0, 0, 0), name='cubic_megameters', ascii_symbol='Mm^3', symbol='Mm³') -per_megameter = Unit(1e-06, Dimensions(-1, 0, 0, 0, 0), name='per_megameter', ascii_symbol='Mm^-1', symbol='Mm⁻¹') -per_square_megameter = Unit(1e-12, Dimensions(-2, 0, 0, 0, 0), name='per_square_megameter', ascii_symbol='Mm^-2', symbol='Mm⁻²') -per_cubic_megameter = Unit(1e-18, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_megameter', ascii_symbol='Mm^-3', symbol='Mm⁻³') -square_kilometers = Unit(1000000.0, Dimensions(2, 0, 0, 0, 0), name='square_kilometers', ascii_symbol='km^2', symbol='km²') -cubic_kilometers = Unit(1000000000.0, Dimensions(3, 0, 0, 0, 0), name='cubic_kilometers', ascii_symbol='km^3', symbol='km³') -per_kilometer = Unit(0.001, Dimensions(-1, 0, 0, 0, 0), name='per_kilometer', ascii_symbol='km^-1', symbol='km⁻¹') -per_square_kilometer = Unit(1e-06, Dimensions(-2, 0, 0, 0, 0), name='per_square_kilometer', ascii_symbol='km^-2', symbol='km⁻²') -per_cubic_kilometer = Unit(1e-09, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_kilometer', ascii_symbol='km^-3', symbol='km⁻³') -square_millimeters = Unit(1e-06, Dimensions(2, 0, 0, 0, 0), name='square_millimeters', ascii_symbol='mm^2', symbol='mm²') -cubic_millimeters = Unit(1e-09, Dimensions(3, 0, 0, 0, 0), name='cubic_millimeters', ascii_symbol='mm^3', symbol='mm³') -per_millimeter = Unit(1000.0, Dimensions(-1, 0, 0, 0, 0), name='per_millimeter', ascii_symbol='mm^-1', symbol='mm⁻¹') -per_square_millimeter = Unit(1000000.0, Dimensions(-2, 0, 0, 0, 0), name='per_square_millimeter', ascii_symbol='mm^-2', symbol='mm⁻²') -per_cubic_millimeter = Unit(999999999.9999999, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_millimeter', ascii_symbol='mm^-3', symbol='mm⁻³') -square_micrometers = Unit(1e-12, Dimensions(2, 0, 0, 0, 0), name='square_micrometers', ascii_symbol='um^2', symbol='µm²') -cubic_micrometers = Unit(9.999999999999999e-19, Dimensions(3, 0, 0, 0, 0), name='cubic_micrometers', ascii_symbol='um^3', symbol='µm³') -per_micrometer = Unit(1000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_micrometer', ascii_symbol='um^-1', symbol='µm⁻¹') -per_square_micrometer = Unit(1000000000000.0001, Dimensions(-2, 0, 0, 0, 0), name='per_square_micrometer', ascii_symbol='um^-2', symbol='µm⁻²') -per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_micrometer', ascii_symbol='um^-3', symbol='µm⁻³') -square_nanometers = Unit(1e-18, Dimensions(2, 0, 0, 0, 0), name='square_nanometers', ascii_symbol='nm^2', symbol='nm²') -cubic_nanometers = Unit(1.0000000000000002e-27, Dimensions(3, 0, 0, 0, 0), name='cubic_nanometers', ascii_symbol='nm^3', symbol='nm³') -per_nanometer = Unit(999999999.9999999, Dimensions(-1, 0, 0, 0, 0), name='per_nanometer', ascii_symbol='nm^-1', symbol='nm⁻¹') -per_square_nanometer = Unit(9.999999999999999e+17, Dimensions(-2, 0, 0, 0, 0), name='per_square_nanometer', ascii_symbol='nm^-2', symbol='nm⁻²') -per_cubic_nanometer = Unit(9.999999999999999e+26, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_nanometer', ascii_symbol='nm^-3', symbol='nm⁻³') -square_picometers = Unit(1e-24, Dimensions(2, 0, 0, 0, 0), name='square_picometers', ascii_symbol='pm^2', symbol='pm²') -cubic_picometers = Unit(1e-36, Dimensions(3, 0, 0, 0, 0), name='cubic_picometers', ascii_symbol='pm^3', symbol='pm³') -per_picometer = Unit(1000000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_picometer', ascii_symbol='pm^-1', symbol='pm⁻¹') -per_square_picometer = Unit(1e+24, Dimensions(-2, 0, 0, 0, 0), name='per_square_picometer', ascii_symbol='pm^-2', symbol='pm⁻²') -per_cubic_picometer = Unit(1e+36, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_picometer', ascii_symbol='pm^-3', symbol='pm⁻³') -square_femtometers = Unit(1e-30, Dimensions(2, 0, 0, 0, 0), name='square_femtometers', ascii_symbol='fm^2', symbol='fm²') -cubic_femtometers = Unit(1.0000000000000003e-45, Dimensions(3, 0, 0, 0, 0), name='cubic_femtometers', ascii_symbol='fm^3', symbol='fm³') -per_femtometer = Unit(999999999999999.9, Dimensions(-1, 0, 0, 0, 0), name='per_femtometer', ascii_symbol='fm^-1', symbol='fm⁻¹') -per_square_femtometer = Unit(9.999999999999999e+29, Dimensions(-2, 0, 0, 0, 0), name='per_square_femtometer', ascii_symbol='fm^-2', symbol='fm⁻²') -per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_femtometer', ascii_symbol='fm^-3', symbol='fm⁻³') -square_attometers = Unit(1.0000000000000001e-36, Dimensions(2, 0, 0, 0, 0), name='square_attometers', ascii_symbol='am^2', symbol='am²') -cubic_attometers = Unit(1.0000000000000002e-54, Dimensions(3, 0, 0, 0, 0), name='cubic_attometers', ascii_symbol='am^3', symbol='am³') -per_attometer = Unit(9.999999999999999e+17, Dimensions(-1, 0, 0, 0, 0), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') -per_square_attometer = Unit(9.999999999999999e+35, Dimensions(-2, 0, 0, 0, 0), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') -per_cubic_attometer = Unit(9.999999999999997e+53, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') -square_angstroms = Unit(1.0000000000000001e-20, Dimensions(2, 0, 0, 0, 0), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ų') -cubic_angstroms = Unit(1e-30, Dimensions(3, 0, 0, 0, 0), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='ų') -per_angstrom = Unit(10000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Å⁻¹') -per_square_angstrom = Unit(1e+20, Dimensions(-2, 0, 0, 0, 0), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Å⁻²') -per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Å⁻³') -meters_per_second = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') -meters_per_square_second = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') -meters_per_millisecond = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') -meters_per_square_millisecond = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') -meters_per_microsecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') -meters_per_square_microsecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') -meters_per_nanosecond = Unit(999999999.9999999, Dimensions(1, -1, 0, 0, 0), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') -meters_per_square_nanosecond = Unit(999999999.9999999, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') -meters_per_picosecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') -meters_per_square_picosecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') -meters_per_femtosecond = Unit(999999999999999.9, Dimensions(1, -1, 0, 0, 0), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') -meters_per_square_femtosecond = Unit(999999999999999.9, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') -meters_per_attosecond = Unit(9.999999999999999e+17, Dimensions(1, -1, 0, 0, 0), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') -meters_per_square_attosecond = Unit(9.999999999999999e+17, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') -meters_per_minute = Unit(0.016666666666666666, Dimensions(1, -1, 0, 0, 0), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') -meters_per_square_minute = Unit(0.016666666666666666, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') -meters_per_hour = Unit(0.002777777777777778, Dimensions(1, -1, 0, 0, 0), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') -meters_per_square_hour = Unit(0.002777777777777778, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') -meters_per_day = Unit(0.00011574074074074075, Dimensions(1, -1, 0, 0, 0), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') -meters_per_square_day = Unit(0.00011574074074074075, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') -meters_per_year = Unit(3.168873850681143e-07, Dimensions(1, -1, 0, 0, 0), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') -meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') -exameters_per_second = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') -exameters_per_square_second = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') -exameters_per_millisecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') -exameters_per_square_millisecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') -exameters_per_microsecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') -exameters_per_square_microsecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') -exameters_per_nanosecond = Unit(9.999999999999999e+26, Dimensions(1, -1, 0, 0, 0), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') -exameters_per_square_nanosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') -exameters_per_picosecond = Unit(1e+30, Dimensions(1, -1, 0, 0, 0), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') -exameters_per_square_picosecond = Unit(1e+30, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') -exameters_per_femtosecond = Unit(1e+33, Dimensions(1, -1, 0, 0, 0), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') -exameters_per_square_femtosecond = Unit(1e+33, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') -exameters_per_attosecond = Unit(9.999999999999999e+35, Dimensions(1, -1, 0, 0, 0), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') -exameters_per_square_attosecond = Unit(9.999999999999999e+35, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') -exameters_per_minute = Unit(1.6666666666666666e+16, Dimensions(1, -1, 0, 0, 0), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') -exameters_per_square_minute = Unit(1.6666666666666666e+16, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') -exameters_per_hour = Unit(2777777777777778.0, Dimensions(1, -1, 0, 0, 0), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') -exameters_per_square_hour = Unit(2777777777777778.0, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') -exameters_per_day = Unit(115740740740740.73, Dimensions(1, -1, 0, 0, 0), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') -exameters_per_square_day = Unit(115740740740740.73, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') -exameters_per_year = Unit(316887385068.1143, Dimensions(1, -1, 0, 0, 0), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') -exameters_per_square_year = Unit(316887385068.1143, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') -petameters_per_second = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') -petameters_per_square_second = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') -petameters_per_millisecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') -petameters_per_square_millisecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') -petameters_per_microsecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') -petameters_per_square_microsecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') -petameters_per_nanosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') -petameters_per_square_nanosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') -petameters_per_picosecond = Unit(1e+27, Dimensions(1, -1, 0, 0, 0), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') -petameters_per_square_picosecond = Unit(1e+27, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') -petameters_per_femtosecond = Unit(9.999999999999999e+29, Dimensions(1, -1, 0, 0, 0), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') -petameters_per_square_femtosecond = Unit(9.999999999999999e+29, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') -petameters_per_attosecond = Unit(1e+33, Dimensions(1, -1, 0, 0, 0), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') -petameters_per_square_attosecond = Unit(1e+33, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') -petameters_per_minute = Unit(16666666666666.666, Dimensions(1, -1, 0, 0, 0), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') -petameters_per_square_minute = Unit(16666666666666.666, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') -petameters_per_hour = Unit(2777777777777.778, Dimensions(1, -1, 0, 0, 0), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') -petameters_per_square_hour = Unit(2777777777777.778, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') -petameters_per_day = Unit(115740740740.74074, Dimensions(1, -1, 0, 0, 0), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') -petameters_per_square_day = Unit(115740740740.74074, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') -petameters_per_year = Unit(316887385.0681143, Dimensions(1, -1, 0, 0, 0), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') -petameters_per_square_year = Unit(316887385.0681143, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') -terameters_per_second = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') -terameters_per_square_second = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') -terameters_per_millisecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') -terameters_per_square_millisecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') -terameters_per_microsecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') -terameters_per_square_microsecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') -terameters_per_nanosecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') -terameters_per_square_nanosecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') -terameters_per_picosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') -terameters_per_square_picosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') -terameters_per_femtosecond = Unit(9.999999999999999e+26, Dimensions(1, -1, 0, 0, 0), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') -terameters_per_square_femtosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') -terameters_per_attosecond = Unit(9.999999999999999e+29, Dimensions(1, -1, 0, 0, 0), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') -terameters_per_square_attosecond = Unit(9.999999999999999e+29, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') -terameters_per_minute = Unit(16666666666.666666, Dimensions(1, -1, 0, 0, 0), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') -terameters_per_square_minute = Unit(16666666666.666666, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') -terameters_per_hour = Unit(2777777777.7777777, Dimensions(1, -1, 0, 0, 0), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') -terameters_per_square_hour = Unit(2777777777.7777777, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') -terameters_per_day = Unit(115740740.74074075, Dimensions(1, -1, 0, 0, 0), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') -terameters_per_square_day = Unit(115740740.74074075, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') -terameters_per_year = Unit(316887.38506811426, Dimensions(1, -1, 0, 0, 0), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') -terameters_per_square_year = Unit(316887.38506811426, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') -gigameters_per_second = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') -gigameters_per_square_second = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') -gigameters_per_millisecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') -gigameters_per_square_millisecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') -gigameters_per_microsecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') -gigameters_per_square_microsecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') -gigameters_per_nanosecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') -gigameters_per_square_nanosecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') -gigameters_per_picosecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') -gigameters_per_square_picosecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') -gigameters_per_femtosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') -gigameters_per_square_femtosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') -gigameters_per_attosecond = Unit(9.999999999999999e+26, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') -gigameters_per_square_attosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') -gigameters_per_minute = Unit(16666666.666666666, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') -gigameters_per_square_minute = Unit(16666666.666666666, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') -gigameters_per_hour = Unit(2777777.777777778, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') -gigameters_per_square_hour = Unit(2777777.777777778, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') -gigameters_per_day = Unit(115740.74074074074, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') -gigameters_per_square_day = Unit(115740.74074074074, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') -gigameters_per_year = Unit(316.88738506811427, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') -gigameters_per_square_year = Unit(316.88738506811427, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') -megameters_per_second = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') -megameters_per_square_second = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') -megameters_per_millisecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') -megameters_per_square_millisecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') -megameters_per_microsecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') -megameters_per_square_microsecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') -megameters_per_nanosecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') -megameters_per_square_nanosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') -megameters_per_picosecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') -megameters_per_square_picosecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') -megameters_per_femtosecond = Unit(9.999999999999999e+20, Dimensions(1, -1, 0, 0, 0), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') -megameters_per_square_femtosecond = Unit(9.999999999999999e+20, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') -megameters_per_attosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') -megameters_per_square_attosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') -megameters_per_minute = Unit(16666.666666666668, Dimensions(1, -1, 0, 0, 0), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') -megameters_per_square_minute = Unit(16666.666666666668, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') -megameters_per_hour = Unit(2777.777777777778, Dimensions(1, -1, 0, 0, 0), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') -megameters_per_square_hour = Unit(2777.777777777778, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') -megameters_per_day = Unit(115.74074074074075, Dimensions(1, -1, 0, 0, 0), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') -megameters_per_square_day = Unit(115.74074074074075, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') -megameters_per_year = Unit(0.3168873850681143, Dimensions(1, -1, 0, 0, 0), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') -megameters_per_square_year = Unit(0.3168873850681143, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') -kilometers_per_second = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') -kilometers_per_square_second = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') -kilometers_per_millisecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') -kilometers_per_square_millisecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') -kilometers_per_microsecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') -kilometers_per_square_microsecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') -kilometers_per_nanosecond = Unit(999999999999.9999, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') -kilometers_per_square_nanosecond = Unit(999999999999.9999, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') -kilometers_per_picosecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') -kilometers_per_square_picosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') -kilometers_per_femtosecond = Unit(9.999999999999999e+17, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') -kilometers_per_square_femtosecond = Unit(9.999999999999999e+17, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') -kilometers_per_attosecond = Unit(9.999999999999999e+20, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') -kilometers_per_square_attosecond = Unit(9.999999999999999e+20, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') -kilometers_per_minute = Unit(16.666666666666668, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') -kilometers_per_square_minute = Unit(16.666666666666668, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') -kilometers_per_hour = Unit(2.7777777777777777, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') -kilometers_per_square_hour = Unit(2.7777777777777777, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') -kilometers_per_day = Unit(0.11574074074074074, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') -kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') -kilometers_per_year = Unit(0.0003168873850681143, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') -kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') -millimeters_per_second = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') -millimeters_per_square_second = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') -millimeters_per_millisecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') -millimeters_per_square_millisecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') -millimeters_per_microsecond = Unit(1000.0000000000001, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') -millimeters_per_square_microsecond = Unit(1000.0000000000001, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') -millimeters_per_nanosecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') -millimeters_per_square_nanosecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') -millimeters_per_picosecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') -millimeters_per_square_picosecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') -millimeters_per_femtosecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') -millimeters_per_square_femtosecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') -millimeters_per_attosecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') -millimeters_per_square_attosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') -millimeters_per_minute = Unit(1.6666666666666667e-05, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') -millimeters_per_square_minute = Unit(1.6666666666666667e-05, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') -millimeters_per_hour = Unit(2.777777777777778e-06, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') -millimeters_per_square_hour = Unit(2.777777777777778e-06, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') -millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') -millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') -millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') -millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') -micrometers_per_second = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') -micrometers_per_square_second = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') -micrometers_per_millisecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') -micrometers_per_square_millisecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') -micrometers_per_microsecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') -micrometers_per_square_microsecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') -micrometers_per_nanosecond = Unit(999.9999999999999, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') -micrometers_per_square_nanosecond = Unit(999.9999999999999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') -micrometers_per_picosecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') -micrometers_per_square_picosecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') -micrometers_per_femtosecond = Unit(999999999.9999999, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') -micrometers_per_square_femtosecond = Unit(999999999.9999999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') -micrometers_per_attosecond = Unit(999999999999.9999, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') -micrometers_per_square_attosecond = Unit(999999999999.9999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') -micrometers_per_minute = Unit(1.6666666666666667e-08, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') -micrometers_per_square_minute = Unit(1.6666666666666667e-08, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') -micrometers_per_hour = Unit(2.7777777777777776e-09, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') -micrometers_per_square_hour = Unit(2.7777777777777776e-09, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') -micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') -micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') -micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') -micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') -nanometers_per_second = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') -nanometers_per_square_second = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') -nanometers_per_millisecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') -nanometers_per_square_millisecond = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') -nanometers_per_microsecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') -nanometers_per_square_microsecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') -nanometers_per_nanosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') -nanometers_per_square_nanosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') -nanometers_per_picosecond = Unit(1000.0000000000001, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') -nanometers_per_square_picosecond = Unit(1000.0000000000001, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') -nanometers_per_femtosecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') -nanometers_per_square_femtosecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') -nanometers_per_attosecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') -nanometers_per_square_attosecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') -nanometers_per_minute = Unit(1.6666666666666667e-11, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') -nanometers_per_square_minute = Unit(1.6666666666666667e-11, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') -nanometers_per_hour = Unit(2.777777777777778e-12, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') -nanometers_per_square_hour = Unit(2.777777777777778e-12, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') -nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') -nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') -nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') -nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') -picometers_per_second = Unit(1e-12, Dimensions(1, -1, 0, 0, 0), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') -picometers_per_square_second = Unit(1e-12, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') -picometers_per_millisecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') -picometers_per_square_millisecond = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') -picometers_per_microsecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') -picometers_per_square_microsecond = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') -picometers_per_nanosecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') -picometers_per_square_nanosecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') -picometers_per_picosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') -picometers_per_square_picosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') -picometers_per_femtosecond = Unit(999.9999999999999, Dimensions(1, -1, 0, 0, 0), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') -picometers_per_square_femtosecond = Unit(999.9999999999999, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') -picometers_per_attosecond = Unit(999999.9999999999, Dimensions(1, -1, 0, 0, 0), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') -picometers_per_square_attosecond = Unit(999999.9999999999, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') -picometers_per_minute = Unit(1.6666666666666667e-14, Dimensions(1, -1, 0, 0, 0), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') -picometers_per_square_minute = Unit(1.6666666666666667e-14, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') -picometers_per_hour = Unit(2.7777777777777776e-15, Dimensions(1, -1, 0, 0, 0), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') -picometers_per_square_hour = Unit(2.7777777777777776e-15, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') -picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(1, -1, 0, 0, 0), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') -picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') -picometers_per_year = Unit(3.168873850681143e-19, Dimensions(1, -1, 0, 0, 0), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') -picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') -femtometers_per_second = Unit(1e-15, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') -femtometers_per_square_second = Unit(1e-15, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') -femtometers_per_millisecond = Unit(1e-12, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') -femtometers_per_square_millisecond = Unit(1e-12, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') -femtometers_per_microsecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') -femtometers_per_square_microsecond = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') -femtometers_per_nanosecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') -femtometers_per_square_nanosecond = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') -femtometers_per_picosecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') -femtometers_per_square_picosecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') -femtometers_per_femtosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') -femtometers_per_square_femtosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') -femtometers_per_attosecond = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') -femtometers_per_square_attosecond = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') -femtometers_per_minute = Unit(1.6666666666666667e-17, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') -femtometers_per_square_minute = Unit(1.6666666666666667e-17, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') -femtometers_per_hour = Unit(2.777777777777778e-18, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') -femtometers_per_square_hour = Unit(2.777777777777778e-18, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') -femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') -femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') -femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') -femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') -attometers_per_second = Unit(1e-18, Dimensions(1, -1, 0, 0, 0), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') -attometers_per_square_second = Unit(1e-18, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') -attometers_per_millisecond = Unit(1e-15, Dimensions(1, -1, 0, 0, 0), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') -attometers_per_square_millisecond = Unit(1e-15, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') -attometers_per_microsecond = Unit(1.0000000000000002e-12, Dimensions(1, -1, 0, 0, 0), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') -attometers_per_square_microsecond = Unit(1.0000000000000002e-12, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') -attometers_per_nanosecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') -attometers_per_square_nanosecond = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') -attometers_per_picosecond = Unit(1.0000000000000002e-06, Dimensions(1, -1, 0, 0, 0), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') -attometers_per_square_picosecond = Unit(1.0000000000000002e-06, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') -attometers_per_femtosecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') -attometers_per_square_femtosecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') -attometers_per_attosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') -attometers_per_square_attosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') -attometers_per_minute = Unit(1.6666666666666668e-20, Dimensions(1, -1, 0, 0, 0), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') -attometers_per_square_minute = Unit(1.6666666666666668e-20, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') -attometers_per_hour = Unit(2.7777777777777778e-21, Dimensions(1, -1, 0, 0, 0), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') -attometers_per_square_hour = Unit(2.7777777777777778e-21, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') -attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(1, -1, 0, 0, 0), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') -attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') -attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(1, -1, 0, 0, 0), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') -attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') -angstroms_per_second = Unit(1e-10, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') -angstroms_per_square_second = Unit(1e-10, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') -angstroms_per_millisecond = Unit(1e-07, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') -angstroms_per_square_millisecond = Unit(1e-07, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') -angstroms_per_microsecond = Unit(0.0001, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') -angstroms_per_square_microsecond = Unit(0.0001, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') -angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') -angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') -angstroms_per_picosecond = Unit(100.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') -angstroms_per_square_picosecond = Unit(100.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') -angstroms_per_femtosecond = Unit(100000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') -angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') -angstroms_per_attosecond = Unit(100000000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') -angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') -angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') -angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') -angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') -angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') -angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') -angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') -angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') -angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') -grams_per_cubic_meter = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_meter = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') -petagrams_per_cubic_meter = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') -teragrams_per_cubic_meter = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_meter = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') -megagrams_per_cubic_meter = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') -kilograms_per_cubic_meter = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgNone⁻³') -milligrams_per_cubic_meter = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgNone⁻³') -micrograms_per_cubic_meter = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgNone⁻³') -nanograms_per_cubic_meter = Unit(1.0000000000000002e-12, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngNone⁻³') -picograms_per_cubic_meter = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') -femtograms_per_cubic_meter = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') -attograms_per_cubic_meter = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') -grams_per_cubic_exameter = Unit(1e-57, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') -exagrams_per_cubic_exameter = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') -petagrams_per_cubic_exameter = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') -teragrams_per_cubic_exameter = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') -gigagrams_per_cubic_exameter = Unit(1e-48, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_exameter', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') -megagrams_per_cubic_exameter = Unit(9.999999999999999e-52, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_exameter', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') -kilograms_per_cubic_exameter = Unit(9.999999999999999e-55, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_exameter', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') -milligrams_per_cubic_exameter = Unit(9.999999999999998e-61, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_exameter', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') -micrograms_per_cubic_exameter = Unit(9.999999999999999e-64, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_exameter', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') -nanograms_per_cubic_exameter = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_exameter', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') -picograms_per_cubic_exameter = Unit(1e-69, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') -femtograms_per_cubic_exameter = Unit(1e-72, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') -attograms_per_cubic_exameter = Unit(1e-75, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -grams_per_cubic_petameter = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') -exagrams_per_cubic_petameter = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') -petagrams_per_cubic_petameter = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') -teragrams_per_cubic_petameter = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') -gigagrams_per_cubic_petameter = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_petameter', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') -megagrams_per_cubic_petameter = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_petameter', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') -kilograms_per_cubic_petameter = Unit(1.0000000000000001e-45, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_petameter', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') -milligrams_per_cubic_petameter = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_petameter', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') -micrograms_per_cubic_petameter = Unit(1.0000000000000002e-54, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_petameter', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') -nanograms_per_cubic_petameter = Unit(1.0000000000000002e-57, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_petameter', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') -picograms_per_cubic_petameter = Unit(1.0000000000000001e-60, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') -femtograms_per_cubic_petameter = Unit(1.0000000000000002e-63, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') -attograms_per_cubic_petameter = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -grams_per_cubic_terameter = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') -exagrams_per_cubic_terameter = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') -petagrams_per_cubic_terameter = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') -teragrams_per_cubic_terameter = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') -gigagrams_per_cubic_terameter = Unit(9.999999999999999e-31, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_terameter', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') -megagrams_per_cubic_terameter = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_terameter', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') -kilograms_per_cubic_terameter = Unit(1e-36, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_terameter', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') -milligrams_per_cubic_terameter = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_terameter', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') -micrograms_per_cubic_terameter = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_terameter', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') -nanograms_per_cubic_terameter = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_terameter', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') -picograms_per_cubic_terameter = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') -femtograms_per_cubic_terameter = Unit(1e-54, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') -attograms_per_cubic_terameter = Unit(1.0000000000000001e-57, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -grams_per_cubic_gigameter = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') -exagrams_per_cubic_gigameter = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') -petagrams_per_cubic_gigameter = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') -teragrams_per_cubic_gigameter = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') -gigagrams_per_cubic_gigameter = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_gigameter', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') -megagrams_per_cubic_gigameter = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_gigameter', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') -kilograms_per_cubic_gigameter = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_gigameter', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') -milligrams_per_cubic_gigameter = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_gigameter', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') -micrograms_per_cubic_gigameter = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_gigameter', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') -nanograms_per_cubic_gigameter = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_gigameter', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') -picograms_per_cubic_gigameter = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') -femtograms_per_cubic_gigameter = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') -attograms_per_cubic_gigameter = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -grams_per_cubic_megameter = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') -exagrams_per_cubic_megameter = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') -petagrams_per_cubic_megameter = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') -teragrams_per_cubic_megameter = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') -gigagrams_per_cubic_megameter = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_megameter', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') -megagrams_per_cubic_megameter = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_megameter', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') -kilograms_per_cubic_megameter = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_megameter', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') -milligrams_per_cubic_megameter = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_megameter', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') -micrograms_per_cubic_megameter = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_megameter', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') -nanograms_per_cubic_megameter = Unit(1.0000000000000003e-30, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_megameter', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') -picograms_per_cubic_megameter = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') -femtograms_per_cubic_megameter = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') -attograms_per_cubic_megameter = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -grams_per_cubic_kilometer = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') -exagrams_per_cubic_kilometer = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') -petagrams_per_cubic_kilometer = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') -teragrams_per_cubic_kilometer = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') -gigagrams_per_cubic_kilometer = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_kilometer', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') -megagrams_per_cubic_kilometer = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_kilometer', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') -kilograms_per_cubic_kilometer = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_kilometer', ascii_symbol='kg km^-3', symbol='kgkm⁻³') -milligrams_per_cubic_kilometer = Unit(9.999999999999999e-16, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_kilometer', ascii_symbol='mg km^-3', symbol='mgkm⁻³') -micrograms_per_cubic_kilometer = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_kilometer', ascii_symbol='ug km^-3', symbol='µgkm⁻³') -nanograms_per_cubic_kilometer = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_kilometer', ascii_symbol='ng km^-3', symbol='ngkm⁻³') -picograms_per_cubic_kilometer = Unit(1.0000000000000001e-24, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') -femtograms_per_cubic_kilometer = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') -attograms_per_cubic_kilometer = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') -grams_per_cubic_millimeter = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') -exagrams_per_cubic_millimeter = Unit(1e+24, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') -petagrams_per_cubic_millimeter = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') -teragrams_per_cubic_millimeter = Unit(1e+18, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') -gigagrams_per_cubic_millimeter = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_millimeter', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') -megagrams_per_cubic_millimeter = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_millimeter', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') -kilograms_per_cubic_millimeter = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_millimeter', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') -milligrams_per_cubic_millimeter = Unit(999.9999999999999, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_millimeter', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') -micrograms_per_cubic_millimeter = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_millimeter', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') -nanograms_per_cubic_millimeter = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_millimeter', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') -picograms_per_cubic_millimeter = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') -femtograms_per_cubic_millimeter = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') -attograms_per_cubic_millimeter = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -grams_per_cubic_micrometer = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') -exagrams_per_cubic_micrometer = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') -petagrams_per_cubic_micrometer = Unit(1.0000000000000002e+30, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') -teragrams_per_cubic_micrometer = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') -gigagrams_per_cubic_micrometer = Unit(1.0000000000000001e+24, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_micrometer', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') -megagrams_per_cubic_micrometer = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_micrometer', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') -kilograms_per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_micrometer', ascii_symbol='kg um^-3', symbol='kgµm⁻³') -milligrams_per_cubic_micrometer = Unit(1000000000000.0001, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_micrometer', ascii_symbol='mg um^-3', symbol='mgµm⁻³') -micrograms_per_cubic_micrometer = Unit(1000000000.0000002, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_micrometer', ascii_symbol='ug um^-3', symbol='µgµm⁻³') -nanograms_per_cubic_micrometer = Unit(1000000.0000000003, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_micrometer', ascii_symbol='ng um^-3', symbol='ngµm⁻³') -picograms_per_cubic_micrometer = Unit(1000.0000000000002, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') -femtograms_per_cubic_micrometer = Unit(1.0000000000000002, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') -attograms_per_cubic_micrometer = Unit(0.0010000000000000002, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') -grams_per_cubic_nanometer = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') -exagrams_per_cubic_nanometer = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') -petagrams_per_cubic_nanometer = Unit(9.999999999999998e+38, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') -teragrams_per_cubic_nanometer = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') -gigagrams_per_cubic_nanometer = Unit(9.999999999999998e+32, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_nanometer', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') -megagrams_per_cubic_nanometer = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_nanometer', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') -kilograms_per_cubic_nanometer = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_nanometer', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') -milligrams_per_cubic_nanometer = Unit(9.999999999999997e+20, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_nanometer', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') -micrograms_per_cubic_nanometer = Unit(9.999999999999999e+17, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_nanometer', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') -nanograms_per_cubic_nanometer = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_nanometer', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') -picograms_per_cubic_nanometer = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') -femtograms_per_cubic_nanometer = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') -attograms_per_cubic_nanometer = Unit(999999.9999999999, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -grams_per_cubic_picometer = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') -exagrams_per_cubic_picometer = Unit(1e+51, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') -petagrams_per_cubic_picometer = Unit(1e+48, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') -teragrams_per_cubic_picometer = Unit(1.0000000000000001e+45, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') -gigagrams_per_cubic_picometer = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_picometer', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') -megagrams_per_cubic_picometer = Unit(1.0000000000000001e+39, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_picometer', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') -kilograms_per_cubic_picometer = Unit(1e+36, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_picometer', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') -milligrams_per_cubic_picometer = Unit(1e+30, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_picometer', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') -micrograms_per_cubic_picometer = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_picometer', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') -nanograms_per_cubic_picometer = Unit(1.0000000000000003e+24, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_picometer', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') -picograms_per_cubic_picometer = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') -femtograms_per_cubic_picometer = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') -attograms_per_cubic_picometer = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -grams_per_cubic_femtometer = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') -exagrams_per_cubic_femtometer = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') -petagrams_per_cubic_femtometer = Unit(9.999999999999997e+56, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') -teragrams_per_cubic_femtometer = Unit(9.999999999999997e+53, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') -gigagrams_per_cubic_femtometer = Unit(9.999999999999997e+50, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_femtometer', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') -megagrams_per_cubic_femtometer = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_femtometer', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') -kilograms_per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_femtometer', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') -milligrams_per_cubic_femtometer = Unit(9.999999999999996e+38, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_femtometer', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') -micrograms_per_cubic_femtometer = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_femtometer', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') -nanograms_per_cubic_femtometer = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_femtometer', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') -picograms_per_cubic_femtometer = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') -femtograms_per_cubic_femtometer = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') -attograms_per_cubic_femtometer = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -grams_per_cubic_attometer = Unit(9.999999999999998e+50, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') -exagrams_per_cubic_attometer = Unit(9.999999999999999e+68, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') -petagrams_per_cubic_attometer = Unit(9.999999999999998e+65, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') -teragrams_per_cubic_attometer = Unit(9.999999999999999e+62, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') -gigagrams_per_cubic_attometer = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_attometer', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') -megagrams_per_cubic_attometer = Unit(9.999999999999999e+56, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_attometer', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') -kilograms_per_cubic_attometer = Unit(9.999999999999999e+53, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_attometer', ascii_symbol='kg am^-3', symbol='kgam⁻³') -milligrams_per_cubic_attometer = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_attometer', ascii_symbol='mg am^-3', symbol='mgam⁻³') -micrograms_per_cubic_attometer = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_attometer', ascii_symbol='ug am^-3', symbol='µgam⁻³') -nanograms_per_cubic_attometer = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_attometer', ascii_symbol='ng am^-3', symbol='ngam⁻³') -picograms_per_cubic_attometer = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') -femtograms_per_cubic_attometer = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') -attograms_per_cubic_attometer = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') -grams_per_cubic_angstrom = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') -exagrams_per_cubic_angstrom = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') -petagrams_per_cubic_angstrom = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') -teragrams_per_cubic_angstrom = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') -gigagrams_per_cubic_angstrom = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_angstrom', ascii_symbol='Gg Ang^-3', symbol='GgÅ⁻³') -megagrams_per_cubic_angstrom = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_angstrom', ascii_symbol='Mg Ang^-3', symbol='MgÅ⁻³') -kilograms_per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_angstrom', ascii_symbol='kg Ang^-3', symbol='kgÅ⁻³') -milligrams_per_cubic_angstrom = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_angstrom', ascii_symbol='mg Ang^-3', symbol='mgÅ⁻³') -micrograms_per_cubic_angstrom = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_angstrom', ascii_symbol='ug Ang^-3', symbol='µgÅ⁻³') -nanograms_per_cubic_angstrom = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_angstrom', ascii_symbol='ng Ang^-3', symbol='ngÅ⁻³') -picograms_per_cubic_angstrom = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') -femtograms_per_cubic_angstrom = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') -attograms_per_cubic_angstrom = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') +degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') +angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') +minutes = Unit(60, Dimensions(0, 1, 0, 0, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') +hours = Unit(360, Dimensions(0, 1, 0, 0, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') +days = Unit(8640, Dimensions(0, 1, 0, 0, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') +years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') +degrees = Unit(57.29577951308232, Dimensions(0, 0, 0, 0, 0, 0, 1),name='degrees',ascii_symbol='deg',symbol='deg') +radians = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 1),name='radians',ascii_symbol='rad',symbol='rad') +stradians = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') +none = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') +litres = Unit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') +electronvolts = Unit(1.602e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') +exaelectronvolts = Unit(0.1602, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') +petaelectronvolts = Unit(0.0001602, Dimensions(2, -2, 1, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') +teraelectronvolts = Unit(1.602e-07, Dimensions(2, -2, 1, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') +gigaelectronvolts = Unit(1.602e-10, Dimensions(2, -2, 1, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') +megaelectronvolts = Unit(1.602e-13, Dimensions(2, -2, 1, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') +kiloelectronvolts = Unit(1.602e-16, Dimensions(2, -2, 1, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') +millielectronvolts = Unit(1.602e-22, Dimensions(2, -2, 1, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') +microelectronvolts = Unit(1.6019999999999998e-25, Dimensions(2, -2, 1, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') +nanoelectronvolts = Unit(1.602e-28, Dimensions(2, -2, 1, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') +picoelectronvolts = Unit(1.6019999999999998e-31, Dimensions(2, -2, 1, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') +femtoelectronvolts = Unit(1.602e-34, Dimensions(2, -2, 1, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') +attoelectronvolts = Unit(1.6020000000000002e-37, Dimensions(2, -2, 1, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') +atomic_mass_units = Unit(1.661e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') +moles = Unit(6.022e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') +millimoles = Unit(6.022e+20, Dimensions(0, 0, 0, 0, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') +micromoles = Unit(6.022e+17, Dimensions(0, 0, 0, 0, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') +nanomoles = Unit(602200000000000.1, Dimensions(0, 0, 0, 0, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') +picomoles = Unit(602200000000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') +femtomoles = Unit(602200000.0000001, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') +attomoles = Unit(602200.0000000001, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') +square_meters = Unit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') +cubic_meters = Unit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') +per_meter = Unit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') +per_square_meter = Unit(1.0, Dimensions(length=-2), name='per_square_meter', ascii_symbol='m^-2', symbol='m⁻²') +per_cubic_meter = Unit(1.0, Dimensions(length=-3), name='per_cubic_meter', ascii_symbol='m^-3', symbol='m⁻³') +square_exameters = Unit(1e+36, Dimensions(length=2), name='square_exameters', ascii_symbol='Em^2', symbol='Em²') +cubic_exameters = Unit(1e+54, Dimensions(length=3), name='cubic_exameters', ascii_symbol='Em^3', symbol='Em³') +per_exameter = Unit(1e-18, Dimensions(length=-1), name='per_exameter', ascii_symbol='Em^-1', symbol='Em⁻¹') +per_square_exameter = Unit(1e-36, Dimensions(length=-2), name='per_square_exameter', ascii_symbol='Em^-2', symbol='Em⁻²') +per_cubic_exameter = Unit(1e-54, Dimensions(length=-3), name='per_cubic_exameter', ascii_symbol='Em^-3', symbol='Em⁻³') +square_petameters = Unit(1e+30, Dimensions(length=2), name='square_petameters', ascii_symbol='Pm^2', symbol='Pm²') +cubic_petameters = Unit(1e+45, Dimensions(length=3), name='cubic_petameters', ascii_symbol='Pm^3', symbol='Pm³') +per_petameter = Unit(1e-15, Dimensions(length=-1), name='per_petameter', ascii_symbol='Pm^-1', symbol='Pm⁻¹') +per_square_petameter = Unit(1e-30, Dimensions(length=-2), name='per_square_petameter', ascii_symbol='Pm^-2', symbol='Pm⁻²') +per_cubic_petameter = Unit(1e-45, Dimensions(length=-3), name='per_cubic_petameter', ascii_symbol='Pm^-3', symbol='Pm⁻³') +square_terameters = Unit(1e+24, Dimensions(length=2), name='square_terameters', ascii_symbol='Tm^2', symbol='Tm²') +cubic_terameters = Unit(1e+36, Dimensions(length=3), name='cubic_terameters', ascii_symbol='Tm^3', symbol='Tm³') +per_terameter = Unit(1e-12, Dimensions(length=-1), name='per_terameter', ascii_symbol='Tm^-1', symbol='Tm⁻¹') +per_square_terameter = Unit(1e-24, Dimensions(length=-2), name='per_square_terameter', ascii_symbol='Tm^-2', symbol='Tm⁻²') +per_cubic_terameter = Unit(1e-36, Dimensions(length=-3), name='per_cubic_terameter', ascii_symbol='Tm^-3', symbol='Tm⁻³') +square_gigameters = Unit(1e+18, Dimensions(length=2), name='square_gigameters', ascii_symbol='Gm^2', symbol='Gm²') +cubic_gigameters = Unit(1e+27, Dimensions(length=3), name='cubic_gigameters', ascii_symbol='Gm^3', symbol='Gm³') +per_gigameter = Unit(1e-09, Dimensions(length=-1), name='per_gigameter', ascii_symbol='Gm^-1', symbol='Gm⁻¹') +per_square_gigameter = Unit(1e-18, Dimensions(length=-2), name='per_square_gigameter', ascii_symbol='Gm^-2', symbol='Gm⁻²') +per_cubic_gigameter = Unit(1e-27, Dimensions(length=-3), name='per_cubic_gigameter', ascii_symbol='Gm^-3', symbol='Gm⁻³') +square_megameters = Unit(1000000000000.0, Dimensions(length=2), name='square_megameters', ascii_symbol='Mm^2', symbol='Mm²') +cubic_megameters = Unit(1e+18, Dimensions(length=3), name='cubic_megameters', ascii_symbol='Mm^3', symbol='Mm³') +per_megameter = Unit(1e-06, Dimensions(length=-1), name='per_megameter', ascii_symbol='Mm^-1', symbol='Mm⁻¹') +per_square_megameter = Unit(1e-12, Dimensions(length=-2), name='per_square_megameter', ascii_symbol='Mm^-2', symbol='Mm⁻²') +per_cubic_megameter = Unit(1e-18, Dimensions(length=-3), name='per_cubic_megameter', ascii_symbol='Mm^-3', symbol='Mm⁻³') +square_kilometers = Unit(1000000.0, Dimensions(length=2), name='square_kilometers', ascii_symbol='km^2', symbol='km²') +cubic_kilometers = Unit(1000000000.0, Dimensions(length=3), name='cubic_kilometers', ascii_symbol='km^3', symbol='km³') +per_kilometer = Unit(0.001, Dimensions(length=-1), name='per_kilometer', ascii_symbol='km^-1', symbol='km⁻¹') +per_square_kilometer = Unit(1e-06, Dimensions(length=-2), name='per_square_kilometer', ascii_symbol='km^-2', symbol='km⁻²') +per_cubic_kilometer = Unit(1e-09, Dimensions(length=-3), name='per_cubic_kilometer', ascii_symbol='km^-3', symbol='km⁻³') +square_millimeters = Unit(1e-06, Dimensions(length=2), name='square_millimeters', ascii_symbol='mm^2', symbol='mm²') +cubic_millimeters = Unit(1e-09, Dimensions(length=3), name='cubic_millimeters', ascii_symbol='mm^3', symbol='mm³') +per_millimeter = Unit(1000.0, Dimensions(length=-1), name='per_millimeter', ascii_symbol='mm^-1', symbol='mm⁻¹') +per_square_millimeter = Unit(1000000.0, Dimensions(length=-2), name='per_square_millimeter', ascii_symbol='mm^-2', symbol='mm⁻²') +per_cubic_millimeter = Unit(999999999.9999999, Dimensions(length=-3), name='per_cubic_millimeter', ascii_symbol='mm^-3', symbol='mm⁻³') +square_micrometers = Unit(1e-12, Dimensions(length=2), name='square_micrometers', ascii_symbol='um^2', symbol='µm²') +cubic_micrometers = Unit(9.999999999999999e-19, Dimensions(length=3), name='cubic_micrometers', ascii_symbol='um^3', symbol='µm³') +per_micrometer = Unit(1000000.0, Dimensions(length=-1), name='per_micrometer', ascii_symbol='um^-1', symbol='µm⁻¹') +per_square_micrometer = Unit(1000000000000.0001, Dimensions(length=-2), name='per_square_micrometer', ascii_symbol='um^-2', symbol='µm⁻²') +per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(length=-3), name='per_cubic_micrometer', ascii_symbol='um^-3', symbol='µm⁻³') +square_nanometers = Unit(1e-18, Dimensions(length=2), name='square_nanometers', ascii_symbol='nm^2', symbol='nm²') +cubic_nanometers = Unit(1.0000000000000002e-27, Dimensions(length=3), name='cubic_nanometers', ascii_symbol='nm^3', symbol='nm³') +per_nanometer = Unit(999999999.9999999, Dimensions(length=-1), name='per_nanometer', ascii_symbol='nm^-1', symbol='nm⁻¹') +per_square_nanometer = Unit(9.999999999999999e+17, Dimensions(length=-2), name='per_square_nanometer', ascii_symbol='nm^-2', symbol='nm⁻²') +per_cubic_nanometer = Unit(9.999999999999999e+26, Dimensions(length=-3), name='per_cubic_nanometer', ascii_symbol='nm^-3', symbol='nm⁻³') +square_picometers = Unit(1e-24, Dimensions(length=2), name='square_picometers', ascii_symbol='pm^2', symbol='pm²') +cubic_picometers = Unit(1e-36, Dimensions(length=3), name='cubic_picometers', ascii_symbol='pm^3', symbol='pm³') +per_picometer = Unit(1000000000000.0, Dimensions(length=-1), name='per_picometer', ascii_symbol='pm^-1', symbol='pm⁻¹') +per_square_picometer = Unit(1e+24, Dimensions(length=-2), name='per_square_picometer', ascii_symbol='pm^-2', symbol='pm⁻²') +per_cubic_picometer = Unit(1e+36, Dimensions(length=-3), name='per_cubic_picometer', ascii_symbol='pm^-3', symbol='pm⁻³') +square_femtometers = Unit(1e-30, Dimensions(length=2), name='square_femtometers', ascii_symbol='fm^2', symbol='fm²') +cubic_femtometers = Unit(1.0000000000000003e-45, Dimensions(length=3), name='cubic_femtometers', ascii_symbol='fm^3', symbol='fm³') +per_femtometer = Unit(999999999999999.9, Dimensions(length=-1), name='per_femtometer', ascii_symbol='fm^-1', symbol='fm⁻¹') +per_square_femtometer = Unit(9.999999999999999e+29, Dimensions(length=-2), name='per_square_femtometer', ascii_symbol='fm^-2', symbol='fm⁻²') +per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(length=-3), name='per_cubic_femtometer', ascii_symbol='fm^-3', symbol='fm⁻³') +square_attometers = Unit(1.0000000000000001e-36, Dimensions(length=2), name='square_attometers', ascii_symbol='am^2', symbol='am²') +cubic_attometers = Unit(1.0000000000000002e-54, Dimensions(length=3), name='cubic_attometers', ascii_symbol='am^3', symbol='am³') +per_attometer = Unit(9.999999999999999e+17, Dimensions(length=-1), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') +per_square_attometer = Unit(9.999999999999999e+35, Dimensions(length=-2), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') +per_cubic_attometer = Unit(9.999999999999997e+53, Dimensions(length=-3), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') +square_decimeters = Unit(0.010000000000000002, Dimensions(length=2), name='square_decimeters', ascii_symbol='dm^2', symbol='dm²') +cubic_decimeters = Unit(0.0010000000000000002, Dimensions(length=3), name='cubic_decimeters', ascii_symbol='dm^3', symbol='dm³') +per_decimeter = Unit(10.0, Dimensions(length=-1), name='per_decimeter', ascii_symbol='dm^-1', symbol='dm⁻¹') +per_square_decimeter = Unit(99.99999999999999, Dimensions(length=-2), name='per_square_decimeter', ascii_symbol='dm^-2', symbol='dm⁻²') +per_cubic_decimeter = Unit(999.9999999999999, Dimensions(length=-3), name='per_cubic_decimeter', ascii_symbol='dm^-3', symbol='dm⁻³') +square_centimeters = Unit(0.0001, Dimensions(length=2), name='square_centimeters', ascii_symbol='cm^2', symbol='cm²') +cubic_centimeters = Unit(1.0000000000000002e-06, Dimensions(length=3), name='cubic_centimeters', ascii_symbol='cm^3', symbol='cm³') +per_centimeter = Unit(100.0, Dimensions(length=-1), name='per_centimeter', ascii_symbol='cm^-1', symbol='cm⁻¹') +per_square_centimeter = Unit(10000.0, Dimensions(length=-2), name='per_square_centimeter', ascii_symbol='cm^-2', symbol='cm⁻²') +per_cubic_centimeter = Unit(999999.9999999999, Dimensions(length=-3), name='per_cubic_centimeter', ascii_symbol='cm^-3', symbol='cm⁻³') +square_angstroms = Unit(1.0000000000000001e-20, Dimensions(length=2), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ų') +cubic_angstroms = Unit(1e-30, Dimensions(length=3), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='ų') +per_angstrom = Unit(10000000000.0, Dimensions(length=-1), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Å⁻¹') +per_square_angstrom = Unit(1e+20, Dimensions(length=-2), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Å⁻²') +per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(length=-3), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Å⁻³') +meters_per_second = Unit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') +meters_per_square_second = Unit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') +meters_per_millisecond = Unit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') +meters_per_square_millisecond = Unit(1000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') +meters_per_microsecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') +meters_per_square_microsecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') +meters_per_nanosecond = Unit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') +meters_per_square_nanosecond = Unit(999999999.9999999, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') +meters_per_picosecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') +meters_per_square_picosecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') +meters_per_femtosecond = Unit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') +meters_per_square_femtosecond = Unit(999999999999999.9, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') +meters_per_attosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') +meters_per_square_attosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') +meters_per_minute = Unit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') +meters_per_square_minute = Unit(0.016666666666666666, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') +meters_per_hour = Unit(0.002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') +meters_per_square_hour = Unit(0.002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') +meters_per_day = Unit(0.00011574074074074075, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') +meters_per_square_day = Unit(0.00011574074074074075, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') +meters_per_year = Unit(3.168873850681143e-07, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') +meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') +exameters_per_second = Unit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') +exameters_per_square_second = Unit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') +exameters_per_millisecond = Unit(1e+21, Dimensions(length=1, time=-1), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') +exameters_per_square_millisecond = Unit(1e+21, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') +exameters_per_microsecond = Unit(1e+24, Dimensions(length=1, time=-1), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') +exameters_per_square_microsecond = Unit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') +exameters_per_nanosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') +exameters_per_square_nanosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') +exameters_per_picosecond = Unit(1e+30, Dimensions(length=1, time=-1), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') +exameters_per_square_picosecond = Unit(1e+30, Dimensions(length=1, time=-2), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') +exameters_per_femtosecond = Unit(1e+33, Dimensions(length=1, time=-1), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') +exameters_per_square_femtosecond = Unit(1e+33, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') +exameters_per_attosecond = Unit(9.999999999999999e+35, Dimensions(length=1, time=-1), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') +exameters_per_square_attosecond = Unit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') +exameters_per_minute = Unit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') +exameters_per_square_minute = Unit(1.6666666666666666e+16, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') +exameters_per_hour = Unit(2777777777777778.0, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') +exameters_per_square_hour = Unit(2777777777777778.0, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') +exameters_per_day = Unit(115740740740740.73, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') +exameters_per_square_day = Unit(115740740740740.73, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') +exameters_per_year = Unit(316887385068.1143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') +exameters_per_square_year = Unit(316887385068.1143, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') +petameters_per_second = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') +petameters_per_square_second = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') +petameters_per_millisecond = Unit(1e+18, Dimensions(length=1, time=-1), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') +petameters_per_square_millisecond = Unit(1e+18, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') +petameters_per_microsecond = Unit(1e+21, Dimensions(length=1, time=-1), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') +petameters_per_square_microsecond = Unit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') +petameters_per_nanosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') +petameters_per_square_nanosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') +petameters_per_picosecond = Unit(1e+27, Dimensions(length=1, time=-1), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') +petameters_per_square_picosecond = Unit(1e+27, Dimensions(length=1, time=-2), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') +petameters_per_femtosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') +petameters_per_square_femtosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') +petameters_per_attosecond = Unit(1e+33, Dimensions(length=1, time=-1), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') +petameters_per_square_attosecond = Unit(1e+33, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') +petameters_per_minute = Unit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') +petameters_per_square_minute = Unit(16666666666666.666, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') +petameters_per_hour = Unit(2777777777777.778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') +petameters_per_square_hour = Unit(2777777777777.778, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') +petameters_per_day = Unit(115740740740.74074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') +petameters_per_square_day = Unit(115740740740.74074, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') +petameters_per_year = Unit(316887385.0681143, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') +petameters_per_square_year = Unit(316887385.0681143, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') +terameters_per_second = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') +terameters_per_square_second = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') +terameters_per_millisecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') +terameters_per_square_millisecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') +terameters_per_microsecond = Unit(1e+18, Dimensions(length=1, time=-1), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') +terameters_per_square_microsecond = Unit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') +terameters_per_nanosecond = Unit(1e+21, Dimensions(length=1, time=-1), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') +terameters_per_square_nanosecond = Unit(1e+21, Dimensions(length=1, time=-2), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') +terameters_per_picosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') +terameters_per_square_picosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') +terameters_per_femtosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') +terameters_per_square_femtosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') +terameters_per_attosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') +terameters_per_square_attosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') +terameters_per_minute = Unit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') +terameters_per_square_minute = Unit(16666666666.666666, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') +terameters_per_hour = Unit(2777777777.7777777, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') +terameters_per_square_hour = Unit(2777777777.7777777, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') +terameters_per_day = Unit(115740740.74074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') +terameters_per_square_day = Unit(115740740.74074075, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') +terameters_per_year = Unit(316887.38506811426, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') +terameters_per_square_year = Unit(316887.38506811426, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') +gigameters_per_second = Unit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') +gigameters_per_square_second = Unit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') +gigameters_per_millisecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') +gigameters_per_square_millisecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') +gigameters_per_microsecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') +gigameters_per_square_microsecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') +gigameters_per_nanosecond = Unit(1e+18, Dimensions(length=1, time=-1), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') +gigameters_per_square_nanosecond = Unit(1e+18, Dimensions(length=1, time=-2), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') +gigameters_per_picosecond = Unit(1e+21, Dimensions(length=1, time=-1), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') +gigameters_per_square_picosecond = Unit(1e+21, Dimensions(length=1, time=-2), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') +gigameters_per_femtosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') +gigameters_per_square_femtosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') +gigameters_per_attosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') +gigameters_per_square_attosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') +gigameters_per_minute = Unit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') +gigameters_per_square_minute = Unit(16666666.666666666, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') +gigameters_per_hour = Unit(2777777.777777778, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') +gigameters_per_square_hour = Unit(2777777.777777778, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') +gigameters_per_day = Unit(115740.74074074074, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') +gigameters_per_square_day = Unit(115740.74074074074, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') +gigameters_per_year = Unit(316.88738506811427, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') +gigameters_per_square_year = Unit(316.88738506811427, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') +megameters_per_second = Unit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') +megameters_per_square_second = Unit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') +megameters_per_millisecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') +megameters_per_square_millisecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') +megameters_per_microsecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') +megameters_per_square_microsecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') +megameters_per_nanosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') +megameters_per_square_nanosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') +megameters_per_picosecond = Unit(1e+18, Dimensions(length=1, time=-1), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') +megameters_per_square_picosecond = Unit(1e+18, Dimensions(length=1, time=-2), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') +megameters_per_femtosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') +megameters_per_square_femtosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') +megameters_per_attosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') +megameters_per_square_attosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') +megameters_per_minute = Unit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') +megameters_per_square_minute = Unit(16666.666666666668, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') +megameters_per_hour = Unit(2777.777777777778, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') +megameters_per_square_hour = Unit(2777.777777777778, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') +megameters_per_day = Unit(115.74074074074075, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') +megameters_per_square_day = Unit(115.74074074074075, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') +megameters_per_year = Unit(0.3168873850681143, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') +megameters_per_square_year = Unit(0.3168873850681143, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') +kilometers_per_second = Unit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') +kilometers_per_square_second = Unit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') +kilometers_per_millisecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') +kilometers_per_square_millisecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') +kilometers_per_microsecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') +kilometers_per_square_microsecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') +kilometers_per_nanosecond = Unit(999999999999.9999, Dimensions(length=1, time=-1), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') +kilometers_per_square_nanosecond = Unit(999999999999.9999, Dimensions(length=1, time=-2), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') +kilometers_per_picosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') +kilometers_per_square_picosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') +kilometers_per_femtosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') +kilometers_per_square_femtosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') +kilometers_per_attosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') +kilometers_per_square_attosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') +kilometers_per_minute = Unit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') +kilometers_per_square_minute = Unit(16.666666666666668, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') +kilometers_per_hour = Unit(2.7777777777777777, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') +kilometers_per_square_hour = Unit(2.7777777777777777, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') +kilometers_per_day = Unit(0.11574074074074074, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') +kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') +kilometers_per_year = Unit(0.0003168873850681143, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') +kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') +millimeters_per_second = Unit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') +millimeters_per_square_second = Unit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') +millimeters_per_millisecond = Unit(1.0, Dimensions(length=1, time=-1), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') +millimeters_per_square_millisecond = Unit(1.0, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') +millimeters_per_microsecond = Unit(1000.0000000000001, Dimensions(length=1, time=-1), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') +millimeters_per_square_microsecond = Unit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') +millimeters_per_nanosecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') +millimeters_per_square_nanosecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') +millimeters_per_picosecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') +millimeters_per_square_picosecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') +millimeters_per_femtosecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') +millimeters_per_square_femtosecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') +millimeters_per_attosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') +millimeters_per_square_attosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') +millimeters_per_minute = Unit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') +millimeters_per_square_minute = Unit(1.6666666666666667e-05, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') +millimeters_per_hour = Unit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') +millimeters_per_square_hour = Unit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') +millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') +millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') +millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') +millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') +micrometers_per_second = Unit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') +micrometers_per_square_second = Unit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') +micrometers_per_millisecond = Unit(0.001, Dimensions(length=1, time=-1), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') +micrometers_per_square_millisecond = Unit(0.001, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') +micrometers_per_microsecond = Unit(1.0, Dimensions(length=1, time=-1), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') +micrometers_per_square_microsecond = Unit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') +micrometers_per_nanosecond = Unit(999.9999999999999, Dimensions(length=1, time=-1), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') +micrometers_per_square_nanosecond = Unit(999.9999999999999, Dimensions(length=1, time=-2), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') +micrometers_per_picosecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') +micrometers_per_square_picosecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') +micrometers_per_femtosecond = Unit(999999999.9999999, Dimensions(length=1, time=-1), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') +micrometers_per_square_femtosecond = Unit(999999999.9999999, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') +micrometers_per_attosecond = Unit(999999999999.9999, Dimensions(length=1, time=-1), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') +micrometers_per_square_attosecond = Unit(999999999999.9999, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') +micrometers_per_minute = Unit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') +micrometers_per_square_minute = Unit(1.6666666666666667e-08, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') +micrometers_per_hour = Unit(2.7777777777777776e-09, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') +micrometers_per_square_hour = Unit(2.7777777777777776e-09, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') +micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') +micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') +micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') +micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') +nanometers_per_second = Unit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') +nanometers_per_square_second = Unit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') +nanometers_per_millisecond = Unit(1e-06, Dimensions(length=1, time=-1), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') +nanometers_per_square_millisecond = Unit(1e-06, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') +nanometers_per_microsecond = Unit(0.001, Dimensions(length=1, time=-1), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') +nanometers_per_square_microsecond = Unit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') +nanometers_per_nanosecond = Unit(1.0, Dimensions(length=1, time=-1), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') +nanometers_per_square_nanosecond = Unit(1.0, Dimensions(length=1, time=-2), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') +nanometers_per_picosecond = Unit(1000.0000000000001, Dimensions(length=1, time=-1), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') +nanometers_per_square_picosecond = Unit(1000.0000000000001, Dimensions(length=1, time=-2), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') +nanometers_per_femtosecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') +nanometers_per_square_femtosecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') +nanometers_per_attosecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') +nanometers_per_square_attosecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') +nanometers_per_minute = Unit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') +nanometers_per_square_minute = Unit(1.6666666666666667e-11, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') +nanometers_per_hour = Unit(2.777777777777778e-12, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') +nanometers_per_square_hour = Unit(2.777777777777778e-12, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') +nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') +nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') +nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') +nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') +picometers_per_second = Unit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') +picometers_per_square_second = Unit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') +picometers_per_millisecond = Unit(1e-09, Dimensions(length=1, time=-1), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') +picometers_per_square_millisecond = Unit(1e-09, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') +picometers_per_microsecond = Unit(1e-06, Dimensions(length=1, time=-1), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') +picometers_per_square_microsecond = Unit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') +picometers_per_nanosecond = Unit(0.001, Dimensions(length=1, time=-1), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') +picometers_per_square_nanosecond = Unit(0.001, Dimensions(length=1, time=-2), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') +picometers_per_picosecond = Unit(1.0, Dimensions(length=1, time=-1), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') +picometers_per_square_picosecond = Unit(1.0, Dimensions(length=1, time=-2), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') +picometers_per_femtosecond = Unit(999.9999999999999, Dimensions(length=1, time=-1), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') +picometers_per_square_femtosecond = Unit(999.9999999999999, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') +picometers_per_attosecond = Unit(999999.9999999999, Dimensions(length=1, time=-1), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') +picometers_per_square_attosecond = Unit(999999.9999999999, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') +picometers_per_minute = Unit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') +picometers_per_square_minute = Unit(1.6666666666666667e-14, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') +picometers_per_hour = Unit(2.7777777777777776e-15, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') +picometers_per_square_hour = Unit(2.7777777777777776e-15, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') +picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') +picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') +picometers_per_year = Unit(3.168873850681143e-19, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') +picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') +femtometers_per_second = Unit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') +femtometers_per_square_second = Unit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') +femtometers_per_millisecond = Unit(1e-12, Dimensions(length=1, time=-1), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') +femtometers_per_square_millisecond = Unit(1e-12, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') +femtometers_per_microsecond = Unit(1e-09, Dimensions(length=1, time=-1), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') +femtometers_per_square_microsecond = Unit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') +femtometers_per_nanosecond = Unit(1e-06, Dimensions(length=1, time=-1), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') +femtometers_per_square_nanosecond = Unit(1e-06, Dimensions(length=1, time=-2), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') +femtometers_per_picosecond = Unit(0.001, Dimensions(length=1, time=-1), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') +femtometers_per_square_picosecond = Unit(0.001, Dimensions(length=1, time=-2), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') +femtometers_per_femtosecond = Unit(1.0, Dimensions(length=1, time=-1), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') +femtometers_per_square_femtosecond = Unit(1.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') +femtometers_per_attosecond = Unit(1000.0, Dimensions(length=1, time=-1), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') +femtometers_per_square_attosecond = Unit(1000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') +femtometers_per_minute = Unit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') +femtometers_per_square_minute = Unit(1.6666666666666667e-17, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') +femtometers_per_hour = Unit(2.777777777777778e-18, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') +femtometers_per_square_hour = Unit(2.777777777777778e-18, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') +femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') +femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') +femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') +femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') +attometers_per_second = Unit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') +attometers_per_square_second = Unit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') +attometers_per_millisecond = Unit(1e-15, Dimensions(length=1, time=-1), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') +attometers_per_square_millisecond = Unit(1e-15, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') +attometers_per_microsecond = Unit(1.0000000000000002e-12, Dimensions(length=1, time=-1), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') +attometers_per_square_microsecond = Unit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') +attometers_per_nanosecond = Unit(1e-09, Dimensions(length=1, time=-1), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') +attometers_per_square_nanosecond = Unit(1e-09, Dimensions(length=1, time=-2), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') +attometers_per_picosecond = Unit(1.0000000000000002e-06, Dimensions(length=1, time=-1), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') +attometers_per_square_picosecond = Unit(1.0000000000000002e-06, Dimensions(length=1, time=-2), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') +attometers_per_femtosecond = Unit(0.001, Dimensions(length=1, time=-1), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') +attometers_per_square_femtosecond = Unit(0.001, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') +attometers_per_attosecond = Unit(1.0, Dimensions(length=1, time=-1), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') +attometers_per_square_attosecond = Unit(1.0, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') +attometers_per_minute = Unit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') +attometers_per_square_minute = Unit(1.6666666666666668e-20, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') +attometers_per_hour = Unit(2.7777777777777778e-21, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') +attometers_per_square_hour = Unit(2.7777777777777778e-21, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') +attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') +attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') +attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') +attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') +decimeters_per_second = Unit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dmNone⁻¹') +decimeters_per_square_second = Unit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dmNone⁻²') +decimeters_per_millisecond = Unit(100.0, Dimensions(length=1, time=-1), name='decimeters_per_millisecond', ascii_symbol='dm/ms', symbol='dmms⁻¹') +decimeters_per_square_millisecond = Unit(100.0, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') +decimeters_per_microsecond = Unit(100000.00000000001, Dimensions(length=1, time=-1), name='decimeters_per_microsecond', ascii_symbol='dm/us', symbol='dmµs⁻¹') +decimeters_per_square_microsecond = Unit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_microsecond', ascii_symbol='dm/us^2', symbol='dmµs⁻²') +decimeters_per_nanosecond = Unit(100000000.0, Dimensions(length=1, time=-1), name='decimeters_per_nanosecond', ascii_symbol='dm/ns', symbol='dmns⁻¹') +decimeters_per_square_nanosecond = Unit(100000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_nanosecond', ascii_symbol='dm/ns^2', symbol='dmns⁻²') +decimeters_per_picosecond = Unit(100000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_picosecond', ascii_symbol='dm/ps', symbol='dmps⁻¹') +decimeters_per_square_picosecond = Unit(100000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_picosecond', ascii_symbol='dm/ps^2', symbol='dmps⁻²') +decimeters_per_femtosecond = Unit(100000000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_femtosecond', ascii_symbol='dm/fs', symbol='dmfs⁻¹') +decimeters_per_square_femtosecond = Unit(100000000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') +decimeters_per_attosecond = Unit(1e+17, Dimensions(length=1, time=-1), name='decimeters_per_attosecond', ascii_symbol='dm/as', symbol='dmas⁻¹') +decimeters_per_square_attosecond = Unit(1e+17, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') +decimeters_per_minute = Unit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmNone⁻¹') +decimeters_per_square_minute = Unit(0.0016666666666666668, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmNone⁻²') +decimeters_per_hour = Unit(0.0002777777777777778, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmNone⁻¹') +decimeters_per_square_hour = Unit(0.0002777777777777778, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmNone⁻²') +decimeters_per_day = Unit(1.1574074074074075e-05, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmNone⁻¹') +decimeters_per_square_day = Unit(1.1574074074074075e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmNone⁻²') +decimeters_per_year = Unit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmNone⁻¹') +decimeters_per_square_year = Unit(3.168873850681143e-08, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmNone⁻²') +centimeters_per_second = Unit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cmNone⁻¹') +centimeters_per_square_second = Unit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cmNone⁻²') +centimeters_per_millisecond = Unit(10.0, Dimensions(length=1, time=-1), name='centimeters_per_millisecond', ascii_symbol='cm/ms', symbol='cmms⁻¹') +centimeters_per_square_millisecond = Unit(10.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') +centimeters_per_microsecond = Unit(10000.0, Dimensions(length=1, time=-1), name='centimeters_per_microsecond', ascii_symbol='cm/us', symbol='cmµs⁻¹') +centimeters_per_square_microsecond = Unit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_microsecond', ascii_symbol='cm/us^2', symbol='cmµs⁻²') +centimeters_per_nanosecond = Unit(10000000.0, Dimensions(length=1, time=-1), name='centimeters_per_nanosecond', ascii_symbol='cm/ns', symbol='cmns⁻¹') +centimeters_per_square_nanosecond = Unit(10000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_nanosecond', ascii_symbol='cm/ns^2', symbol='cmns⁻²') +centimeters_per_picosecond = Unit(10000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_picosecond', ascii_symbol='cm/ps', symbol='cmps⁻¹') +centimeters_per_square_picosecond = Unit(10000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_picosecond', ascii_symbol='cm/ps^2', symbol='cmps⁻²') +centimeters_per_femtosecond = Unit(10000000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_femtosecond', ascii_symbol='cm/fs', symbol='cmfs⁻¹') +centimeters_per_square_femtosecond = Unit(10000000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') +centimeters_per_attosecond = Unit(1e+16, Dimensions(length=1, time=-1), name='centimeters_per_attosecond', ascii_symbol='cm/as', symbol='cmas⁻¹') +centimeters_per_square_attosecond = Unit(1e+16, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') +centimeters_per_minute = Unit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmNone⁻¹') +centimeters_per_square_minute = Unit(0.00016666666666666666, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmNone⁻²') +centimeters_per_hour = Unit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmNone⁻¹') +centimeters_per_square_hour = Unit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmNone⁻²') +centimeters_per_day = Unit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmNone⁻¹') +centimeters_per_square_day = Unit(1.1574074074074074e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmNone⁻²') +centimeters_per_year = Unit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmNone⁻¹') +centimeters_per_square_year = Unit(3.168873850681143e-09, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmNone⁻²') +angstroms_per_second = Unit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') +angstroms_per_square_second = Unit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') +angstroms_per_millisecond = Unit(1e-07, Dimensions(length=1, time=-1), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') +angstroms_per_square_millisecond = Unit(1e-07, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') +angstroms_per_microsecond = Unit(0.0001, Dimensions(length=1, time=-1), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') +angstroms_per_square_microsecond = Unit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') +angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(length=1, time=-1), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') +angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(length=1, time=-2), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') +angstroms_per_picosecond = Unit(100.0, Dimensions(length=1, time=-1), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') +angstroms_per_square_picosecond = Unit(100.0, Dimensions(length=1, time=-2), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') +angstroms_per_femtosecond = Unit(100000.0, Dimensions(length=1, time=-1), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') +angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') +angstroms_per_attosecond = Unit(100000000.0, Dimensions(length=1, time=-1), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') +angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') +angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') +angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') +angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') +angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') +angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') +angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') +angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') +angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') +grams_per_cubic_meter = Unit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_meter = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') +petagrams_per_cubic_meter = Unit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') +teragrams_per_cubic_meter = Unit(1000000000.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_meter = Unit(1000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') +megagrams_per_cubic_meter = Unit(1000.0, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') +kilograms_per_cubic_meter = Unit(1.0, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgNone⁻³') +milligrams_per_cubic_meter = Unit(1e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgNone⁻³') +micrograms_per_cubic_meter = Unit(1e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgNone⁻³') +nanograms_per_cubic_meter = Unit(1.0000000000000002e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngNone⁻³') +picograms_per_cubic_meter = Unit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') +femtograms_per_cubic_meter = Unit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') +attograms_per_cubic_meter = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') +atomic_mass_units_per_cubic_meter = Unit(1.661e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='NoneNone⁻³') +grams_per_cubic_exameter = Unit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') +exagrams_per_cubic_exameter = Unit(1e-39, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') +petagrams_per_cubic_exameter = Unit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') +teragrams_per_cubic_exameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') +gigagrams_per_cubic_exameter = Unit(1e-48, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_exameter', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') +megagrams_per_cubic_exameter = Unit(9.999999999999999e-52, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_exameter', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') +kilograms_per_cubic_exameter = Unit(9.999999999999999e-55, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_exameter', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') +milligrams_per_cubic_exameter = Unit(9.999999999999998e-61, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_exameter', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') +micrograms_per_cubic_exameter = Unit(9.999999999999999e-64, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_exameter', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') +nanograms_per_cubic_exameter = Unit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_exameter', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') +picograms_per_cubic_exameter = Unit(1e-69, Dimensions(length=-3, mass=1), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') +femtograms_per_cubic_exameter = Unit(1e-72, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') +attograms_per_cubic_exameter = Unit(1e-75, Dimensions(length=-3, mass=1), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') +atomic_mass_units_per_cubic_exameter = Unit(1.661e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='NoneEm⁻³') +grams_per_cubic_petameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') +exagrams_per_cubic_petameter = Unit(1e-30, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') +petagrams_per_cubic_petameter = Unit(1e-33, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') +teragrams_per_cubic_petameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') +gigagrams_per_cubic_petameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_petameter', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') +megagrams_per_cubic_petameter = Unit(1e-42, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_petameter', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') +kilograms_per_cubic_petameter = Unit(1.0000000000000001e-45, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_petameter', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') +milligrams_per_cubic_petameter = Unit(1e-51, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_petameter', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') +micrograms_per_cubic_petameter = Unit(1.0000000000000002e-54, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_petameter', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') +nanograms_per_cubic_petameter = Unit(1.0000000000000002e-57, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_petameter', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') +picograms_per_cubic_petameter = Unit(1.0000000000000001e-60, Dimensions(length=-3, mass=1), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') +femtograms_per_cubic_petameter = Unit(1.0000000000000002e-63, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') +attograms_per_cubic_petameter = Unit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') +atomic_mass_units_per_cubic_petameter = Unit(1.6610000000000003e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='NonePm⁻³') +grams_per_cubic_terameter = Unit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') +exagrams_per_cubic_terameter = Unit(1e-21, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') +petagrams_per_cubic_terameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') +teragrams_per_cubic_terameter = Unit(1e-27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') +gigagrams_per_cubic_terameter = Unit(9.999999999999999e-31, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_terameter', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') +megagrams_per_cubic_terameter = Unit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_terameter', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') +kilograms_per_cubic_terameter = Unit(1e-36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_terameter', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') +milligrams_per_cubic_terameter = Unit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_terameter', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') +micrograms_per_cubic_terameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_terameter', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') +nanograms_per_cubic_terameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_terameter', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') +picograms_per_cubic_terameter = Unit(1e-51, Dimensions(length=-3, mass=1), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') +femtograms_per_cubic_terameter = Unit(1e-54, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') +attograms_per_cubic_terameter = Unit(1.0000000000000001e-57, Dimensions(length=-3, mass=1), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') +atomic_mass_units_per_cubic_terameter = Unit(1.6610000000000002e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='NoneTm⁻³') +grams_per_cubic_gigameter = Unit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') +exagrams_per_cubic_gigameter = Unit(1e-12, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') +petagrams_per_cubic_gigameter = Unit(1e-15, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') +teragrams_per_cubic_gigameter = Unit(1e-18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') +gigagrams_per_cubic_gigameter = Unit(1e-21, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_gigameter', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') +megagrams_per_cubic_gigameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_gigameter', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') +kilograms_per_cubic_gigameter = Unit(1e-27, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_gigameter', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') +milligrams_per_cubic_gigameter = Unit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_gigameter', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') +micrograms_per_cubic_gigameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_gigameter', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') +nanograms_per_cubic_gigameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_gigameter', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') +picograms_per_cubic_gigameter = Unit(1e-42, Dimensions(length=-3, mass=1), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') +femtograms_per_cubic_gigameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') +attograms_per_cubic_gigameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') +atomic_mass_units_per_cubic_gigameter = Unit(1.6610000000000002e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='NoneGm⁻³') +grams_per_cubic_megameter = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') +exagrams_per_cubic_megameter = Unit(0.001, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') +petagrams_per_cubic_megameter = Unit(1e-06, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') +teragrams_per_cubic_megameter = Unit(1e-09, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') +gigagrams_per_cubic_megameter = Unit(1e-12, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_megameter', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') +megagrams_per_cubic_megameter = Unit(1e-15, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_megameter', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') +kilograms_per_cubic_megameter = Unit(1e-18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_megameter', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') +milligrams_per_cubic_megameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_megameter', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') +micrograms_per_cubic_megameter = Unit(1e-27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_megameter', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') +nanograms_per_cubic_megameter = Unit(1.0000000000000003e-30, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_megameter', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') +picograms_per_cubic_megameter = Unit(1e-33, Dimensions(length=-3, mass=1), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') +femtograms_per_cubic_megameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') +attograms_per_cubic_megameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') +atomic_mass_units_per_cubic_megameter = Unit(1.661e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='NoneMm⁻³') +grams_per_cubic_kilometer = Unit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') +exagrams_per_cubic_kilometer = Unit(1000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') +petagrams_per_cubic_kilometer = Unit(1000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') +teragrams_per_cubic_kilometer = Unit(1.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') +gigagrams_per_cubic_kilometer = Unit(0.001, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_kilometer', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') +megagrams_per_cubic_kilometer = Unit(1e-06, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_kilometer', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') +kilograms_per_cubic_kilometer = Unit(1e-09, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_kilometer', ascii_symbol='kg km^-3', symbol='kgkm⁻³') +milligrams_per_cubic_kilometer = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_kilometer', ascii_symbol='mg km^-3', symbol='mgkm⁻³') +micrograms_per_cubic_kilometer = Unit(1e-18, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_kilometer', ascii_symbol='ug km^-3', symbol='µgkm⁻³') +nanograms_per_cubic_kilometer = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_kilometer', ascii_symbol='ng km^-3', symbol='ngkm⁻³') +picograms_per_cubic_kilometer = Unit(1.0000000000000001e-24, Dimensions(length=-3, mass=1), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') +femtograms_per_cubic_kilometer = Unit(1e-27, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') +attograms_per_cubic_kilometer = Unit(1e-30, Dimensions(length=-3, mass=1), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') +atomic_mass_units_per_cubic_kilometer = Unit(1.661e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='Nonekm⁻³') +grams_per_cubic_millimeter = Unit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') +exagrams_per_cubic_millimeter = Unit(1e+24, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') +petagrams_per_cubic_millimeter = Unit(1e+21, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') +teragrams_per_cubic_millimeter = Unit(1e+18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') +gigagrams_per_cubic_millimeter = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_millimeter', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') +megagrams_per_cubic_millimeter = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_millimeter', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') +kilograms_per_cubic_millimeter = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_millimeter', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') +milligrams_per_cubic_millimeter = Unit(999.9999999999999, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_millimeter', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') +micrograms_per_cubic_millimeter = Unit(1.0, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_millimeter', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') +nanograms_per_cubic_millimeter = Unit(0.001, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_millimeter', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') +picograms_per_cubic_millimeter = Unit(1e-06, Dimensions(length=-3, mass=1), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') +femtograms_per_cubic_millimeter = Unit(1e-09, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') +attograms_per_cubic_millimeter = Unit(1e-12, Dimensions(length=-3, mass=1), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') +atomic_mass_units_per_cubic_millimeter = Unit(1.661e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='Nonemm⁻³') +grams_per_cubic_micrometer = Unit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') +exagrams_per_cubic_micrometer = Unit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') +petagrams_per_cubic_micrometer = Unit(1.0000000000000002e+30, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') +teragrams_per_cubic_micrometer = Unit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') +gigagrams_per_cubic_micrometer = Unit(1.0000000000000001e+24, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_micrometer', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') +megagrams_per_cubic_micrometer = Unit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_micrometer', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') +kilograms_per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_micrometer', ascii_symbol='kg um^-3', symbol='kgµm⁻³') +milligrams_per_cubic_micrometer = Unit(1000000000000.0001, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_micrometer', ascii_symbol='mg um^-3', symbol='mgµm⁻³') +micrograms_per_cubic_micrometer = Unit(1000000000.0000002, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_micrometer', ascii_symbol='ug um^-3', symbol='µgµm⁻³') +nanograms_per_cubic_micrometer = Unit(1000000.0000000003, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_micrometer', ascii_symbol='ng um^-3', symbol='ngµm⁻³') +picograms_per_cubic_micrometer = Unit(1000.0000000000002, Dimensions(length=-3, mass=1), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') +femtograms_per_cubic_micrometer = Unit(1.0000000000000002, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') +attograms_per_cubic_micrometer = Unit(0.0010000000000000002, Dimensions(length=-3, mass=1), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') +atomic_mass_units_per_cubic_micrometer = Unit(1.6610000000000004e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='Noneµm⁻³') +grams_per_cubic_nanometer = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') +exagrams_per_cubic_nanometer = Unit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') +petagrams_per_cubic_nanometer = Unit(9.999999999999998e+38, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') +teragrams_per_cubic_nanometer = Unit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') +gigagrams_per_cubic_nanometer = Unit(9.999999999999998e+32, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_nanometer', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') +megagrams_per_cubic_nanometer = Unit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_nanometer', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') +kilograms_per_cubic_nanometer = Unit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_nanometer', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') +milligrams_per_cubic_nanometer = Unit(9.999999999999997e+20, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_nanometer', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') +micrograms_per_cubic_nanometer = Unit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_nanometer', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') +nanograms_per_cubic_nanometer = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_nanometer', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') +picograms_per_cubic_nanometer = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') +femtograms_per_cubic_nanometer = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') +attograms_per_cubic_nanometer = Unit(999999.9999999999, Dimensions(length=-3, mass=1), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') +atomic_mass_units_per_cubic_nanometer = Unit(1.6609999999999998, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='Nonenm⁻³') +grams_per_cubic_picometer = Unit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') +exagrams_per_cubic_picometer = Unit(1e+51, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') +petagrams_per_cubic_picometer = Unit(1e+48, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') +teragrams_per_cubic_picometer = Unit(1.0000000000000001e+45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') +gigagrams_per_cubic_picometer = Unit(1e+42, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_picometer', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') +megagrams_per_cubic_picometer = Unit(1.0000000000000001e+39, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_picometer', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') +kilograms_per_cubic_picometer = Unit(1e+36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_picometer', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') +milligrams_per_cubic_picometer = Unit(1e+30, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_picometer', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') +micrograms_per_cubic_picometer = Unit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_picometer', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') +nanograms_per_cubic_picometer = Unit(1.0000000000000003e+24, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_picometer', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') +picograms_per_cubic_picometer = Unit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') +femtograms_per_cubic_picometer = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') +attograms_per_cubic_picometer = Unit(1000000000000000.1, Dimensions(length=-3, mass=1), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') +atomic_mass_units_per_cubic_picometer = Unit(1661000000.0000002, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='Nonepm⁻³') +grams_per_cubic_femtometer = Unit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') +exagrams_per_cubic_femtometer = Unit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') +petagrams_per_cubic_femtometer = Unit(9.999999999999997e+56, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') +teragrams_per_cubic_femtometer = Unit(9.999999999999997e+53, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') +gigagrams_per_cubic_femtometer = Unit(9.999999999999997e+50, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_femtometer', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') +megagrams_per_cubic_femtometer = Unit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_femtometer', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') +kilograms_per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_femtometer', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') +milligrams_per_cubic_femtometer = Unit(9.999999999999996e+38, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_femtometer', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') +micrograms_per_cubic_femtometer = Unit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_femtometer', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') +nanograms_per_cubic_femtometer = Unit(1e+33, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_femtometer', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') +picograms_per_cubic_femtometer = Unit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') +femtograms_per_cubic_femtometer = Unit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') +attograms_per_cubic_femtometer = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') +atomic_mass_units_per_cubic_femtometer = Unit(1.6609999999999997e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='Nonefm⁻³') +grams_per_cubic_attometer = Unit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') +exagrams_per_cubic_attometer = Unit(9.999999999999999e+68, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') +petagrams_per_cubic_attometer = Unit(9.999999999999998e+65, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') +teragrams_per_cubic_attometer = Unit(9.999999999999999e+62, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') +gigagrams_per_cubic_attometer = Unit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_attometer', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') +megagrams_per_cubic_attometer = Unit(9.999999999999999e+56, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_attometer', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') +kilograms_per_cubic_attometer = Unit(9.999999999999999e+53, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_attometer', ascii_symbol='kg am^-3', symbol='kgam⁻³') +milligrams_per_cubic_attometer = Unit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_attometer', ascii_symbol='mg am^-3', symbol='mgam⁻³') +micrograms_per_cubic_attometer = Unit(1e+45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_attometer', ascii_symbol='ug am^-3', symbol='µgam⁻³') +nanograms_per_cubic_attometer = Unit(1e+42, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_attometer', ascii_symbol='ng am^-3', symbol='ngam⁻³') +picograms_per_cubic_attometer = Unit(1e+39, Dimensions(length=-3, mass=1), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') +femtograms_per_cubic_attometer = Unit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') +attograms_per_cubic_attometer = Unit(1e+33, Dimensions(length=-3, mass=1), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') +atomic_mass_units_per_cubic_attometer = Unit(1.661e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='Noneam⁻³') +grams_per_cubic_decimeter = Unit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='Nonedm⁻³') +exagrams_per_cubic_decimeter = Unit(9.999999999999997e+17, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_decimeter', ascii_symbol='Eg dm^-3', symbol='Egdm⁻³') +petagrams_per_cubic_decimeter = Unit(999999999999999.8, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_decimeter', ascii_symbol='Pg dm^-3', symbol='Pgdm⁻³') +teragrams_per_cubic_decimeter = Unit(999999999999.9998, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_decimeter', ascii_symbol='Tg dm^-3', symbol='Tgdm⁻³') +gigagrams_per_cubic_decimeter = Unit(999999999.9999998, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_decimeter', ascii_symbol='Gg dm^-3', symbol='Ggdm⁻³') +megagrams_per_cubic_decimeter = Unit(999999.9999999998, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_decimeter', ascii_symbol='Mg dm^-3', symbol='Mgdm⁻³') +kilograms_per_cubic_decimeter = Unit(999.9999999999998, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_decimeter', ascii_symbol='kg dm^-3', symbol='kgdm⁻³') +milligrams_per_cubic_decimeter = Unit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_decimeter', ascii_symbol='mg dm^-3', symbol='mgdm⁻³') +micrograms_per_cubic_decimeter = Unit(9.999999999999997e-07, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_decimeter', ascii_symbol='ug dm^-3', symbol='µgdm⁻³') +nanograms_per_cubic_decimeter = Unit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_decimeter', ascii_symbol='ng dm^-3', symbol='ngdm⁻³') +picograms_per_cubic_decimeter = Unit(9.999999999999998e-13, Dimensions(length=-3, mass=1), name='picograms_per_cubic_decimeter', ascii_symbol='pg dm^-3', symbol='pgdm⁻³') +femtograms_per_cubic_decimeter = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_decimeter', ascii_symbol='fg dm^-3', symbol='fgdm⁻³') +attograms_per_cubic_decimeter = Unit(9.999999999999999e-19, Dimensions(length=-3, mass=1), name='attograms_per_cubic_decimeter', ascii_symbol='ag dm^-3', symbol='agdm⁻³') +atomic_mass_units_per_cubic_decimeter = Unit(1.661e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='Nonedm⁻³') +grams_per_cubic_centimeter = Unit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='Nonecm⁻³') +exagrams_per_cubic_centimeter = Unit(9.999999999999999e+20, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_centimeter', ascii_symbol='Eg cm^-3', symbol='Egcm⁻³') +petagrams_per_cubic_centimeter = Unit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_centimeter', ascii_symbol='Pg cm^-3', symbol='Pgcm⁻³') +teragrams_per_cubic_centimeter = Unit(999999999999999.9, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_centimeter', ascii_symbol='Tg cm^-3', symbol='Tgcm⁻³') +gigagrams_per_cubic_centimeter = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_centimeter', ascii_symbol='Gg cm^-3', symbol='Ggcm⁻³') +megagrams_per_cubic_centimeter = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_centimeter', ascii_symbol='Mg cm^-3', symbol='Mgcm⁻³') +kilograms_per_cubic_centimeter = Unit(999999.9999999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_centimeter', ascii_symbol='kg cm^-3', symbol='kgcm⁻³') +milligrams_per_cubic_centimeter = Unit(0.9999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_centimeter', ascii_symbol='mg cm^-3', symbol='mgcm⁻³') +micrograms_per_cubic_centimeter = Unit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_centimeter', ascii_symbol='ug cm^-3', symbol='µgcm⁻³') +nanograms_per_cubic_centimeter = Unit(1e-06, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_centimeter', ascii_symbol='ng cm^-3', symbol='ngcm⁻³') +picograms_per_cubic_centimeter = Unit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='picograms_per_cubic_centimeter', ascii_symbol='pg cm^-3', symbol='pgcm⁻³') +femtograms_per_cubic_centimeter = Unit(1e-12, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_centimeter', ascii_symbol='fg cm^-3', symbol='fgcm⁻³') +attograms_per_cubic_centimeter = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='attograms_per_cubic_centimeter', ascii_symbol='ag cm^-3', symbol='agcm⁻³') +atomic_mass_units_per_cubic_centimeter = Unit(1.6609999999999998e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='Nonecm⁻³') +grams_per_cubic_angstrom = Unit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') +exagrams_per_cubic_angstrom = Unit(1e+45, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') +petagrams_per_cubic_angstrom = Unit(9.999999999999999e+41, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') +teragrams_per_cubic_angstrom = Unit(1e+39, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') +gigagrams_per_cubic_angstrom = Unit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_angstrom', ascii_symbol='Gg Ang^-3', symbol='GgÅ⁻³') +megagrams_per_cubic_angstrom = Unit(1e+33, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_angstrom', ascii_symbol='Mg Ang^-3', symbol='MgÅ⁻³') +kilograms_per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_angstrom', ascii_symbol='kg Ang^-3', symbol='kgÅ⁻³') +milligrams_per_cubic_angstrom = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_angstrom', ascii_symbol='mg Ang^-3', symbol='mgÅ⁻³') +micrograms_per_cubic_angstrom = Unit(1e+21, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_angstrom', ascii_symbol='ug Ang^-3', symbol='µgÅ⁻³') +nanograms_per_cubic_angstrom = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_angstrom', ascii_symbol='ng Ang^-3', symbol='ngÅ⁻³') +picograms_per_cubic_angstrom = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') +femtograms_per_cubic_angstrom = Unit(1000000000000.0, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') +attograms_per_cubic_angstrom = Unit(1000000000.0, Dimensions(length=-3, mass=1), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') +atomic_mass_units_per_cubic_angstrom = Unit(1661.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='NoneÅ⁻³') +moles_per_cubic_meter = Unit(6.022e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_meter = Unit(6.022e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_meter = Unit(6.022e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_meter = Unit(602200000000000.1, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_meter = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_meter = Unit(602200000.0000001, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_meter = Unit(602200.0000000001, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolNone⁻³') +moles_per_cubic_exameter = Unit(6.022e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='NoneEm⁻³') +millimoles_per_cubic_exameter = Unit(6.021999999999999e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') +micromoles_per_cubic_exameter = Unit(6.022e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') +nanomoles_per_cubic_exameter = Unit(6.022000000000001e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') +picomoles_per_cubic_exameter = Unit(6.022e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') +femtomoles_per_cubic_exameter = Unit(6.022e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') +attomoles_per_cubic_exameter = Unit(6.0220000000000006e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') +moles_per_cubic_petameter = Unit(6.022e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='NonePm⁻³') +millimoles_per_cubic_petameter = Unit(6.022e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') +micromoles_per_cubic_petameter = Unit(6.022e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') +nanomoles_per_cubic_petameter = Unit(6.0220000000000015e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') +picomoles_per_cubic_petameter = Unit(6.022e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') +femtomoles_per_cubic_petameter = Unit(6.022000000000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') +attomoles_per_cubic_petameter = Unit(6.0220000000000015e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') +moles_per_cubic_terameter = Unit(6.022e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='NoneTm⁻³') +millimoles_per_cubic_terameter = Unit(6.022e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') +micromoles_per_cubic_terameter = Unit(6.022e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') +nanomoles_per_cubic_terameter = Unit(6.022000000000001e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') +picomoles_per_cubic_terameter = Unit(6.0219999999999995e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') +femtomoles_per_cubic_terameter = Unit(6.022000000000001e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') +attomoles_per_cubic_terameter = Unit(6.022000000000001e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') +moles_per_cubic_gigameter = Unit(0.0006022, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='NoneGm⁻³') +millimoles_per_cubic_gigameter = Unit(6.022e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') +micromoles_per_cubic_gigameter = Unit(6.021999999999999e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') +nanomoles_per_cubic_gigameter = Unit(6.022000000000001e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') +picomoles_per_cubic_gigameter = Unit(6.022e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') +femtomoles_per_cubic_gigameter = Unit(6.022000000000002e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') +attomoles_per_cubic_gigameter = Unit(6.022000000000001e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') +moles_per_cubic_megameter = Unit(602200.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='NoneMm⁻³') +millimoles_per_cubic_megameter = Unit(602.2, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') +micromoles_per_cubic_megameter = Unit(0.6022, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') +nanomoles_per_cubic_megameter = Unit(0.0006022000000000001, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') +picomoles_per_cubic_megameter = Unit(6.022e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') +femtomoles_per_cubic_megameter = Unit(6.022000000000002e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') +attomoles_per_cubic_megameter = Unit(6.022000000000001e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') +moles_per_cubic_kilometer = Unit(602200000000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='Nonekm⁻³') +millimoles_per_cubic_kilometer = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') +micromoles_per_cubic_kilometer = Unit(602200000.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') +nanomoles_per_cubic_kilometer = Unit(602200.0000000001, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') +picomoles_per_cubic_kilometer = Unit(602.2, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') +femtomoles_per_cubic_kilometer = Unit(0.6022000000000001, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') +attomoles_per_cubic_kilometer = Unit(0.0006022000000000001, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') +moles_per_cubic_millimeter = Unit(6.022e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='Nonemm⁻³') +millimoles_per_cubic_millimeter = Unit(6.0219999999999996e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') +micromoles_per_cubic_millimeter = Unit(6.022e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') +nanomoles_per_cubic_millimeter = Unit(6.022000000000001e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') +picomoles_per_cubic_millimeter = Unit(6.022e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') +femtomoles_per_cubic_millimeter = Unit(6.022000000000001e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') +attomoles_per_cubic_millimeter = Unit(602200000000000.1, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') +moles_per_cubic_micrometer = Unit(6.022000000000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='Noneµm⁻³') +millimoles_per_cubic_micrometer = Unit(6.0220000000000006e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') +micromoles_per_cubic_micrometer = Unit(6.022000000000001e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') +nanomoles_per_cubic_micrometer = Unit(6.022000000000002e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') +picomoles_per_cubic_micrometer = Unit(6.022000000000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') +femtomoles_per_cubic_micrometer = Unit(6.022000000000002e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') +attomoles_per_cubic_micrometer = Unit(6.0220000000000016e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') +moles_per_cubic_nanometer = Unit(6.021999999999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='Nonenm⁻³') +millimoles_per_cubic_nanometer = Unit(6.0219999999999984e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') +micromoles_per_cubic_nanometer = Unit(6.0219999999999985e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') +nanomoles_per_cubic_nanometer = Unit(6.022e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') +picomoles_per_cubic_nanometer = Unit(6.021999999999999e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') +femtomoles_per_cubic_nanometer = Unit(6.0219999999999995e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') +attomoles_per_cubic_nanometer = Unit(6.022e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') +moles_per_cubic_picometer = Unit(6.022e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='Nonepm⁻³') +millimoles_per_cubic_picometer = Unit(6.0220000000000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') +micromoles_per_cubic_picometer = Unit(6.0220000000000005e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') +nanomoles_per_cubic_picometer = Unit(6.022000000000002e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') +picomoles_per_cubic_picometer = Unit(6.022e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') +femtomoles_per_cubic_picometer = Unit(6.022000000000002e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') +attomoles_per_cubic_picometer = Unit(6.022000000000002e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') +moles_per_cubic_femtometer = Unit(6.021999999999999e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='Nonefm⁻³') +millimoles_per_cubic_femtometer = Unit(6.021999999999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') +micromoles_per_cubic_femtometer = Unit(6.021999999999998e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') +nanomoles_per_cubic_femtometer = Unit(6.021999999999999e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') +picomoles_per_cubic_femtometer = Unit(6.021999999999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') +femtomoles_per_cubic_femtometer = Unit(6.022e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') +attomoles_per_cubic_femtometer = Unit(6.021999999999999e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') +moles_per_cubic_attometer = Unit(6.021999999999999e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='Noneam⁻³') +millimoles_per_cubic_attometer = Unit(6.021999999999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') +micromoles_per_cubic_attometer = Unit(6.021999999999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') +nanomoles_per_cubic_attometer = Unit(6.022000000000001e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') +picomoles_per_cubic_attometer = Unit(6.021999999999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') +femtomoles_per_cubic_attometer = Unit(6.022e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') +attomoles_per_cubic_attometer = Unit(6.022e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') +moles_per_cubic_decimeter = Unit(6.0219999999999985e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='Nonedm⁻³') +millimoles_per_cubic_decimeter = Unit(6.021999999999998e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') +micromoles_per_cubic_decimeter = Unit(6.021999999999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') +nanomoles_per_cubic_decimeter = Unit(6.022e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') +picomoles_per_cubic_decimeter = Unit(602199999999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') +femtomoles_per_cubic_decimeter = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') +attomoles_per_cubic_decimeter = Unit(602200000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') +moles_per_cubic_centimeter = Unit(6.0219999999999996e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='Nonecm⁻³') +millimoles_per_cubic_centimeter = Unit(6.021999999999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') +micromoles_per_cubic_centimeter = Unit(6.021999999999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') +nanomoles_per_cubic_centimeter = Unit(6.022e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') +picomoles_per_cubic_centimeter = Unit(6.021999999999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') +femtomoles_per_cubic_centimeter = Unit(602200000000000.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') +attomoles_per_cubic_centimeter = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') +moles_per_cubic_angstrom = Unit(6.022e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='NoneÅ⁻³') +millimoles_per_cubic_angstrom = Unit(6.021999999999999e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') +micromoles_per_cubic_angstrom = Unit(6.021999999999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') +nanomoles_per_cubic_angstrom = Unit(6.022000000000001e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') +picomoles_per_cubic_angstrom = Unit(6.0219999999999994e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') +femtomoles_per_cubic_angstrom = Unit(6.0220000000000006e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') +attomoles_per_cubic_angstrom = Unit(6.022000000000001e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') # # Lookup table from symbols to units @@ -1006,6 +1313,8 @@ def __init__(self, name: str, units: list[Unit]): "pm": picometers, "fm": femtometers, "am": attometers, + "dm": decimeters, + "cm": centimeters, "s": seconds, "ms": milliseconds, "us": microseconds, @@ -1260,11 +1569,37 @@ def __init__(self, name: str, units: list[Unit]): "rad": radians, "sr": stradians, "none": none, + "l": litres, + "eV": electronvolts, + "EeV": exaelectronvolts, + "PeV": petaelectronvolts, + "TeV": teraelectronvolts, + "GeV": gigaelectronvolts, + "MeV": megaelectronvolts, + "keV": kiloelectronvolts, + "meV": millielectronvolts, + "ueV": microelectronvolts, + "µeV": microelectronvolts, + "neV": nanoelectronvolts, + "peV": picoelectronvolts, + "feV": femtoelectronvolts, + "aeV": attoelectronvolts, + "au": atomic_mass_units, + "mol": moles, + "mmol": millimoles, + "umol": micromoles, + "µmol": micromoles, + "nmol": nanomoles, + "pmol": picomoles, + "fmol": femtomoles, + "amol": attomoles, "yr": years, "year": years, "day": days, "hr": hours, "hour": hours, + "a.u.": atomic_mass_units, + "amu": atomic_mass_units, } @@ -1289,6 +1624,8 @@ def __init__(self, name: str, units: list[Unit]): picometers, femtometers, attometers, + decimeters, + centimeters, angstroms, ]) @@ -1308,12 +1645,15 @@ def __init__(self, name: str, units: list[Unit]): square_picometers, square_femtometers, square_attometers, + square_decimeters, + square_centimeters, square_angstroms, ]) volume = UnitGroup( name = 'volume', units = [ + litres, cubic_meters, cubic_exameters, cubic_petameters, @@ -1327,6 +1667,8 @@ def __init__(self, name: str, units: list[Unit]): cubic_picometers, cubic_femtometers, cubic_attometers, + cubic_decimeters, + cubic_centimeters, cubic_angstroms, ]) @@ -1346,6 +1688,8 @@ def __init__(self, name: str, units: list[Unit]): per_picometer, per_femtometer, per_attometer, + per_decimeter, + per_centimeter, per_angstrom, ]) @@ -1365,6 +1709,8 @@ def __init__(self, name: str, units: list[Unit]): per_square_picometer, per_square_femtometer, per_square_attometer, + per_square_decimeter, + per_square_centimeter, per_square_angstrom, ]) @@ -1384,6 +1730,8 @@ def __init__(self, name: str, units: list[Unit]): per_cubic_picometer, per_cubic_femtometer, per_cubic_attometer, + per_cubic_decimeter, + per_cubic_centimeter, per_cubic_angstrom, ]) @@ -1568,6 +1916,28 @@ def __init__(self, name: str, units: list[Unit]): attometers_per_hour, attometers_per_day, attometers_per_year, + decimeters_per_second, + decimeters_per_millisecond, + decimeters_per_microsecond, + decimeters_per_nanosecond, + decimeters_per_picosecond, + decimeters_per_femtosecond, + decimeters_per_attosecond, + decimeters_per_minute, + decimeters_per_hour, + decimeters_per_day, + decimeters_per_year, + centimeters_per_second, + centimeters_per_millisecond, + centimeters_per_microsecond, + centimeters_per_nanosecond, + centimeters_per_picosecond, + centimeters_per_femtosecond, + centimeters_per_attosecond, + centimeters_per_minute, + centimeters_per_hour, + centimeters_per_day, + centimeters_per_year, angstroms_per_second, angstroms_per_millisecond, angstroms_per_microsecond, @@ -1727,6 +2097,28 @@ def __init__(self, name: str, units: list[Unit]): attometers_per_square_hour, attometers_per_square_day, attometers_per_square_year, + decimeters_per_square_second, + decimeters_per_square_millisecond, + decimeters_per_square_microsecond, + decimeters_per_square_nanosecond, + decimeters_per_square_picosecond, + decimeters_per_square_femtosecond, + decimeters_per_square_attosecond, + decimeters_per_square_minute, + decimeters_per_square_hour, + decimeters_per_square_day, + decimeters_per_square_year, + centimeters_per_square_second, + centimeters_per_square_millisecond, + centimeters_per_square_microsecond, + centimeters_per_square_nanosecond, + centimeters_per_square_picosecond, + centimeters_per_square_femtosecond, + centimeters_per_square_attosecond, + centimeters_per_square_minute, + centimeters_per_square_hour, + centimeters_per_square_day, + centimeters_per_square_year, angstroms_per_square_second, angstroms_per_square_millisecond, angstroms_per_square_microsecond, @@ -1743,6 +2135,230 @@ def __init__(self, name: str, units: list[Unit]): density = UnitGroup( name = 'density', units = [ + grams_per_cubic_meter, + exagrams_per_cubic_meter, + petagrams_per_cubic_meter, + teragrams_per_cubic_meter, + gigagrams_per_cubic_meter, + megagrams_per_cubic_meter, + kilograms_per_cubic_meter, + milligrams_per_cubic_meter, + micrograms_per_cubic_meter, + nanograms_per_cubic_meter, + picograms_per_cubic_meter, + femtograms_per_cubic_meter, + attograms_per_cubic_meter, + atomic_mass_units_per_cubic_meter, + grams_per_cubic_exameter, + exagrams_per_cubic_exameter, + petagrams_per_cubic_exameter, + teragrams_per_cubic_exameter, + gigagrams_per_cubic_exameter, + megagrams_per_cubic_exameter, + kilograms_per_cubic_exameter, + milligrams_per_cubic_exameter, + micrograms_per_cubic_exameter, + nanograms_per_cubic_exameter, + picograms_per_cubic_exameter, + femtograms_per_cubic_exameter, + attograms_per_cubic_exameter, + atomic_mass_units_per_cubic_exameter, + grams_per_cubic_petameter, + exagrams_per_cubic_petameter, + petagrams_per_cubic_petameter, + teragrams_per_cubic_petameter, + gigagrams_per_cubic_petameter, + megagrams_per_cubic_petameter, + kilograms_per_cubic_petameter, + milligrams_per_cubic_petameter, + micrograms_per_cubic_petameter, + nanograms_per_cubic_petameter, + picograms_per_cubic_petameter, + femtograms_per_cubic_petameter, + attograms_per_cubic_petameter, + atomic_mass_units_per_cubic_petameter, + grams_per_cubic_terameter, + exagrams_per_cubic_terameter, + petagrams_per_cubic_terameter, + teragrams_per_cubic_terameter, + gigagrams_per_cubic_terameter, + megagrams_per_cubic_terameter, + kilograms_per_cubic_terameter, + milligrams_per_cubic_terameter, + micrograms_per_cubic_terameter, + nanograms_per_cubic_terameter, + picograms_per_cubic_terameter, + femtograms_per_cubic_terameter, + attograms_per_cubic_terameter, + atomic_mass_units_per_cubic_terameter, + grams_per_cubic_gigameter, + exagrams_per_cubic_gigameter, + petagrams_per_cubic_gigameter, + teragrams_per_cubic_gigameter, + gigagrams_per_cubic_gigameter, + megagrams_per_cubic_gigameter, + kilograms_per_cubic_gigameter, + milligrams_per_cubic_gigameter, + micrograms_per_cubic_gigameter, + nanograms_per_cubic_gigameter, + picograms_per_cubic_gigameter, + femtograms_per_cubic_gigameter, + attograms_per_cubic_gigameter, + atomic_mass_units_per_cubic_gigameter, + grams_per_cubic_megameter, + exagrams_per_cubic_megameter, + petagrams_per_cubic_megameter, + teragrams_per_cubic_megameter, + gigagrams_per_cubic_megameter, + megagrams_per_cubic_megameter, + kilograms_per_cubic_megameter, + milligrams_per_cubic_megameter, + micrograms_per_cubic_megameter, + nanograms_per_cubic_megameter, + picograms_per_cubic_megameter, + femtograms_per_cubic_megameter, + attograms_per_cubic_megameter, + atomic_mass_units_per_cubic_megameter, + grams_per_cubic_kilometer, + exagrams_per_cubic_kilometer, + petagrams_per_cubic_kilometer, + teragrams_per_cubic_kilometer, + gigagrams_per_cubic_kilometer, + megagrams_per_cubic_kilometer, + kilograms_per_cubic_kilometer, + milligrams_per_cubic_kilometer, + micrograms_per_cubic_kilometer, + nanograms_per_cubic_kilometer, + picograms_per_cubic_kilometer, + femtograms_per_cubic_kilometer, + attograms_per_cubic_kilometer, + atomic_mass_units_per_cubic_kilometer, + grams_per_cubic_millimeter, + exagrams_per_cubic_millimeter, + petagrams_per_cubic_millimeter, + teragrams_per_cubic_millimeter, + gigagrams_per_cubic_millimeter, + megagrams_per_cubic_millimeter, + kilograms_per_cubic_millimeter, + milligrams_per_cubic_millimeter, + micrograms_per_cubic_millimeter, + nanograms_per_cubic_millimeter, + picograms_per_cubic_millimeter, + femtograms_per_cubic_millimeter, + attograms_per_cubic_millimeter, + atomic_mass_units_per_cubic_millimeter, + grams_per_cubic_micrometer, + exagrams_per_cubic_micrometer, + petagrams_per_cubic_micrometer, + teragrams_per_cubic_micrometer, + gigagrams_per_cubic_micrometer, + megagrams_per_cubic_micrometer, + kilograms_per_cubic_micrometer, + milligrams_per_cubic_micrometer, + micrograms_per_cubic_micrometer, + nanograms_per_cubic_micrometer, + picograms_per_cubic_micrometer, + femtograms_per_cubic_micrometer, + attograms_per_cubic_micrometer, + atomic_mass_units_per_cubic_micrometer, + grams_per_cubic_nanometer, + exagrams_per_cubic_nanometer, + petagrams_per_cubic_nanometer, + teragrams_per_cubic_nanometer, + gigagrams_per_cubic_nanometer, + megagrams_per_cubic_nanometer, + kilograms_per_cubic_nanometer, + milligrams_per_cubic_nanometer, + micrograms_per_cubic_nanometer, + nanograms_per_cubic_nanometer, + picograms_per_cubic_nanometer, + femtograms_per_cubic_nanometer, + attograms_per_cubic_nanometer, + atomic_mass_units_per_cubic_nanometer, + grams_per_cubic_picometer, + exagrams_per_cubic_picometer, + petagrams_per_cubic_picometer, + teragrams_per_cubic_picometer, + gigagrams_per_cubic_picometer, + megagrams_per_cubic_picometer, + kilograms_per_cubic_picometer, + milligrams_per_cubic_picometer, + micrograms_per_cubic_picometer, + nanograms_per_cubic_picometer, + picograms_per_cubic_picometer, + femtograms_per_cubic_picometer, + attograms_per_cubic_picometer, + atomic_mass_units_per_cubic_picometer, + grams_per_cubic_femtometer, + exagrams_per_cubic_femtometer, + petagrams_per_cubic_femtometer, + teragrams_per_cubic_femtometer, + gigagrams_per_cubic_femtometer, + megagrams_per_cubic_femtometer, + kilograms_per_cubic_femtometer, + milligrams_per_cubic_femtometer, + micrograms_per_cubic_femtometer, + nanograms_per_cubic_femtometer, + picograms_per_cubic_femtometer, + femtograms_per_cubic_femtometer, + attograms_per_cubic_femtometer, + atomic_mass_units_per_cubic_femtometer, + grams_per_cubic_attometer, + exagrams_per_cubic_attometer, + petagrams_per_cubic_attometer, + teragrams_per_cubic_attometer, + gigagrams_per_cubic_attometer, + megagrams_per_cubic_attometer, + kilograms_per_cubic_attometer, + milligrams_per_cubic_attometer, + micrograms_per_cubic_attometer, + nanograms_per_cubic_attometer, + picograms_per_cubic_attometer, + femtograms_per_cubic_attometer, + attograms_per_cubic_attometer, + atomic_mass_units_per_cubic_attometer, + grams_per_cubic_decimeter, + exagrams_per_cubic_decimeter, + petagrams_per_cubic_decimeter, + teragrams_per_cubic_decimeter, + gigagrams_per_cubic_decimeter, + megagrams_per_cubic_decimeter, + kilograms_per_cubic_decimeter, + milligrams_per_cubic_decimeter, + micrograms_per_cubic_decimeter, + nanograms_per_cubic_decimeter, + picograms_per_cubic_decimeter, + femtograms_per_cubic_decimeter, + attograms_per_cubic_decimeter, + atomic_mass_units_per_cubic_decimeter, + grams_per_cubic_centimeter, + exagrams_per_cubic_centimeter, + petagrams_per_cubic_centimeter, + teragrams_per_cubic_centimeter, + gigagrams_per_cubic_centimeter, + megagrams_per_cubic_centimeter, + kilograms_per_cubic_centimeter, + milligrams_per_cubic_centimeter, + micrograms_per_cubic_centimeter, + nanograms_per_cubic_centimeter, + picograms_per_cubic_centimeter, + femtograms_per_cubic_centimeter, + attograms_per_cubic_centimeter, + atomic_mass_units_per_cubic_centimeter, + grams_per_cubic_angstrom, + exagrams_per_cubic_angstrom, + petagrams_per_cubic_angstrom, + teragrams_per_cubic_angstrom, + gigagrams_per_cubic_angstrom, + megagrams_per_cubic_angstrom, + kilograms_per_cubic_angstrom, + milligrams_per_cubic_angstrom, + micrograms_per_cubic_angstrom, + nanograms_per_cubic_angstrom, + picograms_per_cubic_angstrom, + femtograms_per_cubic_angstrom, + attograms_per_cubic_angstrom, + atomic_mass_units_per_cubic_angstrom, ]) force = UnitGroup( @@ -1797,6 +2413,19 @@ def __init__(self, name: str, units: list[Unit]): picojoules, femtojoules, attojoules, + electronvolts, + exaelectronvolts, + petaelectronvolts, + teraelectronvolts, + gigaelectronvolts, + megaelectronvolts, + kiloelectronvolts, + millielectronvolts, + microelectronvolts, + nanoelectronvolts, + picoelectronvolts, + femtoelectronvolts, + attoelectronvolts, ]) power = UnitGroup( @@ -1982,9 +2611,148 @@ def __init__(self, name: str, units: list[Unit]): dimensionless = UnitGroup( name = 'dimensionless', + units = [ + none, +]) + +angle = UnitGroup( + name = 'angle', units = [ degrees, radians, +]) + +solid_angle = UnitGroup( + name = 'solid_angle', + units = [ stradians, - none, +]) + +amount = UnitGroup( + name = 'amount', + units = [ + moles, + millimoles, + micromoles, + nanomoles, + picomoles, + femtomoles, + attomoles, +]) + +concentration = UnitGroup( + name = 'concentration', + units = [ + moles_per_cubic_meter, + millimoles_per_cubic_meter, + micromoles_per_cubic_meter, + nanomoles_per_cubic_meter, + picomoles_per_cubic_meter, + femtomoles_per_cubic_meter, + attomoles_per_cubic_meter, + moles_per_cubic_exameter, + millimoles_per_cubic_exameter, + micromoles_per_cubic_exameter, + nanomoles_per_cubic_exameter, + picomoles_per_cubic_exameter, + femtomoles_per_cubic_exameter, + attomoles_per_cubic_exameter, + moles_per_cubic_petameter, + millimoles_per_cubic_petameter, + micromoles_per_cubic_petameter, + nanomoles_per_cubic_petameter, + picomoles_per_cubic_petameter, + femtomoles_per_cubic_petameter, + attomoles_per_cubic_petameter, + moles_per_cubic_terameter, + millimoles_per_cubic_terameter, + micromoles_per_cubic_terameter, + nanomoles_per_cubic_terameter, + picomoles_per_cubic_terameter, + femtomoles_per_cubic_terameter, + attomoles_per_cubic_terameter, + moles_per_cubic_gigameter, + millimoles_per_cubic_gigameter, + micromoles_per_cubic_gigameter, + nanomoles_per_cubic_gigameter, + picomoles_per_cubic_gigameter, + femtomoles_per_cubic_gigameter, + attomoles_per_cubic_gigameter, + moles_per_cubic_megameter, + millimoles_per_cubic_megameter, + micromoles_per_cubic_megameter, + nanomoles_per_cubic_megameter, + picomoles_per_cubic_megameter, + femtomoles_per_cubic_megameter, + attomoles_per_cubic_megameter, + moles_per_cubic_kilometer, + millimoles_per_cubic_kilometer, + micromoles_per_cubic_kilometer, + nanomoles_per_cubic_kilometer, + picomoles_per_cubic_kilometer, + femtomoles_per_cubic_kilometer, + attomoles_per_cubic_kilometer, + moles_per_cubic_millimeter, + millimoles_per_cubic_millimeter, + micromoles_per_cubic_millimeter, + nanomoles_per_cubic_millimeter, + picomoles_per_cubic_millimeter, + femtomoles_per_cubic_millimeter, + attomoles_per_cubic_millimeter, + moles_per_cubic_micrometer, + millimoles_per_cubic_micrometer, + micromoles_per_cubic_micrometer, + nanomoles_per_cubic_micrometer, + picomoles_per_cubic_micrometer, + femtomoles_per_cubic_micrometer, + attomoles_per_cubic_micrometer, + moles_per_cubic_nanometer, + millimoles_per_cubic_nanometer, + micromoles_per_cubic_nanometer, + nanomoles_per_cubic_nanometer, + picomoles_per_cubic_nanometer, + femtomoles_per_cubic_nanometer, + attomoles_per_cubic_nanometer, + moles_per_cubic_picometer, + millimoles_per_cubic_picometer, + micromoles_per_cubic_picometer, + nanomoles_per_cubic_picometer, + picomoles_per_cubic_picometer, + femtomoles_per_cubic_picometer, + attomoles_per_cubic_picometer, + moles_per_cubic_femtometer, + millimoles_per_cubic_femtometer, + micromoles_per_cubic_femtometer, + nanomoles_per_cubic_femtometer, + picomoles_per_cubic_femtometer, + femtomoles_per_cubic_femtometer, + attomoles_per_cubic_femtometer, + moles_per_cubic_attometer, + millimoles_per_cubic_attometer, + micromoles_per_cubic_attometer, + nanomoles_per_cubic_attometer, + picomoles_per_cubic_attometer, + femtomoles_per_cubic_attometer, + attomoles_per_cubic_attometer, + moles_per_cubic_decimeter, + millimoles_per_cubic_decimeter, + micromoles_per_cubic_decimeter, + nanomoles_per_cubic_decimeter, + picomoles_per_cubic_decimeter, + femtomoles_per_cubic_decimeter, + attomoles_per_cubic_decimeter, + moles_per_cubic_centimeter, + millimoles_per_cubic_centimeter, + micromoles_per_cubic_centimeter, + nanomoles_per_cubic_centimeter, + picomoles_per_cubic_centimeter, + femtomoles_per_cubic_centimeter, + attomoles_per_cubic_centimeter, + moles_per_cubic_angstrom, + millimoles_per_cubic_angstrom, + micromoles_per_cubic_angstrom, + nanomoles_per_cubic_angstrom, + picomoles_per_cubic_angstrom, + femtomoles_per_cubic_angstrom, + attomoles_per_cubic_angstrom, ]) From b0729a6f3b749b215184d05151668e53261c0937 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 13:54:18 +0100 Subject: [PATCH 010/675] Units and accessors draft ready to begin tests on --- sasdata/data.py | 2 +- sasdata/metadata.py | 2 +- sasdata/quantities/_accessor_base.py | 149 +- sasdata/quantities/_autogen_warning.py | 136 +- sasdata/quantities/_build_tables.py | 302 +- sasdata/quantities/_units_base.py | 6 + sasdata/quantities/_units_table.py | 333 - sasdata/quantities/accessors.py | 8789 ++++-------------------- sasdata/quantities/quantities.py | 162 - sasdata/quantities/quantity.py | 1612 +---- sasdata/quantities/units.py | 388 +- sasdata/transforms/operation.py | 2 +- 12 files changed, 1588 insertions(+), 10295 deletions(-) delete mode 100644 sasdata/quantities/_units_table.py delete mode 100644 sasdata/quantities/quantities.py diff --git a/sasdata/data.py b/sasdata/data.py index e4919948a..f6ca6260d 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from quantities.quantities import Quantity, NamedQuantity +from quantities.quantity import Quantity, NamedQuantity from sasdata.metadata import MetaData import numpy as np diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 6713c452f..ed9396cf5 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -2,7 +2,7 @@ from numpy._typing import ArrayLike -from sasdata.quantities.quantities import Unit, Quantity +from sasdata.quantities.quantity import Unit, Quantity class RawMetaData: diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 09a1b6e01..c887b7242 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -1,152 +1,17 @@ from typing import TypeVar -import sasdata.quantities.units as units -from sasdata.data_backing import Dataset, Group from sasdata.quantities.quantity import Quantity -from sasdata.quantities.unit_parser import parse_unit -from sasdata.quantities.units import Unit - - -# logger = logging.getLogger("Accessors") -class LoggerDummy: - def info(self, data): - print(data) -logger = LoggerDummy() - -DataType = TypeVar("DataType") -OutputType = TypeVar("OutputType") - - -class AccessorTarget: - def __init__(self, data: Group, verbose=False, prefix_tokens: tuple=()): - self._data = data - self.verbose = verbose - - self.prefix_tokens = list(prefix_tokens) - - def with_path_prefix(self, path_prexix: str): - """ Get an accessor that looks at a subtree of the metadata with the supplied prefix - - For example, accessors aiming at a.b, when the target it c.d will look at c.d.a.b - """ - return AccessorTarget(self._data, - verbose=self.verbose, - prefix_tokens=tuple(self.prefix_tokens + [path_prexix])) - - def get_value(self, path: str): - - tokens = self.prefix_tokens + path.split(".") - - if self.verbose: - logger.info(f"Finding: {path}") - logger.info(f"Full path: {tokens}") - - # Navigate the tree from the entry we need - - current_tree_position: Group | Dataset = self._data - - for token in tokens: - - options = token.split("|") - - if isinstance(current_tree_position, Group): - - found = False - for option in options: - if option in current_tree_position.children: - current_tree_position = current_tree_position.children[option] - found = True - - if self.verbose: - logger.info(f"Found option: {option}") - - if not found: - if self.verbose: - logger.info(f"Failed to find any of {options} on group {current_tree_position.name}. Options: " + - ",".join([key for key in current_tree_position.children])) - return None - - elif isinstance(current_tree_position, Dataset): - - found = False - for option in options: - if option in current_tree_position.attributes: - current_tree_position = current_tree_position.attributes[option] - found = True - - if self.verbose: - logger.info(f"Found option: {option}") - - if not found: - if self.verbose: - logger.info(f"Failed to find any of {options} on attribute {current_tree_position.name}. Options: " + - ",".join([key for key in current_tree_position.attributes])) - return None - - if self.verbose: - logger.info(f"Found value: {current_tree_position}") - - return current_tree_position.data +import sasdata.quantities.units as units +T = TypeVar("T") -class Accessor[DataType, OutputType]: +class Accessor[T]: """ Base class """ - def __init__(self, target_object: AccessorTarget, value_target: str): - self.target_object = target_object - self.value_target = value_target - - @property - def value(self) -> OutputType | None: - return self.target_object.get_value(self.value_target) - -class StringAccessor(Accessor[str, str]): - """ String based fields """ - @property - def value(self) -> str | None: - return self.target_object.get_value(self.value_target) - -class FloatAccessor(Accessor[float, float]): - """ Float based fields """ - @property - def value(self) -> float | None: - return self.target_object.get_value(self.value_target) - - - - -class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): - """ Base class for accessors that work with quantities that have units """ - def __init__(self, target_object: AccessorTarget, value_target: str, unit_target: str, default_unit=units.none): - super().__init__(target_object, value_target) + def __init__(self, value_target: str, unit_target: str): + self._value_target = value_target self._unit_target = unit_target - self.default_unit = default_unit - - def _numerical_part(self) -> DataType | None: - """ Numerical part of the data """ - return self.target_object.get_value(self.value_target) - - def _unit_part(self) -> str | None: - """ String form of units for the data """ - return self.target_object.get_value(self._unit_target) @property - def unit(self) -> Unit: - u = self._unit_part() - if u is None: - return self.default_unit - else: - return parse_unit(u) - - @property - def value(self) -> Quantity[DataType] | None: - if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self._numerical_part(), self.unit) - return None - - @property - def quantity(self): - if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self._numerical_part(), self.unit) - return None - + def quantity(self) -> Quantity[T]: + raise NotImplementedError("Not implemented yet") diff --git a/sasdata/quantities/_autogen_warning.py b/sasdata/quantities/_autogen_warning.py index 9a8c9372e..fc8be67dc 100644 --- a/sasdata/quantities/_autogen_warning.py +++ b/sasdata/quantities/_autogen_warning.py @@ -5,75 +5,75 @@ Do not edit by hand, instead edit the files that build it (%s) - - -DDDDDDDDDDDDD NNNNNNNN NNNNNNNN tttt -D::::::::::::DDD N:::::::N N::::::N ttt:::t -D:::::::::::::::DD N::::::::N N::::::N t:::::t -DDD:::::DDDDD:::::D N:::::::::N N::::::N t:::::t - D:::::D D:::::D ooooooooooo N::::::::::N N::::::N ooooooooooo ttttttt:::::ttttttt - D:::::D D:::::D oo:::::::::::oo N:::::::::::N N::::::N oo:::::::::::oo t:::::::::::::::::t - D:::::D D:::::Do:::::::::::::::o N:::::::N::::N N::::::No:::::::::::::::ot:::::::::::::::::t - D:::::D D:::::Do:::::ooooo:::::o N::::::N N::::N N::::::No:::::ooooo:::::otttttt:::::::tttttt - D:::::D D:::::Do::::o o::::o N::::::N N::::N:::::::No::::o o::::o t:::::t - D:::::D D:::::Do::::o o::::o N::::::N N:::::::::::No::::o o::::o t:::::t - D:::::D D:::::Do::::o o::::o N::::::N N::::::::::No::::o o::::o t:::::t - D:::::D D:::::D o::::o o::::o N::::::N N:::::::::No::::o o::::o t:::::t tttttt -DDD:::::DDDDD:::::D o:::::ooooo:::::o N::::::N N::::::::No:::::ooooo:::::o t::::::tttt:::::t -D:::::::::::::::DD o:::::::::::::::o N::::::N N:::::::No:::::::::::::::o tt::::::::::::::t -D::::::::::::DDD oo:::::::::::oo N::::::N N::::::N oo:::::::::::oo tt:::::::::::tt -DDDDDDDDDDDDD ooooooooooo NNNNNNNN NNNNNNN ooooooooooo ttttttttttt - - - - - - - - - dddddddd -EEEEEEEEEEEEEEEEEEEEEE d::::::d iiii tttt BBBBBBBBBBBBBBBBB -E::::::::::::::::::::E d::::::d i::::i ttt:::t B::::::::::::::::B -E::::::::::::::::::::E d::::::d iiii t:::::t B::::::BBBBBB:::::B -EE::::::EEEEEEEEE::::E d:::::d t:::::t BB:::::B B:::::B + + +DDDDDDDDDDDDD NNNNNNNN NNNNNNNN tttt +D::::::::::::DDD N:::::::N N::::::N ttt:::t +D:::::::::::::::DD N::::::::N N::::::N t:::::t +DDD:::::DDDDD:::::D N:::::::::N N::::::N t:::::t + D:::::D D:::::D ooooooooooo N::::::::::N N::::::N ooooooooooo ttttttt:::::ttttttt + D:::::D D:::::D oo:::::::::::oo N:::::::::::N N::::::N oo:::::::::::oo t:::::::::::::::::t + D:::::D D:::::Do:::::::::::::::o N:::::::N::::N N::::::No:::::::::::::::ot:::::::::::::::::t + D:::::D D:::::Do:::::ooooo:::::o N::::::N N::::N N::::::No:::::ooooo:::::otttttt:::::::tttttt + D:::::D D:::::Do::::o o::::o N::::::N N::::N:::::::No::::o o::::o t:::::t + D:::::D D:::::Do::::o o::::o N::::::N N:::::::::::No::::o o::::o t:::::t + D:::::D D:::::Do::::o o::::o N::::::N N::::::::::No::::o o::::o t:::::t + D:::::D D:::::D o::::o o::::o N::::::N N:::::::::No::::o o::::o t:::::t tttttt +DDD:::::DDDDD:::::D o:::::ooooo:::::o N::::::N N::::::::No:::::ooooo:::::o t::::::tttt:::::t +D:::::::::::::::DD o:::::::::::::::o N::::::N N:::::::No:::::::::::::::o tt::::::::::::::t +D::::::::::::DDD oo:::::::::::oo N::::::N N::::::N oo:::::::::::oo tt:::::::::::tt +DDDDDDDDDDDDD ooooooooooo NNNNNNNN NNNNNNN ooooooooooo ttttttttttt + + + + + + + + + dddddddd +EEEEEEEEEEEEEEEEEEEEEE d::::::d iiii tttt BBBBBBBBBBBBBBBBB +E::::::::::::::::::::E d::::::d i::::i ttt:::t B::::::::::::::::B +E::::::::::::::::::::E d::::::d iiii t:::::t B::::::BBBBBB:::::B +EE::::::EEEEEEEEE::::E d:::::d t:::::t BB:::::B B:::::B E:::::E EEEEEE ddddddddd:::::d iiiiiiittttttt:::::ttttttt B::::B B:::::Byyyyyyy yyyyyyy - E:::::E dd::::::::::::::d i:::::it:::::::::::::::::t B::::B B:::::B y:::::y y:::::y - E::::::EEEEEEEEEE d::::::::::::::::d i::::it:::::::::::::::::t B::::BBBBBB:::::B y:::::y y:::::y - E:::::::::::::::E d:::::::ddddd:::::d i::::itttttt:::::::tttttt B:::::::::::::BB y:::::y y:::::y - E:::::::::::::::E d::::::d d:::::d i::::i t:::::t B::::BBBBBB:::::B y:::::y y:::::y - E::::::EEEEEEEEEE d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y y:::::y - E:::::E d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y:::::y - E:::::E EEEEEEd:::::d d:::::d i::::i t:::::t tttttt B::::B B:::::B y:::::::::y -EE::::::EEEEEEEE:::::Ed::::::ddddd::::::ddi::::::i t::::::tttt:::::t BB:::::BBBBBB::::::B y:::::::y -E::::::::::::::::::::E d:::::::::::::::::di::::::i tt::::::::::::::t B:::::::::::::::::B y:::::y -E::::::::::::::::::::E d:::::::::ddd::::di::::::i tt:::::::::::tt B::::::::::::::::B y:::::y -EEEEEEEEEEEEEEEEEEEEEE ddddddddd dddddiiiiiiii ttttttttttt BBBBBBBBBBBBBBBBB y:::::y - y:::::y - y:::::y - y:::::y - y:::::y - yyyyyyy - - - - dddddddd -HHHHHHHHH HHHHHHHHH d::::::d -H:::::::H H:::::::H d::::::d -H:::::::H H:::::::H d::::::d -HH::::::H H::::::HH d:::::d - H:::::H H:::::H aaaaaaaaaaaaa nnnn nnnnnnnn ddddddddd:::::d - H:::::H H:::::H a::::::::::::a n:::nn::::::::nn dd::::::::::::::d - H::::::HHHHH::::::H aaaaaaaaa:::::an::::::::::::::nn d::::::::::::::::d - H:::::::::::::::::H a::::ann:::::::::::::::nd:::::::ddddd:::::d - H:::::::::::::::::H aaaaaaa:::::a n:::::nnnn:::::nd::::::d d:::::d - H::::::HHHHH::::::H aa::::::::::::a n::::n n::::nd:::::d d:::::d - H:::::H H:::::H a::::aaaa::::::a n::::n n::::nd:::::d d:::::d - H:::::H H:::::H a::::a a:::::a n::::n n::::nd:::::d d:::::d -HH::::::H H::::::HHa::::a a:::::a n::::n n::::nd::::::ddddd::::::dd -H:::::::H H:::::::Ha:::::aaaa::::::a n::::n n::::n d:::::::::::::::::d -H:::::::H H:::::::H a::::::::::aa:::a n::::n n::::n d:::::::::ddd::::d -HHHHHHHHH HHHHHHHHH aaaaaaaaaa aaaa nnnnnn nnnnnn ddddddddd ddddd - + E:::::E dd::::::::::::::d i:::::it:::::::::::::::::t B::::B B:::::B y:::::y y:::::y + E::::::EEEEEEEEEE d::::::::::::::::d i::::it:::::::::::::::::t B::::BBBBBB:::::B y:::::y y:::::y + E:::::::::::::::E d:::::::ddddd:::::d i::::itttttt:::::::tttttt B:::::::::::::BB y:::::y y:::::y + E:::::::::::::::E d::::::d d:::::d i::::i t:::::t B::::BBBBBB:::::B y:::::y y:::::y + E::::::EEEEEEEEEE d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y y:::::y + E:::::E d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y:::::y + E:::::E EEEEEEd:::::d d:::::d i::::i t:::::t tttttt B::::B B:::::B y:::::::::y +EE::::::EEEEEEEE:::::Ed::::::ddddd::::::ddi::::::i t::::::tttt:::::t BB:::::BBBBBB::::::B y:::::::y +E::::::::::::::::::::E d:::::::::::::::::di::::::i tt::::::::::::::t B:::::::::::::::::B y:::::y +E::::::::::::::::::::E d:::::::::ddd::::di::::::i tt:::::::::::tt B::::::::::::::::B y:::::y +EEEEEEEEEEEEEEEEEEEEEE ddddddddd dddddiiiiiiii ttttttttttt BBBBBBBBBBBBBBBBB y:::::y + y:::::y + y:::::y + y:::::y + y:::::y + yyyyyyy + + + + dddddddd +HHHHHHHHH HHHHHHHHH d::::::d +H:::::::H H:::::::H d::::::d +H:::::::H H:::::::H d::::::d +HH::::::H H::::::HH d:::::d + H:::::H H:::::H aaaaaaaaaaaaa nnnn nnnnnnnn ddddddddd:::::d + H:::::H H:::::H a::::::::::::a n:::nn::::::::nn dd::::::::::::::d + H::::::HHHHH::::::H aaaaaaaaa:::::an::::::::::::::nn d::::::::::::::::d + H:::::::::::::::::H a::::ann:::::::::::::::nd:::::::ddddd:::::d + H:::::::::::::::::H aaaaaaa:::::a n:::::nnnn:::::nd::::::d d:::::d + H::::::HHHHH::::::H aa::::::::::::a n::::n n::::nd:::::d d:::::d + H:::::H H:::::H a::::aaaa::::::a n::::n n::::nd:::::d d:::::d + H:::::H H:::::H a::::a a:::::a n::::n n::::nd:::::d d:::::d +HH::::::H H::::::HHa::::a a:::::a n::::n n::::nd::::::ddddd::::::dd +H:::::::H H:::::::Ha:::::aaaa::::::a n::::n n::::n d:::::::::::::::::d +H:::::::H H:::::::H a::::::::::aa:::a n::::n n::::n d:::::::::ddd::::d +HHHHHHHHH HHHHHHHHH aaaaaaaaaa aaaa nnnnnn nnnnnn ddddddddd ddddd + """ diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index ded60c5d3..fb4ab7bf8 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -2,123 +2,84 @@ Builds a data file containing details of units """ -from collections import defaultdict, namedtuple - import numpy as np +from collections import defaultdict +from _units_base import Dimensions, Unit from _autogen_warning import warning_text -from _units_base import Dimensions - -Magnitude = namedtuple("Magnitude", ["symbol", "special_symbol", "latex_symbol", "name", "scale"]) - -bigger_magnitudes: list[Magnitude] = [ - Magnitude("E", None, None, "exa", 1e18), - Magnitude("P", None, None, "peta", 1e15), - Magnitude("T", None, None, "tera", 1e12), - Magnitude("G", None, None, "giga", 1e9), - Magnitude("M", None, None, "mega", 1e6), - Magnitude("k", None, None, "kilo", 1e3) ] - -smaller_magnitudes: list[Magnitude] = [ - Magnitude("m", None, None, "milli", 1e-3), - Magnitude("u", "µ", r"\mu", "micro", 1e-6), - Magnitude("n", None, None, "nano", 1e-9), - Magnitude("p", None, None, "pico", 1e-12), - Magnitude("f", None, None, "femto", 1e-15), - Magnitude("a", None, None, "atto", 1e-18)] - -unusual_magnitudes: list[Magnitude] = [ - Magnitude("d", None, None, "deci", 1e-1), - Magnitude("c", None, None, "centi", 1e-2) + +bigger_magnitudes = [ + ("E", None, "exa", 1e18), + ("P", None, "peta", 1e15), + ("T", None, "tera", 1e12), + ("G", None, "giga", 1e9), + ("M", None, "mega", 1e6), + ("k", None, "kilo", 1e3) ] + +smaller_magnitudes = [ + ("m", None, "milli", 1e-3), + ("u", "µ", "micro", 1e-6), + ("n", None, "nano", 1e-9), + ("p", None, "pico", 1e-12), + ("f", None, "femto", 1e-15), + ("a", None, "atto", 1e-18)] + +unusual_magnitudes = [ + ("d", None, "deci", 1e-1), + ("c", None, "centi", 1e-2) ] all_magnitudes = bigger_magnitudes + smaller_magnitudes -UnitData = namedtuple("UnitData", ["symbol", "special_symbol", "latex_symbol", "singular", "plural", "scale", "length", "time", "mass", "current", "temperature", "moles_hint", "angle_hint", "magnitudes"]) - # Length, time, mass, current, temperature base_si_units = [ - UnitData("m", None, None, "meter", "meters", 1, 1, 0, 0, 0, 0, 0, 0, all_magnitudes + unusual_magnitudes), - UnitData("s", None, None, "second", "seconds", 1, 0, 1, 0, 0, 0, 0, 0, smaller_magnitudes), - UnitData("g", None, None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, 0, 0, all_magnitudes), - UnitData("A", None, None, "ampere", "amperes", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), - UnitData("K", None, None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, 0, 0, all_magnitudes) ] + ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, 0, 0, all_magnitudes + unusual_magnitudes), + ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, 0, 0, smaller_magnitudes), + ("g", None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, 0, 0, all_magnitudes), + ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), + ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, 0, 0, all_magnitudes) ] derived_si_units = [ - UnitData("Hz", None, None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, 0, 0, all_magnitudes), - UnitData("N", None, None, "newton", "newtons", 1, 1, -2, 1, 0, 0, 0, 0, all_magnitudes), - UnitData("Pa", None, None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, 0, 0, all_magnitudes), - UnitData("J", None, None, "joule", "joules", 1, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), - UnitData("W", None, None, "watt", "watts", 1, 2, -3, 1, 0, 0, 0, 0, all_magnitudes), - UnitData("C", None, None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, 0, 0, all_magnitudes), - UnitData("V", None, None, "volts", "volts", 1, 2, -3, 1, -1, 0, 0, 0, all_magnitudes), - UnitData("Ohm", "Ω", r"\Omega", "ohm", "ohms", 1, 2, -3, 1, -2, 0, 0, 0, all_magnitudes), - UnitData("F", None, None, "farad", "farads", 1, -2, 4, -1, 2, 0, 0, 0, all_magnitudes), - UnitData("S", None, None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, 0, 0, all_magnitudes), - UnitData("Wb", None, None, "weber", "webers", 1, 2, -2, 1, -1, 0, 0, 0, all_magnitudes), - UnitData("T", None, None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, 0, 0, all_magnitudes), - UnitData("H", None, None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), + ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, 0, 0, all_magnitudes), + ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, 0, 0, all_magnitudes), + ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, 0, 0, all_magnitudes), + ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), + ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, 0, 0, all_magnitudes), + ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, 0, 0, all_magnitudes), + ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, 0, 0, all_magnitudes), + ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, 0, 0, all_magnitudes), + ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, 0, 0, all_magnitudes), + ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, 0, 0, all_magnitudes), + ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, 0, 0, all_magnitudes), + ("T", None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, 0, 0, all_magnitudes), + ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), + ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []) ] -non_si_dimensioned_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ - UnitData("Ang", "Å", r"\AA", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, 0, 0, []), - UnitData("micron", None, None, "micron", "microns", 1e-6, 1, 0, 0, 0, 0, 0, 0, []), - UnitData("min", None, None, "minute", "minutes", 60, 0, 1, 0, 0, 0, 0, 0, []), - UnitData("rpm", None, None, "revolutions per minute", "revolutions per minute", 1/60, 0, -1, 0, 0, 0, 0, 0, []), - UnitData("h", None, None, "hour", "hours", 3600, 0, 1, 0, 0, 0, 0, 0, []), - UnitData("d", None, None, "day", "days", 3600*24, 0, 1, 0, 0, 0, 0, 0, []), - UnitData("y", None, None, "year", "years", 3600*24*365.2425, 0, 1, 0, 0, 0, 0, 0, []), - UnitData("deg", None, None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, 0, 1, []), - UnitData("rad", None, None, "radian", "radians", 1, 0, 0, 0, 0, 0, 0, 1, []), - UnitData("rot", None, None, "rotation", "rotations", 2*np.pi, 0, 0, 0, 0, 0, 0, 1, []), - UnitData("sr", None, None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, 0, 2, []), - UnitData("l", None, None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), - UnitData("eV", None, None, "electronvolt", "electronvolts", 1.602176634e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), - UnitData("au", None, None, "atomic mass unit", "atomic mass units", 1.660538921e-27, 0, 0, 1, 0, 0, 0, 0, []), - UnitData("mol", None, None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes), - UnitData("kgForce", None, None, "kg force", "kg force", 9.80665, 1, -2, 1, 0, 0, 0, 0, []), - UnitData("C", None, None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []), - UnitData("miles", None, None, "mile", "miles", 1760*3*0.3048, 1, 0, 0, 0, 0, 0, 0, []), - UnitData("yrd", None, None, "yard", "yards", 3*0.3048, 1, 0, 0, 0, 0, 0, 0, []), - UnitData("ft", None, None, "foot", "feet", 0.3048, 1, 0, 0, 0, 0, 0, 0, []), - UnitData("in", None, None, "inch", "inches", 0.0254, 1, 0, 0, 0, 0, 0, 0, []), - UnitData("lb", None, None, "pound", "pounds", 0.45359237, 0, 0, 1, 0, 0, 0, 0, []), - UnitData("lbf", None, None, "pound force", "pounds force", 4.448222, 1, -2, 1, 0, 0, 0, 0, []), - UnitData("oz", None, None, "ounce", "ounces", 0.45359237/16, 0, 0, 1, 0, 0, 0, 0, []), - UnitData("psi", None, None, "pound force per square inch", "pounds force per square inch", 4.448222/(0.0254**2), -1, -2, 1, 0, 0, 0, 0, []), +non_si_units = [ + ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, 0, 0, []), + ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, 0, 0, []), + ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, 0, 0, []), + ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, 0, 0, []), + ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, 0, 0, []), + ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, 0, 1, []), + ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, 0, 1, []), + ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, 0, 2, []), + ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), + ("l", None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), + ("eV", None, "electronvolt", "electronvolts", 1.602176634e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), + ("au", None, "atomic mass unit", "atomic mass units", 1.660538921e-27, 0, 0, 1, 0, 0, 0, 0, []), + ("mol", None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes) ] -non_si_dimensionless_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ - UnitData("none", None, None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), - UnitData("percent", "%", r"\%", "percent", "percent", 0.01, 0, 0, 0, 0, 0, 0, 0, []) -] - -non_si_units = non_si_dimensioned_units + non_si_dimensionless_units - -# TODO: -# Add Hartree? Rydberg? Bohrs? -# Add CGS - -# Two stages of aliases, to make sure units don't get lost - -aliases_1 = { - "A": ["Amps", "amps"], - "C": ["Coulombs", "coulombs"] -} - -aliases_2 = { +aliases = { "y": ["yr", "year"], "d": ["day"], "h": ["hr", "hour"], "Ang": ["A", "Å"], - "au": ["amu"], - "percent": ["%"], - "deg": ["degr", "Deg", "degree", "degrees", "Degrees"], - "none": ["Counts", "counts", "cnts", "Cnts", "a.u.", "fraction", "Fraction"], - "K": ["C"] # Ugh, cansas + "au": ["a.u.", "amu"] } - all_units = base_si_units + derived_si_units + non_si_units encoding = "utf-8" @@ -137,16 +98,9 @@ def format_name(name: str): "# Included from _units_base.py\n" "#\n\n") - with open("_units_base.py") as base: + with open("_units_base.py", 'r') as base: for line in base: - # unicode_superscript is a local module when called from - # _unit_tables.py but a submodule of sasdata.quantities - # when called from units.py. This condition patches the - # line when the copy is made. - if line.startswith("from unicode_superscript"): - fid.write(line.replace("from unicode_superscript", "\nfrom sasdata.quantities.unicode_superscript")) - else: - fid.write(line) + fid.write(line) # Write in unit definitions fid.write("\n\n" @@ -158,56 +112,46 @@ def format_name(name: str): unit_types_temp = defaultdict(list) # Keep track of unit types unit_types = defaultdict(list) - for unit_def in all_units: + for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, moles_hint, angle_hint, magnitudes in all_units: - formatted_plural = format_name(unit_def.plural) - formatted_singular = format_name(unit_def.singular) + formatted_plural = format_name(plural) + formatted_singular = format_name(singular) - dimensions = Dimensions(unit_def.length, unit_def.time, unit_def.mass, unit_def.current, unit_def.temperature, unit_def.moles_hint, unit_def.angle_hint) - fid.write(f"{formatted_plural} = NamedUnit({unit_def.scale}, Dimensions({unit_def.length}, {unit_def.time}, {unit_def.mass}, {unit_def.current}, {unit_def.temperature}, {unit_def.moles_hint}, {unit_def.angle_hint})," + dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) + fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," f"name='{formatted_plural}'," - f"ascii_symbol='{unit_def.symbol}'," - f"{'' if unit_def.latex_symbol is None else f"""latex_symbol=r'{unit_def.latex_symbol}',""" }" - f"symbol='{unit_def.symbol if unit_def.special_symbol is None else unit_def.special_symbol}')\n") + f"ascii_symbol='{symbol}'," + f"symbol='{symbol if special_symbol is None else special_symbol}')\n") - symbol_lookup[unit_def.symbol] = formatted_plural - if unit_def.special_symbol is not None: - symbol_lookup[unit_def.special_symbol] = formatted_plural + symbol_lookup[symbol] = formatted_plural + if special_symbol is not None: + symbol_lookup[special_symbol] = formatted_plural unit_types_temp[hash(dimensions)].append( - (unit_def.symbol, unit_def.special_symbol, formatted_singular, formatted_plural, unit_def.scale, dimensions)) + (symbol, special_symbol, formatted_singular, formatted_plural, scale, dimensions)) unit_types[hash(dimensions)].append(formatted_plural) - for mag in unit_def.magnitudes: + for mag_symbol, mag_special_symbol, name, mag_scale in magnitudes: # Work out the combined symbol, accounts for unicode or not - combined_special_symbol = (mag.symbol if mag.special_symbol is None else mag.special_symbol) + \ - (unit_def.symbol if unit_def.special_symbol is None else unit_def.special_symbol) + combined_special_symbol = (mag_symbol if mag_special_symbol is None else mag_special_symbol) + \ + (symbol if special_symbol is None else special_symbol) - combined_symbol = mag.symbol + unit_def.symbol + combined_symbol = mag_symbol + symbol # Combined unit name - combined_name_singular = f"{mag.name}{formatted_singular}" - combined_name_plural = f"{mag.name}{formatted_plural}" - - combined_scale = unit_def.scale * mag.scale + combined_name_singular = f"{name}{formatted_singular}" + combined_name_plural = f"{name}{formatted_plural}" - latex_symbol = None - if unit_def.latex_symbol is not None and mag.latex_symbol is not None: - latex_symbol = f"{{{mag.latex_symbol}}}{unit_def.latex_symbol}" - elif unit_def.latex_symbol is not None: - latex_symbol = f"{mag.symbol}{unit_def.latex_symbol}" - elif mag.latex_symbol is not None: - latex_symbol = f"{{{mag.latex_symbol}}}{unit_def.symbol}" + combined_scale = scale * mag_scale # Units - dimensions = Dimensions(unit_def.length, unit_def.time, unit_def.mass, unit_def.current, unit_def.temperature, unit_def.moles_hint, unit_def.angle_hint) - fid.write(f"{combined_name_plural} = NamedUnit({combined_scale}, " - f"Dimensions({unit_def.length}, {unit_def.time}, {unit_def.mass}, {unit_def.current}, {unit_def.temperature}, {unit_def.moles_hint}, {unit_def.angle_hint})," + dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) + fid.write(f"{combined_name_plural} = Unit({combined_scale}, " + f"Dimensions({length}, {time}, {mass}, {current}, {temperature})," f"name='{combined_name_plural}'," f"ascii_symbol='{combined_symbol}'," - f"{'' if latex_symbol is None else f"""latex_symbol=r'{latex_symbol}',""" }" f"symbol='{combined_special_symbol}')\n") symbol_lookup[combined_symbol] = combined_name_plural @@ -241,7 +185,7 @@ def format_name(name: str): unit_name = prefix + name unit_special_symbol = (symbol if special_symbol is None else special_symbol) + unicode_suffix unit_symbol = symbol + f"^{power}" - fid.write(f"{unit_name} = NamedUnit({scale**power}, Dimensions(length={power}), " + fid.write(f"{unit_name} = Unit({scale**power}, Dimensions(length={power}), " f"name='{unit_name}', " f"ascii_symbol='{unit_symbol}', " f"symbol='{unit_special_symbol}')\n") @@ -257,21 +201,18 @@ def format_name(name: str): speed_dimensions = Dimensions(length=1, time=-1) accel_dimensions = Dimensions(length=1, time=-2) - length_special = length_special_symbol if length_special_symbol is not None else length_symbol - time_special = time_special_symbol if time_special_symbol is not None else time_symbol - fid.write(f"{speed_name} " - f"= NamedUnit({length_scale / time_scale}, " + f"= Unit({length_scale / time_scale}, " f"Dimensions(length=1, time=-1), " f"name='{speed_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}', " - f"symbol='{length_special}{time_special}⁻¹')\n") + f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") - fid.write(f"{accel_name} = NamedUnit({length_scale / time_scale**2}, " + fid.write(f"{accel_name} = Unit({length_scale / time_scale}, " f"Dimensions(length=1, time=-2), " f"name='{accel_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}^2', " - f"symbol='{length_special}{time_special}⁻²')\n") + f"symbol='{length_special_symbol}{time_special_symbol}⁻²')\n") unit_types[hash(speed_dimensions)].append(speed_name) unit_types[hash(accel_dimensions)].append(accel_name) @@ -284,15 +225,12 @@ def format_name(name: str): dimensions = Dimensions(length=-3, mass=1) - mass_special = mass_symbol if mass_special_symbol is None else mass_special_symbol - length_special = length_symbol if length_special_symbol is None else length_special_symbol - fid.write(f"{name} " - f"= NamedUnit({mass_scale / length_scale**3}, " + f"= Unit({mass_scale / length_scale**3}, " f"Dimensions(length=-3, mass=1), " f"name='{name}', " f"ascii_symbol='{mass_symbol} {length_symbol}^-3', " - f"symbol='{mass_special}{length_special}⁻³')\n") + f"symbol='{mass_special_symbol}{length_special_symbol}⁻³')\n") unit_types[hash(dimensions)].append(name) @@ -304,30 +242,24 @@ def format_name(name: str): dimensions = Dimensions(length=-3, moles_hint=1) - length_special = length_symbol if length_special_symbol is None else length_special_symbol - amount_special = amount_symbol if amount_special_symbol is None else amount_special_symbol - fid.write(f"{name} " - f"= NamedUnit({amount_scale / length_scale**3}, " + f"= Unit({amount_scale / length_scale**3}, " f"Dimensions(length=-3, moles_hint=1), " f"name='{name}', " f"ascii_symbol='{amount_symbol} {length_symbol}^-3', " - f"symbol='{amount_special}{length_special}⁻³')\n") + f"symbol='{amount_special_symbol}{length_special_symbol}⁻³')\n") unit_types[hash(dimensions)].append(name) - # TODO: Torque, Momentum, Entropy # # Add aliases to symbol lookup table # - # Apply the alias transforms sequentially - for aliases in [aliases_1, aliases_2]: - for base_name in aliases: - alias_list = aliases[base_name] - for alias in alias_list: - symbol_lookup[alias] = symbol_lookup[base_name] + for base_name in aliases: + alias_list = aliases[base_name] + for alias in alias_list: + symbol_lookup[alias] = symbol_lookup[base_name] # # Write out the symbol lookup table @@ -335,8 +267,7 @@ def format_name(name: str): fid.write("\n#\n# Lookup table from symbols to units\n#\n\n") fid.write("symbol_lookup = {\n") for k in symbol_lookup: - if k != "none": - fid.write(f' "{k}": {symbol_lookup[k]},\n') + fid.write(f' "{k}": {symbol_lookup[k]},\n') fid.write("}\n\n") # @@ -372,7 +303,7 @@ def format_name(name: str): ("angle", Dimensions(angle_hint=1)), ("solid_angle", Dimensions(angle_hint=2)), ("amount", Dimensions(moles_hint=1)), - ("concentration", Dimensions(length=-3, moles_hint=1)), + ("concentration", Dimensions(length=-3, moles_hint=1)) ] fid.write("\n#\n# Units by type\n#\n\n") @@ -390,26 +321,12 @@ def format_name(name: str): fid.write("])\n") - - # List of dimensions - fid.write("\n\n") - fid.write("unit_group_names = [\n") - for dimension_name, _ in dimension_names: - fid.write(f" '{dimension_name}',\n") - fid.write("]\n\n") - - fid.write("unit_groups = {\n") - for dimension_name, _ in dimension_names: - fid.write(f" '{dimension_name}': {dimension_name},\n") - fid.write("}\n\n") - - with open("accessors.py", 'w', encoding=encoding) as fid: fid.write('"""'+(warning_text%"_build_tables.py, _accessor_base.py")+'"""\n\n') - with open("_accessor_base.py") as base: + with open("_accessor_base.py", 'r') as base: for line in base: fid.write(line) @@ -418,37 +335,14 @@ def format_name(name: str): accessor_name = dimension_name.capitalize().replace("_", "") + "Accessor" fid.write(f"\n" - f"class {accessor_name}[T](QuantityAccessor[T]):\n" + f"class {accessor_name}[T](Accessor[T]):\n" f" dimension_name = '{dimension_name}'\n" f"\n") for unit_name in unit_types[hash(dimensions)]: fid.write(f" @property\n" f" def {unit_name}(self) -> T:\n" - f" quantity = self.quantity\n" - f" if quantity is None:\n" - f" return None\n" - f" else:\n" - f" return quantity.in_units_of(units.{unit_name})\n" + f" return self.quantity.in_units_of(units.{unit_name})\n" f"\n") fid.write("\n") - -with open("si.py", 'w') as fid: - - si_unit_names = [values.plural for values in base_si_units + derived_si_units if values.plural != "grams"] + ["kilograms"] - si_unit_names.sort() - - fid.write('"""'+(warning_text%"_build_tables.py")+'"""\n\n') - fid.write("from sasdata.quantities.units import (\n") - - for name in si_unit_names: - fid.write(f" {name},\n") - - fid.write(")\n") - fid.write("\nall_si = [\n") - - for name in si_unit_names: - fid.write(f" {name},\n") - - fid.write("]\n") diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index b8bfd24e0..adbf86399 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -32,6 +32,11 @@ def __init__(self, self.moles_hint = moles_hint self.angle_hint = angle_hint + @property + def is_dimensionless(self): + """ Is this dimension dimensionless (ignores moles_hint and angle_hint) """ + return self.length == 0 and self.time == 0 and self.mass == 0 and self.current == 0 and self.temperature == 0 + def __mul__(self: Self, other: Self): if not isinstance(other, Dimensions): @@ -223,6 +228,7 @@ class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): class NamedUnit: # TODO: Add named unit class + pass # # Parsing plan: diff --git a/sasdata/quantities/_units_table.py b/sasdata/quantities/_units_table.py deleted file mode 100644 index c69b9577a..000000000 --- a/sasdata/quantities/_units_table.py +++ /dev/null @@ -1,333 +0,0 @@ -""" -Builds a data file containing details of units -""" - -import numpy as np -from collections import defaultdict -from _units_base import Dimensions, Unit - -bigger_magnitudes = [ - ("E", None, "exa", 1e18), - ("P", None, "peta", 1e15), - ("T", None, "tera", 1e12), - ("G", None, "giga", 1e9), - ("M", None, "mega", 1e6), - ("k", None, "kilo", 1e3) ] - -smaller_magnitudes = [ - ("m", None, "milli", 1e-3), - ("u", "µ", "micro", 1e-6), - ("n", None, "nano", 1e-9), - ("p", None, "pico", 1e-12), - ("f", None, "femto", 1e-15), - ("a", None, "atto", 1e-18)] - -unusual_magnitudes = [ - ("d", None, "deci", 1e-1), - ("c", None, "centi", 1e-2) -] - -all_magnitudes = bigger_magnitudes + smaller_magnitudes - -# Length, time, mass, current, temperature -base_si_units = [ - ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, 0, 0, all_magnitudes + unusual_magnitudes), - ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, 0, 0, smaller_magnitudes), - ("g", None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, 0, 0, all_magnitudes), - ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), - ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, 0, 0, all_magnitudes) ] - -derived_si_units = [ - ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, 0, 0, all_magnitudes), - ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, 0, 0, all_magnitudes), - ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, 0, 0, all_magnitudes), - ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), - ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, 0, 0, all_magnitudes), - ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, 0, 0, all_magnitudes), - ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, 0, 0, all_magnitudes), - ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, 0, 0, all_magnitudes), - ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, 0, 0, all_magnitudes), - ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, 0, 0, all_magnitudes), - ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, 0, 0, all_magnitudes), - ("T", None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, 0, 0, all_magnitudes), - ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), - ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []) -] - -non_si_units = [ - ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, 0, 0, []), - ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, 0, 0, []), - ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, 0, 0, []), - ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, 0, 0, []), - ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, 0, 0, []), - ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, 0, 1, []), - ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, 0, 1, []), - ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, 0, 2, []), - ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), - ("l", None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), - ("eV", None, "electronvolt", "electronvolts", 1.602e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), - ("au", None, "atomic mass unit", "atomic mass units", 1.661e-27, 0, 0, 1, 0, 0, 0, 0, []), - ("mol", None, "mole", "moles", 6.022e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes) -] - -aliases = { - "y": ["yr", "year"], - "d": ["day"], - "h": ["hr", "hour"], - "Ang": ["A", "Å"], - "au": ["a.u.", "amu"] -} - - -all_units = base_si_units + derived_si_units + non_si_units - -encoding = "utf-8" - -def format_name(name: str): - return name.lower().replace(" ", "_") - -header = """ - -Autogenerated file by _units_table.py - - - - ******** DO NOT EDIT BY HAND ******** - - - -""" - -with open("units.py", 'w', encoding=encoding) as fid: - - # Write warning header - fid.write('"""'+header+'"""') - - # Write in class definitions - fid.write("\n\n" - "#\n" - "# Included from _units_base.py\n" - "#\n\n") - - with open("_units_base.py", 'r') as base: - for line in base: - fid.write(line) - - # Write in unit definitions - fid.write("\n\n" - "#\n" - "# Specific units \n" - "#\n\n") - - symbol_lookup = {} - unit_types_temp = defaultdict(list) # Keep track of unit types - unit_types = defaultdict(list) - - for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, moles_hint, angle_hint, magnitudes in all_units: - - formatted_plural = format_name(plural) - formatted_singular = format_name(singular) - - dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) - fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," - f"name='{formatted_plural}'," - f"ascii_symbol='{symbol}'," - f"symbol='{symbol if special_symbol is None else special_symbol}')\n") - - symbol_lookup[symbol] = formatted_plural - if special_symbol is not None: - symbol_lookup[special_symbol] = formatted_plural - - unit_types_temp[hash(dimensions)].append( - (symbol, special_symbol, formatted_singular, formatted_plural, scale, dimensions)) - - unit_types[hash(dimensions)].append(formatted_plural) - - for mag_symbol, mag_special_symbol, name, mag_scale in magnitudes: - - # Work out the combined symbol, accounts for unicode or not - combined_special_symbol = (mag_symbol if mag_special_symbol is None else mag_special_symbol) + \ - (symbol if special_symbol is None else special_symbol) - - combined_symbol = mag_symbol + symbol - - # Combined unit name - combined_name_singular = f"{name}{formatted_singular}" - combined_name_plural = f"{name}{formatted_plural}" - - combined_scale = scale * mag_scale - - # Units - dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) - fid.write(f"{combined_name_plural} = Unit({combined_scale}, " - f"Dimensions({length}, {time}, {mass}, {current}, {temperature})," - f"name='{combined_name_plural}'," - f"ascii_symbol='{combined_symbol}'," - f"symbol='{combined_special_symbol}')\n") - - symbol_lookup[combined_symbol] = combined_name_plural - symbol_lookup[combined_special_symbol] = combined_name_plural - - unit_types_temp[hash(dimensions)].append( - (combined_symbol, combined_special_symbol, combined_name_singular, - combined_name_plural, combined_scale, dimensions)) - - unit_types[hash(dimensions)].append(combined_name_plural) - - # - # Higher dimensioned types - # - - length_units = unit_types_temp[hash(Dimensions(length=1))] - time_units = unit_types_temp[hash(Dimensions(time=1))] - mass_units = unit_types_temp[hash(Dimensions(mass=1))] - amount_units = unit_types_temp[hash(Dimensions(moles_hint=1))] - - # Length based - for symbol, special_symbol, singular, plural, scale, _ in length_units: - for prefix, power, name, unicode_suffix in [ - ("square_", 2, plural, '²'), - ("cubic_", 3, plural, '³'), - ("per_", -1, singular, '⁻¹'), - ("per_square_", -2, singular,'⁻²'), - ("per_cubic_", -3, singular,'⁻³')]: - - dimensions = Dimensions(length=power) - unit_name = prefix + name - unit_special_symbol = (symbol if special_symbol is None else special_symbol) + unicode_suffix - unit_symbol = symbol + f"^{power}" - fid.write(f"{unit_name} = Unit({scale**power}, Dimensions(length={power}), " - f"name='{unit_name}', " - f"ascii_symbol='{unit_symbol}', " - f"symbol='{unit_special_symbol}')\n") - - unit_types[hash(dimensions)].append(unit_name) - - # Speed and acceleration - for length_symbol, length_special_symbol, _, length_name, length_scale, _ in length_units: - for time_symbol, time_special_symbol, time_name, _, time_scale, _ in time_units: - speed_name = length_name + "_per_" + time_name - accel_name = length_name + "_per_square_" + time_name - - speed_dimensions = Dimensions(length=1, time=-1) - accel_dimensions = Dimensions(length=1, time=-2) - - fid.write(f"{speed_name} " - f"= Unit({length_scale / time_scale}, " - f"Dimensions(length=1, time=-1), " - f"name='{speed_name}', " - f"ascii_symbol='{length_symbol}/{time_symbol}', " - f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") - - fid.write(f"{accel_name} = Unit({length_scale / time_scale}, " - f"Dimensions(length=1, time=-2), " - f"name='{accel_name}', " - f"ascii_symbol='{length_symbol}/{time_symbol}^2', " - f"symbol='{length_special_symbol}{time_special_symbol}⁻²')\n") - - unit_types[hash(speed_dimensions)].append(speed_name) - unit_types[hash(accel_dimensions)].append(accel_name) - - # Density - for length_symbol, length_special_symbol, length_name, _, length_scale, _ in length_units: - for mass_symbol, mass_special_symbol, _, mass_name, mass_scale, _ in mass_units: - - name = mass_name + "_per_cubic_" + length_name - - dimensions = Dimensions(length=-3, mass=1) - - fid.write(f"{name} " - f"= Unit({mass_scale / length_scale**3}, " - f"Dimensions(length=-3, mass=1), " - f"name='{name}', " - f"ascii_symbol='{mass_symbol} {length_symbol}^-3', " - f"symbol='{mass_special_symbol}{length_special_symbol}⁻³')\n") - - unit_types[hash(dimensions)].append(name) - - # Concentration - for length_symbol, length_special_symbol, length_name, _, length_scale, _ in length_units: - for amount_symbol, amount_special_symbol, _, amount_name, amount_scale, _ in amount_units: - - name = amount_name + "_per_cubic_" + length_name - - dimensions = Dimensions(length=-3, moles_hint=1) - - fid.write(f"{name} " - f"= Unit({amount_scale / length_scale**3}, " - f"Dimensions(length=-3, moles_hint=1), " - f"name='{name}', " - f"ascii_symbol='{amount_symbol} {length_symbol}^-3', " - f"symbol='{amount_special_symbol}{length_special_symbol}⁻³')\n") - - unit_types[hash(dimensions)].append(name) - - - # - # Add aliases to symbol lookup table - # - - for base_name in aliases: - alias_list = aliases[base_name] - for alias in alias_list: - symbol_lookup[alias] = symbol_lookup[base_name] - - # - # Write out the symbol lookup table - # - fid.write("\n#\n# Lookup table from symbols to units\n#\n\n") - fid.write("symbol_lookup = {\n") - for k in symbol_lookup: - fid.write(f' "{k}": {symbol_lookup[k]},\n') - fid.write("}\n\n") - - # - # Collections of units by type - # - - dimension_names = [ - ("length", Dimensions(length=1)), - ("area", Dimensions(length=2)), - ("volume", Dimensions(length=3)), - ("inverse_length", Dimensions(length=-1)), - ("inverse_area", Dimensions(length=-2)), - ("inverse_volume", Dimensions(length=-3)), - ("time", Dimensions(time=1)), - ("rate", Dimensions(time=-1)), - ("speed", Dimensions(length=1, time=-1)), - ("acceleration", Dimensions(length=1, time=-2)), - ("density", Dimensions(length=-3, mass=1)), - ("force", Dimensions(1, -2, 1, 0, 0)), - ("pressure", Dimensions(-1, -2, 1, 0, 0)), - ("energy", Dimensions(2, -2, 1, 0, 0)), - ("power", Dimensions(2, -3, 1, 0, 0)), - ("charge", Dimensions(0, 1, 0, 1, 0)), - ("potential", Dimensions(2, -3, 1, -1, 0)), - ("resistance", Dimensions(2, -3, 1, -2, 0)), - ("capacitance", Dimensions(-2, 4, -1, 2, 0)), - ("conductance", Dimensions(-2, 3, -1, 2, 0)), - ("magnetic_flux", Dimensions(2, -2, 1, -1, 0)), - ("magnetic_flux_density", Dimensions(0, -2, 1, -1, 0)), - ("inductance", Dimensions(2, -2, 1, -2, 0)), - ("temperature", Dimensions(temperature=1)), - ("dimensionless", Dimensions()), - ("angle", Dimensions(angle_hint=1)), - ("solid_angle", Dimensions(angle_hint=2)), - ("amount", Dimensions(moles_hint=1)), - ("concentration", Dimensions(length=-3, moles_hint=1)) - ] - - fid.write("\n#\n# Units by type \n#\n\n") - - for dimension_name, dimensions in dimension_names: - - - fid.write(f"\n" - f"{dimension_name} = UnitGroup(\n" - f" name = '{dimension_name}', \n" - f" units = [\n") - - for unit_name in unit_types[hash(dimensions)]: - fid.write(" " + unit_name + ",\n") - - fid.write("])\n") \ No newline at end of file diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index d23268544..575298eb9 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -5,10733 +5,4250 @@ Do not edit by hand, instead edit the files that build it (_build_tables.py, _accessor_base.py) - - -DDDDDDDDDDDDD NNNNNNNN NNNNNNNN tttt -D::::::::::::DDD N:::::::N N::::::N ttt:::t -D:::::::::::::::DD N::::::::N N::::::N t:::::t -DDD:::::DDDDD:::::D N:::::::::N N::::::N t:::::t - D:::::D D:::::D ooooooooooo N::::::::::N N::::::N ooooooooooo ttttttt:::::ttttttt - D:::::D D:::::D oo:::::::::::oo N:::::::::::N N::::::N oo:::::::::::oo t:::::::::::::::::t - D:::::D D:::::Do:::::::::::::::o N:::::::N::::N N::::::No:::::::::::::::ot:::::::::::::::::t - D:::::D D:::::Do:::::ooooo:::::o N::::::N N::::N N::::::No:::::ooooo:::::otttttt:::::::tttttt - D:::::D D:::::Do::::o o::::o N::::::N N::::N:::::::No::::o o::::o t:::::t - D:::::D D:::::Do::::o o::::o N::::::N N:::::::::::No::::o o::::o t:::::t - D:::::D D:::::Do::::o o::::o N::::::N N::::::::::No::::o o::::o t:::::t - D:::::D D:::::D o::::o o::::o N::::::N N:::::::::No::::o o::::o t:::::t tttttt -DDD:::::DDDDD:::::D o:::::ooooo:::::o N::::::N N::::::::No:::::ooooo:::::o t::::::tttt:::::t -D:::::::::::::::DD o:::::::::::::::o N::::::N N:::::::No:::::::::::::::o tt::::::::::::::t -D::::::::::::DDD oo:::::::::::oo N::::::N N::::::N oo:::::::::::oo tt:::::::::::tt -DDDDDDDDDDDDD ooooooooooo NNNNNNNN NNNNNNN ooooooooooo ttttttttttt - - - - - - - - - dddddddd -EEEEEEEEEEEEEEEEEEEEEE d::::::d iiii tttt BBBBBBBBBBBBBBBBB -E::::::::::::::::::::E d::::::d i::::i ttt:::t B::::::::::::::::B -E::::::::::::::::::::E d::::::d iiii t:::::t B::::::BBBBBB:::::B -EE::::::EEEEEEEEE::::E d:::::d t:::::t BB:::::B B:::::B + + +DDDDDDDDDDDDD NNNNNNNN NNNNNNNN tttt +D::::::::::::DDD N:::::::N N::::::N ttt:::t +D:::::::::::::::DD N::::::::N N::::::N t:::::t +DDD:::::DDDDD:::::D N:::::::::N N::::::N t:::::t + D:::::D D:::::D ooooooooooo N::::::::::N N::::::N ooooooooooo ttttttt:::::ttttttt + D:::::D D:::::D oo:::::::::::oo N:::::::::::N N::::::N oo:::::::::::oo t:::::::::::::::::t + D:::::D D:::::Do:::::::::::::::o N:::::::N::::N N::::::No:::::::::::::::ot:::::::::::::::::t + D:::::D D:::::Do:::::ooooo:::::o N::::::N N::::N N::::::No:::::ooooo:::::otttttt:::::::tttttt + D:::::D D:::::Do::::o o::::o N::::::N N::::N:::::::No::::o o::::o t:::::t + D:::::D D:::::Do::::o o::::o N::::::N N:::::::::::No::::o o::::o t:::::t + D:::::D D:::::Do::::o o::::o N::::::N N::::::::::No::::o o::::o t:::::t + D:::::D D:::::D o::::o o::::o N::::::N N:::::::::No::::o o::::o t:::::t tttttt +DDD:::::DDDDD:::::D o:::::ooooo:::::o N::::::N N::::::::No:::::ooooo:::::o t::::::tttt:::::t +D:::::::::::::::DD o:::::::::::::::o N::::::N N:::::::No:::::::::::::::o tt::::::::::::::t +D::::::::::::DDD oo:::::::::::oo N::::::N N::::::N oo:::::::::::oo tt:::::::::::tt +DDDDDDDDDDDDD ooooooooooo NNNNNNNN NNNNNNN ooooooooooo ttttttttttt + + + + + + + + + dddddddd +EEEEEEEEEEEEEEEEEEEEEE d::::::d iiii tttt BBBBBBBBBBBBBBBBB +E::::::::::::::::::::E d::::::d i::::i ttt:::t B::::::::::::::::B +E::::::::::::::::::::E d::::::d iiii t:::::t B::::::BBBBBB:::::B +EE::::::EEEEEEEEE::::E d:::::d t:::::t BB:::::B B:::::B E:::::E EEEEEE ddddddddd:::::d iiiiiiittttttt:::::ttttttt B::::B B:::::Byyyyyyy yyyyyyy - E:::::E dd::::::::::::::d i:::::it:::::::::::::::::t B::::B B:::::B y:::::y y:::::y - E::::::EEEEEEEEEE d::::::::::::::::d i::::it:::::::::::::::::t B::::BBBBBB:::::B y:::::y y:::::y - E:::::::::::::::E d:::::::ddddd:::::d i::::itttttt:::::::tttttt B:::::::::::::BB y:::::y y:::::y - E:::::::::::::::E d::::::d d:::::d i::::i t:::::t B::::BBBBBB:::::B y:::::y y:::::y - E::::::EEEEEEEEEE d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y y:::::y - E:::::E d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y:::::y - E:::::E EEEEEEd:::::d d:::::d i::::i t:::::t tttttt B::::B B:::::B y:::::::::y -EE::::::EEEEEEEE:::::Ed::::::ddddd::::::ddi::::::i t::::::tttt:::::t BB:::::BBBBBB::::::B y:::::::y -E::::::::::::::::::::E d:::::::::::::::::di::::::i tt::::::::::::::t B:::::::::::::::::B y:::::y -E::::::::::::::::::::E d:::::::::ddd::::di::::::i tt:::::::::::tt B::::::::::::::::B y:::::y -EEEEEEEEEEEEEEEEEEEEEE ddddddddd dddddiiiiiiii ttttttttttt BBBBBBBBBBBBBBBBB y:::::y - y:::::y - y:::::y - y:::::y - y:::::y - yyyyyyy - - - - dddddddd -HHHHHHHHH HHHHHHHHH d::::::d -H:::::::H H:::::::H d::::::d -H:::::::H H:::::::H d::::::d -HH::::::H H::::::HH d:::::d - H:::::H H:::::H aaaaaaaaaaaaa nnnn nnnnnnnn ddddddddd:::::d - H:::::H H:::::H a::::::::::::a n:::nn::::::::nn dd::::::::::::::d - H::::::HHHHH::::::H aaaaaaaaa:::::an::::::::::::::nn d::::::::::::::::d - H:::::::::::::::::H a::::ann:::::::::::::::nd:::::::ddddd:::::d - H:::::::::::::::::H aaaaaaa:::::a n:::::nnnn:::::nd::::::d d:::::d - H::::::HHHHH::::::H aa::::::::::::a n::::n n::::nd:::::d d:::::d - H:::::H H:::::H a::::aaaa::::::a n::::n n::::nd:::::d d:::::d - H:::::H H:::::H a::::a a:::::a n::::n n::::nd:::::d d:::::d -HH::::::H H::::::HHa::::a a:::::a n::::n n::::nd::::::ddddd::::::dd -H:::::::H H:::::::Ha:::::aaaa::::::a n::::n n::::n d:::::::::::::::::d -H:::::::H H:::::::H a::::::::::aa:::a n::::n n::::n d:::::::::ddd::::d -HHHHHHHHH HHHHHHHHH aaaaaaaaaa aaaa nnnnnn nnnnnn ddddddddd ddddd - + E:::::E dd::::::::::::::d i:::::it:::::::::::::::::t B::::B B:::::B y:::::y y:::::y + E::::::EEEEEEEEEE d::::::::::::::::d i::::it:::::::::::::::::t B::::BBBBBB:::::B y:::::y y:::::y + E:::::::::::::::E d:::::::ddddd:::::d i::::itttttt:::::::tttttt B:::::::::::::BB y:::::y y:::::y + E:::::::::::::::E d::::::d d:::::d i::::i t:::::t B::::BBBBBB:::::B y:::::y y:::::y + E::::::EEEEEEEEEE d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y y:::::y + E:::::E d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y:::::y + E:::::E EEEEEEd:::::d d:::::d i::::i t:::::t tttttt B::::B B:::::B y:::::::::y +EE::::::EEEEEEEE:::::Ed::::::ddddd::::::ddi::::::i t::::::tttt:::::t BB:::::BBBBBB::::::B y:::::::y +E::::::::::::::::::::E d:::::::::::::::::di::::::i tt::::::::::::::t B:::::::::::::::::B y:::::y +E::::::::::::::::::::E d:::::::::ddd::::di::::::i tt:::::::::::tt B::::::::::::::::B y:::::y +EEEEEEEEEEEEEEEEEEEEEE ddddddddd dddddiiiiiiii ttttttttttt BBBBBBBBBBBBBBBBB y:::::y + y:::::y + y:::::y + y:::::y + y:::::y + yyyyyyy + + + + dddddddd +HHHHHHHHH HHHHHHHHH d::::::d +H:::::::H H:::::::H d::::::d +H:::::::H H:::::::H d::::::d +HH::::::H H::::::HH d:::::d + H:::::H H:::::H aaaaaaaaaaaaa nnnn nnnnnnnn ddddddddd:::::d + H:::::H H:::::H a::::::::::::a n:::nn::::::::nn dd::::::::::::::d + H::::::HHHHH::::::H aaaaaaaaa:::::an::::::::::::::nn d::::::::::::::::d + H:::::::::::::::::H a::::ann:::::::::::::::nd:::::::ddddd:::::d + H:::::::::::::::::H aaaaaaa:::::a n:::::nnnn:::::nd::::::d d:::::d + H::::::HHHHH::::::H aa::::::::::::a n::::n n::::nd:::::d d:::::d + H:::::H H:::::H a::::aaaa::::::a n::::n n::::nd:::::d d:::::d + H:::::H H:::::H a::::a a:::::a n::::n n::::nd:::::d d:::::d +HH::::::H H::::::HHa::::a a:::::a n::::n n::::nd::::::ddddd::::::dd +H:::::::H H:::::::Ha:::::aaaa::::::a n::::n n::::n d:::::::::::::::::d +H:::::::H H:::::::H a::::::::::aa:::a n::::n n::::n d:::::::::ddd::::d +HHHHHHHHH HHHHHHHHH aaaaaaaaaa aaaa nnnnnn nnnnnn ddddddddd ddddd + """ from typing import TypeVar -import sasdata.quantities.units as units -from sasdata.data_backing import Dataset, Group from sasdata.quantities.quantity import Quantity -from sasdata.quantities.unit_parser import parse_unit -from sasdata.quantities.units import Unit - - -# logger = logging.getLogger("Accessors") -class LoggerDummy: - def info(self, data): - print(data) -logger = LoggerDummy() - -DataType = TypeVar("DataType") -OutputType = TypeVar("OutputType") - - -class AccessorTarget: - def __init__(self, data: Group, verbose=False, prefix_tokens: tuple=()): - self._data = data - self.verbose = verbose - - self.prefix_tokens = list(prefix_tokens) - - def with_path_prefix(self, path_prexix: str): - """ Get an accessor that looks at a subtree of the metadata with the supplied prefix - - For example, accessors aiming at a.b, when the target it c.d will look at c.d.a.b - """ - return AccessorTarget(self._data, - verbose=self.verbose, - prefix_tokens=tuple(self.prefix_tokens + [path_prexix])) - - def get_value(self, path: str): - - tokens = self.prefix_tokens + path.split(".") - - if self.verbose: - logger.info(f"Finding: {path}") - logger.info(f"Full path: {tokens}") - - # Navigate the tree from the entry we need - - current_tree_position: Group | Dataset = self._data - - for token in tokens: - - options = token.split("|") - - if isinstance(current_tree_position, Group): - - found = False - for option in options: - if option in current_tree_position.children: - current_tree_position = current_tree_position.children[option] - found = True - - if self.verbose: - logger.info(f"Found option: {option}") - - if not found: - if self.verbose: - logger.info(f"Failed to find any of {options} on group {current_tree_position.name}. Options: " + - ",".join([key for key in current_tree_position.children])) - return None - - elif isinstance(current_tree_position, Dataset): - - found = False - for option in options: - if option in current_tree_position.attributes: - current_tree_position = current_tree_position.attributes[option] - found = True - - if self.verbose: - logger.info(f"Found option: {option}") - - if not found: - if self.verbose: - logger.info(f"Failed to find any of {options} on attribute {current_tree_position.name}. Options: " + - ",".join([key for key in current_tree_position.attributes])) - return None - - if self.verbose: - logger.info(f"Found value: {current_tree_position}") - - return current_tree_position.data +import sasdata.quantities.units as units +T = TypeVar("T") -class Accessor[DataType, OutputType]: +class Accessor[T]: """ Base class """ - def __init__(self, target_object: AccessorTarget, value_target: str): - self.target_object = target_object - self.value_target = value_target - - @property - def value(self) -> OutputType | None: - return self.target_object.get_value(self.value_target) - -class StringAccessor(Accessor[str, str]): - """ String based fields """ - @property - def value(self) -> str | None: - return self.target_object.get_value(self.value_target) - -class FloatAccessor(Accessor[float, float]): - """ Float based fields """ - @property - def value(self) -> float | None: - return self.target_object.get_value(self.value_target) - - - - -class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): - """ Base class for accessors that work with quantities that have units """ - def __init__(self, target_object: AccessorTarget, value_target: str, unit_target: str, default_unit=units.none): - super().__init__(target_object, value_target) + def __init__(self, value_target: str, unit_target: str): + self._value_target = value_target self._unit_target = unit_target - self.default_unit = default_unit - - def _numerical_part(self) -> DataType | None: - """ Numerical part of the data """ - return self.target_object.get_value(self.value_target) - - def _unit_part(self) -> str | None: - """ String form of units for the data """ - return self.target_object.get_value(self._unit_target) - - @property - def unit(self) -> Unit: - u = self._unit_part() - if u is None: - return self.default_unit - else: - return parse_unit(u) @property - def value(self) -> Quantity[DataType] | None: - if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self._numerical_part(), self.unit) - return None + def quantity(self) -> Quantity[T]: + raise NotImplementedError("Not implemented yet") - @property - def quantity(self): - if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self._numerical_part(), self.unit) - return None - - -class LengthAccessor[T](QuantityAccessor[T]): +class LengthAccessor[T](Accessor[T]): dimension_name = 'length' - + @property def meters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters) + return self.quantity.in_units_of(units.meters) @property def exameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters) + return self.quantity.in_units_of(units.exameters) @property def petameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters) + return self.quantity.in_units_of(units.petameters) @property def terameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters) + return self.quantity.in_units_of(units.terameters) @property def gigameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters) + return self.quantity.in_units_of(units.gigameters) @property def megameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters) + return self.quantity.in_units_of(units.megameters) @property def kilometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers) + return self.quantity.in_units_of(units.kilometers) @property def millimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters) + return self.quantity.in_units_of(units.millimeters) @property def micrometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers) + return self.quantity.in_units_of(units.micrometers) @property def nanometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers) + return self.quantity.in_units_of(units.nanometers) @property def picometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers) + return self.quantity.in_units_of(units.picometers) @property def femtometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers) + return self.quantity.in_units_of(units.femtometers) @property def attometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers) + return self.quantity.in_units_of(units.attometers) @property def decimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters) + return self.quantity.in_units_of(units.decimeters) @property def centimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters) + return self.quantity.in_units_of(units.centimeters) @property def angstroms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms) - - @property - def microns(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns) - - @property - def miles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles) - - @property - def yards(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards) - - @property - def feet(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet) - - @property - def inches(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches) + return self.quantity.in_units_of(units.angstroms) -class AreaAccessor[T](QuantityAccessor[T]): +class AreaAccessor[T](Accessor[T]): dimension_name = 'area' - + @property def square_meters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_meters) + return self.quantity.in_units_of(units.square_meters) @property def square_exameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_exameters) + return self.quantity.in_units_of(units.square_exameters) @property def square_petameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_petameters) + return self.quantity.in_units_of(units.square_petameters) @property def square_terameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_terameters) + return self.quantity.in_units_of(units.square_terameters) @property def square_gigameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_gigameters) + return self.quantity.in_units_of(units.square_gigameters) @property def square_megameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_megameters) + return self.quantity.in_units_of(units.square_megameters) @property def square_kilometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_kilometers) + return self.quantity.in_units_of(units.square_kilometers) @property def square_millimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_millimeters) + return self.quantity.in_units_of(units.square_millimeters) @property def square_micrometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_micrometers) + return self.quantity.in_units_of(units.square_micrometers) @property def square_nanometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_nanometers) + return self.quantity.in_units_of(units.square_nanometers) @property def square_picometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_picometers) + return self.quantity.in_units_of(units.square_picometers) @property def square_femtometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_femtometers) + return self.quantity.in_units_of(units.square_femtometers) @property def square_attometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_attometers) + return self.quantity.in_units_of(units.square_attometers) @property def square_decimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_decimeters) + return self.quantity.in_units_of(units.square_decimeters) @property def square_centimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_centimeters) + return self.quantity.in_units_of(units.square_centimeters) @property def square_angstroms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_angstroms) - - @property - def square_microns(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_microns) - - @property - def square_miles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_miles) - - @property - def square_yards(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_yards) - - @property - def square_feet(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_feet) - - @property - def square_inches(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_inches) + return self.quantity.in_units_of(units.square_angstroms) -class VolumeAccessor[T](QuantityAccessor[T]): +class VolumeAccessor[T](Accessor[T]): dimension_name = 'volume' - + @property def litres(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.litres) + return self.quantity.in_units_of(units.litres) @property def cubic_meters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_meters) + return self.quantity.in_units_of(units.cubic_meters) @property def cubic_exameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_exameters) + return self.quantity.in_units_of(units.cubic_exameters) @property def cubic_petameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_petameters) + return self.quantity.in_units_of(units.cubic_petameters) @property def cubic_terameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_terameters) + return self.quantity.in_units_of(units.cubic_terameters) @property def cubic_gigameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_gigameters) + return self.quantity.in_units_of(units.cubic_gigameters) @property def cubic_megameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_megameters) + return self.quantity.in_units_of(units.cubic_megameters) @property def cubic_kilometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_kilometers) + return self.quantity.in_units_of(units.cubic_kilometers) @property def cubic_millimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_millimeters) + return self.quantity.in_units_of(units.cubic_millimeters) @property def cubic_micrometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_micrometers) + return self.quantity.in_units_of(units.cubic_micrometers) @property def cubic_nanometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_nanometers) + return self.quantity.in_units_of(units.cubic_nanometers) @property def cubic_picometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_picometers) + return self.quantity.in_units_of(units.cubic_picometers) @property def cubic_femtometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_femtometers) + return self.quantity.in_units_of(units.cubic_femtometers) @property def cubic_attometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_attometers) + return self.quantity.in_units_of(units.cubic_attometers) @property def cubic_decimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_decimeters) + return self.quantity.in_units_of(units.cubic_decimeters) @property def cubic_centimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_centimeters) + return self.quantity.in_units_of(units.cubic_centimeters) @property def cubic_angstroms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_angstroms) - - @property - def cubic_microns(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_microns) - - @property - def cubic_miles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_miles) - - @property - def cubic_yards(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_yards) - - @property - def cubic_feet(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_feet) - - @property - def cubic_inches(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_inches) + return self.quantity.in_units_of(units.cubic_angstroms) -class InverselengthAccessor[T](QuantityAccessor[T]): +class InverselengthAccessor[T](Accessor[T]): dimension_name = 'inverse_length' - + @property def per_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_meter) + return self.quantity.in_units_of(units.per_meter) @property def per_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_exameter) + return self.quantity.in_units_of(units.per_exameter) @property def per_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_petameter) + return self.quantity.in_units_of(units.per_petameter) @property def per_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_terameter) + return self.quantity.in_units_of(units.per_terameter) @property def per_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_gigameter) + return self.quantity.in_units_of(units.per_gigameter) @property def per_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_megameter) + return self.quantity.in_units_of(units.per_megameter) @property def per_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_kilometer) + return self.quantity.in_units_of(units.per_kilometer) @property def per_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_millimeter) + return self.quantity.in_units_of(units.per_millimeter) @property def per_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_micrometer) + return self.quantity.in_units_of(units.per_micrometer) @property def per_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_nanometer) + return self.quantity.in_units_of(units.per_nanometer) @property def per_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_picometer) + return self.quantity.in_units_of(units.per_picometer) @property def per_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_femtometer) + return self.quantity.in_units_of(units.per_femtometer) @property def per_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_attometer) + return self.quantity.in_units_of(units.per_attometer) @property def per_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_decimeter) + return self.quantity.in_units_of(units.per_decimeter) @property def per_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_centimeter) + return self.quantity.in_units_of(units.per_centimeter) @property def per_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_angstrom) - - @property - def per_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_micron) - - @property - def per_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_mile) - - @property - def per_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_yard) - - @property - def per_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_foot) - - @property - def per_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_inch) + return self.quantity.in_units_of(units.per_angstrom) -class InverseareaAccessor[T](QuantityAccessor[T]): +class InverseareaAccessor[T](Accessor[T]): dimension_name = 'inverse_area' - + @property def per_square_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_meter) + return self.quantity.in_units_of(units.per_square_meter) @property def per_square_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_exameter) + return self.quantity.in_units_of(units.per_square_exameter) @property def per_square_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_petameter) + return self.quantity.in_units_of(units.per_square_petameter) @property def per_square_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_terameter) + return self.quantity.in_units_of(units.per_square_terameter) @property def per_square_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_gigameter) + return self.quantity.in_units_of(units.per_square_gigameter) @property def per_square_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_megameter) + return self.quantity.in_units_of(units.per_square_megameter) @property def per_square_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_kilometer) + return self.quantity.in_units_of(units.per_square_kilometer) @property def per_square_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_millimeter) + return self.quantity.in_units_of(units.per_square_millimeter) @property def per_square_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_micrometer) + return self.quantity.in_units_of(units.per_square_micrometer) @property def per_square_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_nanometer) + return self.quantity.in_units_of(units.per_square_nanometer) @property def per_square_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_picometer) + return self.quantity.in_units_of(units.per_square_picometer) @property def per_square_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_femtometer) + return self.quantity.in_units_of(units.per_square_femtometer) @property def per_square_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_attometer) + return self.quantity.in_units_of(units.per_square_attometer) @property def per_square_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_decimeter) + return self.quantity.in_units_of(units.per_square_decimeter) @property def per_square_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_centimeter) + return self.quantity.in_units_of(units.per_square_centimeter) @property def per_square_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_angstrom) - - @property - def per_square_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_micron) - - @property - def per_square_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_mile) - - @property - def per_square_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_yard) - - @property - def per_square_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_foot) - - @property - def per_square_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_inch) + return self.quantity.in_units_of(units.per_square_angstrom) -class InversevolumeAccessor[T](QuantityAccessor[T]): +class InversevolumeAccessor[T](Accessor[T]): dimension_name = 'inverse_volume' - + @property def per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_meter) + return self.quantity.in_units_of(units.per_cubic_meter) @property def per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_exameter) + return self.quantity.in_units_of(units.per_cubic_exameter) @property def per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_petameter) + return self.quantity.in_units_of(units.per_cubic_petameter) @property def per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_terameter) + return self.quantity.in_units_of(units.per_cubic_terameter) @property def per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_gigameter) + return self.quantity.in_units_of(units.per_cubic_gigameter) @property def per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_megameter) + return self.quantity.in_units_of(units.per_cubic_megameter) @property def per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_kilometer) + return self.quantity.in_units_of(units.per_cubic_kilometer) @property def per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_millimeter) + return self.quantity.in_units_of(units.per_cubic_millimeter) @property def per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_micrometer) + return self.quantity.in_units_of(units.per_cubic_micrometer) @property def per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_nanometer) + return self.quantity.in_units_of(units.per_cubic_nanometer) @property def per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_picometer) + return self.quantity.in_units_of(units.per_cubic_picometer) @property def per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_femtometer) + return self.quantity.in_units_of(units.per_cubic_femtometer) @property def per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_attometer) + return self.quantity.in_units_of(units.per_cubic_attometer) @property def per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_decimeter) + return self.quantity.in_units_of(units.per_cubic_decimeter) @property def per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_centimeter) + return self.quantity.in_units_of(units.per_cubic_centimeter) @property def per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_angstrom) - - @property - def per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_micron) - - @property - def per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_mile) - - @property - def per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_yard) - - @property - def per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_foot) - - @property - def per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_inch) + return self.quantity.in_units_of(units.per_cubic_angstrom) -class TimeAccessor[T](QuantityAccessor[T]): +class TimeAccessor[T](Accessor[T]): dimension_name = 'time' - + @property def seconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.seconds) + return self.quantity.in_units_of(units.seconds) @property def milliseconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milliseconds) + return self.quantity.in_units_of(units.milliseconds) @property def microseconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microseconds) + return self.quantity.in_units_of(units.microseconds) @property def nanoseconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanoseconds) + return self.quantity.in_units_of(units.nanoseconds) @property def picoseconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picoseconds) + return self.quantity.in_units_of(units.picoseconds) @property def femtoseconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtoseconds) + return self.quantity.in_units_of(units.femtoseconds) @property def attoseconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attoseconds) + return self.quantity.in_units_of(units.attoseconds) @property def minutes(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.minutes) + return self.quantity.in_units_of(units.minutes) @property def hours(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.hours) + return self.quantity.in_units_of(units.hours) @property def days(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.days) + return self.quantity.in_units_of(units.days) @property def years(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.years) + return self.quantity.in_units_of(units.years) -class RateAccessor[T](QuantityAccessor[T]): +class RateAccessor[T](Accessor[T]): dimension_name = 'rate' - + @property def hertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.hertz) + return self.quantity.in_units_of(units.hertz) @property def exahertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exahertz) + return self.quantity.in_units_of(units.exahertz) @property def petahertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petahertz) + return self.quantity.in_units_of(units.petahertz) @property def terahertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terahertz) + return self.quantity.in_units_of(units.terahertz) @property def gigahertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigahertz) + return self.quantity.in_units_of(units.gigahertz) @property def megahertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megahertz) + return self.quantity.in_units_of(units.megahertz) @property def kilohertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilohertz) + return self.quantity.in_units_of(units.kilohertz) @property def millihertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millihertz) + return self.quantity.in_units_of(units.millihertz) @property def microhertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microhertz) + return self.quantity.in_units_of(units.microhertz) @property def nanohertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanohertz) + return self.quantity.in_units_of(units.nanohertz) @property def picohertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picohertz) + return self.quantity.in_units_of(units.picohertz) @property def femtohertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtohertz) + return self.quantity.in_units_of(units.femtohertz) @property def attohertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attohertz) - - @property - def revolutions_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.revolutions_per_minute) + return self.quantity.in_units_of(units.attohertz) -class SpeedAccessor[T](QuantityAccessor[T]): +class SpeedAccessor[T](Accessor[T]): dimension_name = 'speed' - + @property def meters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_second) + return self.quantity.in_units_of(units.meters_per_second) @property def meters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_millisecond) + return self.quantity.in_units_of(units.meters_per_millisecond) @property def meters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_microsecond) + return self.quantity.in_units_of(units.meters_per_microsecond) @property def meters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_nanosecond) + return self.quantity.in_units_of(units.meters_per_nanosecond) @property def meters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_picosecond) + return self.quantity.in_units_of(units.meters_per_picosecond) @property def meters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_femtosecond) + return self.quantity.in_units_of(units.meters_per_femtosecond) @property def meters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_attosecond) + return self.quantity.in_units_of(units.meters_per_attosecond) @property def meters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_minute) + return self.quantity.in_units_of(units.meters_per_minute) @property def meters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_hour) + return self.quantity.in_units_of(units.meters_per_hour) @property def meters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_day) + return self.quantity.in_units_of(units.meters_per_day) @property def meters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_year) + return self.quantity.in_units_of(units.meters_per_year) @property def exameters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_second) + return self.quantity.in_units_of(units.exameters_per_second) @property def exameters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_millisecond) + return self.quantity.in_units_of(units.exameters_per_millisecond) @property def exameters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_microsecond) + return self.quantity.in_units_of(units.exameters_per_microsecond) @property def exameters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_nanosecond) + return self.quantity.in_units_of(units.exameters_per_nanosecond) @property def exameters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_picosecond) + return self.quantity.in_units_of(units.exameters_per_picosecond) @property def exameters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_femtosecond) + return self.quantity.in_units_of(units.exameters_per_femtosecond) @property def exameters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_attosecond) + return self.quantity.in_units_of(units.exameters_per_attosecond) @property def exameters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_minute) + return self.quantity.in_units_of(units.exameters_per_minute) @property def exameters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_hour) + return self.quantity.in_units_of(units.exameters_per_hour) @property def exameters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_day) + return self.quantity.in_units_of(units.exameters_per_day) @property def exameters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_year) + return self.quantity.in_units_of(units.exameters_per_year) @property def petameters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_second) + return self.quantity.in_units_of(units.petameters_per_second) @property def petameters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_millisecond) + return self.quantity.in_units_of(units.petameters_per_millisecond) @property def petameters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_microsecond) + return self.quantity.in_units_of(units.petameters_per_microsecond) @property def petameters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_nanosecond) + return self.quantity.in_units_of(units.petameters_per_nanosecond) @property def petameters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_picosecond) + return self.quantity.in_units_of(units.petameters_per_picosecond) @property def petameters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_femtosecond) + return self.quantity.in_units_of(units.petameters_per_femtosecond) @property def petameters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_attosecond) + return self.quantity.in_units_of(units.petameters_per_attosecond) @property def petameters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_minute) + return self.quantity.in_units_of(units.petameters_per_minute) @property def petameters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_hour) + return self.quantity.in_units_of(units.petameters_per_hour) @property def petameters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_day) + return self.quantity.in_units_of(units.petameters_per_day) @property def petameters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_year) + return self.quantity.in_units_of(units.petameters_per_year) @property def terameters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_second) + return self.quantity.in_units_of(units.terameters_per_second) @property def terameters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_millisecond) + return self.quantity.in_units_of(units.terameters_per_millisecond) @property def terameters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_microsecond) + return self.quantity.in_units_of(units.terameters_per_microsecond) @property def terameters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_nanosecond) + return self.quantity.in_units_of(units.terameters_per_nanosecond) @property def terameters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_picosecond) + return self.quantity.in_units_of(units.terameters_per_picosecond) @property def terameters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_femtosecond) + return self.quantity.in_units_of(units.terameters_per_femtosecond) @property def terameters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_attosecond) + return self.quantity.in_units_of(units.terameters_per_attosecond) @property def terameters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_minute) + return self.quantity.in_units_of(units.terameters_per_minute) @property def terameters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_hour) + return self.quantity.in_units_of(units.terameters_per_hour) @property def terameters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_day) + return self.quantity.in_units_of(units.terameters_per_day) @property def terameters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_year) + return self.quantity.in_units_of(units.terameters_per_year) @property def gigameters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_second) + return self.quantity.in_units_of(units.gigameters_per_second) @property def gigameters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_millisecond) + return self.quantity.in_units_of(units.gigameters_per_millisecond) @property def gigameters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_microsecond) + return self.quantity.in_units_of(units.gigameters_per_microsecond) @property def gigameters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_nanosecond) + return self.quantity.in_units_of(units.gigameters_per_nanosecond) @property def gigameters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_picosecond) + return self.quantity.in_units_of(units.gigameters_per_picosecond) @property def gigameters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_femtosecond) + return self.quantity.in_units_of(units.gigameters_per_femtosecond) @property def gigameters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_attosecond) + return self.quantity.in_units_of(units.gigameters_per_attosecond) @property def gigameters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_minute) + return self.quantity.in_units_of(units.gigameters_per_minute) @property def gigameters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_hour) + return self.quantity.in_units_of(units.gigameters_per_hour) @property def gigameters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_day) + return self.quantity.in_units_of(units.gigameters_per_day) @property def gigameters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_year) + return self.quantity.in_units_of(units.gigameters_per_year) @property def megameters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_second) + return self.quantity.in_units_of(units.megameters_per_second) @property def megameters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_millisecond) + return self.quantity.in_units_of(units.megameters_per_millisecond) @property def megameters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_microsecond) + return self.quantity.in_units_of(units.megameters_per_microsecond) @property def megameters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_nanosecond) + return self.quantity.in_units_of(units.megameters_per_nanosecond) @property def megameters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_picosecond) + return self.quantity.in_units_of(units.megameters_per_picosecond) @property def megameters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_femtosecond) + return self.quantity.in_units_of(units.megameters_per_femtosecond) @property def megameters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_attosecond) + return self.quantity.in_units_of(units.megameters_per_attosecond) @property def megameters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_minute) + return self.quantity.in_units_of(units.megameters_per_minute) @property def megameters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_hour) + return self.quantity.in_units_of(units.megameters_per_hour) @property def megameters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_day) + return self.quantity.in_units_of(units.megameters_per_day) @property def megameters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_year) + return self.quantity.in_units_of(units.megameters_per_year) @property def kilometers_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_second) + return self.quantity.in_units_of(units.kilometers_per_second) @property def kilometers_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_millisecond) + return self.quantity.in_units_of(units.kilometers_per_millisecond) @property def kilometers_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_microsecond) + return self.quantity.in_units_of(units.kilometers_per_microsecond) @property def kilometers_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_nanosecond) + return self.quantity.in_units_of(units.kilometers_per_nanosecond) @property def kilometers_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_picosecond) + return self.quantity.in_units_of(units.kilometers_per_picosecond) @property def kilometers_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_femtosecond) + return self.quantity.in_units_of(units.kilometers_per_femtosecond) @property def kilometers_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_attosecond) + return self.quantity.in_units_of(units.kilometers_per_attosecond) @property def kilometers_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_minute) + return self.quantity.in_units_of(units.kilometers_per_minute) @property def kilometers_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_hour) + return self.quantity.in_units_of(units.kilometers_per_hour) @property def kilometers_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_day) + return self.quantity.in_units_of(units.kilometers_per_day) @property def kilometers_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_year) + return self.quantity.in_units_of(units.kilometers_per_year) @property def millimeters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_second) + return self.quantity.in_units_of(units.millimeters_per_second) @property def millimeters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_millisecond) + return self.quantity.in_units_of(units.millimeters_per_millisecond) @property def millimeters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_microsecond) + return self.quantity.in_units_of(units.millimeters_per_microsecond) @property def millimeters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_nanosecond) + return self.quantity.in_units_of(units.millimeters_per_nanosecond) @property def millimeters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_picosecond) + return self.quantity.in_units_of(units.millimeters_per_picosecond) @property def millimeters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_femtosecond) + return self.quantity.in_units_of(units.millimeters_per_femtosecond) @property def millimeters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_attosecond) + return self.quantity.in_units_of(units.millimeters_per_attosecond) @property def millimeters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_minute) + return self.quantity.in_units_of(units.millimeters_per_minute) @property def millimeters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_hour) + return self.quantity.in_units_of(units.millimeters_per_hour) @property def millimeters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_day) + return self.quantity.in_units_of(units.millimeters_per_day) @property def millimeters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_year) + return self.quantity.in_units_of(units.millimeters_per_year) @property def micrometers_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_second) + return self.quantity.in_units_of(units.micrometers_per_second) @property def micrometers_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_millisecond) + return self.quantity.in_units_of(units.micrometers_per_millisecond) @property def micrometers_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_microsecond) + return self.quantity.in_units_of(units.micrometers_per_microsecond) @property def micrometers_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_nanosecond) + return self.quantity.in_units_of(units.micrometers_per_nanosecond) @property def micrometers_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_picosecond) + return self.quantity.in_units_of(units.micrometers_per_picosecond) @property def micrometers_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_femtosecond) + return self.quantity.in_units_of(units.micrometers_per_femtosecond) @property def micrometers_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_attosecond) + return self.quantity.in_units_of(units.micrometers_per_attosecond) @property def micrometers_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_minute) + return self.quantity.in_units_of(units.micrometers_per_minute) @property def micrometers_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_hour) + return self.quantity.in_units_of(units.micrometers_per_hour) @property def micrometers_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_day) + return self.quantity.in_units_of(units.micrometers_per_day) @property def micrometers_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_year) + return self.quantity.in_units_of(units.micrometers_per_year) @property def nanometers_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_second) + return self.quantity.in_units_of(units.nanometers_per_second) @property def nanometers_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_millisecond) + return self.quantity.in_units_of(units.nanometers_per_millisecond) @property def nanometers_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_microsecond) + return self.quantity.in_units_of(units.nanometers_per_microsecond) @property def nanometers_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_nanosecond) + return self.quantity.in_units_of(units.nanometers_per_nanosecond) @property def nanometers_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_picosecond) + return self.quantity.in_units_of(units.nanometers_per_picosecond) @property def nanometers_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_femtosecond) + return self.quantity.in_units_of(units.nanometers_per_femtosecond) @property def nanometers_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_attosecond) + return self.quantity.in_units_of(units.nanometers_per_attosecond) @property def nanometers_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_minute) + return self.quantity.in_units_of(units.nanometers_per_minute) @property def nanometers_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_hour) + return self.quantity.in_units_of(units.nanometers_per_hour) @property def nanometers_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_day) + return self.quantity.in_units_of(units.nanometers_per_day) @property def nanometers_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_year) + return self.quantity.in_units_of(units.nanometers_per_year) @property def picometers_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_second) + return self.quantity.in_units_of(units.picometers_per_second) @property def picometers_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_millisecond) + return self.quantity.in_units_of(units.picometers_per_millisecond) @property def picometers_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_microsecond) + return self.quantity.in_units_of(units.picometers_per_microsecond) @property def picometers_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_nanosecond) + return self.quantity.in_units_of(units.picometers_per_nanosecond) @property def picometers_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_picosecond) + return self.quantity.in_units_of(units.picometers_per_picosecond) @property def picometers_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_femtosecond) + return self.quantity.in_units_of(units.picometers_per_femtosecond) @property def picometers_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_attosecond) + return self.quantity.in_units_of(units.picometers_per_attosecond) @property def picometers_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_minute) + return self.quantity.in_units_of(units.picometers_per_minute) @property def picometers_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_hour) + return self.quantity.in_units_of(units.picometers_per_hour) @property def picometers_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_day) + return self.quantity.in_units_of(units.picometers_per_day) @property def picometers_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_year) + return self.quantity.in_units_of(units.picometers_per_year) @property def femtometers_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_second) + return self.quantity.in_units_of(units.femtometers_per_second) @property def femtometers_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_millisecond) + return self.quantity.in_units_of(units.femtometers_per_millisecond) @property def femtometers_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_microsecond) + return self.quantity.in_units_of(units.femtometers_per_microsecond) @property def femtometers_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_nanosecond) + return self.quantity.in_units_of(units.femtometers_per_nanosecond) @property def femtometers_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_picosecond) + return self.quantity.in_units_of(units.femtometers_per_picosecond) @property def femtometers_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_femtosecond) + return self.quantity.in_units_of(units.femtometers_per_femtosecond) @property def femtometers_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_attosecond) + return self.quantity.in_units_of(units.femtometers_per_attosecond) @property def femtometers_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_minute) + return self.quantity.in_units_of(units.femtometers_per_minute) @property def femtometers_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_hour) + return self.quantity.in_units_of(units.femtometers_per_hour) @property def femtometers_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_day) + return self.quantity.in_units_of(units.femtometers_per_day) @property def femtometers_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_year) + return self.quantity.in_units_of(units.femtometers_per_year) @property def attometers_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_second) + return self.quantity.in_units_of(units.attometers_per_second) @property def attometers_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_millisecond) + return self.quantity.in_units_of(units.attometers_per_millisecond) @property def attometers_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_microsecond) + return self.quantity.in_units_of(units.attometers_per_microsecond) @property def attometers_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_nanosecond) + return self.quantity.in_units_of(units.attometers_per_nanosecond) @property def attometers_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_picosecond) + return self.quantity.in_units_of(units.attometers_per_picosecond) @property def attometers_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_femtosecond) + return self.quantity.in_units_of(units.attometers_per_femtosecond) @property def attometers_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_attosecond) + return self.quantity.in_units_of(units.attometers_per_attosecond) @property def attometers_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_minute) + return self.quantity.in_units_of(units.attometers_per_minute) @property def attometers_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_hour) + return self.quantity.in_units_of(units.attometers_per_hour) @property def attometers_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_day) + return self.quantity.in_units_of(units.attometers_per_day) @property def attometers_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_year) + return self.quantity.in_units_of(units.attometers_per_year) @property def decimeters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_second) + return self.quantity.in_units_of(units.decimeters_per_second) @property def decimeters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_millisecond) + return self.quantity.in_units_of(units.decimeters_per_millisecond) @property def decimeters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_microsecond) + return self.quantity.in_units_of(units.decimeters_per_microsecond) @property def decimeters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_nanosecond) + return self.quantity.in_units_of(units.decimeters_per_nanosecond) @property def decimeters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_picosecond) + return self.quantity.in_units_of(units.decimeters_per_picosecond) @property def decimeters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_femtosecond) + return self.quantity.in_units_of(units.decimeters_per_femtosecond) @property def decimeters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_attosecond) + return self.quantity.in_units_of(units.decimeters_per_attosecond) @property def decimeters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_minute) + return self.quantity.in_units_of(units.decimeters_per_minute) @property def decimeters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_hour) + return self.quantity.in_units_of(units.decimeters_per_hour) @property def decimeters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_day) + return self.quantity.in_units_of(units.decimeters_per_day) @property def decimeters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_year) + return self.quantity.in_units_of(units.decimeters_per_year) @property def centimeters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_second) + return self.quantity.in_units_of(units.centimeters_per_second) @property def centimeters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_millisecond) + return self.quantity.in_units_of(units.centimeters_per_millisecond) @property def centimeters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_microsecond) + return self.quantity.in_units_of(units.centimeters_per_microsecond) @property def centimeters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_nanosecond) + return self.quantity.in_units_of(units.centimeters_per_nanosecond) @property def centimeters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_picosecond) + return self.quantity.in_units_of(units.centimeters_per_picosecond) @property def centimeters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_femtosecond) + return self.quantity.in_units_of(units.centimeters_per_femtosecond) @property def centimeters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_attosecond) + return self.quantity.in_units_of(units.centimeters_per_attosecond) @property def centimeters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_minute) + return self.quantity.in_units_of(units.centimeters_per_minute) @property def centimeters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_hour) + return self.quantity.in_units_of(units.centimeters_per_hour) @property def centimeters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_day) + return self.quantity.in_units_of(units.centimeters_per_day) @property def centimeters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_year) + return self.quantity.in_units_of(units.centimeters_per_year) @property def angstroms_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_second) + return self.quantity.in_units_of(units.angstroms_per_second) @property def angstroms_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_millisecond) + return self.quantity.in_units_of(units.angstroms_per_millisecond) @property def angstroms_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_microsecond) + return self.quantity.in_units_of(units.angstroms_per_microsecond) @property def angstroms_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_nanosecond) + return self.quantity.in_units_of(units.angstroms_per_nanosecond) @property def angstroms_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_picosecond) + return self.quantity.in_units_of(units.angstroms_per_picosecond) @property def angstroms_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_femtosecond) + return self.quantity.in_units_of(units.angstroms_per_femtosecond) @property def angstroms_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_attosecond) + return self.quantity.in_units_of(units.angstroms_per_attosecond) @property def angstroms_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_minute) + return self.quantity.in_units_of(units.angstroms_per_minute) @property def angstroms_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_hour) + return self.quantity.in_units_of(units.angstroms_per_hour) @property def angstroms_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_day) + return self.quantity.in_units_of(units.angstroms_per_day) @property def angstroms_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_year) - - @property - def microns_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_second) - - @property - def microns_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_millisecond) - - @property - def microns_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_microsecond) - - @property - def microns_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_nanosecond) - - @property - def microns_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_picosecond) - - @property - def microns_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_femtosecond) - - @property - def microns_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_attosecond) - - @property - def microns_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_minute) - - @property - def microns_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_hour) - - @property - def microns_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_day) - - @property - def microns_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_year) - - @property - def miles_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_second) - - @property - def miles_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_millisecond) - - @property - def miles_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_microsecond) - - @property - def miles_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_nanosecond) - - @property - def miles_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_picosecond) - - @property - def miles_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_femtosecond) - - @property - def miles_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_attosecond) - - @property - def miles_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_minute) - - @property - def miles_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_hour) - - @property - def miles_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_day) - - @property - def miles_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_year) - - @property - def yards_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_second) - - @property - def yards_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_millisecond) - - @property - def yards_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_microsecond) - - @property - def yards_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_nanosecond) - - @property - def yards_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_picosecond) - - @property - def yards_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_femtosecond) - - @property - def yards_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_attosecond) - - @property - def yards_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_minute) - - @property - def yards_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_hour) - - @property - def yards_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_day) - - @property - def yards_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_year) - - @property - def feet_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_second) - - @property - def feet_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_millisecond) - - @property - def feet_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_microsecond) - - @property - def feet_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_nanosecond) - - @property - def feet_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_picosecond) - - @property - def feet_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_femtosecond) - - @property - def feet_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_attosecond) - - @property - def feet_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_minute) - - @property - def feet_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_hour) - - @property - def feet_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_day) - - @property - def feet_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_year) - - @property - def inches_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_second) - - @property - def inches_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_millisecond) - - @property - def inches_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_microsecond) - - @property - def inches_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_nanosecond) - - @property - def inches_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_picosecond) - - @property - def inches_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_femtosecond) - - @property - def inches_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_attosecond) - - @property - def inches_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_minute) - - @property - def inches_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_hour) - - @property - def inches_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_day) - - @property - def inches_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_year) + return self.quantity.in_units_of(units.angstroms_per_year) -class AccelerationAccessor[T](QuantityAccessor[T]): +class AccelerationAccessor[T](Accessor[T]): dimension_name = 'acceleration' - + @property def meters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_second) + return self.quantity.in_units_of(units.meters_per_square_second) @property def meters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_millisecond) + return self.quantity.in_units_of(units.meters_per_square_millisecond) @property def meters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_microsecond) + return self.quantity.in_units_of(units.meters_per_square_microsecond) @property def meters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_nanosecond) + return self.quantity.in_units_of(units.meters_per_square_nanosecond) @property def meters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_picosecond) + return self.quantity.in_units_of(units.meters_per_square_picosecond) @property def meters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_femtosecond) + return self.quantity.in_units_of(units.meters_per_square_femtosecond) @property def meters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_attosecond) + return self.quantity.in_units_of(units.meters_per_square_attosecond) @property def meters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_minute) + return self.quantity.in_units_of(units.meters_per_square_minute) @property def meters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_hour) + return self.quantity.in_units_of(units.meters_per_square_hour) @property def meters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_day) + return self.quantity.in_units_of(units.meters_per_square_day) @property def meters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_year) + return self.quantity.in_units_of(units.meters_per_square_year) @property def exameters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_second) + return self.quantity.in_units_of(units.exameters_per_square_second) @property def exameters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_millisecond) + return self.quantity.in_units_of(units.exameters_per_square_millisecond) @property def exameters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_microsecond) + return self.quantity.in_units_of(units.exameters_per_square_microsecond) @property def exameters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_nanosecond) + return self.quantity.in_units_of(units.exameters_per_square_nanosecond) @property def exameters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_picosecond) + return self.quantity.in_units_of(units.exameters_per_square_picosecond) @property def exameters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_femtosecond) + return self.quantity.in_units_of(units.exameters_per_square_femtosecond) @property def exameters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_attosecond) + return self.quantity.in_units_of(units.exameters_per_square_attosecond) @property def exameters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_minute) + return self.quantity.in_units_of(units.exameters_per_square_minute) @property def exameters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_hour) + return self.quantity.in_units_of(units.exameters_per_square_hour) @property def exameters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_day) + return self.quantity.in_units_of(units.exameters_per_square_day) @property def exameters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_year) + return self.quantity.in_units_of(units.exameters_per_square_year) @property def petameters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_second) + return self.quantity.in_units_of(units.petameters_per_square_second) @property def petameters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_millisecond) + return self.quantity.in_units_of(units.petameters_per_square_millisecond) @property def petameters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_microsecond) + return self.quantity.in_units_of(units.petameters_per_square_microsecond) @property def petameters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_nanosecond) + return self.quantity.in_units_of(units.petameters_per_square_nanosecond) @property def petameters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_picosecond) + return self.quantity.in_units_of(units.petameters_per_square_picosecond) @property def petameters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_femtosecond) + return self.quantity.in_units_of(units.petameters_per_square_femtosecond) @property def petameters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_attosecond) + return self.quantity.in_units_of(units.petameters_per_square_attosecond) @property def petameters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_minute) + return self.quantity.in_units_of(units.petameters_per_square_minute) @property def petameters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_hour) + return self.quantity.in_units_of(units.petameters_per_square_hour) @property def petameters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_day) + return self.quantity.in_units_of(units.petameters_per_square_day) @property def petameters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_year) + return self.quantity.in_units_of(units.petameters_per_square_year) @property def terameters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_second) + return self.quantity.in_units_of(units.terameters_per_square_second) @property def terameters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_millisecond) + return self.quantity.in_units_of(units.terameters_per_square_millisecond) @property def terameters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_microsecond) + return self.quantity.in_units_of(units.terameters_per_square_microsecond) @property def terameters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_nanosecond) + return self.quantity.in_units_of(units.terameters_per_square_nanosecond) @property def terameters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_picosecond) + return self.quantity.in_units_of(units.terameters_per_square_picosecond) @property def terameters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_femtosecond) + return self.quantity.in_units_of(units.terameters_per_square_femtosecond) @property def terameters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_attosecond) + return self.quantity.in_units_of(units.terameters_per_square_attosecond) @property def terameters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_minute) + return self.quantity.in_units_of(units.terameters_per_square_minute) @property def terameters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_hour) + return self.quantity.in_units_of(units.terameters_per_square_hour) @property def terameters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_day) + return self.quantity.in_units_of(units.terameters_per_square_day) @property def terameters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_year) + return self.quantity.in_units_of(units.terameters_per_square_year) @property def gigameters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_second) + return self.quantity.in_units_of(units.gigameters_per_square_second) @property def gigameters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_millisecond) + return self.quantity.in_units_of(units.gigameters_per_square_millisecond) @property def gigameters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_microsecond) + return self.quantity.in_units_of(units.gigameters_per_square_microsecond) @property def gigameters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_nanosecond) + return self.quantity.in_units_of(units.gigameters_per_square_nanosecond) @property def gigameters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_picosecond) + return self.quantity.in_units_of(units.gigameters_per_square_picosecond) @property def gigameters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_femtosecond) + return self.quantity.in_units_of(units.gigameters_per_square_femtosecond) @property def gigameters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_attosecond) + return self.quantity.in_units_of(units.gigameters_per_square_attosecond) @property def gigameters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_minute) + return self.quantity.in_units_of(units.gigameters_per_square_minute) @property def gigameters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_hour) + return self.quantity.in_units_of(units.gigameters_per_square_hour) @property def gigameters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_day) + return self.quantity.in_units_of(units.gigameters_per_square_day) @property def gigameters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_year) + return self.quantity.in_units_of(units.gigameters_per_square_year) @property def megameters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_second) + return self.quantity.in_units_of(units.megameters_per_square_second) @property def megameters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_millisecond) + return self.quantity.in_units_of(units.megameters_per_square_millisecond) @property def megameters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_microsecond) + return self.quantity.in_units_of(units.megameters_per_square_microsecond) @property def megameters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_nanosecond) + return self.quantity.in_units_of(units.megameters_per_square_nanosecond) @property def megameters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_picosecond) + return self.quantity.in_units_of(units.megameters_per_square_picosecond) @property def megameters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_femtosecond) + return self.quantity.in_units_of(units.megameters_per_square_femtosecond) @property def megameters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_attosecond) + return self.quantity.in_units_of(units.megameters_per_square_attosecond) @property def megameters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_minute) + return self.quantity.in_units_of(units.megameters_per_square_minute) @property def megameters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_hour) + return self.quantity.in_units_of(units.megameters_per_square_hour) @property def megameters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_day) + return self.quantity.in_units_of(units.megameters_per_square_day) @property def megameters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_year) + return self.quantity.in_units_of(units.megameters_per_square_year) @property def kilometers_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_second) + return self.quantity.in_units_of(units.kilometers_per_square_second) @property def kilometers_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_millisecond) + return self.quantity.in_units_of(units.kilometers_per_square_millisecond) @property def kilometers_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_microsecond) + return self.quantity.in_units_of(units.kilometers_per_square_microsecond) @property def kilometers_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_nanosecond) + return self.quantity.in_units_of(units.kilometers_per_square_nanosecond) @property def kilometers_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_picosecond) + return self.quantity.in_units_of(units.kilometers_per_square_picosecond) @property def kilometers_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_femtosecond) + return self.quantity.in_units_of(units.kilometers_per_square_femtosecond) @property def kilometers_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_attosecond) + return self.quantity.in_units_of(units.kilometers_per_square_attosecond) @property def kilometers_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_minute) + return self.quantity.in_units_of(units.kilometers_per_square_minute) @property def kilometers_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_hour) + return self.quantity.in_units_of(units.kilometers_per_square_hour) @property def kilometers_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_day) + return self.quantity.in_units_of(units.kilometers_per_square_day) @property def kilometers_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_year) + return self.quantity.in_units_of(units.kilometers_per_square_year) @property def millimeters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_second) + return self.quantity.in_units_of(units.millimeters_per_square_second) @property def millimeters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_millisecond) + return self.quantity.in_units_of(units.millimeters_per_square_millisecond) @property def millimeters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_microsecond) + return self.quantity.in_units_of(units.millimeters_per_square_microsecond) @property def millimeters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_nanosecond) + return self.quantity.in_units_of(units.millimeters_per_square_nanosecond) @property def millimeters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_picosecond) + return self.quantity.in_units_of(units.millimeters_per_square_picosecond) @property def millimeters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_femtosecond) + return self.quantity.in_units_of(units.millimeters_per_square_femtosecond) @property def millimeters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_attosecond) + return self.quantity.in_units_of(units.millimeters_per_square_attosecond) @property def millimeters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_minute) + return self.quantity.in_units_of(units.millimeters_per_square_minute) @property def millimeters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_hour) + return self.quantity.in_units_of(units.millimeters_per_square_hour) @property def millimeters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_day) + return self.quantity.in_units_of(units.millimeters_per_square_day) @property def millimeters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_year) + return self.quantity.in_units_of(units.millimeters_per_square_year) @property def micrometers_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_second) + return self.quantity.in_units_of(units.micrometers_per_square_second) @property def micrometers_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_millisecond) + return self.quantity.in_units_of(units.micrometers_per_square_millisecond) @property def micrometers_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_microsecond) + return self.quantity.in_units_of(units.micrometers_per_square_microsecond) @property def micrometers_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_nanosecond) + return self.quantity.in_units_of(units.micrometers_per_square_nanosecond) @property def micrometers_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_picosecond) + return self.quantity.in_units_of(units.micrometers_per_square_picosecond) @property def micrometers_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_femtosecond) + return self.quantity.in_units_of(units.micrometers_per_square_femtosecond) @property def micrometers_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_attosecond) + return self.quantity.in_units_of(units.micrometers_per_square_attosecond) @property def micrometers_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_minute) + return self.quantity.in_units_of(units.micrometers_per_square_minute) @property def micrometers_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_hour) + return self.quantity.in_units_of(units.micrometers_per_square_hour) @property def micrometers_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_day) + return self.quantity.in_units_of(units.micrometers_per_square_day) @property def micrometers_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_year) + return self.quantity.in_units_of(units.micrometers_per_square_year) @property def nanometers_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_second) + return self.quantity.in_units_of(units.nanometers_per_square_second) @property def nanometers_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_millisecond) + return self.quantity.in_units_of(units.nanometers_per_square_millisecond) @property def nanometers_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_microsecond) + return self.quantity.in_units_of(units.nanometers_per_square_microsecond) @property def nanometers_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_nanosecond) + return self.quantity.in_units_of(units.nanometers_per_square_nanosecond) @property def nanometers_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_picosecond) + return self.quantity.in_units_of(units.nanometers_per_square_picosecond) @property def nanometers_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_femtosecond) + return self.quantity.in_units_of(units.nanometers_per_square_femtosecond) @property def nanometers_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_attosecond) + return self.quantity.in_units_of(units.nanometers_per_square_attosecond) @property def nanometers_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_minute) + return self.quantity.in_units_of(units.nanometers_per_square_minute) @property def nanometers_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_hour) + return self.quantity.in_units_of(units.nanometers_per_square_hour) @property def nanometers_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_day) + return self.quantity.in_units_of(units.nanometers_per_square_day) @property def nanometers_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_year) + return self.quantity.in_units_of(units.nanometers_per_square_year) @property def picometers_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_second) + return self.quantity.in_units_of(units.picometers_per_square_second) @property def picometers_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_millisecond) + return self.quantity.in_units_of(units.picometers_per_square_millisecond) @property def picometers_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_microsecond) + return self.quantity.in_units_of(units.picometers_per_square_microsecond) @property def picometers_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_nanosecond) + return self.quantity.in_units_of(units.picometers_per_square_nanosecond) @property def picometers_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_picosecond) + return self.quantity.in_units_of(units.picometers_per_square_picosecond) @property def picometers_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_femtosecond) + return self.quantity.in_units_of(units.picometers_per_square_femtosecond) @property def picometers_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_attosecond) + return self.quantity.in_units_of(units.picometers_per_square_attosecond) @property def picometers_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_minute) + return self.quantity.in_units_of(units.picometers_per_square_minute) @property def picometers_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_hour) + return self.quantity.in_units_of(units.picometers_per_square_hour) @property def picometers_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_day) + return self.quantity.in_units_of(units.picometers_per_square_day) @property def picometers_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_year) + return self.quantity.in_units_of(units.picometers_per_square_year) @property def femtometers_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_second) + return self.quantity.in_units_of(units.femtometers_per_square_second) @property def femtometers_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_millisecond) + return self.quantity.in_units_of(units.femtometers_per_square_millisecond) @property def femtometers_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_microsecond) + return self.quantity.in_units_of(units.femtometers_per_square_microsecond) @property def femtometers_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_nanosecond) + return self.quantity.in_units_of(units.femtometers_per_square_nanosecond) @property def femtometers_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_picosecond) + return self.quantity.in_units_of(units.femtometers_per_square_picosecond) @property def femtometers_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_femtosecond) + return self.quantity.in_units_of(units.femtometers_per_square_femtosecond) @property def femtometers_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_attosecond) + return self.quantity.in_units_of(units.femtometers_per_square_attosecond) @property def femtometers_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_minute) + return self.quantity.in_units_of(units.femtometers_per_square_minute) @property def femtometers_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_hour) + return self.quantity.in_units_of(units.femtometers_per_square_hour) @property def femtometers_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_day) + return self.quantity.in_units_of(units.femtometers_per_square_day) @property def femtometers_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_year) + return self.quantity.in_units_of(units.femtometers_per_square_year) @property def attometers_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_second) + return self.quantity.in_units_of(units.attometers_per_square_second) @property def attometers_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_millisecond) + return self.quantity.in_units_of(units.attometers_per_square_millisecond) @property def attometers_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_microsecond) + return self.quantity.in_units_of(units.attometers_per_square_microsecond) @property def attometers_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_nanosecond) + return self.quantity.in_units_of(units.attometers_per_square_nanosecond) @property def attometers_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_picosecond) + return self.quantity.in_units_of(units.attometers_per_square_picosecond) @property def attometers_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_femtosecond) + return self.quantity.in_units_of(units.attometers_per_square_femtosecond) @property def attometers_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_attosecond) + return self.quantity.in_units_of(units.attometers_per_square_attosecond) @property def attometers_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_minute) + return self.quantity.in_units_of(units.attometers_per_square_minute) @property def attometers_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_hour) + return self.quantity.in_units_of(units.attometers_per_square_hour) @property def attometers_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_day) + return self.quantity.in_units_of(units.attometers_per_square_day) @property def attometers_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_year) + return self.quantity.in_units_of(units.attometers_per_square_year) @property def decimeters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_second) + return self.quantity.in_units_of(units.decimeters_per_square_second) @property def decimeters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_millisecond) + return self.quantity.in_units_of(units.decimeters_per_square_millisecond) @property def decimeters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_microsecond) + return self.quantity.in_units_of(units.decimeters_per_square_microsecond) @property def decimeters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_nanosecond) + return self.quantity.in_units_of(units.decimeters_per_square_nanosecond) @property def decimeters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_picosecond) + return self.quantity.in_units_of(units.decimeters_per_square_picosecond) @property def decimeters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_femtosecond) + return self.quantity.in_units_of(units.decimeters_per_square_femtosecond) @property def decimeters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_attosecond) + return self.quantity.in_units_of(units.decimeters_per_square_attosecond) @property def decimeters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_minute) + return self.quantity.in_units_of(units.decimeters_per_square_minute) @property def decimeters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_hour) + return self.quantity.in_units_of(units.decimeters_per_square_hour) @property def decimeters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_day) + return self.quantity.in_units_of(units.decimeters_per_square_day) @property def decimeters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_year) + return self.quantity.in_units_of(units.decimeters_per_square_year) @property def centimeters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_second) + return self.quantity.in_units_of(units.centimeters_per_square_second) @property def centimeters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_millisecond) + return self.quantity.in_units_of(units.centimeters_per_square_millisecond) @property def centimeters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_microsecond) + return self.quantity.in_units_of(units.centimeters_per_square_microsecond) @property def centimeters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_nanosecond) + return self.quantity.in_units_of(units.centimeters_per_square_nanosecond) @property def centimeters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_picosecond) + return self.quantity.in_units_of(units.centimeters_per_square_picosecond) @property def centimeters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_femtosecond) + return self.quantity.in_units_of(units.centimeters_per_square_femtosecond) @property def centimeters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_attosecond) + return self.quantity.in_units_of(units.centimeters_per_square_attosecond) @property def centimeters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_minute) + return self.quantity.in_units_of(units.centimeters_per_square_minute) @property def centimeters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_hour) + return self.quantity.in_units_of(units.centimeters_per_square_hour) @property def centimeters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_day) + return self.quantity.in_units_of(units.centimeters_per_square_day) @property def centimeters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_year) + return self.quantity.in_units_of(units.centimeters_per_square_year) @property def angstroms_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_second) + return self.quantity.in_units_of(units.angstroms_per_square_second) @property def angstroms_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_millisecond) + return self.quantity.in_units_of(units.angstroms_per_square_millisecond) @property def angstroms_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_microsecond) + return self.quantity.in_units_of(units.angstroms_per_square_microsecond) @property def angstroms_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_nanosecond) + return self.quantity.in_units_of(units.angstroms_per_square_nanosecond) @property def angstroms_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_picosecond) + return self.quantity.in_units_of(units.angstroms_per_square_picosecond) @property def angstroms_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_femtosecond) + return self.quantity.in_units_of(units.angstroms_per_square_femtosecond) @property def angstroms_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_attosecond) + return self.quantity.in_units_of(units.angstroms_per_square_attosecond) @property def angstroms_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_minute) + return self.quantity.in_units_of(units.angstroms_per_square_minute) @property def angstroms_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_hour) + return self.quantity.in_units_of(units.angstroms_per_square_hour) @property def angstroms_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_day) + return self.quantity.in_units_of(units.angstroms_per_square_day) @property def angstroms_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_year) + return self.quantity.in_units_of(units.angstroms_per_square_year) - @property - def microns_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_square_second) - @property - def microns_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_square_millisecond) +class DensityAccessor[T](Accessor[T]): + dimension_name = 'density' + @property - def microns_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_square_microsecond) + def grams_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.grams_per_cubic_meter) @property - def microns_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_square_nanosecond) + def exagrams_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.exagrams_per_cubic_meter) @property - def microns_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_square_picosecond) + def petagrams_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.petagrams_per_cubic_meter) @property - def microns_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_square_femtosecond) + def teragrams_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.teragrams_per_cubic_meter) @property - def microns_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_square_attosecond) + def gigagrams_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.gigagrams_per_cubic_meter) @property - def microns_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_square_minute) + def megagrams_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.megagrams_per_cubic_meter) @property - def microns_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_square_hour) + def kilograms_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.kilograms_per_cubic_meter) @property - def microns_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_square_day) + def milligrams_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.milligrams_per_cubic_meter) @property - def microns_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microns_per_square_year) + def micrograms_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.micrograms_per_cubic_meter) @property - def miles_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_second) + def nanograms_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.nanograms_per_cubic_meter) @property - def miles_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_millisecond) + def picograms_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.picograms_per_cubic_meter) @property - def miles_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_microsecond) + def femtograms_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.femtograms_per_cubic_meter) @property - def miles_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_nanosecond) + def attograms_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.attograms_per_cubic_meter) @property - def miles_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_picosecond) + def atomic_mass_units_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_meter) @property - def miles_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_femtosecond) + def grams_per_cubic_exameter(self) -> T: + return self.quantity.in_units_of(units.grams_per_cubic_exameter) @property - def miles_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_attosecond) - - @property - def miles_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_minute) - - @property - def miles_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_hour) - - @property - def miles_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_day) - - @property - def miles_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_year) - - @property - def yards_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_second) - - @property - def yards_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_millisecond) - - @property - def yards_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_microsecond) - - @property - def yards_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_nanosecond) - - @property - def yards_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_picosecond) - - @property - def yards_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_femtosecond) - - @property - def yards_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_attosecond) - - @property - def yards_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_minute) - - @property - def yards_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_hour) - - @property - def yards_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_day) - - @property - def yards_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_year) - - @property - def feet_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_second) - - @property - def feet_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_millisecond) - - @property - def feet_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_microsecond) - - @property - def feet_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_nanosecond) - - @property - def feet_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_picosecond) - - @property - def feet_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_femtosecond) - - @property - def feet_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_attosecond) - - @property - def feet_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_minute) - - @property - def feet_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_hour) - - @property - def feet_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_day) - - @property - def feet_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_year) - - @property - def inches_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_second) - - @property - def inches_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_millisecond) - - @property - def inches_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_microsecond) - - @property - def inches_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_nanosecond) - - @property - def inches_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_picosecond) - - @property - def inches_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_femtosecond) - - @property - def inches_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_attosecond) - - @property - def inches_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_minute) - - @property - def inches_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_hour) - - @property - def inches_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_day) - - @property - def inches_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_year) - - - -class DensityAccessor[T](QuantityAccessor[T]): - dimension_name = 'density' - - @property - def grams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_meter) - - @property - def exagrams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_meter) - - @property - def petagrams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_meter) - - @property - def teragrams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_meter) - - @property - def gigagrams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_meter) - - @property - def megagrams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_meter) - - @property - def kilograms_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_meter) - - @property - def milligrams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_meter) - - @property - def micrograms_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_meter) - - @property - def nanograms_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_meter) - - @property - def picograms_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_meter) - - @property - def femtograms_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_meter) - - @property - def attograms_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_meter) - - @property - def atomic_mass_units_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_meter) - - @property - def pounds_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_meter) - - @property - def ounces_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_meter) - - @property - def grams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_exameter) - - @property - def exagrams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_exameter) + def exagrams_per_cubic_exameter(self) -> T: + return self.quantity.in_units_of(units.exagrams_per_cubic_exameter) @property def petagrams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_exameter) + return self.quantity.in_units_of(units.petagrams_per_cubic_exameter) @property def teragrams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_exameter) + return self.quantity.in_units_of(units.teragrams_per_cubic_exameter) @property def gigagrams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_exameter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_exameter) @property def megagrams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_exameter) + return self.quantity.in_units_of(units.megagrams_per_cubic_exameter) @property def kilograms_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_exameter) + return self.quantity.in_units_of(units.kilograms_per_cubic_exameter) @property def milligrams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_exameter) + return self.quantity.in_units_of(units.milligrams_per_cubic_exameter) @property def micrograms_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_exameter) + return self.quantity.in_units_of(units.micrograms_per_cubic_exameter) @property def nanograms_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_exameter) + return self.quantity.in_units_of(units.nanograms_per_cubic_exameter) @property def picograms_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_exameter) + return self.quantity.in_units_of(units.picograms_per_cubic_exameter) @property def femtograms_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_exameter) + return self.quantity.in_units_of(units.femtograms_per_cubic_exameter) @property def attograms_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_exameter) + return self.quantity.in_units_of(units.attograms_per_cubic_exameter) @property def atomic_mass_units_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_exameter) - - @property - def pounds_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_exameter) - - @property - def ounces_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_exameter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_exameter) @property def grams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_petameter) + return self.quantity.in_units_of(units.grams_per_cubic_petameter) @property def exagrams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_petameter) + return self.quantity.in_units_of(units.exagrams_per_cubic_petameter) @property def petagrams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_petameter) + return self.quantity.in_units_of(units.petagrams_per_cubic_petameter) @property def teragrams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_petameter) + return self.quantity.in_units_of(units.teragrams_per_cubic_petameter) @property def gigagrams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_petameter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_petameter) @property def megagrams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_petameter) + return self.quantity.in_units_of(units.megagrams_per_cubic_petameter) @property def kilograms_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_petameter) + return self.quantity.in_units_of(units.kilograms_per_cubic_petameter) @property def milligrams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_petameter) + return self.quantity.in_units_of(units.milligrams_per_cubic_petameter) @property def micrograms_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_petameter) + return self.quantity.in_units_of(units.micrograms_per_cubic_petameter) @property def nanograms_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_petameter) + return self.quantity.in_units_of(units.nanograms_per_cubic_petameter) @property def picograms_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_petameter) + return self.quantity.in_units_of(units.picograms_per_cubic_petameter) @property def femtograms_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_petameter) + return self.quantity.in_units_of(units.femtograms_per_cubic_petameter) @property def attograms_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_petameter) + return self.quantity.in_units_of(units.attograms_per_cubic_petameter) @property def atomic_mass_units_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_petameter) - - @property - def pounds_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_petameter) - - @property - def ounces_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_petameter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_petameter) @property def grams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_terameter) + return self.quantity.in_units_of(units.grams_per_cubic_terameter) @property def exagrams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_terameter) + return self.quantity.in_units_of(units.exagrams_per_cubic_terameter) @property def petagrams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_terameter) + return self.quantity.in_units_of(units.petagrams_per_cubic_terameter) @property def teragrams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_terameter) + return self.quantity.in_units_of(units.teragrams_per_cubic_terameter) @property def gigagrams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_terameter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_terameter) @property def megagrams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_terameter) + return self.quantity.in_units_of(units.megagrams_per_cubic_terameter) @property def kilograms_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_terameter) + return self.quantity.in_units_of(units.kilograms_per_cubic_terameter) @property def milligrams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_terameter) + return self.quantity.in_units_of(units.milligrams_per_cubic_terameter) @property def micrograms_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_terameter) + return self.quantity.in_units_of(units.micrograms_per_cubic_terameter) @property def nanograms_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_terameter) + return self.quantity.in_units_of(units.nanograms_per_cubic_terameter) @property def picograms_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_terameter) + return self.quantity.in_units_of(units.picograms_per_cubic_terameter) @property def femtograms_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_terameter) + return self.quantity.in_units_of(units.femtograms_per_cubic_terameter) @property def attograms_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_terameter) + return self.quantity.in_units_of(units.attograms_per_cubic_terameter) @property def atomic_mass_units_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_terameter) - - @property - def pounds_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_terameter) - - @property - def ounces_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_terameter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_terameter) @property def grams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_gigameter) + return self.quantity.in_units_of(units.grams_per_cubic_gigameter) @property def exagrams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_gigameter) + return self.quantity.in_units_of(units.exagrams_per_cubic_gigameter) @property def petagrams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_gigameter) + return self.quantity.in_units_of(units.petagrams_per_cubic_gigameter) @property def teragrams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_gigameter) + return self.quantity.in_units_of(units.teragrams_per_cubic_gigameter) @property def gigagrams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_gigameter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_gigameter) @property def megagrams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_gigameter) + return self.quantity.in_units_of(units.megagrams_per_cubic_gigameter) @property def kilograms_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_gigameter) + return self.quantity.in_units_of(units.kilograms_per_cubic_gigameter) @property def milligrams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_gigameter) + return self.quantity.in_units_of(units.milligrams_per_cubic_gigameter) @property def micrograms_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_gigameter) + return self.quantity.in_units_of(units.micrograms_per_cubic_gigameter) @property def nanograms_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_gigameter) + return self.quantity.in_units_of(units.nanograms_per_cubic_gigameter) @property def picograms_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_gigameter) + return self.quantity.in_units_of(units.picograms_per_cubic_gigameter) @property def femtograms_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_gigameter) + return self.quantity.in_units_of(units.femtograms_per_cubic_gigameter) @property def attograms_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_gigameter) + return self.quantity.in_units_of(units.attograms_per_cubic_gigameter) @property def atomic_mass_units_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_gigameter) - - @property - def pounds_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_gigameter) - - @property - def ounces_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_gigameter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_gigameter) @property def grams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_megameter) + return self.quantity.in_units_of(units.grams_per_cubic_megameter) @property def exagrams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_megameter) + return self.quantity.in_units_of(units.exagrams_per_cubic_megameter) @property def petagrams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_megameter) + return self.quantity.in_units_of(units.petagrams_per_cubic_megameter) @property def teragrams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_megameter) + return self.quantity.in_units_of(units.teragrams_per_cubic_megameter) @property def gigagrams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_megameter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_megameter) @property def megagrams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_megameter) + return self.quantity.in_units_of(units.megagrams_per_cubic_megameter) @property def kilograms_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_megameter) + return self.quantity.in_units_of(units.kilograms_per_cubic_megameter) @property def milligrams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_megameter) + return self.quantity.in_units_of(units.milligrams_per_cubic_megameter) @property def micrograms_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_megameter) + return self.quantity.in_units_of(units.micrograms_per_cubic_megameter) @property def nanograms_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_megameter) + return self.quantity.in_units_of(units.nanograms_per_cubic_megameter) @property def picograms_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_megameter) + return self.quantity.in_units_of(units.picograms_per_cubic_megameter) @property def femtograms_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_megameter) + return self.quantity.in_units_of(units.femtograms_per_cubic_megameter) @property def attograms_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_megameter) + return self.quantity.in_units_of(units.attograms_per_cubic_megameter) @property def atomic_mass_units_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_megameter) - - @property - def pounds_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_megameter) - - @property - def ounces_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_megameter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_megameter) @property def grams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_kilometer) + return self.quantity.in_units_of(units.grams_per_cubic_kilometer) @property def exagrams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_kilometer) + return self.quantity.in_units_of(units.exagrams_per_cubic_kilometer) @property def petagrams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_kilometer) + return self.quantity.in_units_of(units.petagrams_per_cubic_kilometer) @property def teragrams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_kilometer) + return self.quantity.in_units_of(units.teragrams_per_cubic_kilometer) @property def gigagrams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_kilometer) + return self.quantity.in_units_of(units.gigagrams_per_cubic_kilometer) @property def megagrams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_kilometer) + return self.quantity.in_units_of(units.megagrams_per_cubic_kilometer) @property def kilograms_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_kilometer) + return self.quantity.in_units_of(units.kilograms_per_cubic_kilometer) @property def milligrams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_kilometer) + return self.quantity.in_units_of(units.milligrams_per_cubic_kilometer) @property def micrograms_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_kilometer) + return self.quantity.in_units_of(units.micrograms_per_cubic_kilometer) @property def nanograms_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_kilometer) + return self.quantity.in_units_of(units.nanograms_per_cubic_kilometer) @property def picograms_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_kilometer) + return self.quantity.in_units_of(units.picograms_per_cubic_kilometer) @property def femtograms_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_kilometer) + return self.quantity.in_units_of(units.femtograms_per_cubic_kilometer) @property def attograms_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_kilometer) + return self.quantity.in_units_of(units.attograms_per_cubic_kilometer) @property def atomic_mass_units_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_kilometer) - - @property - def pounds_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_kilometer) - - @property - def ounces_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_kilometer) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_kilometer) @property def grams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_millimeter) + return self.quantity.in_units_of(units.grams_per_cubic_millimeter) @property def exagrams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_millimeter) + return self.quantity.in_units_of(units.exagrams_per_cubic_millimeter) @property def petagrams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_millimeter) + return self.quantity.in_units_of(units.petagrams_per_cubic_millimeter) @property def teragrams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_millimeter) + return self.quantity.in_units_of(units.teragrams_per_cubic_millimeter) @property def gigagrams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_millimeter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_millimeter) @property def megagrams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_millimeter) + return self.quantity.in_units_of(units.megagrams_per_cubic_millimeter) @property def kilograms_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_millimeter) + return self.quantity.in_units_of(units.kilograms_per_cubic_millimeter) @property def milligrams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_millimeter) + return self.quantity.in_units_of(units.milligrams_per_cubic_millimeter) @property def micrograms_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_millimeter) + return self.quantity.in_units_of(units.micrograms_per_cubic_millimeter) @property def nanograms_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_millimeter) + return self.quantity.in_units_of(units.nanograms_per_cubic_millimeter) @property def picograms_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_millimeter) + return self.quantity.in_units_of(units.picograms_per_cubic_millimeter) @property def femtograms_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_millimeter) + return self.quantity.in_units_of(units.femtograms_per_cubic_millimeter) @property def attograms_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_millimeter) + return self.quantity.in_units_of(units.attograms_per_cubic_millimeter) @property def atomic_mass_units_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_millimeter) - - @property - def pounds_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_millimeter) - - @property - def ounces_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_millimeter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_millimeter) @property def grams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_micrometer) + return self.quantity.in_units_of(units.grams_per_cubic_micrometer) @property def exagrams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_micrometer) + return self.quantity.in_units_of(units.exagrams_per_cubic_micrometer) @property def petagrams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_micrometer) + return self.quantity.in_units_of(units.petagrams_per_cubic_micrometer) @property def teragrams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_micrometer) + return self.quantity.in_units_of(units.teragrams_per_cubic_micrometer) @property def gigagrams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_micrometer) + return self.quantity.in_units_of(units.gigagrams_per_cubic_micrometer) @property def megagrams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_micrometer) + return self.quantity.in_units_of(units.megagrams_per_cubic_micrometer) @property def kilograms_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_micrometer) + return self.quantity.in_units_of(units.kilograms_per_cubic_micrometer) @property def milligrams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_micrometer) + return self.quantity.in_units_of(units.milligrams_per_cubic_micrometer) @property def micrograms_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_micrometer) + return self.quantity.in_units_of(units.micrograms_per_cubic_micrometer) @property def nanograms_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_micrometer) + return self.quantity.in_units_of(units.nanograms_per_cubic_micrometer) @property def picograms_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_micrometer) + return self.quantity.in_units_of(units.picograms_per_cubic_micrometer) @property def femtograms_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_micrometer) + return self.quantity.in_units_of(units.femtograms_per_cubic_micrometer) @property def attograms_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_micrometer) + return self.quantity.in_units_of(units.attograms_per_cubic_micrometer) @property def atomic_mass_units_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_micrometer) - - @property - def pounds_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_micrometer) - - @property - def ounces_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_micrometer) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_micrometer) @property def grams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_nanometer) + return self.quantity.in_units_of(units.grams_per_cubic_nanometer) @property def exagrams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_nanometer) + return self.quantity.in_units_of(units.exagrams_per_cubic_nanometer) @property def petagrams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_nanometer) + return self.quantity.in_units_of(units.petagrams_per_cubic_nanometer) @property def teragrams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_nanometer) + return self.quantity.in_units_of(units.teragrams_per_cubic_nanometer) @property def gigagrams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_nanometer) + return self.quantity.in_units_of(units.gigagrams_per_cubic_nanometer) @property def megagrams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_nanometer) + return self.quantity.in_units_of(units.megagrams_per_cubic_nanometer) @property def kilograms_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_nanometer) + return self.quantity.in_units_of(units.kilograms_per_cubic_nanometer) @property def milligrams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_nanometer) + return self.quantity.in_units_of(units.milligrams_per_cubic_nanometer) @property def micrograms_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_nanometer) + return self.quantity.in_units_of(units.micrograms_per_cubic_nanometer) @property def nanograms_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_nanometer) + return self.quantity.in_units_of(units.nanograms_per_cubic_nanometer) @property def picograms_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_nanometer) + return self.quantity.in_units_of(units.picograms_per_cubic_nanometer) @property def femtograms_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_nanometer) + return self.quantity.in_units_of(units.femtograms_per_cubic_nanometer) @property def attograms_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_nanometer) + return self.quantity.in_units_of(units.attograms_per_cubic_nanometer) @property def atomic_mass_units_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_nanometer) - - @property - def pounds_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_nanometer) - - @property - def ounces_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_nanometer) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_nanometer) @property def grams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_picometer) + return self.quantity.in_units_of(units.grams_per_cubic_picometer) @property def exagrams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_picometer) + return self.quantity.in_units_of(units.exagrams_per_cubic_picometer) @property def petagrams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_picometer) + return self.quantity.in_units_of(units.petagrams_per_cubic_picometer) @property def teragrams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_picometer) + return self.quantity.in_units_of(units.teragrams_per_cubic_picometer) @property def gigagrams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_picometer) + return self.quantity.in_units_of(units.gigagrams_per_cubic_picometer) @property def megagrams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_picometer) + return self.quantity.in_units_of(units.megagrams_per_cubic_picometer) @property def kilograms_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_picometer) + return self.quantity.in_units_of(units.kilograms_per_cubic_picometer) @property def milligrams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_picometer) + return self.quantity.in_units_of(units.milligrams_per_cubic_picometer) @property def micrograms_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_picometer) + return self.quantity.in_units_of(units.micrograms_per_cubic_picometer) @property def nanograms_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_picometer) + return self.quantity.in_units_of(units.nanograms_per_cubic_picometer) @property def picograms_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_picometer) + return self.quantity.in_units_of(units.picograms_per_cubic_picometer) @property def femtograms_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_picometer) + return self.quantity.in_units_of(units.femtograms_per_cubic_picometer) @property def attograms_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_picometer) + return self.quantity.in_units_of(units.attograms_per_cubic_picometer) @property def atomic_mass_units_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_picometer) - - @property - def pounds_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_picometer) - - @property - def ounces_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_picometer) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_picometer) @property def grams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_femtometer) + return self.quantity.in_units_of(units.grams_per_cubic_femtometer) @property def exagrams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_femtometer) + return self.quantity.in_units_of(units.exagrams_per_cubic_femtometer) @property def petagrams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_femtometer) + return self.quantity.in_units_of(units.petagrams_per_cubic_femtometer) @property def teragrams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_femtometer) + return self.quantity.in_units_of(units.teragrams_per_cubic_femtometer) @property def gigagrams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_femtometer) + return self.quantity.in_units_of(units.gigagrams_per_cubic_femtometer) @property def megagrams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_femtometer) + return self.quantity.in_units_of(units.megagrams_per_cubic_femtometer) @property def kilograms_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_femtometer) + return self.quantity.in_units_of(units.kilograms_per_cubic_femtometer) @property def milligrams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_femtometer) + return self.quantity.in_units_of(units.milligrams_per_cubic_femtometer) @property def micrograms_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_femtometer) + return self.quantity.in_units_of(units.micrograms_per_cubic_femtometer) @property def nanograms_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_femtometer) + return self.quantity.in_units_of(units.nanograms_per_cubic_femtometer) @property def picograms_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_femtometer) + return self.quantity.in_units_of(units.picograms_per_cubic_femtometer) @property def femtograms_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_femtometer) + return self.quantity.in_units_of(units.femtograms_per_cubic_femtometer) @property def attograms_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_femtometer) + return self.quantity.in_units_of(units.attograms_per_cubic_femtometer) @property def atomic_mass_units_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_femtometer) - - @property - def pounds_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_femtometer) - - @property - def ounces_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_femtometer) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_femtometer) @property def grams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_attometer) + return self.quantity.in_units_of(units.grams_per_cubic_attometer) @property def exagrams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_attometer) + return self.quantity.in_units_of(units.exagrams_per_cubic_attometer) @property def petagrams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_attometer) + return self.quantity.in_units_of(units.petagrams_per_cubic_attometer) @property def teragrams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_attometer) + return self.quantity.in_units_of(units.teragrams_per_cubic_attometer) @property def gigagrams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_attometer) + return self.quantity.in_units_of(units.gigagrams_per_cubic_attometer) @property def megagrams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_attometer) + return self.quantity.in_units_of(units.megagrams_per_cubic_attometer) @property def kilograms_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_attometer) + return self.quantity.in_units_of(units.kilograms_per_cubic_attometer) @property def milligrams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_attometer) + return self.quantity.in_units_of(units.milligrams_per_cubic_attometer) @property def micrograms_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_attometer) + return self.quantity.in_units_of(units.micrograms_per_cubic_attometer) @property def nanograms_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_attometer) + return self.quantity.in_units_of(units.nanograms_per_cubic_attometer) @property def picograms_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_attometer) + return self.quantity.in_units_of(units.picograms_per_cubic_attometer) @property def femtograms_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_attometer) + return self.quantity.in_units_of(units.femtograms_per_cubic_attometer) @property def attograms_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_attometer) + return self.quantity.in_units_of(units.attograms_per_cubic_attometer) @property def atomic_mass_units_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_attometer) - - @property - def pounds_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_attometer) - - @property - def ounces_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_attometer) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_attometer) @property def grams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_decimeter) + return self.quantity.in_units_of(units.grams_per_cubic_decimeter) @property def exagrams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_decimeter) + return self.quantity.in_units_of(units.exagrams_per_cubic_decimeter) @property def petagrams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_decimeter) + return self.quantity.in_units_of(units.petagrams_per_cubic_decimeter) @property def teragrams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_decimeter) + return self.quantity.in_units_of(units.teragrams_per_cubic_decimeter) @property def gigagrams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_decimeter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_decimeter) @property def megagrams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_decimeter) + return self.quantity.in_units_of(units.megagrams_per_cubic_decimeter) @property def kilograms_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_decimeter) + return self.quantity.in_units_of(units.kilograms_per_cubic_decimeter) @property def milligrams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_decimeter) + return self.quantity.in_units_of(units.milligrams_per_cubic_decimeter) @property def micrograms_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_decimeter) + return self.quantity.in_units_of(units.micrograms_per_cubic_decimeter) @property def nanograms_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_decimeter) + return self.quantity.in_units_of(units.nanograms_per_cubic_decimeter) @property def picograms_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_decimeter) + return self.quantity.in_units_of(units.picograms_per_cubic_decimeter) @property def femtograms_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_decimeter) + return self.quantity.in_units_of(units.femtograms_per_cubic_decimeter) @property def attograms_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_decimeter) + return self.quantity.in_units_of(units.attograms_per_cubic_decimeter) @property def atomic_mass_units_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_decimeter) - - @property - def pounds_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_decimeter) - - @property - def ounces_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_decimeter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_decimeter) @property def grams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_centimeter) + return self.quantity.in_units_of(units.grams_per_cubic_centimeter) @property def exagrams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_centimeter) + return self.quantity.in_units_of(units.exagrams_per_cubic_centimeter) @property def petagrams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_centimeter) + return self.quantity.in_units_of(units.petagrams_per_cubic_centimeter) @property def teragrams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_centimeter) + return self.quantity.in_units_of(units.teragrams_per_cubic_centimeter) @property def gigagrams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_centimeter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_centimeter) @property def megagrams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_centimeter) + return self.quantity.in_units_of(units.megagrams_per_cubic_centimeter) @property def kilograms_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_centimeter) + return self.quantity.in_units_of(units.kilograms_per_cubic_centimeter) @property def milligrams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_centimeter) + return self.quantity.in_units_of(units.milligrams_per_cubic_centimeter) @property def micrograms_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_centimeter) + return self.quantity.in_units_of(units.micrograms_per_cubic_centimeter) @property def nanograms_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_centimeter) + return self.quantity.in_units_of(units.nanograms_per_cubic_centimeter) @property def picograms_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_centimeter) + return self.quantity.in_units_of(units.picograms_per_cubic_centimeter) @property def femtograms_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_centimeter) + return self.quantity.in_units_of(units.femtograms_per_cubic_centimeter) @property def attograms_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_centimeter) + return self.quantity.in_units_of(units.attograms_per_cubic_centimeter) @property def atomic_mass_units_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_centimeter) - - @property - def pounds_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_centimeter) - - @property - def ounces_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_centimeter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_centimeter) @property def grams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_angstrom) + return self.quantity.in_units_of(units.grams_per_cubic_angstrom) @property def exagrams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_angstrom) + return self.quantity.in_units_of(units.exagrams_per_cubic_angstrom) @property def petagrams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_angstrom) + return self.quantity.in_units_of(units.petagrams_per_cubic_angstrom) @property def teragrams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_angstrom) + return self.quantity.in_units_of(units.teragrams_per_cubic_angstrom) @property def gigagrams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_angstrom) + return self.quantity.in_units_of(units.gigagrams_per_cubic_angstrom) @property def megagrams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_angstrom) + return self.quantity.in_units_of(units.megagrams_per_cubic_angstrom) @property def kilograms_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_angstrom) + return self.quantity.in_units_of(units.kilograms_per_cubic_angstrom) @property def milligrams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_angstrom) + return self.quantity.in_units_of(units.milligrams_per_cubic_angstrom) @property def micrograms_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_angstrom) + return self.quantity.in_units_of(units.micrograms_per_cubic_angstrom) @property def nanograms_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_angstrom) + return self.quantity.in_units_of(units.nanograms_per_cubic_angstrom) @property def picograms_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_angstrom) + return self.quantity.in_units_of(units.picograms_per_cubic_angstrom) @property def femtograms_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_angstrom) + return self.quantity.in_units_of(units.femtograms_per_cubic_angstrom) @property def attograms_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_angstrom) + return self.quantity.in_units_of(units.attograms_per_cubic_angstrom) @property def atomic_mass_units_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_angstrom) - - @property - def pounds_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_angstrom) - - @property - def ounces_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_angstrom) - - @property - def grams_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_micron) - - @property - def exagrams_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_micron) - - @property - def petagrams_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_micron) - - @property - def teragrams_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_micron) - - @property - def gigagrams_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_micron) - - @property - def megagrams_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_micron) - - @property - def kilograms_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_micron) - - @property - def milligrams_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_micron) - - @property - def micrograms_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_micron) - - @property - def nanograms_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_micron) - - @property - def picograms_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_micron) - - @property - def femtograms_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_micron) - - @property - def attograms_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_micron) - - @property - def atomic_mass_units_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_micron) - - @property - def pounds_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_micron) - - @property - def ounces_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_micron) - - @property - def grams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_mile) - - @property - def exagrams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_mile) - - @property - def petagrams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_mile) - - @property - def teragrams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_mile) - - @property - def gigagrams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_mile) - - @property - def megagrams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_mile) - - @property - def kilograms_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_mile) - - @property - def milligrams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_mile) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_angstrom) - @property - def micrograms_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_mile) - - @property - def nanograms_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_mile) - - @property - def picograms_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_mile) - - @property - def femtograms_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_mile) - @property - def attograms_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_mile) - - @property - def atomic_mass_units_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_mile) - - @property - def pounds_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_mile) - - @property - def ounces_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_mile) - - @property - def grams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_yard) - - @property - def exagrams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_yard) - - @property - def petagrams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_yard) - - @property - def teragrams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_yard) - - @property - def gigagrams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_yard) - - @property - def megagrams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_yard) - - @property - def kilograms_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_yard) - - @property - def milligrams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_yard) - - @property - def micrograms_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_yard) - - @property - def nanograms_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_yard) - - @property - def picograms_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_yard) - - @property - def femtograms_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_yard) - - @property - def attograms_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_yard) - - @property - def atomic_mass_units_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_yard) - - @property - def pounds_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_yard) - - @property - def ounces_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_yard) - - @property - def grams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_foot) - - @property - def exagrams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_foot) - - @property - def petagrams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_foot) - - @property - def teragrams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_foot) - - @property - def gigagrams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_foot) - - @property - def megagrams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_foot) - - @property - def kilograms_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_foot) - - @property - def milligrams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_foot) - - @property - def micrograms_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_foot) - - @property - def nanograms_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_foot) - - @property - def picograms_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_foot) - - @property - def femtograms_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_foot) - - @property - def attograms_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_foot) - - @property - def atomic_mass_units_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_foot) - - @property - def pounds_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_foot) - - @property - def ounces_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_foot) - - @property - def grams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_inch) - - @property - def exagrams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_inch) - - @property - def petagrams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_inch) - - @property - def teragrams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_inch) - - @property - def gigagrams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_inch) - @property - def megagrams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_inch) - - @property - def kilograms_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_inch) - - @property - def milligrams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_inch) - - @property - def micrograms_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_inch) - - @property - def nanograms_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_inch) - - @property - def picograms_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_inch) - - @property - def femtograms_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_inch) - - @property - def attograms_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_inch) - - @property - def atomic_mass_units_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_inch) - - @property - def pounds_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_inch) - - @property - def ounces_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_inch) - - - -class ForceAccessor[T](QuantityAccessor[T]): +class ForceAccessor[T](Accessor[T]): dimension_name = 'force' - + @property def newtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.newtons) + return self.quantity.in_units_of(units.newtons) @property def exanewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exanewtons) + return self.quantity.in_units_of(units.exanewtons) @property def petanewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petanewtons) + return self.quantity.in_units_of(units.petanewtons) @property def teranewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teranewtons) + return self.quantity.in_units_of(units.teranewtons) @property def giganewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.giganewtons) + return self.quantity.in_units_of(units.giganewtons) @property def meganewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meganewtons) + return self.quantity.in_units_of(units.meganewtons) @property def kilonewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilonewtons) + return self.quantity.in_units_of(units.kilonewtons) @property def millinewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millinewtons) + return self.quantity.in_units_of(units.millinewtons) @property def micronewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micronewtons) + return self.quantity.in_units_of(units.micronewtons) @property def nanonewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanonewtons) + return self.quantity.in_units_of(units.nanonewtons) @property def piconewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.piconewtons) + return self.quantity.in_units_of(units.piconewtons) @property def femtonewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtonewtons) + return self.quantity.in_units_of(units.femtonewtons) @property def attonewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attonewtons) - - @property - def kg_force(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kg_force) - - @property - def pounds_force(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_force) + return self.quantity.in_units_of(units.attonewtons) -class PressureAccessor[T](QuantityAccessor[T]): +class PressureAccessor[T](Accessor[T]): dimension_name = 'pressure' - + @property def pascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pascals) + return self.quantity.in_units_of(units.pascals) @property def exapascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exapascals) + return self.quantity.in_units_of(units.exapascals) @property def petapascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petapascals) + return self.quantity.in_units_of(units.petapascals) @property def terapascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terapascals) + return self.quantity.in_units_of(units.terapascals) @property def gigapascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigapascals) + return self.quantity.in_units_of(units.gigapascals) @property def megapascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megapascals) + return self.quantity.in_units_of(units.megapascals) @property def kilopascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilopascals) + return self.quantity.in_units_of(units.kilopascals) @property def millipascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millipascals) + return self.quantity.in_units_of(units.millipascals) @property def micropascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micropascals) + return self.quantity.in_units_of(units.micropascals) @property def nanopascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanopascals) + return self.quantity.in_units_of(units.nanopascals) @property def picopascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picopascals) + return self.quantity.in_units_of(units.picopascals) @property def femtopascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtopascals) + return self.quantity.in_units_of(units.femtopascals) @property def attopascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attopascals) + return self.quantity.in_units_of(units.attopascals) - @property - def pounds_force_per_square_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_force_per_square_inch) - -class EnergyAccessor[T](QuantityAccessor[T]): +class EnergyAccessor[T](Accessor[T]): dimension_name = 'energy' - + @property def joules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.joules) + return self.quantity.in_units_of(units.joules) @property def exajoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exajoules) + return self.quantity.in_units_of(units.exajoules) @property def petajoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petajoules) + return self.quantity.in_units_of(units.petajoules) @property def terajoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terajoules) + return self.quantity.in_units_of(units.terajoules) @property def gigajoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigajoules) + return self.quantity.in_units_of(units.gigajoules) @property def megajoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megajoules) + return self.quantity.in_units_of(units.megajoules) @property def kilojoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilojoules) + return self.quantity.in_units_of(units.kilojoules) @property def millijoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millijoules) + return self.quantity.in_units_of(units.millijoules) @property def microjoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microjoules) + return self.quantity.in_units_of(units.microjoules) @property def nanojoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanojoules) + return self.quantity.in_units_of(units.nanojoules) @property def picojoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picojoules) + return self.quantity.in_units_of(units.picojoules) @property def femtojoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtojoules) + return self.quantity.in_units_of(units.femtojoules) @property def attojoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attojoules) + return self.quantity.in_units_of(units.attojoules) @property def electronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.electronvolts) + return self.quantity.in_units_of(units.electronvolts) @property def exaelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exaelectronvolts) + return self.quantity.in_units_of(units.exaelectronvolts) @property def petaelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petaelectronvolts) + return self.quantity.in_units_of(units.petaelectronvolts) @property def teraelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teraelectronvolts) + return self.quantity.in_units_of(units.teraelectronvolts) @property def gigaelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigaelectronvolts) + return self.quantity.in_units_of(units.gigaelectronvolts) @property def megaelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megaelectronvolts) + return self.quantity.in_units_of(units.megaelectronvolts) @property def kiloelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kiloelectronvolts) + return self.quantity.in_units_of(units.kiloelectronvolts) @property def millielectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millielectronvolts) + return self.quantity.in_units_of(units.millielectronvolts) @property def microelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microelectronvolts) + return self.quantity.in_units_of(units.microelectronvolts) @property def nanoelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanoelectronvolts) + return self.quantity.in_units_of(units.nanoelectronvolts) @property def picoelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picoelectronvolts) + return self.quantity.in_units_of(units.picoelectronvolts) @property def femtoelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtoelectronvolts) + return self.quantity.in_units_of(units.femtoelectronvolts) @property def attoelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attoelectronvolts) + return self.quantity.in_units_of(units.attoelectronvolts) -class PowerAccessor[T](QuantityAccessor[T]): +class PowerAccessor[T](Accessor[T]): dimension_name = 'power' - + @property def watts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.watts) + return self.quantity.in_units_of(units.watts) @property def exawatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exawatts) + return self.quantity.in_units_of(units.exawatts) @property def petawatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petawatts) + return self.quantity.in_units_of(units.petawatts) @property def terawatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terawatts) + return self.quantity.in_units_of(units.terawatts) @property def gigawatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigawatts) + return self.quantity.in_units_of(units.gigawatts) @property def megawatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megawatts) + return self.quantity.in_units_of(units.megawatts) @property def kilowatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilowatts) + return self.quantity.in_units_of(units.kilowatts) @property def milliwatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milliwatts) + return self.quantity.in_units_of(units.milliwatts) @property def microwatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microwatts) + return self.quantity.in_units_of(units.microwatts) @property def nanowatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanowatts) + return self.quantity.in_units_of(units.nanowatts) @property def picowatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picowatts) + return self.quantity.in_units_of(units.picowatts) @property def femtowatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtowatts) + return self.quantity.in_units_of(units.femtowatts) @property def attowatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attowatts) + return self.quantity.in_units_of(units.attowatts) -class ChargeAccessor[T](QuantityAccessor[T]): +class ChargeAccessor[T](Accessor[T]): dimension_name = 'charge' - + @property def coulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.coulombs) + return self.quantity.in_units_of(units.coulombs) @property def exacoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exacoulombs) + return self.quantity.in_units_of(units.exacoulombs) @property def petacoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petacoulombs) + return self.quantity.in_units_of(units.petacoulombs) @property def teracoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teracoulombs) + return self.quantity.in_units_of(units.teracoulombs) @property def gigacoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigacoulombs) + return self.quantity.in_units_of(units.gigacoulombs) @property def megacoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megacoulombs) + return self.quantity.in_units_of(units.megacoulombs) @property def kilocoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilocoulombs) + return self.quantity.in_units_of(units.kilocoulombs) @property def millicoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millicoulombs) + return self.quantity.in_units_of(units.millicoulombs) @property def microcoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microcoulombs) + return self.quantity.in_units_of(units.microcoulombs) @property def nanocoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanocoulombs) + return self.quantity.in_units_of(units.nanocoulombs) @property def picocoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picocoulombs) + return self.quantity.in_units_of(units.picocoulombs) @property def femtocoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtocoulombs) + return self.quantity.in_units_of(units.femtocoulombs) @property def attocoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attocoulombs) + return self.quantity.in_units_of(units.attocoulombs) -class PotentialAccessor[T](QuantityAccessor[T]): +class PotentialAccessor[T](Accessor[T]): dimension_name = 'potential' - + @property def volts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.volts) + return self.quantity.in_units_of(units.volts) @property def exavolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exavolts) + return self.quantity.in_units_of(units.exavolts) @property def petavolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petavolts) + return self.quantity.in_units_of(units.petavolts) @property def teravolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teravolts) + return self.quantity.in_units_of(units.teravolts) @property def gigavolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigavolts) + return self.quantity.in_units_of(units.gigavolts) @property def megavolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megavolts) + return self.quantity.in_units_of(units.megavolts) @property def kilovolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilovolts) + return self.quantity.in_units_of(units.kilovolts) @property def millivolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millivolts) + return self.quantity.in_units_of(units.millivolts) @property def microvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microvolts) + return self.quantity.in_units_of(units.microvolts) @property def nanovolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanovolts) + return self.quantity.in_units_of(units.nanovolts) @property def picovolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picovolts) + return self.quantity.in_units_of(units.picovolts) @property def femtovolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtovolts) + return self.quantity.in_units_of(units.femtovolts) @property def attovolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attovolts) + return self.quantity.in_units_of(units.attovolts) -class ResistanceAccessor[T](QuantityAccessor[T]): +class ResistanceAccessor[T](Accessor[T]): dimension_name = 'resistance' - + @property def ohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ohms) + return self.quantity.in_units_of(units.ohms) @property def exaohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exaohms) + return self.quantity.in_units_of(units.exaohms) @property def petaohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petaohms) + return self.quantity.in_units_of(units.petaohms) @property def teraohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teraohms) + return self.quantity.in_units_of(units.teraohms) @property def gigaohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigaohms) + return self.quantity.in_units_of(units.gigaohms) @property def megaohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megaohms) + return self.quantity.in_units_of(units.megaohms) @property def kiloohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kiloohms) + return self.quantity.in_units_of(units.kiloohms) @property def milliohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milliohms) + return self.quantity.in_units_of(units.milliohms) @property def microohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microohms) + return self.quantity.in_units_of(units.microohms) @property def nanoohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanoohms) + return self.quantity.in_units_of(units.nanoohms) @property def picoohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picoohms) + return self.quantity.in_units_of(units.picoohms) @property def femtoohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtoohms) + return self.quantity.in_units_of(units.femtoohms) @property def attoohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attoohms) + return self.quantity.in_units_of(units.attoohms) -class CapacitanceAccessor[T](QuantityAccessor[T]): +class CapacitanceAccessor[T](Accessor[T]): dimension_name = 'capacitance' - + @property def farads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.farads) + return self.quantity.in_units_of(units.farads) @property def exafarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exafarads) + return self.quantity.in_units_of(units.exafarads) @property def petafarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petafarads) + return self.quantity.in_units_of(units.petafarads) @property def terafarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terafarads) + return self.quantity.in_units_of(units.terafarads) @property def gigafarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigafarads) + return self.quantity.in_units_of(units.gigafarads) @property def megafarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megafarads) + return self.quantity.in_units_of(units.megafarads) @property def kilofarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilofarads) + return self.quantity.in_units_of(units.kilofarads) @property def millifarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millifarads) + return self.quantity.in_units_of(units.millifarads) @property def microfarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microfarads) + return self.quantity.in_units_of(units.microfarads) @property def nanofarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanofarads) + return self.quantity.in_units_of(units.nanofarads) @property def picofarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picofarads) + return self.quantity.in_units_of(units.picofarads) @property def femtofarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtofarads) + return self.quantity.in_units_of(units.femtofarads) @property def attofarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attofarads) + return self.quantity.in_units_of(units.attofarads) -class ConductanceAccessor[T](QuantityAccessor[T]): +class ConductanceAccessor[T](Accessor[T]): dimension_name = 'conductance' - + @property def siemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.siemens) + return self.quantity.in_units_of(units.siemens) @property def exasiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exasiemens) + return self.quantity.in_units_of(units.exasiemens) @property def petasiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petasiemens) + return self.quantity.in_units_of(units.petasiemens) @property def terasiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terasiemens) + return self.quantity.in_units_of(units.terasiemens) @property def gigasiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigasiemens) + return self.quantity.in_units_of(units.gigasiemens) @property def megasiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megasiemens) + return self.quantity.in_units_of(units.megasiemens) @property def kilosiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilosiemens) + return self.quantity.in_units_of(units.kilosiemens) @property def millisiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millisiemens) + return self.quantity.in_units_of(units.millisiemens) @property def microsiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microsiemens) + return self.quantity.in_units_of(units.microsiemens) @property def nanosiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanosiemens) + return self.quantity.in_units_of(units.nanosiemens) @property def picosiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picosiemens) + return self.quantity.in_units_of(units.picosiemens) @property def femtosiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtosiemens) + return self.quantity.in_units_of(units.femtosiemens) @property def attosiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attosiemens) + return self.quantity.in_units_of(units.attosiemens) -class MagneticfluxAccessor[T](QuantityAccessor[T]): +class MagneticfluxAccessor[T](Accessor[T]): dimension_name = 'magnetic_flux' - + @property def webers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.webers) + return self.quantity.in_units_of(units.webers) @property def exawebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exawebers) + return self.quantity.in_units_of(units.exawebers) @property def petawebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petawebers) + return self.quantity.in_units_of(units.petawebers) @property def terawebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terawebers) + return self.quantity.in_units_of(units.terawebers) @property def gigawebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigawebers) + return self.quantity.in_units_of(units.gigawebers) @property def megawebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megawebers) + return self.quantity.in_units_of(units.megawebers) @property def kilowebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilowebers) + return self.quantity.in_units_of(units.kilowebers) @property def milliwebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milliwebers) + return self.quantity.in_units_of(units.milliwebers) @property def microwebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microwebers) + return self.quantity.in_units_of(units.microwebers) @property def nanowebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanowebers) + return self.quantity.in_units_of(units.nanowebers) @property def picowebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picowebers) + return self.quantity.in_units_of(units.picowebers) @property def femtowebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtowebers) + return self.quantity.in_units_of(units.femtowebers) @property def attowebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attowebers) + return self.quantity.in_units_of(units.attowebers) -class MagneticfluxdensityAccessor[T](QuantityAccessor[T]): +class MagneticfluxdensityAccessor[T](Accessor[T]): dimension_name = 'magnetic_flux_density' - + @property def tesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.tesla) + return self.quantity.in_units_of(units.tesla) @property def exatesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exatesla) + return self.quantity.in_units_of(units.exatesla) @property def petatesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petatesla) + return self.quantity.in_units_of(units.petatesla) @property def teratesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teratesla) + return self.quantity.in_units_of(units.teratesla) @property def gigatesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigatesla) + return self.quantity.in_units_of(units.gigatesla) @property def megatesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megatesla) + return self.quantity.in_units_of(units.megatesla) @property def kilotesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilotesla) + return self.quantity.in_units_of(units.kilotesla) @property def millitesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millitesla) + return self.quantity.in_units_of(units.millitesla) @property def microtesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microtesla) + return self.quantity.in_units_of(units.microtesla) @property def nanotesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanotesla) + return self.quantity.in_units_of(units.nanotesla) @property def picotesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picotesla) + return self.quantity.in_units_of(units.picotesla) @property def femtotesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtotesla) + return self.quantity.in_units_of(units.femtotesla) @property def attotesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attotesla) + return self.quantity.in_units_of(units.attotesla) -class InductanceAccessor[T](QuantityAccessor[T]): +class InductanceAccessor[T](Accessor[T]): dimension_name = 'inductance' - + @property def henry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.henry) + return self.quantity.in_units_of(units.henry) @property def exahenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exahenry) + return self.quantity.in_units_of(units.exahenry) @property def petahenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petahenry) + return self.quantity.in_units_of(units.petahenry) @property def terahenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terahenry) + return self.quantity.in_units_of(units.terahenry) @property def gigahenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigahenry) + return self.quantity.in_units_of(units.gigahenry) @property def megahenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megahenry) + return self.quantity.in_units_of(units.megahenry) @property def kilohenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilohenry) + return self.quantity.in_units_of(units.kilohenry) @property def millihenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millihenry) + return self.quantity.in_units_of(units.millihenry) @property def microhenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microhenry) + return self.quantity.in_units_of(units.microhenry) @property def nanohenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanohenry) + return self.quantity.in_units_of(units.nanohenry) @property def picohenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picohenry) + return self.quantity.in_units_of(units.picohenry) @property def femtohenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtohenry) + return self.quantity.in_units_of(units.femtohenry) @property def attohenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attohenry) + return self.quantity.in_units_of(units.attohenry) -class TemperatureAccessor[T](QuantityAccessor[T]): +class TemperatureAccessor[T](Accessor[T]): dimension_name = 'temperature' - + @property def kelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kelvin) + return self.quantity.in_units_of(units.kelvin) @property def exakelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exakelvin) + return self.quantity.in_units_of(units.exakelvin) @property def petakelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petakelvin) + return self.quantity.in_units_of(units.petakelvin) @property def terakelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terakelvin) + return self.quantity.in_units_of(units.terakelvin) @property def gigakelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigakelvin) + return self.quantity.in_units_of(units.gigakelvin) @property def megakelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megakelvin) + return self.quantity.in_units_of(units.megakelvin) @property def kilokelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilokelvin) + return self.quantity.in_units_of(units.kilokelvin) @property def millikelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millikelvin) + return self.quantity.in_units_of(units.millikelvin) @property def microkelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microkelvin) + return self.quantity.in_units_of(units.microkelvin) @property def nanokelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanokelvin) + return self.quantity.in_units_of(units.nanokelvin) @property def picokelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picokelvin) + return self.quantity.in_units_of(units.picokelvin) @property def femtokelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtokelvin) + return self.quantity.in_units_of(units.femtokelvin) @property def attokelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attokelvin) + return self.quantity.in_units_of(units.attokelvin) @property def degrees_celsius(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.degrees_celsius) + return self.quantity.in_units_of(units.degrees_celsius) -class DimensionlessAccessor[T](QuantityAccessor[T]): +class DimensionlessAccessor[T](Accessor[T]): dimension_name = 'dimensionless' - + @property def none(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.none) + return self.quantity.in_units_of(units.none) - @property - def percent(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.percent) - -class AngleAccessor[T](QuantityAccessor[T]): +class AngleAccessor[T](Accessor[T]): dimension_name = 'angle' - + @property def degrees(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.degrees) + return self.quantity.in_units_of(units.degrees) @property def radians(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.radians) + return self.quantity.in_units_of(units.radians) -class SolidangleAccessor[T](QuantityAccessor[T]): +class SolidangleAccessor[T](Accessor[T]): dimension_name = 'solid_angle' - + @property def stradians(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.stradians) + return self.quantity.in_units_of(units.stradians) -class AmountAccessor[T](QuantityAccessor[T]): +class AmountAccessor[T](Accessor[T]): dimension_name = 'amount' - + @property def moles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles) + return self.quantity.in_units_of(units.moles) @property def millimoles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles) + return self.quantity.in_units_of(units.millimoles) @property def micromoles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles) + return self.quantity.in_units_of(units.micromoles) @property def nanomoles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles) + return self.quantity.in_units_of(units.nanomoles) @property def picomoles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles) + return self.quantity.in_units_of(units.picomoles) @property def femtomoles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles) + return self.quantity.in_units_of(units.femtomoles) @property def attomoles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles) + return self.quantity.in_units_of(units.attomoles) -class ConcentrationAccessor[T](QuantityAccessor[T]): +class ConcentrationAccessor[T](Accessor[T]): dimension_name = 'concentration' - + @property def moles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_meter) + return self.quantity.in_units_of(units.moles_per_cubic_meter) @property def millimoles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_meter) + return self.quantity.in_units_of(units.millimoles_per_cubic_meter) @property def micromoles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_meter) + return self.quantity.in_units_of(units.micromoles_per_cubic_meter) @property def nanomoles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_meter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_meter) @property def picomoles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_meter) + return self.quantity.in_units_of(units.picomoles_per_cubic_meter) @property def femtomoles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_meter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_meter) @property def attomoles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_meter) + return self.quantity.in_units_of(units.attomoles_per_cubic_meter) @property def moles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_exameter) + return self.quantity.in_units_of(units.moles_per_cubic_exameter) @property def millimoles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_exameter) + return self.quantity.in_units_of(units.millimoles_per_cubic_exameter) @property def micromoles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_exameter) + return self.quantity.in_units_of(units.micromoles_per_cubic_exameter) @property def nanomoles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_exameter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_exameter) @property def picomoles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_exameter) + return self.quantity.in_units_of(units.picomoles_per_cubic_exameter) @property def femtomoles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_exameter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_exameter) @property def attomoles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_exameter) + return self.quantity.in_units_of(units.attomoles_per_cubic_exameter) @property def moles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_petameter) + return self.quantity.in_units_of(units.moles_per_cubic_petameter) @property def millimoles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_petameter) + return self.quantity.in_units_of(units.millimoles_per_cubic_petameter) @property def micromoles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_petameter) + return self.quantity.in_units_of(units.micromoles_per_cubic_petameter) @property def nanomoles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_petameter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_petameter) @property def picomoles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_petameter) + return self.quantity.in_units_of(units.picomoles_per_cubic_petameter) @property def femtomoles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_petameter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_petameter) @property def attomoles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_petameter) + return self.quantity.in_units_of(units.attomoles_per_cubic_petameter) @property def moles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_terameter) + return self.quantity.in_units_of(units.moles_per_cubic_terameter) @property def millimoles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_terameter) + return self.quantity.in_units_of(units.millimoles_per_cubic_terameter) @property def micromoles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_terameter) + return self.quantity.in_units_of(units.micromoles_per_cubic_terameter) @property def nanomoles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_terameter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_terameter) @property def picomoles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_terameter) + return self.quantity.in_units_of(units.picomoles_per_cubic_terameter) @property def femtomoles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_terameter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_terameter) @property def attomoles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_terameter) + return self.quantity.in_units_of(units.attomoles_per_cubic_terameter) @property def moles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_gigameter) + return self.quantity.in_units_of(units.moles_per_cubic_gigameter) @property def millimoles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_gigameter) + return self.quantity.in_units_of(units.millimoles_per_cubic_gigameter) @property def micromoles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_gigameter) + return self.quantity.in_units_of(units.micromoles_per_cubic_gigameter) @property def nanomoles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_gigameter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_gigameter) @property def picomoles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_gigameter) + return self.quantity.in_units_of(units.picomoles_per_cubic_gigameter) @property def femtomoles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_gigameter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_gigameter) @property def attomoles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_gigameter) + return self.quantity.in_units_of(units.attomoles_per_cubic_gigameter) @property def moles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_megameter) + return self.quantity.in_units_of(units.moles_per_cubic_megameter) @property def millimoles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_megameter) + return self.quantity.in_units_of(units.millimoles_per_cubic_megameter) @property def micromoles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_megameter) + return self.quantity.in_units_of(units.micromoles_per_cubic_megameter) @property def nanomoles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_megameter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_megameter) @property def picomoles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_megameter) + return self.quantity.in_units_of(units.picomoles_per_cubic_megameter) @property def femtomoles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_megameter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_megameter) @property def attomoles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_megameter) + return self.quantity.in_units_of(units.attomoles_per_cubic_megameter) @property def moles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_kilometer) + return self.quantity.in_units_of(units.moles_per_cubic_kilometer) @property def millimoles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_kilometer) + return self.quantity.in_units_of(units.millimoles_per_cubic_kilometer) @property def micromoles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_kilometer) + return self.quantity.in_units_of(units.micromoles_per_cubic_kilometer) @property def nanomoles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_kilometer) + return self.quantity.in_units_of(units.nanomoles_per_cubic_kilometer) @property def picomoles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_kilometer) + return self.quantity.in_units_of(units.picomoles_per_cubic_kilometer) @property def femtomoles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_kilometer) + return self.quantity.in_units_of(units.femtomoles_per_cubic_kilometer) @property def attomoles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_kilometer) + return self.quantity.in_units_of(units.attomoles_per_cubic_kilometer) @property def moles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_millimeter) + return self.quantity.in_units_of(units.moles_per_cubic_millimeter) @property def millimoles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_millimeter) + return self.quantity.in_units_of(units.millimoles_per_cubic_millimeter) @property def micromoles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_millimeter) + return self.quantity.in_units_of(units.micromoles_per_cubic_millimeter) @property def nanomoles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_millimeter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_millimeter) @property def picomoles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_millimeter) + return self.quantity.in_units_of(units.picomoles_per_cubic_millimeter) @property def femtomoles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_millimeter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_millimeter) @property def attomoles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_millimeter) + return self.quantity.in_units_of(units.attomoles_per_cubic_millimeter) @property def moles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_micrometer) + return self.quantity.in_units_of(units.moles_per_cubic_micrometer) @property def millimoles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_micrometer) + return self.quantity.in_units_of(units.millimoles_per_cubic_micrometer) @property def micromoles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_micrometer) + return self.quantity.in_units_of(units.micromoles_per_cubic_micrometer) @property def nanomoles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_micrometer) + return self.quantity.in_units_of(units.nanomoles_per_cubic_micrometer) @property def picomoles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_micrometer) + return self.quantity.in_units_of(units.picomoles_per_cubic_micrometer) @property def femtomoles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_micrometer) + return self.quantity.in_units_of(units.femtomoles_per_cubic_micrometer) @property def attomoles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_micrometer) + return self.quantity.in_units_of(units.attomoles_per_cubic_micrometer) @property def moles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_nanometer) + return self.quantity.in_units_of(units.moles_per_cubic_nanometer) @property def millimoles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_nanometer) + return self.quantity.in_units_of(units.millimoles_per_cubic_nanometer) @property def micromoles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_nanometer) + return self.quantity.in_units_of(units.micromoles_per_cubic_nanometer) @property def nanomoles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_nanometer) + return self.quantity.in_units_of(units.nanomoles_per_cubic_nanometer) @property def picomoles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_nanometer) + return self.quantity.in_units_of(units.picomoles_per_cubic_nanometer) @property def femtomoles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_nanometer) + return self.quantity.in_units_of(units.femtomoles_per_cubic_nanometer) @property def attomoles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_nanometer) + return self.quantity.in_units_of(units.attomoles_per_cubic_nanometer) @property def moles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_picometer) + return self.quantity.in_units_of(units.moles_per_cubic_picometer) @property def millimoles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_picometer) + return self.quantity.in_units_of(units.millimoles_per_cubic_picometer) @property def micromoles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_picometer) + return self.quantity.in_units_of(units.micromoles_per_cubic_picometer) @property def nanomoles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_picometer) + return self.quantity.in_units_of(units.nanomoles_per_cubic_picometer) @property def picomoles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_picometer) + return self.quantity.in_units_of(units.picomoles_per_cubic_picometer) @property def femtomoles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_picometer) + return self.quantity.in_units_of(units.femtomoles_per_cubic_picometer) @property def attomoles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_picometer) + return self.quantity.in_units_of(units.attomoles_per_cubic_picometer) @property def moles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_femtometer) + return self.quantity.in_units_of(units.moles_per_cubic_femtometer) @property def millimoles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_femtometer) + return self.quantity.in_units_of(units.millimoles_per_cubic_femtometer) @property def micromoles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_femtometer) + return self.quantity.in_units_of(units.micromoles_per_cubic_femtometer) @property def nanomoles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_femtometer) + return self.quantity.in_units_of(units.nanomoles_per_cubic_femtometer) @property def picomoles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_femtometer) + return self.quantity.in_units_of(units.picomoles_per_cubic_femtometer) @property def femtomoles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_femtometer) + return self.quantity.in_units_of(units.femtomoles_per_cubic_femtometer) @property def attomoles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_femtometer) + return self.quantity.in_units_of(units.attomoles_per_cubic_femtometer) @property def moles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_attometer) + return self.quantity.in_units_of(units.moles_per_cubic_attometer) @property def millimoles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_attometer) + return self.quantity.in_units_of(units.millimoles_per_cubic_attometer) @property def micromoles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_attometer) + return self.quantity.in_units_of(units.micromoles_per_cubic_attometer) @property def nanomoles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_attometer) + return self.quantity.in_units_of(units.nanomoles_per_cubic_attometer) @property def picomoles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_attometer) + return self.quantity.in_units_of(units.picomoles_per_cubic_attometer) @property def femtomoles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_attometer) + return self.quantity.in_units_of(units.femtomoles_per_cubic_attometer) @property def attomoles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_attometer) + return self.quantity.in_units_of(units.attomoles_per_cubic_attometer) @property def moles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_decimeter) + return self.quantity.in_units_of(units.moles_per_cubic_decimeter) @property def millimoles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_decimeter) + return self.quantity.in_units_of(units.millimoles_per_cubic_decimeter) @property def micromoles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_decimeter) + return self.quantity.in_units_of(units.micromoles_per_cubic_decimeter) @property def nanomoles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_decimeter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_decimeter) @property def picomoles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_decimeter) + return self.quantity.in_units_of(units.picomoles_per_cubic_decimeter) @property def femtomoles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_decimeter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_decimeter) @property def attomoles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_decimeter) + return self.quantity.in_units_of(units.attomoles_per_cubic_decimeter) @property def moles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_centimeter) + return self.quantity.in_units_of(units.moles_per_cubic_centimeter) @property def millimoles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_centimeter) + return self.quantity.in_units_of(units.millimoles_per_cubic_centimeter) @property def micromoles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_centimeter) + return self.quantity.in_units_of(units.micromoles_per_cubic_centimeter) @property def nanomoles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_centimeter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_centimeter) @property def picomoles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_centimeter) + return self.quantity.in_units_of(units.picomoles_per_cubic_centimeter) @property def femtomoles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_centimeter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_centimeter) @property def attomoles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_centimeter) + return self.quantity.in_units_of(units.attomoles_per_cubic_centimeter) @property def moles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_angstrom) + return self.quantity.in_units_of(units.moles_per_cubic_angstrom) @property def millimoles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_angstrom) + return self.quantity.in_units_of(units.millimoles_per_cubic_angstrom) @property def micromoles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_angstrom) + return self.quantity.in_units_of(units.micromoles_per_cubic_angstrom) @property def nanomoles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_angstrom) + return self.quantity.in_units_of(units.nanomoles_per_cubic_angstrom) @property def picomoles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_angstrom) + return self.quantity.in_units_of(units.picomoles_per_cubic_angstrom) @property def femtomoles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_angstrom) + return self.quantity.in_units_of(units.femtomoles_per_cubic_angstrom) @property def attomoles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_angstrom) - - @property - def moles_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_micron) - - @property - def millimoles_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_micron) - - @property - def micromoles_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_micron) - - @property - def nanomoles_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_micron) - - @property - def picomoles_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_micron) - - @property - def femtomoles_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_micron) - - @property - def attomoles_per_cubic_micron(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_micron) - - @property - def moles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_mile) - - @property - def millimoles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_mile) - - @property - def micromoles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_mile) - - @property - def nanomoles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_mile) - - @property - def picomoles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_mile) - - @property - def femtomoles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_mile) - - @property - def attomoles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_mile) - - @property - def moles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_yard) - - @property - def millimoles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_yard) - - @property - def micromoles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_yard) - - @property - def nanomoles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_yard) - - @property - def picomoles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_yard) - - @property - def femtomoles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_yard) - - @property - def attomoles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_yard) - - @property - def moles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_foot) - - @property - def millimoles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_foot) - - @property - def micromoles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_foot) - - @property - def nanomoles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_foot) - - @property - def picomoles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_foot) - - @property - def femtomoles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_foot) - - @property - def attomoles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_foot) - - @property - def moles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_inch) - - @property - def millimoles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_inch) - - @property - def micromoles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_inch) - - @property - def nanomoles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_inch) - - @property - def picomoles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_inch) - - @property - def femtomoles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_inch) - - @property - def attomoles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_inch) + return self.quantity.in_units_of(units.attomoles_per_cubic_angstrom) diff --git a/sasdata/quantities/quantities.py b/sasdata/quantities/quantities.py deleted file mode 100644 index fb6b4e2db..000000000 --- a/sasdata/quantities/quantities.py +++ /dev/null @@ -1,162 +0,0 @@ -from typing import Collection, Sequence, TypeVar, Generic -from dataclasses import dataclass - -from numpy._typing import ArrayLike - -from sasdata.quantities.units import Unit - -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, other: "Dimensions"): - - 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, other: "Dimensions"): - - 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, other: "Dimensions"): - 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 - - - -@dataclass -class UnitName: - ascii_name: str - unicode_name: str | None = None - - @property - def best_name(self): - if self.unicode_name is None: - return self.ascii_name - else: - return self.unicode_name - -class Unit: - def __init__(self, - si_scaling_factor: float, - dimensions: Dimensions, - name: UnitName | None = None): - - self.scale = si_scaling_factor - self.dimensions = dimensions - self.name = name - - def _components(self, tokens: Sequence["UnitToken"]): - pass - - def __mul__(self, other: "Unit"): - if not isinstance(other, Unit): - return NotImplemented - - return Unit(self.scale * other.scale, self.dimensions * other.dimensions) - - def __truediv__(self, other: "Unit"): - if not isinstance(other, Unit): - return NotImplemented - - return Unit(self.scale / other.scale, self.dimensions / other.dimensions) - - def __pow__(self, power: int): - if not isinstance(power, int): - return NotImplemented - - return Unit(self.scale**power, self.dimensions**power) - - def equivalent(self, other: "Unit"): - return self.dimensions == other.dimensions - - -# QuantityType = TypeVar("QuantityType") -class Quantity[QuantityType]: - def __init__(self, value: QuantityType, units: Unit): - self.value = value - self.units = units - - def in_units_of(self, units: Unit) -> QuantityType: - if self.units.equivalent(units): - return (units.scale / self.units.scale) * self.value - else: - raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") - - def __mul__(self, other: ArrayLike | "Quantity" ) -> "Quantity": - if isinstance(other, Quantity): - pass - - else: - pass - - def __truediv__(self, other: float | "Quantity") -> "Quantity": - if isinstance(other, Quantity): - pass - - else: - pass - - def __rdiv__(self, other: float | "Quantity") -> "Quantity": - if isinstance(other, Quantity): - pass - - else: - pass - def __add__(self, other: "Quantity") -> "Quantity": - if isinstance(other, Quantity): - pass - - def __sub__(self, other: "Quantity") -> "Quantity": - if isinstance(other, Quantity): - pass diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 08e0be6fe..f69a5afbb 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,1524 +1,70 @@ -import hashlib -import json -import math -from typing import Any, Self, TypeVar, Union +from typing import Collection, Sequence, TypeVar, Generic, Self +from dataclasses import dataclass -import h5py -import numpy as np from numpy._typing import ArrayLike -from sasdata.quantities import units -from sasdata.quantities.numerical_encoding import numerical_decode, numerical_encode -from sasdata.quantities.unit_parser import parse_unit -from sasdata.quantities.units import NamedUnit, Unit - -T = TypeVar("T") - - -################### Quantity based operations, need to be here to avoid cyclic dependencies ##################### - - -def transpose(a: Union["Quantity[ArrayLike]", ArrayLike], axes: tuple | None = None): - """Transpose an array or an array based quantity, can also do reordering of axes""" - if isinstance(a, Quantity): - if axes is None: - return DerivedQuantity( - value=np.transpose(a.value, axes=axes), - units=a.units, - history=QuantityHistory.apply_operation(Transpose, a.history), - ) - - else: - return DerivedQuantity( - value=np.transpose(a.value, axes=axes), - units=a.units, - history=QuantityHistory.apply_operation(Transpose, a.history, axes=axes), - ) - - else: - return np.transpose(a, axes=axes) - - -def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike]", ArrayLike]): - """Dot product of two arrays or two array based quantities""" - a_is_quantity = isinstance(a, Quantity) - b_is_quantity = isinstance(b, Quantity) - - if a_is_quantity or b_is_quantity: - # If its only one of them that is a quantity, convert the other one - - if not a_is_quantity: - a = Quantity(a, units.none) - - if not b_is_quantity: - b = Quantity(b, units.none) - - return DerivedQuantity( - value=np.dot(a.value, b.value), - units=a.units * b.units, - history=QuantityHistory.apply_operation(Dot, a.history, b.history), - ) - - else: - return np.dot(a, b) - - -def tensordot( - a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, - b: Union["Quantity[ArrayLike]", ArrayLike], - a_index: int, - b_index: int, -): - """Tensor dot product - equivalent to contracting two tensors, such as - - A_{i0, i1, i2, i3...} and B_{j0, j1, j2...} - - e.g. if a_index is 1 and b_index is zero, it will be the sum - - C_{i0, i2, i3 ..., j1, j2 ...} = sum_k A_{i0, k, i2, i3 ...} B_{k, j1, j2 ...} - - (I think, have to check what happens with indices TODO!) - - """ - - a_is_quantity = isinstance(a, Quantity) - b_is_quantity = isinstance(b, Quantity) - - if a_is_quantity or b_is_quantity: - # If its only one of them that is a quantity, convert the other one - - if not a_is_quantity: - a = Quantity(a, units.none) - - if not b_is_quantity: - b = Quantity(b, units.none) - - return DerivedQuantity( - value=np.tensordot(a.value, b.value, axes=(a_index, b_index)), - units=a.units * b.units, - history=QuantityHistory.apply_operation(TensorDot, a.history, b.history, a_index=a_index, b_index=b_index), - ) - - else: - return np.tensordot(a, b, axes=(a_index, b_index)) - - -################### Operation Definitions ####################################### - - -def hash_and_name(hash_or_name: int | str): - """Infer the name of a variable from a hash, or the hash from the name - - Note: hash_and_name(hash_and_name(number)[1]) is not the identity - however: hash_and_name(hash_and_name(number)) is - """ - - if isinstance(hash_or_name, str): - hash_value = hash(hash_or_name) - name = hash_or_name - - return hash_value, name - - elif isinstance(hash_or_name, int): - hash_value = hash_or_name - name = f"#{hash_or_name}" - - return hash_value, name - - elif isinstance(hash_or_name, tuple): - return hash_or_name - - else: - raise TypeError("Variable name_or_hash_value must be either str or int") - - -class Operation: - serialisation_name = "unknown" - - def summary(self, indent_amount: int = 0, indent: str = " "): - """Summary of the operation tree""" - - s = f"{indent_amount * indent}{self.__class__.__name__}(\n" - - for chunk in self._summary_components(): - s += chunk.summary(indent_amount + 1, indent) + "\n" - - s += f"{indent_amount * indent})" - - return s - - def _summary_components(self) -> list["Operation"]: - return [] - - def evaluate(self, variables: dict[int, T]) -> T: - """Evaluate this operation""" - pass - - def _derivative(self, hash_value: int) -> "Operation": - """Get the derivative of this operation""" - pass - - def _clean(self): - """Clean up this operation - i.e. remove silly things like 1*x""" - return self - - def derivative(self, variable: Union[str, int, "Variable"], simplify=True): - if isinstance(variable, Variable): - hash_value = variable.hash_value - else: - hash_value, _ = hash_and_name(variable) - - derivative = self._derivative(hash_value) - - if not simplify: - return derivative - - derivative_string = derivative.serialise() - - # print("---------------") - # print("Base") - # print("---------------") - # print(derivative.summary()) - - # Inefficient way of doing repeated simplification, but it will work - for i in range(100): # set max iterations - derivative = derivative._clean() - # - # print("-------------------") - # print("Iteration", i+1) - # print("-------------------") - # print(derivative.summary()) - # print("-------------------") - - new_derivative_string = derivative.serialise() - - if derivative_string == new_derivative_string: - break - - derivative_string = new_derivative_string - - return derivative - - @staticmethod - def deserialise(data: str) -> "Operation": - json_data = json.loads(data) - return Operation.deserialise_json(json_data) - - @staticmethod - def deserialise_json(json_data: dict) -> "Operation": - operation = json_data["operation"] - parameters = json_data["parameters"] - class_ = _serialisation_lookup[operation] - - try: - return class_._deserialise(parameters) - except NotImplementedError: - raise NotImplementedError(f"No method to deserialise {operation} with {parameters} (class={class_})") - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - raise NotImplementedError("Deserialise not implemented for this class") - - def serialise(self) -> str: - return json.dumps(self._serialise_json()) - - def _serialise_json(self) -> dict[str, Any]: - return {"operation": self.serialisation_name, "parameters": self._serialise_parameters()} - - def _serialise_parameters(self) -> dict[str, Any]: - raise NotImplementedError("_serialise_parameters not implemented for this class") - - def __eq__(self, other: "Operation"): - return NotImplemented - - -class ConstantBase(Operation): - pass - - -class AdditiveIdentity(ConstantBase): - serialisation_name = "zero" - - def evaluate(self, variables: dict[int, T]) -> T: - return 0 - - def _derivative(self, hash_value: int) -> "Operation": - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return AdditiveIdentity() - - def _serialise_parameters(self) -> dict[str, Any]: - return {} - - def summary(self, indent_amount: int = 0, indent=" "): - return f"{indent_amount * indent}0 [Add.Id.]" - - def __eq__(self, other): - if isinstance(other, AdditiveIdentity): - return True - elif isinstance(other, Constant): - if other.value == 0: - return True - - return False - - -class MultiplicativeIdentity(ConstantBase): - serialisation_name = "one" - - def evaluate(self, variables: dict[int, T]) -> T: - return 1 - - def _derivative(self, hash_value: int): - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return MultiplicativeIdentity() - - def _serialise_parameters(self) -> dict[str, Any]: - return {} - - def summary(self, indent_amount: int = 0, indent=" "): - return f"{indent_amount * indent}1 [Mul.Id.]" - - def __eq__(self, other): - if isinstance(other, MultiplicativeIdentity): - return True - elif isinstance(other, Constant): - if other.value == 1: - return True - - return False - - -class Constant(ConstantBase): - serialisation_name = "constant" - - def __init__(self, value): - self.value = value - - def evaluate(self, variables: dict[int, T]) -> T: - return self.value - - def _derivative(self, hash_value: int): - return AdditiveIdentity() - - def _clean(self): - if self.value == 0: - return AdditiveIdentity() - - elif self.value == 1: - return MultiplicativeIdentity() - - else: - return self - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - value = numerical_decode(parameters["value"]) - return Constant(value) - - def _serialise_parameters(self) -> dict[str, Any]: - return {"value": numerical_encode(self.value)} - - def summary(self, indent_amount: int = 0, indent=" "): - return f"{indent_amount * indent}{self.value}" - - def __eq__(self, other): - if isinstance(other, AdditiveIdentity): - return self.value == 0 - - elif isinstance(other, MultiplicativeIdentity): - return self.value == 1 - - elif isinstance(other, Constant): - return other.value == self.value - - return False - - -class Variable(Operation): - serialisation_name = "variable" - - def __init__(self, name_or_hash_value: int | str | tuple[int, str]): - self.hash_value, self.name = hash_and_name(name_or_hash_value) - - def evaluate(self, variables: dict[int, T]) -> T: - try: - return variables[self.hash_value] - except KeyError: - raise ValueError(f"Variable dictionary didn't have an entry for {self.name} (hash={self.hash_value})") - - def _derivative(self, hash_value: int) -> Operation: - if hash_value == self.hash_value: - return MultiplicativeIdentity() - else: - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - hash_value = parameters["hash_value"] - name = parameters["name"] - - return Variable((hash_value, name)) - - def _serialise_parameters(self) -> dict[str, Any]: - return {"hash_value": self.hash_value, "name": self.name} - - def summary(self, indent_amount: int = 0, indent: str = " "): - return f"{indent_amount * indent}{self.name}" - - def __eq__(self, other): - if isinstance(other, Variable): - return self.hash_value == other.hash_value - return False - - -class UnaryOperation(Operation): - def __init__(self, a: Operation): - self.a = a - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": self.a._serialise_json()} - - @classmethod - def _deserialise(cls, parameters: dict) -> "UnaryOperation": - return cls(Operation.deserialise_json(parameters["a"])) - - def _summary_components(self) -> list["Operation"]: - return [self.a] - - def __eq__(self, other): - if isinstance(other, self.__class__): - return self.a == other.a - return False - - -class Neg(UnaryOperation): - serialisation_name = "neg" - - def evaluate(self, variables: dict[int, T]) -> T: - return -self.a.evaluate(variables) - - def _derivative(self, hash_value: int): - return Neg(self.a._derivative(hash_value)) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, Neg): - # Removes double negations - return clean_a.a - - elif isinstance(clean_a, Constant): - return Constant(-clean_a.value)._clean() - - else: - return Neg(clean_a) - - -class Inv(UnaryOperation): - serialisation_name = "reciprocal" - - def evaluate(self, variables: dict[int, T]) -> T: - return 1.0 / self.a.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Neg(Div(self.a._derivative(hash_value), Mul(self.a, self.a))) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, Inv): - # Removes double inversions - return clean_a.a - - elif isinstance(clean_a, Neg): - # cannonicalise 1/-a to -(1/a) - # over multiple iterations this should have the effect of ordering and gathering Neg and Inv - return Neg(Inv(clean_a.a)) - - elif isinstance(clean_a, Constant): - return Constant(1.0 / clean_a.value)._clean() - - else: - return Inv(clean_a) - - -class Ln(UnaryOperation): - serialisation_name = "ln" - - def evaluate(self, variables: dict[int, T]) -> T: - return math.log(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Div(self.a._derivative(hash_value), self.a) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, Exp): - # Convert ln(exp(x)) to x - return clean_a.a - - elif isinstance(clean_a, MultiplicativeIdentity): - # Convert ln(1) to 0 - return AdditiveIdentity() - - elif clean_a == math.e: - # Convert ln(e) to 1 - return MultiplicativeIdentity() - - else: - return Ln(clean_a) - - -class Exp(UnaryOperation): - serialisation_name = "exp" - - def evaluate(self, variables: dict[int, T]) -> T: - return math.exp(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Mul(self.a._derivative(hash_value), Exp(self.a)) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, Ln): - # Convert exp(ln(x)) to x - return clean_a.a - - elif isinstance(clean_a, MultiplicativeIdentity): - # Convert e**1 to e - return math.e - - elif isinstance(clean_a, AdditiveIdentity): - # Convert e**0 to 1 - return 1 - - else: - return Exp(clean_a) - - -class Sin(UnaryOperation): - serialisation_name = "sin" - - def evaluate(self, variables: dict[int, T]) -> T: - return np.sin(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Mul(self.a._derivative(hash_value), Cos(self.a)) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, ArcSin): - return clean_a.a - - elif isinstance(clean_a, AdditiveIdentity): - # Convert sin(0) to 0 - return AdditiveIdentity() - - else: - return Sin(clean_a) - - -class ArcSin(UnaryOperation): - serialisation_name = "arcsin" - - def evaluate(self, variables: dict[int, T]) -> T: - return np.arcsin(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Div(self.a._derivative(hash_value), Sqrt(Sub(MultiplicativeIdentity(), Mul(self.a, self.a)))) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, Sin): - return clean_a.a - - elif isinstance(clean_a, AdditiveIdentity): - # Convert arcsin(0) to 0 - return AdditiveIdentity() - - elif isinstance(clean_a, MultiplicativeIdentity): - # Convert arcsin(1) to pi/2 - return Constant(0.5 * math.pi) - - else: - return ArcSin(clean_a) - - -class Cos(UnaryOperation): - serialisation_name = "cos" - - def evaluate(self, variables: dict[int, T]) -> T: - return np.cos(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Mul(self.a._derivative(hash_value), Neg(Sin(self.a))) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, ArcCos): - return clean_a.a - - elif isinstance(clean_a, AdditiveIdentity): - # Convert cos(0) to 1 - return MultiplicativeIdentity() - - else: - return Cos(clean_a) - - -class ArcCos(UnaryOperation): - serialisation_name = "arccos" - - def evaluate(self, variables: dict[int, T]) -> T: - return np.arccos(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Neg(Div(self.a._derivative(hash_value), Sqrt(Sub(MultiplicativeIdentity(), Mul(self.a, self.a))))) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, Cos): - return clean_a.a - - elif isinstance(clean_a, AdditiveIdentity): - # Convert arccos(0) to pi/2 - return Constant(0.5 * math.pi) - - elif isinstance(clean_a, MultiplicativeIdentity): - # Convert arccos(1) to 0 - return AdditiveIdentity() - - else: - return ArcCos(clean_a) - - -class Tan(UnaryOperation): - serialisation_name = "tan" - - def evaluate(self, variables: dict[int, T]) -> T: - return np.tan(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Div(self.a._derivative(hash_value), Mul(Cos(self.a), Cos(self.a))) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, ArcTan): - return clean_a.a - - elif isinstance(clean_a, AdditiveIdentity): - # Convert tan(0) to 0 - return AdditiveIdentity() - - else: - return Tan(clean_a) - - -class ArcTan(UnaryOperation): - serialisation_name = "arctan" - - def evaluate(self, variables: dict[int, T]) -> T: - return np.arctan(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Div(self.a._derivative(hash_value), Add(MultiplicativeIdentity(), Mul(self.a, self.a))) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, Tan): - return clean_a.a - - elif isinstance(clean_a, AdditiveIdentity): - # Convert arctan(0) to 0 - return AdditiveIdentity() - - elif isinstance(clean_a, MultiplicativeIdentity): - # Convert arctan(1) to pi/4 - return Constant(0.25 * math.pi) - - else: - return ArcTan(clean_a) - - -class BinaryOperation(Operation): - def __init__(self, a: Operation, b: Operation): - self.a = a - self.b = b - - def _clean(self): - return self._clean_ab(self.a._clean(), self.b._clean()) - - def _clean_ab(self, a, b): - raise NotImplementedError("_clean_ab not implemented") - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": self.a._serialise_json(), "b": self.b._serialise_json()} - - @classmethod - def _deserialise(cls, parameters: dict) -> "BinaryOperation": - return cls(*BinaryOperation._deserialise_ab(parameters)) - - @staticmethod - def _deserialise_ab(parameters) -> tuple[Operation, Operation]: - return (Operation.deserialise_json(parameters["a"]), Operation.deserialise_json(parameters["b"])) - - def _summary_components(self) -> list["Operation"]: - return [self.a, self.b] - - def __eq__(self, other): - if isinstance(other, self.__class__): - return self.a == other.a and self.b == other.b - return False - - -class Add(BinaryOperation): - serialisation_name = "add" - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) + self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add(self.a._derivative(hash_value), self.b._derivative(hash_value)) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity): - # Convert 0 + b to b - return b - - elif isinstance(b, AdditiveIdentity): - # Convert a + 0 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"+"b" to "a+b" - return Constant(a.evaluate({}) + b.evaluate({}))._clean() - - elif isinstance(a, Neg): - if isinstance(b, Neg): - # Convert (-a)+(-b) to -(a+b) - return Neg(Add(a.a, b.a)) - else: - # Convert (-a) + b to b-a - return Sub(b, a.a) - - elif isinstance(b, Neg): - # Convert a+(-b) to a-b - return Sub(a, b.a) - - elif a == b: - return Mul(Constant(2), a) - - else: - return Add(a, b) - - -class Sub(BinaryOperation): - serialisation_name = "sub" - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) - self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Sub(self.a._derivative(hash_value), self.b._derivative(hash_value)) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity): - # Convert 0 - b to -b - return Neg(b) - - elif isinstance(b, AdditiveIdentity): - # Convert a - 0 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant pair "a" - "b" to "a-b" - return Constant(a.evaluate({}) - b.evaluate({}))._clean() - - elif isinstance(a, Neg): - if isinstance(b, Neg): - # Convert (-a)-(-b) to b-a - return Sub(b.a, a.a) - else: - # Convert (-a)-b to -(a+b) - return Neg(Add(a.a, b)) - - elif isinstance(b, Neg): - # Convert a-(-b) to a+b - return Add(a, b.a) - - elif a == b: - return AdditiveIdentity() - - else: - return Sub(a, b) - - -class Mul(BinaryOperation): - serialisation_name = "mul" - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) * self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add(Mul(self.a, self.b._derivative(hash_value)), Mul(self.a._derivative(hash_value), self.b)) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): - # Convert 0*b or a*0 to 0 - return AdditiveIdentity() - - elif isinstance(a, MultiplicativeIdentity): - # Convert 1*b to b - return b - - elif isinstance(b, MultiplicativeIdentity): - # Convert a*1 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"*"b" to "a*b" - return Constant(a.evaluate({}) * b.evaluate({}))._clean() - - elif isinstance(a, Inv) and isinstance(b, Inv): - return Inv(Mul(a.a, b.a)) - - elif isinstance(a, Inv) and not isinstance(b, Inv): - return Div(b, a.a) - - elif not isinstance(a, Inv) and isinstance(b, Inv): - return Div(a, b.a) - - elif isinstance(a, Neg): - return Neg(Mul(a.a, b)) - - elif isinstance(b, Neg): - return Neg(Mul(a, b.a)) - - elif a == b: - return Pow(a, 2) - - elif isinstance(a, Pow) and a.a == b: - return Pow(b, a.power + 1) - - elif isinstance(b, Pow) and b.a == a: - return Pow(a, b.power + 1) - - elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: - return Pow(a.a, a.power + b.power) - - else: - return Mul(a, b) - - -class Div(BinaryOperation): - serialisation_name = "div" - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) / self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Div( - Sub(Mul(self.a.derivative(hash_value), self.b), Mul(self.a, self.b.derivative(hash_value))), - Mul(self.b, self.b), - ) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity): - # Convert 0/b to 0 - return AdditiveIdentity() - - elif isinstance(a, MultiplicativeIdentity): - # Convert 1/b to inverse of b - return Inv(b) - - elif isinstance(b, MultiplicativeIdentity): - # Convert a/1 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constants "a"/"b" to "a/b" - return Constant(self.a.evaluate({}) / self.b.evaluate({}))._clean() - - elif isinstance(a, Inv) and isinstance(b, Inv): - return Div(b.a, a.a) - - elif isinstance(a, Inv) and not isinstance(b, Inv): - return Inv(Mul(a.a, b)) - - elif not isinstance(a, Inv) and isinstance(b, Inv): - return Mul(a, b.a) - - elif a == b: - return MultiplicativeIdentity() - - elif isinstance(a, Pow) and a.a == b: - return Pow(b, a.power - 1) - - elif isinstance(b, Pow) and b.a == a: - return Pow(a, 1 - b.power) - - elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: - return Pow(a.a, a.power - b.power) - - else: - return Div(a, b) - - -class Log(Operation): - serialisation_name = "log" - - def __init__(self, a: Operation, base: float): - self.a = a - self.base = base - - def evaluate(self, variables: dict[int, T]) -> T: - return math.log(self.a.evaluate(variables), self.base) - - def _derivative(self, hash_value: int) -> Operation: - return Div(self.a.derivative(hash_value), Mul(self.a, Ln(Constant(self.base)))) - - def _clean_ab(self) -> Operation: - a = self.a._clean() - - if isinstance(a, MultiplicativeIdentity): - # Convert log(1) to 0 - return AdditiveIdentity() - - elif a == self.base: - # Convert log(base) to 1 - return MultiplicativeIdentity() - - else: - return Log(a, self.base) - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": Operation._serialise_json(self.a), "base": self.base} - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Log(Operation.deserialise_json(parameters["a"]), parameters["base"]) - - def summary(self, indent_amount: int = 0, indent=" "): - return ( - f"{indent_amount * indent}Log(\n" - + self.a.summary(indent_amount + 1, indent) - + "\n" - + f"{(indent_amount + 1) * indent}{self.base}\n" - + f"{indent_amount * indent})" - ) - - def __eq__(self, other): - if isinstance(other, Log): - return self.a == other.a and self.base == other.base - return False - - -class Pow(Operation): - serialisation_name = "pow" - - def __init__(self, a: Operation, power: float): - self.a = a - self.power = power - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) ** self.power - - def _derivative(self, hash_value: int) -> Operation: - if self.power == 0: - return AdditiveIdentity() - - elif self.power == 1: - return self.a._derivative(hash_value) - - else: - return Mul(Constant(self.power), Mul(Pow(self.a, self.power - 1), self.a._derivative(hash_value))) - - def _clean(self): - a = self.a._clean() - - if self.power == 1: - return a - - elif self.power == 0: - return MultiplicativeIdentity() - - elif self.power == -1: - return Inv(a) - - else: - return Pow(a, self.power) - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": Operation._serialise_json(self.a), "power": self.power} - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Pow(Operation.deserialise_json(parameters["a"]), parameters["power"]) - - def summary(self, indent_amount: int = 0, indent=" "): - return ( - f"{indent_amount * indent}Pow(\n" - + self.a.summary(indent_amount + 1, indent) - + "\n" - + f"{(indent_amount + 1) * indent}{self.power}\n" - + f"{indent_amount * indent})" - ) - - def __eq__(self, other): - if isinstance(other, Pow): - return self.a == other.a and self.power == other.power - return False - - -# -# Matrix operations -# - - -class Transpose(Operation): - """Transpose operation - as per numpy""" - - serialisation_name = "transpose" - - def __init__(self, a: Operation, axes: tuple[int] | None = None): - self.a = a - self.axes = axes - - def evaluate(self, variables: dict[int, T]) -> T: - return np.transpose(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Transpose(self.a.derivative(hash_value)) # TODO: Check! - - def _clean(self): - clean_a = self.a._clean() - return Transpose(clean_a) - - def _serialise_parameters(self) -> dict[str, Any]: - if self.axes is None: - return {"a": self.a._serialise_json()} - else: - return {"a": self.a._serialise_json(), "axes": list(self.axes)} - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - if "axes" in parameters: - return Transpose(a=Operation.deserialise_json(parameters["a"]), axes=tuple(parameters["axes"])) - else: - return Transpose(a=Operation.deserialise_json(parameters["a"])) - - def summary(self, indent_amount: int = 0, indent=" "): - if self.axes is None: - return ( - f"{indent_amount * indent}Transpose(\n" - + self.a.summary(indent_amount + 1, indent) - + "\n" - + f"{indent_amount * indent})" - ) - else: - return ( - f"{indent_amount * indent}Transpose(\n" - + self.a.summary(indent_amount + 1, indent) - + "\n" - + f"{(indent_amount + 1) * indent}{self.axes}\n" - + f"{indent_amount * indent})" - ) - - def __eq__(self, other): - if isinstance(other, Transpose): - return other.a == self.a - return False - - -class Dot(BinaryOperation): - """Dot product - backed by numpy's dot method""" - - serialisation_name = "dot" - - def evaluate(self, variables: dict[int, T]) -> T: - return dot(self.a.evaluate(variables), self.b.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Add(Dot(self.a, self.b._derivative(hash_value)), Dot(self.a._derivative(hash_value), self.b)) - - def _clean_ab(self, a, b): - return Dot(a, b) # Do nothing for now - - -# TODO: Add to base operation class, and to quantities -class MatMul(BinaryOperation): - """Matrix multiplication, using __matmul__ dunder""" - - serialisation_name = "matmul" - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) @ self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add(MatMul(self.a, self.b._derivative(hash_value)), MatMul(self.a._derivative(hash_value), self.b)) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): - # Convert 0*b or a*0 to 0 - return AdditiveIdentity() - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"@"b" to "a@b" - return Constant(a.evaluate({}) @ b.evaluate({}))._clean() - - elif isinstance(a, Neg): - return Neg(Mul(a.a, b)) - - elif isinstance(b, Neg): - return Neg(Mul(a, b.a)) - - return MatMul(a, b) - - -class TensorDot(Operation): - serialisation_name = "tensor_product" - - def __init__(self, a: Operation, b: Operation, a_index: int, b_index: int): - self.a = a - self.b = b - self.a_index = a_index - self.b_index = b_index - - def evaluate(self, variables: dict[int, T]) -> T: - return tensordot(self.a, self.b, self.a_index, self.b_index) - - def _serialise_parameters(self) -> dict[str, Any]: - return { - "a": self.a._serialise_json(), - "b": self.b._serialise_json(), - "a_index": self.a_index, - "b_index": self.b_index, - } - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return TensorDot( - a=Operation.deserialise_json(parameters["a"]), - b=Operation.deserialise_json(parameters["b"]), - a_index=int(parameters["a_index"]), - b_index=int(parameters["b_index"]), - ) - - -_serialisable_classes = [ - AdditiveIdentity, - MultiplicativeIdentity, - Constant, - Variable, - Neg, - Inv, - Ln, - Exp, - Sin, - ArcSin, - Cos, - ArcCos, - Tan, - ArcTan, - Add, - Sub, - Mul, - Div, - Pow, - Log, - Transpose, - Dot, - MatMul, - TensorDot, -] - -_serialisation_lookup = {class_.serialisation_name: class_ for class_ in _serialisable_classes} +from sasdata.quantities.units import Unit class UnitError(Exception): """Errors caused by unit specification not being correct""" -def hash_data_via_numpy(*data: ArrayLike): - md5_hash = hashlib.md5() - - for datum in data: - data_bytes = np.array(datum).tobytes() - md5_hash.update(data_bytes) - - # Hash function returns a hex string, we want an int - return int(md5_hash.hexdigest(), 16) - - -##################################### -# # -# # -# # -# Quantities begin here # -# # -# # -# # -##################################### - - QuantityType = TypeVar("QuantityType") - -class QuantityHistory: - """Class that holds the information for keeping track of operations done on quantities""" - - def __init__(self, operation_tree: Operation, references: dict[int, "Quantity"]): - self.operation_tree = operation_tree - self.references = references - - self.reference_key_list = [key for key in self.references] - self.si_reference_values = {key: self.references[key].in_si() for key in self.references} - - def jacobian(self) -> list[Operation]: - """Derivative of this quantity's operation history with respect to each of the references""" - - # Use the hash value to specify the variable of differentiation - return [self.operation_tree.derivative(key) for key in self.reference_key_list] - - def _recalculate(self): - """Recalculate the value of this object - primary use case is for testing""" - return self.operation_tree.evaluate(self.references) - - def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, int] : "Quantity"] = {}): - """Do standard error propagation to calculate the uncertainties associated with this quantity - - :param quantity_units: units in which the output should be calculated - :param covariances: off diagonal entries for the covariance matrix - """ - - if covariances: - raise NotImplementedError("User specified covariances not currently implemented") - - jacobian = self.jacobian() - - evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] - - hash_values = [key for key in self.references] - output = None - - for hash_value, jac_component in zip(hash_values, evaluated_jacobian): - if output is None: - output = jac_component * (self.references[hash_value].variance * jac_component) - else: - output += jac_component * (self.references[hash_value].variance * jac_component) - - return output - - @staticmethod - def variable(quantity: "Quantity"): - """Create a history that starts with the provided data""" - return QuantityHistory(Variable(quantity.hash_value), {quantity.hash_value: quantity}) - - @staticmethod - def apply_operation( - operation: type[Operation], *histories: "QuantityHistory", **extra_parameters - ) -> "QuantityHistory": - """Apply an operation to the history - - This is slightly unsafe as it is possible to attempt to apply an n-ary operation to a number of trees other - than n, but it is relatively concise. Because it is concise we'll go with this for now and see if it causes - any problems down the line. It is a private static method to discourage misuse. - - """ - - # Copy references over, even though it overrides on collision, - # this should behave because only data based variables should be represented. - # Should not be a problem any more than losing histories - references = {} - for history in histories: - references.update(history.references) - - return QuantityHistory( - operation(*[history.operation_tree for history in histories], **extra_parameters), references - ) - - def has_variance(self): - for key in self.references: - if self.references[key].has_variance: - return True - - return False - - def summary(self): - variable_strings = [self.references[key].string_repr for key in self.references] - - s = "Variables: " + ",".join(variable_strings) - s += "\n" - s += self.operation_tree.summary() - - return s - - class Quantity[QuantityType]: - def __init__( - self, - value: QuantityType, - units: Unit, - standard_error: QuantityType | None = None, - hash_seed="", - name="", - id_header="", - ): + def __init__(self, value: QuantityType, units: Unit): self.value = value - """ Numerical value of this data, in the specified units""" - self.units = units - """ Units of this data """ - - self._hash_seed = hash_seed - """ Retain this for copying operations""" - - self.hash_value = -1 - """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ - - self._variance = None - """ Contains the variance if it is data driven """ - - if standard_error is None: - self.hash_value = hash_data_via_numpy(hash_seed, value) - else: - self._variance = standard_error**2 - self.hash_value = hash_data_via_numpy(hash_seed, value, standard_error) - - self.history = QuantityHistory.variable(self) - - self._id_header = id_header - self.name = name - - # TODO: Adding this method as a temporary measure but we need a single - # method that does this. - def with_standard_error(self, standard_error: "Quantity"): - if standard_error.units.equivalent(self.units): - return Quantity( - value=self.value, - units=self.units, - standard_error=standard_error.in_units_of(self.units), - name=self.name, - id_header=self._id_header, - ) - else: - raise UnitError( - f"Standard error units ({standard_error.units}) are not compatible with value units ({self.units})" - ) - - @property - def has_variance(self): - return self._variance is not None - - @property - def variance(self) -> "Quantity": - """Get the variance of this object""" - if self._variance is None: - return Quantity(np.zeros_like(self.value), self.units**2, name=self.name, id_header=self._id_header) - else: - return Quantity(self._variance, self.units**2) - - def _base62_hash(self) -> str: - """Encode the hash_value in base62 for better readability""" - hashed = "" - current_hash = self.hash_value - while current_hash: - digit = current_hash % 62 - if digit < 10: - hashed = f"{digit}{hashed}" - elif digit < 36: - hashed = f"{chr(55 + digit)}{hashed}" - else: - hashed = f"{chr(61 + digit)}{hashed}" - current_hash = (current_hash - digit) // 62 - return hashed - - @property - def unique_id(self) -> str: - """Get a human readable unique id for a data set""" - return f"{self._id_header}:{self.name}:{self._base62_hash()}" - - def standard_deviation(self) -> "Quantity": - return self.variance**0.5 def in_units_of(self, units: Unit) -> QuantityType: - """Get this quantity in other units""" if self.units.equivalent(units): - return (self.units.scale / units.scale) * self.value + return (units.scale / self.units.scale) * self.value else: raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") - def to_units_of(self, new_units: Unit) -> "Quantity[QuantityType]": - new_value, new_error = self.in_units_of_with_standard_error(new_units) - return Quantity( - value=new_value, - units=new_units, - standard_error=new_error, - hash_seed=self._hash_seed, - id_header=self._id_header, - ) - - def variance_in_units_of(self, units: Unit) -> QuantityType: - """Get the variance of quantity in other units""" - variance = self.variance - if variance.units.equivalent(units): - return (variance.units.scale / units.scale) * variance - else: - raise UnitError(f"Target units ({units}) not compatible with existing units ({variance.units}).") - - def in_si(self): - si_units = self.units.si_equivalent() - return self.in_units_of(si_units) - - def in_units_of_with_standard_error(self, units): - variance = self.variance - units_squared = units**2 - - if variance.units.equivalent(units_squared): - return self.in_units_of(units), np.sqrt(self.variance.in_units_of(units_squared)) - else: - raise UnitError(f"Target units ({units}) not compatible with existing units ({variance.units}).") - - def in_si_with_standard_error(self): - if self.has_variance: - return self.in_units_of_with_standard_error(self.units.si_equivalent()) - else: - return self.in_si(), None - - def explicitly_formatted(self, unit_string: str) -> str: - """Returns quantity as a string with specific unit formatting - - Performs any necessary unit conversions, but maintains the exact unit - formatting provided by the user. This can be useful if you have a - power expressed in horsepower and you want it expressed as "745.7 N m/s" and not as "745.7 W".""" - unit = parse_unit(unit_string) - quantity = self.in_units_of(unit) - return f"{quantity} {unit_string}" - - def __eq__(self: Self, other: Self) -> bool | np.ndarray: - return self.value == other.in_units_of(self.units) - - def __mul__(self: Self, other: ArrayLike | Self) -> Self: + def __mul__(self: Self, other: ArrayLike | Self ) -> Self: if isinstance(other, Quantity): - return DerivedQuantity( - self.value * other.value, - self.units * other.units, - history=QuantityHistory.apply_operation(Mul, self.history, other.history), - ) + return Quantity(self.value * other.value, self.units * other.units) else: - return DerivedQuantity( - self.value * other, - self.units, - QuantityHistory(Mul(self.history.operation_tree, Constant(other)), self.history.references), - ) + return Quantity(self.value * other, self.units) def __rmul__(self: Self, other: ArrayLike | Self): if isinstance(other, Quantity): - return DerivedQuantity( - other.value * self.value, - other.units * self.units, - history=QuantityHistory.apply_operation(Mul, other.history, self.history), - ) + return Quantity(other.value * self.value, other.units * self.units) else: - return DerivedQuantity( - other * self.value, - self.units, - QuantityHistory(Mul(Constant(other), self.history.operation_tree), self.history.references), - ) - - def __matmul__(self, other: ArrayLike | Self): - if isinstance(other, Quantity): - return DerivedQuantity( - self.value @ other.value, - self.units * other.units, - history=QuantityHistory.apply_operation(MatMul, self.history, other.history), - ) - else: - return DerivedQuantity( - self.value @ other, - self.units, - QuantityHistory(MatMul(self.history.operation_tree, Constant(other)), self.history.references), - ) + return Quantity(other * self.value, self.units) - def __rmatmul__(self, other: ArrayLike | Self): - if isinstance(other, Quantity): - return DerivedQuantity( - other.value @ self.value, - other.units * self.units, - history=QuantityHistory.apply_operation(MatMul, other.history, self.history), - ) - - else: - return DerivedQuantity( - other @ self.value, - self.units, - QuantityHistory(MatMul(Constant(other), self.history.operation_tree), self.history.references), - ) def __truediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): - return DerivedQuantity( - self.value / other.value, - self.units / other.units, - history=QuantityHistory.apply_operation(Div, self.history, other.history), - ) + return Quantity(self.value / other.value, self.units / other.units) else: - return DerivedQuantity( - self.value / other, - self.units, - QuantityHistory(Div(Constant(other), self.history.operation_tree), self.history.references), - ) + return Quantity(self.value / other, self.units) def __rtruediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): - return DerivedQuantity( - other.value / self.value, - other.units / self.units, - history=QuantityHistory.apply_operation(Div, other.history, self.history), - ) + return Quantity(self.value / other.value, self.units / other.units) else: - return DerivedQuantity( - other / self.value, - self.units**-1, - QuantityHistory(Div(Constant(other), self.history.operation_tree), self.history.references), - ) + return Quantity(self.value / other, self.units) def __add__(self: Self, other: Self | ArrayLike) -> Self: if isinstance(other, Quantity): if self.units.equivalent(other.units): - return DerivedQuantity( - self.value + (other.value * other.units.scale) / self.units.scale, - self.units, - QuantityHistory.apply_operation(Add, self.history, other.history), - ) - else: - raise UnitError(f"Units do not have the same dimensionality: {self.units} vs {other.units}") + return Quantity - else: - raise UnitError(f"Cannot perform addition/subtraction non-quantity {type(other)} with quantity") + elif self.units.dimensions.is_dimensionless: + return Quantity(other/self.units.scale, self.units) - # Don't need __radd__ because only quantity/quantity operations should be allowed + else: + raise UnitError(f"Cannot combine type {type(other)} with quantity") def __neg__(self): - return DerivedQuantity(-self.value, self.units, QuantityHistory.apply_operation(Neg, self.history)) + return Quantity(-self.value, self.units) def __sub__(self: Self, other: Self | ArrayLike) -> Self: return self + (-other) @@ -1526,121 +72,3 @@ def __sub__(self: Self, other: Self | ArrayLike) -> Self: def __rsub__(self: Self, other: Self | ArrayLike) -> Self: return (-self) + other - def __pow__(self: Self, other: int | float): - return DerivedQuantity( - self.value**other, - self.units**other, - QuantityHistory(Pow(self.history.operation_tree, other), self.history.references), - ) - - @staticmethod - def _array_repr_format(arr: np.ndarray): - """Format the array""" - order = len(arr.shape) - reshaped = arr.reshape(-1) - if len(reshaped) <= 2: - numbers = ",".join([f"{n}" for n in reshaped]) - else: - numbers = f"{reshaped[0]} ... {reshaped[-1]}" - - # if len(reshaped) <= 4: - # numbers = ",".join([f"{n}" for n in reshaped]) - # else: - # numbers = f"{reshaped[0]}, {reshaped[1]} ... {reshaped[-2]}, {reshaped[-1]}" - - return "[" * order + numbers + "]" * order - - def __repr__(self): - if isinstance(self.units, NamedUnit): - value = self.value - error = self.standard_deviation().in_units_of(self.units) - unit_string = self.units.symbol - - else: - value, error = self.in_si_with_standard_error() - unit_string = self.units.dimensions.si_repr() - - if isinstance(self.value, np.ndarray): - # Get the array in short form - numeric_string = self._array_repr_format(value) - - if self.has_variance: - numeric_string += " ± " + self._array_repr_format(error) - - else: - numeric_string = f"{value}" - if self.has_variance: - numeric_string += f" ± {error}" - - return numeric_string + " " + unit_string - - @staticmethod - def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): - pass - - @property - def string_repr(self): - return str(self.hash_value) - - def as_h5(self, group: h5py.Group, name: str): - """Add this data onto a group as a dataset under the given name""" - boxed = self.value if type(self.value) is np.ndarray else [self.value] - data = group.create_dataset(name, data=boxed) - data.attrs["units"] = self.units.ascii_symbol - - -class NamedQuantity[QuantityType](Quantity[QuantityType]): - def __init__( - self, name: str, value: QuantityType, units: Unit, standard_error: QuantityType | None = None, id_header="" - ): - super().__init__(value, units, standard_error=standard_error, hash_seed=name, name=name, id_header=id_header) - - def __repr__(self): - return f"[{self.name}] " + super().__repr__() - - def to_units_of(self, new_units: Unit) -> "NamedQuantity[QuantityType]": - new_value, new_error = self.in_units_of_with_standard_error(new_units) - return NamedQuantity(value=new_value, units=new_units, standard_error=new_error, name=self.name) - - def with_standard_error(self, standard_error: Quantity): - if standard_error.units.equivalent(self.units): - return NamedQuantity( - value=self.value, - units=self.units, - standard_error=standard_error.in_units_of(self.units), - name=self.name, - id_header=self._id_header, - ) - - else: - raise UnitError( - f"Standard error units ({standard_error.units}) are not compatible with value units ({self.units})" - ) - - @property - def string_repr(self): - return self.name - - -class DerivedQuantity[QuantityType](Quantity[QuantityType]): - def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): - super().__init__(value, units, standard_error=None) - - self.history = history - self._variance_cache = None - self._has_variance = history.has_variance() - - def to_units_of(self, new_units: Unit) -> "Quantity[QuantityType]": - # TODO: Lots of tests needed for this - return DerivedQuantity(value=self.in_units_of(new_units), units=new_units, history=self.history) - - @property - def has_variance(self): - return self._has_variance - - @property - def variance(self) -> Quantity: - if self._variance_cache is None: - self._variance_cache = self.history.variance_propagate(self.units) - - return self._variance_cache diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index c68f6f676..f15243536 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -1,11 +1,79 @@ """ -Autogenerated file by _units_table.py - - - - ******** DO NOT EDIT BY HAND ******** - +This file is autogenerated! + +Do not edit by hand, instead edit the files that build it (_build_tables.py, _units_base.py) + + + + +DDDDDDDDDDDDD NNNNNNNN NNNNNNNN tttt +D::::::::::::DDD N:::::::N N::::::N ttt:::t +D:::::::::::::::DD N::::::::N N::::::N t:::::t +DDD:::::DDDDD:::::D N:::::::::N N::::::N t:::::t + D:::::D D:::::D ooooooooooo N::::::::::N N::::::N ooooooooooo ttttttt:::::ttttttt + D:::::D D:::::D oo:::::::::::oo N:::::::::::N N::::::N oo:::::::::::oo t:::::::::::::::::t + D:::::D D:::::Do:::::::::::::::o N:::::::N::::N N::::::No:::::::::::::::ot:::::::::::::::::t + D:::::D D:::::Do:::::ooooo:::::o N::::::N N::::N N::::::No:::::ooooo:::::otttttt:::::::tttttt + D:::::D D:::::Do::::o o::::o N::::::N N::::N:::::::No::::o o::::o t:::::t + D:::::D D:::::Do::::o o::::o N::::::N N:::::::::::No::::o o::::o t:::::t + D:::::D D:::::Do::::o o::::o N::::::N N::::::::::No::::o o::::o t:::::t + D:::::D D:::::D o::::o o::::o N::::::N N:::::::::No::::o o::::o t:::::t tttttt +DDD:::::DDDDD:::::D o:::::ooooo:::::o N::::::N N::::::::No:::::ooooo:::::o t::::::tttt:::::t +D:::::::::::::::DD o:::::::::::::::o N::::::N N:::::::No:::::::::::::::o tt::::::::::::::t +D::::::::::::DDD oo:::::::::::oo N::::::N N::::::N oo:::::::::::oo tt:::::::::::tt +DDDDDDDDDDDDD ooooooooooo NNNNNNNN NNNNNNN ooooooooooo ttttttttttt + + + + + + + + + dddddddd +EEEEEEEEEEEEEEEEEEEEEE d::::::d iiii tttt BBBBBBBBBBBBBBBBB +E::::::::::::::::::::E d::::::d i::::i ttt:::t B::::::::::::::::B +E::::::::::::::::::::E d::::::d iiii t:::::t B::::::BBBBBB:::::B +EE::::::EEEEEEEEE::::E d:::::d t:::::t BB:::::B B:::::B + E:::::E EEEEEE ddddddddd:::::d iiiiiiittttttt:::::ttttttt B::::B B:::::Byyyyyyy yyyyyyy + E:::::E dd::::::::::::::d i:::::it:::::::::::::::::t B::::B B:::::B y:::::y y:::::y + E::::::EEEEEEEEEE d::::::::::::::::d i::::it:::::::::::::::::t B::::BBBBBB:::::B y:::::y y:::::y + E:::::::::::::::E d:::::::ddddd:::::d i::::itttttt:::::::tttttt B:::::::::::::BB y:::::y y:::::y + E:::::::::::::::E d::::::d d:::::d i::::i t:::::t B::::BBBBBB:::::B y:::::y y:::::y + E::::::EEEEEEEEEE d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y y:::::y + E:::::E d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y:::::y + E:::::E EEEEEEd:::::d d:::::d i::::i t:::::t tttttt B::::B B:::::B y:::::::::y +EE::::::EEEEEEEE:::::Ed::::::ddddd::::::ddi::::::i t::::::tttt:::::t BB:::::BBBBBB::::::B y:::::::y +E::::::::::::::::::::E d:::::::::::::::::di::::::i tt::::::::::::::t B:::::::::::::::::B y:::::y +E::::::::::::::::::::E d:::::::::ddd::::di::::::i tt:::::::::::tt B::::::::::::::::B y:::::y +EEEEEEEEEEEEEEEEEEEEEE ddddddddd dddddiiiiiiii ttttttttttt BBBBBBBBBBBBBBBBB y:::::y + y:::::y + y:::::y + y:::::y + y:::::y + yyyyyyy + + + + dddddddd +HHHHHHHHH HHHHHHHHH d::::::d +H:::::::H H:::::::H d::::::d +H:::::::H H:::::::H d::::::d +HH::::::H H::::::HH d:::::d + H:::::H H:::::H aaaaaaaaaaaaa nnnn nnnnnnnn ddddddddd:::::d + H:::::H H:::::H a::::::::::::a n:::nn::::::::nn dd::::::::::::::d + H::::::HHHHH::::::H aaaaaaaaa:::::an::::::::::::::nn d::::::::::::::::d + H:::::::::::::::::H a::::ann:::::::::::::::nd:::::::ddddd:::::d + H:::::::::::::::::H aaaaaaa:::::a n:::::nnnn:::::nd::::::d d:::::d + H::::::HHHHH::::::H aa::::::::::::a n::::n n::::nd:::::d d:::::d + H:::::H H:::::H a::::aaaa::::::a n::::n n::::nd:::::d d:::::d + H:::::H H:::::H a::::a a:::::a n::::n n::::nd:::::d d:::::d +HH::::::H H::::::HHa::::a a:::::a n::::n n::::nd::::::ddddd::::::dd +H:::::::H H:::::::Ha:::::aaaa::::::a n::::n n::::n d:::::::::::::::::d +H:::::::H H:::::::H a::::::::::aa:::a n::::n n::::n d:::::::::ddd::::d +HHHHHHHHH HHHHHHHHH aaaaaaaaaa aaaa nnnnnn nnnnnn ddddddddd ddddd + """ @@ -48,6 +116,11 @@ def __init__(self, self.moles_hint = moles_hint self.angle_hint = angle_hint + @property + def is_dimensionless(self): + """ Is this dimension dimensionless (ignores moles_hint and angle_hint) """ + return self.length == 0 and self.time == 0 and self.mass == 0 and self.current == 0 and self.temperature == 0 + def __mul__(self: Self, other: Self): if not isinstance(other, Dimensions): @@ -208,6 +281,11 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): for processor in format_process: pass + +class NamedUnit: + # TODO: Add named unit class + pass + # # Parsing plan: # Require unknown amounts of units to be explicitly positive or negative? @@ -504,27 +582,27 @@ def __init__(self, name: str, units: list[Unit]): stradians = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') none = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') litres = Unit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') -electronvolts = Unit(1.602e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') -exaelectronvolts = Unit(0.1602, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') -petaelectronvolts = Unit(0.0001602, Dimensions(2, -2, 1, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') -teraelectronvolts = Unit(1.602e-07, Dimensions(2, -2, 1, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') -gigaelectronvolts = Unit(1.602e-10, Dimensions(2, -2, 1, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') -megaelectronvolts = Unit(1.602e-13, Dimensions(2, -2, 1, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') -kiloelectronvolts = Unit(1.602e-16, Dimensions(2, -2, 1, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') -millielectronvolts = Unit(1.602e-22, Dimensions(2, -2, 1, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') -microelectronvolts = Unit(1.6019999999999998e-25, Dimensions(2, -2, 1, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') -nanoelectronvolts = Unit(1.602e-28, Dimensions(2, -2, 1, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') -picoelectronvolts = Unit(1.6019999999999998e-31, Dimensions(2, -2, 1, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') -femtoelectronvolts = Unit(1.602e-34, Dimensions(2, -2, 1, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') -attoelectronvolts = Unit(1.6020000000000002e-37, Dimensions(2, -2, 1, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') -atomic_mass_units = Unit(1.661e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') -moles = Unit(6.022e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') -millimoles = Unit(6.022e+20, Dimensions(0, 0, 0, 0, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') -micromoles = Unit(6.022e+17, Dimensions(0, 0, 0, 0, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') -nanomoles = Unit(602200000000000.1, Dimensions(0, 0, 0, 0, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') -picomoles = Unit(602200000000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') -femtomoles = Unit(602200000.0000001, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') -attomoles = Unit(602200.0000000001, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') +electronvolts = Unit(1.602176634e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') +exaelectronvolts = Unit(0.1602176634, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') +petaelectronvolts = Unit(0.0001602176634, Dimensions(2, -2, 1, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') +teraelectronvolts = Unit(1.602176634e-07, Dimensions(2, -2, 1, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') +gigaelectronvolts = Unit(1.6021766339999998e-10, Dimensions(2, -2, 1, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') +megaelectronvolts = Unit(1.6021766339999998e-13, Dimensions(2, -2, 1, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') +kiloelectronvolts = Unit(1.602176634e-16, Dimensions(2, -2, 1, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') +millielectronvolts = Unit(1.6021766339999998e-22, Dimensions(2, -2, 1, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') +microelectronvolts = Unit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') +nanoelectronvolts = Unit(1.602176634e-28, Dimensions(2, -2, 1, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') +picoelectronvolts = Unit(1.6021766339999998e-31, Dimensions(2, -2, 1, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') +femtoelectronvolts = Unit(1.602176634e-34, Dimensions(2, -2, 1, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') +attoelectronvolts = Unit(1.602176634e-37, Dimensions(2, -2, 1, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') +atomic_mass_units = Unit(1.660538921e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') +moles = Unit(6.02214076e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') +millimoles = Unit(6.02214076e+20, Dimensions(0, 0, 0, 0, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') +micromoles = Unit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') +nanomoles = Unit(602214076000000.0, Dimensions(0, 0, 0, 0, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') +picomoles = Unit(602214076000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') +femtomoles = Unit(602214076.0, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') +attomoles = Unit(602214.076, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') square_meters = Unit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = Unit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = Unit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -970,7 +1048,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_meter = Unit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') femtograms_per_cubic_meter = Unit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') attograms_per_cubic_meter = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_meter = Unit(1.661e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='NoneNone⁻³') +atomic_mass_units_per_cubic_meter = Unit(1.660538921e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='NoneNone⁻³') grams_per_cubic_exameter = Unit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') exagrams_per_cubic_exameter = Unit(1e-39, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') petagrams_per_cubic_exameter = Unit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') @@ -984,7 +1062,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_exameter = Unit(1e-69, Dimensions(length=-3, mass=1), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') femtograms_per_cubic_exameter = Unit(1e-72, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') attograms_per_cubic_exameter = Unit(1e-75, Dimensions(length=-3, mass=1), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -atomic_mass_units_per_cubic_exameter = Unit(1.661e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='NoneEm⁻³') +atomic_mass_units_per_cubic_exameter = Unit(1.6605389209999996e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='NoneEm⁻³') grams_per_cubic_petameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') exagrams_per_cubic_petameter = Unit(1e-30, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') petagrams_per_cubic_petameter = Unit(1e-33, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') @@ -998,7 +1076,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_petameter = Unit(1.0000000000000001e-60, Dimensions(length=-3, mass=1), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') femtograms_per_cubic_petameter = Unit(1.0000000000000002e-63, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') attograms_per_cubic_petameter = Unit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -atomic_mass_units_per_cubic_petameter = Unit(1.6610000000000003e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='NonePm⁻³') +atomic_mass_units_per_cubic_petameter = Unit(1.660538921e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='NonePm⁻³') grams_per_cubic_terameter = Unit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') exagrams_per_cubic_terameter = Unit(1e-21, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') petagrams_per_cubic_terameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') @@ -1012,7 +1090,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_terameter = Unit(1e-51, Dimensions(length=-3, mass=1), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') femtograms_per_cubic_terameter = Unit(1e-54, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') attograms_per_cubic_terameter = Unit(1.0000000000000001e-57, Dimensions(length=-3, mass=1), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -atomic_mass_units_per_cubic_terameter = Unit(1.6610000000000002e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='NoneTm⁻³') +atomic_mass_units_per_cubic_terameter = Unit(1.6605389209999997e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='NoneTm⁻³') grams_per_cubic_gigameter = Unit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') exagrams_per_cubic_gigameter = Unit(1e-12, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') petagrams_per_cubic_gigameter = Unit(1e-15, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') @@ -1026,7 +1104,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_gigameter = Unit(1e-42, Dimensions(length=-3, mass=1), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') femtograms_per_cubic_gigameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') attograms_per_cubic_gigameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -atomic_mass_units_per_cubic_gigameter = Unit(1.6610000000000002e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='NoneGm⁻³') +atomic_mass_units_per_cubic_gigameter = Unit(1.660538921e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='NoneGm⁻³') grams_per_cubic_megameter = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') exagrams_per_cubic_megameter = Unit(0.001, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') petagrams_per_cubic_megameter = Unit(1e-06, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') @@ -1040,7 +1118,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_megameter = Unit(1e-33, Dimensions(length=-3, mass=1), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') femtograms_per_cubic_megameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') attograms_per_cubic_megameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -atomic_mass_units_per_cubic_megameter = Unit(1.661e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='NoneMm⁻³') +atomic_mass_units_per_cubic_megameter = Unit(1.6605389209999997e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='NoneMm⁻³') grams_per_cubic_kilometer = Unit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') exagrams_per_cubic_kilometer = Unit(1000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') petagrams_per_cubic_kilometer = Unit(1000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') @@ -1054,7 +1132,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_kilometer = Unit(1.0000000000000001e-24, Dimensions(length=-3, mass=1), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') femtograms_per_cubic_kilometer = Unit(1e-27, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') attograms_per_cubic_kilometer = Unit(1e-30, Dimensions(length=-3, mass=1), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') -atomic_mass_units_per_cubic_kilometer = Unit(1.661e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='Nonekm⁻³') +atomic_mass_units_per_cubic_kilometer = Unit(1.6605389209999997e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='Nonekm⁻³') grams_per_cubic_millimeter = Unit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') exagrams_per_cubic_millimeter = Unit(1e+24, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') petagrams_per_cubic_millimeter = Unit(1e+21, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') @@ -1068,7 +1146,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_millimeter = Unit(1e-06, Dimensions(length=-3, mass=1), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') femtograms_per_cubic_millimeter = Unit(1e-09, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') attograms_per_cubic_millimeter = Unit(1e-12, Dimensions(length=-3, mass=1), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -atomic_mass_units_per_cubic_millimeter = Unit(1.661e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='Nonemm⁻³') +atomic_mass_units_per_cubic_millimeter = Unit(1.6605389209999997e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='Nonemm⁻³') grams_per_cubic_micrometer = Unit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') exagrams_per_cubic_micrometer = Unit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') petagrams_per_cubic_micrometer = Unit(1.0000000000000002e+30, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') @@ -1082,7 +1160,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_micrometer = Unit(1000.0000000000002, Dimensions(length=-3, mass=1), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') femtograms_per_cubic_micrometer = Unit(1.0000000000000002, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') attograms_per_cubic_micrometer = Unit(0.0010000000000000002, Dimensions(length=-3, mass=1), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') -atomic_mass_units_per_cubic_micrometer = Unit(1.6610000000000004e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='Noneµm⁻³') +atomic_mass_units_per_cubic_micrometer = Unit(1.660538921e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='Noneµm⁻³') grams_per_cubic_nanometer = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') exagrams_per_cubic_nanometer = Unit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') petagrams_per_cubic_nanometer = Unit(9.999999999999998e+38, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') @@ -1096,7 +1174,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_nanometer = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') femtograms_per_cubic_nanometer = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') attograms_per_cubic_nanometer = Unit(999999.9999999999, Dimensions(length=-3, mass=1), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -atomic_mass_units_per_cubic_nanometer = Unit(1.6609999999999998, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='Nonenm⁻³') +atomic_mass_units_per_cubic_nanometer = Unit(1.6605389209999994, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='Nonenm⁻³') grams_per_cubic_picometer = Unit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') exagrams_per_cubic_picometer = Unit(1e+51, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') petagrams_per_cubic_picometer = Unit(1e+48, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') @@ -1110,7 +1188,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_picometer = Unit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') femtograms_per_cubic_picometer = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') attograms_per_cubic_picometer = Unit(1000000000000000.1, Dimensions(length=-3, mass=1), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -atomic_mass_units_per_cubic_picometer = Unit(1661000000.0000002, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='Nonepm⁻³') +atomic_mass_units_per_cubic_picometer = Unit(1660538921.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='Nonepm⁻³') grams_per_cubic_femtometer = Unit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') exagrams_per_cubic_femtometer = Unit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') petagrams_per_cubic_femtometer = Unit(9.999999999999997e+56, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') @@ -1124,7 +1202,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_femtometer = Unit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') femtograms_per_cubic_femtometer = Unit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') attograms_per_cubic_femtometer = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -atomic_mass_units_per_cubic_femtometer = Unit(1.6609999999999997e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='Nonefm⁻³') +atomic_mass_units_per_cubic_femtometer = Unit(1.6605389209999992e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='Nonefm⁻³') grams_per_cubic_attometer = Unit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') exagrams_per_cubic_attometer = Unit(9.999999999999999e+68, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') petagrams_per_cubic_attometer = Unit(9.999999999999998e+65, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') @@ -1138,7 +1216,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_attometer = Unit(1e+39, Dimensions(length=-3, mass=1), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') femtograms_per_cubic_attometer = Unit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') attograms_per_cubic_attometer = Unit(1e+33, Dimensions(length=-3, mass=1), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') -atomic_mass_units_per_cubic_attometer = Unit(1.661e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='Noneam⁻³') +atomic_mass_units_per_cubic_attometer = Unit(1.6605389209999997e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='Noneam⁻³') grams_per_cubic_decimeter = Unit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='Nonedm⁻³') exagrams_per_cubic_decimeter = Unit(9.999999999999997e+17, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_decimeter', ascii_symbol='Eg dm^-3', symbol='Egdm⁻³') petagrams_per_cubic_decimeter = Unit(999999999999999.8, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_decimeter', ascii_symbol='Pg dm^-3', symbol='Pgdm⁻³') @@ -1152,7 +1230,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_decimeter = Unit(9.999999999999998e-13, Dimensions(length=-3, mass=1), name='picograms_per_cubic_decimeter', ascii_symbol='pg dm^-3', symbol='pgdm⁻³') femtograms_per_cubic_decimeter = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_decimeter', ascii_symbol='fg dm^-3', symbol='fgdm⁻³') attograms_per_cubic_decimeter = Unit(9.999999999999999e-19, Dimensions(length=-3, mass=1), name='attograms_per_cubic_decimeter', ascii_symbol='ag dm^-3', symbol='agdm⁻³') -atomic_mass_units_per_cubic_decimeter = Unit(1.661e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='Nonedm⁻³') +atomic_mass_units_per_cubic_decimeter = Unit(1.6605389209999993e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='Nonedm⁻³') grams_per_cubic_centimeter = Unit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='Nonecm⁻³') exagrams_per_cubic_centimeter = Unit(9.999999999999999e+20, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_centimeter', ascii_symbol='Eg cm^-3', symbol='Egcm⁻³') petagrams_per_cubic_centimeter = Unit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_centimeter', ascii_symbol='Pg cm^-3', symbol='Pgcm⁻³') @@ -1166,7 +1244,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_centimeter = Unit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='picograms_per_cubic_centimeter', ascii_symbol='pg cm^-3', symbol='pgcm⁻³') femtograms_per_cubic_centimeter = Unit(1e-12, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_centimeter', ascii_symbol='fg cm^-3', symbol='fgcm⁻³') attograms_per_cubic_centimeter = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='attograms_per_cubic_centimeter', ascii_symbol='ag cm^-3', symbol='agcm⁻³') -atomic_mass_units_per_cubic_centimeter = Unit(1.6609999999999998e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='Nonecm⁻³') +atomic_mass_units_per_cubic_centimeter = Unit(1.6605389209999996e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='Nonecm⁻³') grams_per_cubic_angstrom = Unit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') exagrams_per_cubic_angstrom = Unit(1e+45, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') petagrams_per_cubic_angstrom = Unit(9.999999999999999e+41, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') @@ -1180,119 +1258,119 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_angstrom = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') femtograms_per_cubic_angstrom = Unit(1000000000000.0, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') attograms_per_cubic_angstrom = Unit(1000000000.0, Dimensions(length=-3, mass=1), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') -atomic_mass_units_per_cubic_angstrom = Unit(1661.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='NoneÅ⁻³') -moles_per_cubic_meter = Unit(6.022e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_meter = Unit(6.022e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_meter = Unit(6.022e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_meter = Unit(602200000000000.1, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_meter = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_meter = Unit(602200000.0000001, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_meter = Unit(602200.0000000001, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolNone⁻³') -moles_per_cubic_exameter = Unit(6.022e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='NoneEm⁻³') -millimoles_per_cubic_exameter = Unit(6.021999999999999e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') -micromoles_per_cubic_exameter = Unit(6.022e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') -nanomoles_per_cubic_exameter = Unit(6.022000000000001e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') -picomoles_per_cubic_exameter = Unit(6.022e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') -femtomoles_per_cubic_exameter = Unit(6.022e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') -attomoles_per_cubic_exameter = Unit(6.0220000000000006e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') -moles_per_cubic_petameter = Unit(6.022e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='NonePm⁻³') -millimoles_per_cubic_petameter = Unit(6.022e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') -micromoles_per_cubic_petameter = Unit(6.022e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') -nanomoles_per_cubic_petameter = Unit(6.0220000000000015e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') -picomoles_per_cubic_petameter = Unit(6.022e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') -femtomoles_per_cubic_petameter = Unit(6.022000000000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') -attomoles_per_cubic_petameter = Unit(6.0220000000000015e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') -moles_per_cubic_terameter = Unit(6.022e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='NoneTm⁻³') -millimoles_per_cubic_terameter = Unit(6.022e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') -micromoles_per_cubic_terameter = Unit(6.022e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') -nanomoles_per_cubic_terameter = Unit(6.022000000000001e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') -picomoles_per_cubic_terameter = Unit(6.0219999999999995e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') -femtomoles_per_cubic_terameter = Unit(6.022000000000001e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') -attomoles_per_cubic_terameter = Unit(6.022000000000001e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') -moles_per_cubic_gigameter = Unit(0.0006022, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='NoneGm⁻³') -millimoles_per_cubic_gigameter = Unit(6.022e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') -micromoles_per_cubic_gigameter = Unit(6.021999999999999e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') -nanomoles_per_cubic_gigameter = Unit(6.022000000000001e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') -picomoles_per_cubic_gigameter = Unit(6.022e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') -femtomoles_per_cubic_gigameter = Unit(6.022000000000002e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') -attomoles_per_cubic_gigameter = Unit(6.022000000000001e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') -moles_per_cubic_megameter = Unit(602200.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='NoneMm⁻³') -millimoles_per_cubic_megameter = Unit(602.2, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') -micromoles_per_cubic_megameter = Unit(0.6022, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') -nanomoles_per_cubic_megameter = Unit(0.0006022000000000001, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') -picomoles_per_cubic_megameter = Unit(6.022e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') -femtomoles_per_cubic_megameter = Unit(6.022000000000002e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') -attomoles_per_cubic_megameter = Unit(6.022000000000001e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') -moles_per_cubic_kilometer = Unit(602200000000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='Nonekm⁻³') -millimoles_per_cubic_kilometer = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') -micromoles_per_cubic_kilometer = Unit(602200000.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') -nanomoles_per_cubic_kilometer = Unit(602200.0000000001, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') -picomoles_per_cubic_kilometer = Unit(602.2, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') -femtomoles_per_cubic_kilometer = Unit(0.6022000000000001, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') -attomoles_per_cubic_kilometer = Unit(0.0006022000000000001, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') -moles_per_cubic_millimeter = Unit(6.022e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='Nonemm⁻³') -millimoles_per_cubic_millimeter = Unit(6.0219999999999996e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') -micromoles_per_cubic_millimeter = Unit(6.022e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') -nanomoles_per_cubic_millimeter = Unit(6.022000000000001e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') -picomoles_per_cubic_millimeter = Unit(6.022e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') -femtomoles_per_cubic_millimeter = Unit(6.022000000000001e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') -attomoles_per_cubic_millimeter = Unit(602200000000000.1, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') -moles_per_cubic_micrometer = Unit(6.022000000000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='Noneµm⁻³') -millimoles_per_cubic_micrometer = Unit(6.0220000000000006e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') -micromoles_per_cubic_micrometer = Unit(6.022000000000001e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') -nanomoles_per_cubic_micrometer = Unit(6.022000000000002e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') -picomoles_per_cubic_micrometer = Unit(6.022000000000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') -femtomoles_per_cubic_micrometer = Unit(6.022000000000002e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') -attomoles_per_cubic_micrometer = Unit(6.0220000000000016e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') -moles_per_cubic_nanometer = Unit(6.021999999999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='Nonenm⁻³') -millimoles_per_cubic_nanometer = Unit(6.0219999999999984e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') -micromoles_per_cubic_nanometer = Unit(6.0219999999999985e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') -nanomoles_per_cubic_nanometer = Unit(6.022e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') -picomoles_per_cubic_nanometer = Unit(6.021999999999999e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') -femtomoles_per_cubic_nanometer = Unit(6.0219999999999995e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') -attomoles_per_cubic_nanometer = Unit(6.022e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') -moles_per_cubic_picometer = Unit(6.022e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='Nonepm⁻³') -millimoles_per_cubic_picometer = Unit(6.0220000000000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') -micromoles_per_cubic_picometer = Unit(6.0220000000000005e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') -nanomoles_per_cubic_picometer = Unit(6.022000000000002e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') -picomoles_per_cubic_picometer = Unit(6.022e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') -femtomoles_per_cubic_picometer = Unit(6.022000000000002e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') -attomoles_per_cubic_picometer = Unit(6.022000000000002e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') -moles_per_cubic_femtometer = Unit(6.021999999999999e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='Nonefm⁻³') -millimoles_per_cubic_femtometer = Unit(6.021999999999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') -micromoles_per_cubic_femtometer = Unit(6.021999999999998e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') -nanomoles_per_cubic_femtometer = Unit(6.021999999999999e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') -picomoles_per_cubic_femtometer = Unit(6.021999999999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') -femtomoles_per_cubic_femtometer = Unit(6.022e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') -attomoles_per_cubic_femtometer = Unit(6.021999999999999e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') -moles_per_cubic_attometer = Unit(6.021999999999999e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='Noneam⁻³') -millimoles_per_cubic_attometer = Unit(6.021999999999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') -micromoles_per_cubic_attometer = Unit(6.021999999999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') -nanomoles_per_cubic_attometer = Unit(6.022000000000001e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') -picomoles_per_cubic_attometer = Unit(6.021999999999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') -femtomoles_per_cubic_attometer = Unit(6.022e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') -attomoles_per_cubic_attometer = Unit(6.022e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') -moles_per_cubic_decimeter = Unit(6.0219999999999985e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='Nonedm⁻³') -millimoles_per_cubic_decimeter = Unit(6.021999999999998e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') -micromoles_per_cubic_decimeter = Unit(6.021999999999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') -nanomoles_per_cubic_decimeter = Unit(6.022e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') -picomoles_per_cubic_decimeter = Unit(602199999999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') -femtomoles_per_cubic_decimeter = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') -attomoles_per_cubic_decimeter = Unit(602200000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') -moles_per_cubic_centimeter = Unit(6.0219999999999996e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='Nonecm⁻³') -millimoles_per_cubic_centimeter = Unit(6.021999999999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') -micromoles_per_cubic_centimeter = Unit(6.021999999999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') -nanomoles_per_cubic_centimeter = Unit(6.022e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') -picomoles_per_cubic_centimeter = Unit(6.021999999999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') -femtomoles_per_cubic_centimeter = Unit(602200000000000.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') -attomoles_per_cubic_centimeter = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') -moles_per_cubic_angstrom = Unit(6.022e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='NoneÅ⁻³') -millimoles_per_cubic_angstrom = Unit(6.021999999999999e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') -micromoles_per_cubic_angstrom = Unit(6.021999999999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') -nanomoles_per_cubic_angstrom = Unit(6.022000000000001e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') -picomoles_per_cubic_angstrom = Unit(6.0219999999999994e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') -femtomoles_per_cubic_angstrom = Unit(6.0220000000000006e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') -attomoles_per_cubic_angstrom = Unit(6.022000000000001e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') +atomic_mass_units_per_cubic_angstrom = Unit(1660.5389209999996, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='NoneÅ⁻³') +moles_per_cubic_meter = Unit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_meter = Unit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_meter = Unit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_meter = Unit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_meter = Unit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_meter = Unit(602214076.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_meter = Unit(602214.076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolNone⁻³') +moles_per_cubic_exameter = Unit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='NoneEm⁻³') +millimoles_per_cubic_exameter = Unit(6.02214076e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') +micromoles_per_cubic_exameter = Unit(6.02214076e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') +nanomoles_per_cubic_exameter = Unit(6.022140759999999e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') +picomoles_per_cubic_exameter = Unit(6.022140759999999e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') +femtomoles_per_cubic_exameter = Unit(6.02214076e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') +attomoles_per_cubic_exameter = Unit(6.022140759999999e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') +moles_per_cubic_petameter = Unit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='NonePm⁻³') +millimoles_per_cubic_petameter = Unit(6.0221407600000005e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') +micromoles_per_cubic_petameter = Unit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') +nanomoles_per_cubic_petameter = Unit(6.02214076e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') +picomoles_per_cubic_petameter = Unit(6.0221407600000005e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') +femtomoles_per_cubic_petameter = Unit(6.022140760000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') +attomoles_per_cubic_petameter = Unit(6.022140760000001e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') +moles_per_cubic_terameter = Unit(6.022140759999999e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='NoneTm⁻³') +millimoles_per_cubic_terameter = Unit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') +micromoles_per_cubic_terameter = Unit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') +nanomoles_per_cubic_terameter = Unit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') +picomoles_per_cubic_terameter = Unit(6.02214076e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') +femtomoles_per_cubic_terameter = Unit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') +attomoles_per_cubic_terameter = Unit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') +moles_per_cubic_gigameter = Unit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='NoneGm⁻³') +millimoles_per_cubic_gigameter = Unit(6.022140760000001e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') +micromoles_per_cubic_gigameter = Unit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') +nanomoles_per_cubic_gigameter = Unit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') +picomoles_per_cubic_gigameter = Unit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') +femtomoles_per_cubic_gigameter = Unit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') +attomoles_per_cubic_gigameter = Unit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') +moles_per_cubic_megameter = Unit(602214.076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='NoneMm⁻³') +millimoles_per_cubic_megameter = Unit(602.214076, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') +micromoles_per_cubic_megameter = Unit(0.602214076, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') +nanomoles_per_cubic_megameter = Unit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') +picomoles_per_cubic_megameter = Unit(6.02214076e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') +femtomoles_per_cubic_megameter = Unit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') +attomoles_per_cubic_megameter = Unit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') +moles_per_cubic_kilometer = Unit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='Nonekm⁻³') +millimoles_per_cubic_kilometer = Unit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') +micromoles_per_cubic_kilometer = Unit(602214076.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') +nanomoles_per_cubic_kilometer = Unit(602214.076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') +picomoles_per_cubic_kilometer = Unit(602.214076, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') +femtomoles_per_cubic_kilometer = Unit(0.602214076, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') +attomoles_per_cubic_kilometer = Unit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') +moles_per_cubic_millimeter = Unit(6.0221407599999995e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='Nonemm⁻³') +millimoles_per_cubic_millimeter = Unit(6.02214076e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') +micromoles_per_cubic_millimeter = Unit(6.0221407599999996e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') +nanomoles_per_cubic_millimeter = Unit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') +picomoles_per_cubic_millimeter = Unit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') +femtomoles_per_cubic_millimeter = Unit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') +attomoles_per_cubic_millimeter = Unit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') +moles_per_cubic_micrometer = Unit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='Noneµm⁻³') +millimoles_per_cubic_micrometer = Unit(6.022140760000001e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') +micromoles_per_cubic_micrometer = Unit(6.0221407600000004e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') +nanomoles_per_cubic_micrometer = Unit(6.022140760000001e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') +picomoles_per_cubic_micrometer = Unit(6.022140760000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') +femtomoles_per_cubic_micrometer = Unit(6.022140760000001e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') +attomoles_per_cubic_micrometer = Unit(6.0221407600000005e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') +moles_per_cubic_nanometer = Unit(6.022140759999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='Nonenm⁻³') +millimoles_per_cubic_nanometer = Unit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') +micromoles_per_cubic_nanometer = Unit(6.022140759999999e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') +nanomoles_per_cubic_nanometer = Unit(6.022140759999998e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') +picomoles_per_cubic_nanometer = Unit(6.0221407599999985e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') +femtomoles_per_cubic_nanometer = Unit(6.022140759999999e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') +attomoles_per_cubic_nanometer = Unit(6.022140759999999e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') +moles_per_cubic_picometer = Unit(6.0221407600000005e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='Nonepm⁻³') +millimoles_per_cubic_picometer = Unit(6.0221407600000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') +micromoles_per_cubic_picometer = Unit(6.022140760000001e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') +nanomoles_per_cubic_picometer = Unit(6.0221407600000005e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') +picomoles_per_cubic_picometer = Unit(6.02214076e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') +femtomoles_per_cubic_picometer = Unit(6.022140760000001e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') +attomoles_per_cubic_picometer = Unit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') +moles_per_cubic_femtometer = Unit(6.022140759999998e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='Nonefm⁻³') +millimoles_per_cubic_femtometer = Unit(6.022140759999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') +micromoles_per_cubic_femtometer = Unit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') +nanomoles_per_cubic_femtometer = Unit(6.022140759999998e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') +picomoles_per_cubic_femtometer = Unit(6.022140759999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') +femtomoles_per_cubic_femtometer = Unit(6.022140759999998e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') +attomoles_per_cubic_femtometer = Unit(6.022140759999998e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') +moles_per_cubic_attometer = Unit(6.022140759999998e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='Noneam⁻³') +millimoles_per_cubic_attometer = Unit(6.022140759999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') +micromoles_per_cubic_attometer = Unit(6.022140759999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') +nanomoles_per_cubic_attometer = Unit(6.022140759999999e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') +picomoles_per_cubic_attometer = Unit(6.022140759999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') +femtomoles_per_cubic_attometer = Unit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') +attomoles_per_cubic_attometer = Unit(6.022140759999999e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') +moles_per_cubic_decimeter = Unit(6.022140759999998e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='Nonedm⁻³') +millimoles_per_cubic_decimeter = Unit(6.0221407599999985e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') +micromoles_per_cubic_decimeter = Unit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') +nanomoles_per_cubic_decimeter = Unit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') +picomoles_per_cubic_decimeter = Unit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') +femtomoles_per_cubic_decimeter = Unit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') +attomoles_per_cubic_decimeter = Unit(602214075.9999999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') +moles_per_cubic_centimeter = Unit(6.022140759999999e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='Nonecm⁻³') +millimoles_per_cubic_centimeter = Unit(6.022140759999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') +micromoles_per_cubic_centimeter = Unit(6.022140759999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') +nanomoles_per_cubic_centimeter = Unit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') +picomoles_per_cubic_centimeter = Unit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') +femtomoles_per_cubic_centimeter = Unit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') +attomoles_per_cubic_centimeter = Unit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') +moles_per_cubic_angstrom = Unit(6.022140759999999e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='NoneÅ⁻³') +millimoles_per_cubic_angstrom = Unit(6.02214076e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') +micromoles_per_cubic_angstrom = Unit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') +nanomoles_per_cubic_angstrom = Unit(6.02214076e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') +picomoles_per_cubic_angstrom = Unit(6.02214076e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') +femtomoles_per_cubic_angstrom = Unit(6.022140759999999e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') +attomoles_per_cubic_angstrom = Unit(6.02214076e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') # # Lookup table from symbols to units diff --git a/sasdata/transforms/operation.py b/sasdata/transforms/operation.py index e8bf15cfd..59121882d 100644 --- a/sasdata/transforms/operation.py +++ b/sasdata/transforms/operation.py @@ -1,5 +1,5 @@ import numpy as np -from sasdata.quantities.quantities import Quantity +from sasdata.quantities.quantity import Quantity class Operation: """ Sketch of what model post-processing classes might look like """ From c79ec8df6c4452623cd6155262322d8fa97e99da Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 15:09:31 +0100 Subject: [PATCH 011/675] Some tests --- sasdata/quantities/_build_tables.py | 5 ++- sasdata/quantities/accessors.py | 4 ++ sasdata/quantities/quantities_tests.py | 37 +++++++++++++++++ sasdata/quantities/quantity.py | 4 +- sasdata/quantities/units.py | 55 ++++++++++++++------------ sasdata/quantities/units_tests.py | 46 +++++++++++++++++++++ 6 files changed, 122 insertions(+), 29 deletions(-) create mode 100644 sasdata/quantities/quantities_tests.py create mode 100644 sasdata/quantities/units_tests.py diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index fb4ab7bf8..d123a2170 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -35,7 +35,7 @@ ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, 0, 0, all_magnitudes + unusual_magnitudes), ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, 0, 0, smaller_magnitudes), ("g", None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, 0, 0, all_magnitudes), - ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), + ("A", None, "ampere", "amperes", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, 0, 0, all_magnitudes) ] derived_si_units = [ @@ -68,7 +68,8 @@ ("l", None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), ("eV", None, "electronvolt", "electronvolts", 1.602176634e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), ("au", None, "atomic mass unit", "atomic mass units", 1.660538921e-27, 0, 0, 1, 0, 0, 0, 0, []), - ("mol", None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes) + ("mol", None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes), + ("kgForce", None, "kg force", "kg force", 9.80665, 1, -2, 1, 0, 0, 0, 0, []) ] aliases = { diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 575298eb9..53cfca732 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -2994,6 +2994,10 @@ def femtonewtons(self) -> T: def attonewtons(self) -> T: return self.quantity.in_units_of(units.attonewtons) + @property + def kg_force(self) -> T: + return self.quantity.in_units_of(units.kg_force) + class PressureAccessor[T](Accessor[T]): diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py new file mode 100644 index 000000000..6c8a2d728 --- /dev/null +++ b/sasdata/quantities/quantities_tests.py @@ -0,0 +1,37 @@ +import numpy as np + +from sasdata.quantities.quantity import Quantity, UnitError +import sasdata.quantities.units as units +import pytest +def test_in_units_of_calculation(): + """ Just a couple of unit conversions """ + assert Quantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 + assert Quantity(10, units.minutes).in_units_of(units.seconds) == 600 + assert Quantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000/9.81, abs=1) + assert Quantity(0, units.meters).in_units_of(units.exameters) == 0 + + +def test_unit_compounding_pow(): + assert (Quantity(1, units.millimeters)**2).in_units_of(units.square_meters) == 1e-6 + assert (Quantity(1, units.minutes)**3).in_units_of(units.seconds**3) == 60**3 + +def test_unit_compounding_mul(): + assert (Quantity(4, units.minutes) * Quantity(0.25, units.hertz)).in_units_of(units.none) == 60 + assert (Quantity(250, units.volts) * Quantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 + +def test_unit_compounding_div(): + assert (Quantity(10, units.kilometers) / Quantity(2, units.minutes) + ).in_units_of(units.meters_per_second) == pytest.approx(250/3, abs=1e-6) + + assert (Quantity(1, units.nanowebers) / (Quantity(1, units.millimeters)**2)).in_units_of(units.millitesla) == 1 + +def test_value_mul(): + assert (Quantity(1j, units.seconds) * Quantity(1j, units.watts)).in_units_of(units.joules) == -1 + + +def test_conversion_errors(): + + + + with pytest.raises(UnitError): + Quantity(1, units.seconds).in_units_of(units.meters) \ No newline at end of file diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index f69a5afbb..7d42dccef 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -19,7 +19,7 @@ def __init__(self, value: QuantityType, units: Unit): def in_units_of(self, units: Unit) -> QuantityType: if self.units.equivalent(units): - return (units.scale / self.units.scale) * self.value + return (self.units.scale / units.scale) * self.value else: raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") @@ -72,3 +72,5 @@ def __sub__(self: Self, other: Self | ArrayLike) -> Self: def __rsub__(self: Self, other: Self | ArrayLike) -> Self: return (-self) + other + def __pow__(self: Self, other: int): + return Quantity(self.value**other, self.units**other) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index f15243536..24ca20761 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -376,19 +376,19 @@ def __init__(self, name: str, units: list[Unit]): picograms = Unit(1e-15, Dimensions(0, 0, 1, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') femtograms = Unit(1e-18, Dimensions(0, 0, 1, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') attograms = Unit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') -amps = Unit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amps',ascii_symbol='A',symbol='A') -exaamps = Unit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamps',ascii_symbol='EA',symbol='EA') -petaamps = Unit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamps',ascii_symbol='PA',symbol='PA') -teraamps = Unit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamps',ascii_symbol='TA',symbol='TA') -gigaamps = Unit(1000000000.0, Dimensions(0, 0, 0, 1, 0),name='gigaamps',ascii_symbol='GA',symbol='GA') -megaamps = Unit(1000000.0, Dimensions(0, 0, 0, 1, 0),name='megaamps',ascii_symbol='MA',symbol='MA') -kiloamps = Unit(1000.0, Dimensions(0, 0, 0, 1, 0),name='kiloamps',ascii_symbol='kA',symbol='kA') -milliamps = Unit(0.001, Dimensions(0, 0, 0, 1, 0),name='milliamps',ascii_symbol='mA',symbol='mA') -microamps = Unit(1e-06, Dimensions(0, 0, 0, 1, 0),name='microamps',ascii_symbol='uA',symbol='µA') -nanoamps = Unit(1e-09, Dimensions(0, 0, 0, 1, 0),name='nanoamps',ascii_symbol='nA',symbol='nA') -picoamps = Unit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamps',ascii_symbol='pA',symbol='pA') -femtoamps = Unit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamps',ascii_symbol='fA',symbol='fA') -attoamps = Unit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamps',ascii_symbol='aA',symbol='aA') +amperes = Unit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amperes',ascii_symbol='A',symbol='A') +exaamperes = Unit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamperes',ascii_symbol='EA',symbol='EA') +petaamperes = Unit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamperes',ascii_symbol='PA',symbol='PA') +teraamperes = Unit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamperes',ascii_symbol='TA',symbol='TA') +gigaamperes = Unit(1000000000.0, Dimensions(0, 0, 0, 1, 0),name='gigaamperes',ascii_symbol='GA',symbol='GA') +megaamperes = Unit(1000000.0, Dimensions(0, 0, 0, 1, 0),name='megaamperes',ascii_symbol='MA',symbol='MA') +kiloamperes = Unit(1000.0, Dimensions(0, 0, 0, 1, 0),name='kiloamperes',ascii_symbol='kA',symbol='kA') +milliamperes = Unit(0.001, Dimensions(0, 0, 0, 1, 0),name='milliamperes',ascii_symbol='mA',symbol='mA') +microamperes = Unit(1e-06, Dimensions(0, 0, 0, 1, 0),name='microamperes',ascii_symbol='uA',symbol='µA') +nanoamperes = Unit(1e-09, Dimensions(0, 0, 0, 1, 0),name='nanoamperes',ascii_symbol='nA',symbol='nA') +picoamperes = Unit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamperes',ascii_symbol='pA',symbol='pA') +femtoamperes = Unit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamperes',ascii_symbol='fA',symbol='fA') +attoamperes = Unit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamperes',ascii_symbol='aA',symbol='aA') kelvin = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kelvin',ascii_symbol='K',symbol='K') exakelvin = Unit(1e+18, Dimensions(0, 0, 0, 0, 1),name='exakelvin',ascii_symbol='EK',symbol='EK') petakelvin = Unit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1),name='petakelvin',ascii_symbol='PK',symbol='PK') @@ -603,6 +603,7 @@ def __init__(self, name: str, units: list[Unit]): picomoles = Unit(602214076000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') femtomoles = Unit(602214076.0, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') attomoles = Unit(602214.076, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') +kg_force = Unit(9.80665, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kg_force',ascii_symbol='kgForce',symbol='kgForce') square_meters = Unit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = Unit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = Unit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -1416,19 +1417,19 @@ def __init__(self, name: str, units: list[Unit]): "fg": femtograms, "ag": attograms, "A": angstroms, - "EA": exaamps, - "PA": petaamps, - "TA": teraamps, - "GA": gigaamps, - "MA": megaamps, - "kA": kiloamps, - "mA": milliamps, - "uA": microamps, - "µA": microamps, - "nA": nanoamps, - "pA": picoamps, - "fA": femtoamps, - "aA": attoamps, + "EA": exaamperes, + "PA": petaamperes, + "TA": teraamperes, + "GA": gigaamperes, + "MA": megaamperes, + "kA": kiloamperes, + "mA": milliamperes, + "uA": microamperes, + "µA": microamperes, + "nA": nanoamperes, + "pA": picoamperes, + "fA": femtoamperes, + "aA": attoamperes, "K": kelvin, "EK": exakelvin, "PK": petakelvin, @@ -1671,6 +1672,7 @@ def __init__(self, name: str, units: list[Unit]): "pmol": picomoles, "fmol": femtomoles, "amol": attomoles, + "kgForce": kg_force, "yr": years, "year": years, "day": days, @@ -2455,6 +2457,7 @@ def __init__(self, name: str, units: list[Unit]): piconewtons, femtonewtons, attonewtons, + kg_force, ]) pressure = UnitGroup( diff --git a/sasdata/quantities/units_tests.py b/sasdata/quantities/units_tests.py new file mode 100644 index 000000000..d0d090934 --- /dev/null +++ b/sasdata/quantities/units_tests.py @@ -0,0 +1,46 @@ +import sasdata.quantities.units as units +from sasdata.quantities.units import Unit + +class EqualUnits: + def __init__(self, test_name: str, *units): + self.test_name = "Equality: " + test_name + self.units: list[Unit] = list(units) + + def run_test(self): + for i, unit_1 in enumerate(self.units): + for unit_2 in self.units[i+1:]: + assert unit_1.equivalent(unit_2), "Units should be equivalent" + assert unit_1 == unit_2, "Units should be equal" + + +class EquivalentButUnequalUnits: + def __init__(self, test_name: str, *units): + self.test_name = "Equivalence: " + test_name + self.units: list[Unit] = list(units) + + def run_test(self): + for i, unit_1 in enumerate(self.units): + for unit_2 in self.units[i+1:]: + assert unit_1.equivalent(unit_2), "Units should be equivalent" + assert unit_1 != unit_2, "Units should not be equal" + + +tests = [ + + EqualUnits("Pressure", + units.pascals, + units.newtons / units.meters ** 2, + units.micronewtons * units.millimeters ** -2), + + EqualUnits("Resistance", + units.ohms, + units.volts / units.amperes, + 1e-3/units.millisiemens) + + +] + + +for test in tests: + print(test.test_name) + test.run_test() \ No newline at end of file From 6f1daabfa1ab88ac846acbb00312546d7b727b0a Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 15:16:51 +0100 Subject: [PATCH 012/675] SI unit module --- sasdata/quantities/_build_tables.py | 13 +- sasdata/quantities/si.py | 195 ++++++++++++---------------- sasdata/quantities/units.py | 2 +- 3 files changed, 97 insertions(+), 113 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index d123a2170..a7f9c1ab3 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -52,7 +52,6 @@ ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, 0, 0, all_magnitudes), ("T", None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, 0, 0, all_magnitudes), ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), - ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []) ] non_si_units = [ @@ -69,7 +68,8 @@ ("eV", None, "electronvolt", "electronvolts", 1.602176634e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), ("au", None, "atomic mass unit", "atomic mass units", 1.660538921e-27, 0, 0, 1, 0, 0, 0, 0, []), ("mol", None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes), - ("kgForce", None, "kg force", "kg force", 9.80665, 1, -2, 1, 0, 0, 0, 0, []) + ("kgForce", None, "kg force", "kg force", 9.80665, 1, -2, 1, 0, 0, 0, 0, []), + ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []) ] aliases = { @@ -346,4 +346,11 @@ def format_name(name: str): f" return self.quantity.in_units_of(units.{unit_name})\n" f"\n") - fid.write("\n") + fid.write("\n")with open("si.py", 'w') as fid: + + fid.write('"""'+(warning_text%"_build_tables.py")+'"""\n\n') + si_unit_names = [values[3] for values in base_si_units + derived_si_units if values[3] != "grams"] + ["kilograms"] + + for name in si_unit_names: + + fid.write(f"from sasdata.quantities.units import {name}\n") \ No newline at end of file diff --git a/sasdata/quantities/si.py b/sasdata/quantities/si.py index 45961b0bf..039cdb8e8 100644 --- a/sasdata/quantities/si.py +++ b/sasdata/quantities/si.py @@ -5,117 +5,94 @@ Do not edit by hand, instead edit the files that build it (_build_tables.py) - - -DDDDDDDDDDDDD NNNNNNNN NNNNNNNN tttt -D::::::::::::DDD N:::::::N N::::::N ttt:::t -D:::::::::::::::DD N::::::::N N::::::N t:::::t -DDD:::::DDDDD:::::D N:::::::::N N::::::N t:::::t - D:::::D D:::::D ooooooooooo N::::::::::N N::::::N ooooooooooo ttttttt:::::ttttttt - D:::::D D:::::D oo:::::::::::oo N:::::::::::N N::::::N oo:::::::::::oo t:::::::::::::::::t - D:::::D D:::::Do:::::::::::::::o N:::::::N::::N N::::::No:::::::::::::::ot:::::::::::::::::t - D:::::D D:::::Do:::::ooooo:::::o N::::::N N::::N N::::::No:::::ooooo:::::otttttt:::::::tttttt - D:::::D D:::::Do::::o o::::o N::::::N N::::N:::::::No::::o o::::o t:::::t - D:::::D D:::::Do::::o o::::o N::::::N N:::::::::::No::::o o::::o t:::::t - D:::::D D:::::Do::::o o::::o N::::::N N::::::::::No::::o o::::o t:::::t - D:::::D D:::::D o::::o o::::o N::::::N N:::::::::No::::o o::::o t:::::t tttttt -DDD:::::DDDDD:::::D o:::::ooooo:::::o N::::::N N::::::::No:::::ooooo:::::o t::::::tttt:::::t -D:::::::::::::::DD o:::::::::::::::o N::::::N N:::::::No:::::::::::::::o tt::::::::::::::t -D::::::::::::DDD oo:::::::::::oo N::::::N N::::::N oo:::::::::::oo tt:::::::::::tt -DDDDDDDDDDDDD ooooooooooo NNNNNNNN NNNNNNN ooooooooooo ttttttttttt - - - - - - - - - dddddddd -EEEEEEEEEEEEEEEEEEEEEE d::::::d iiii tttt BBBBBBBBBBBBBBBBB -E::::::::::::::::::::E d::::::d i::::i ttt:::t B::::::::::::::::B -E::::::::::::::::::::E d::::::d iiii t:::::t B::::::BBBBBB:::::B -EE::::::EEEEEEEEE::::E d:::::d t:::::t BB:::::B B:::::B + + +DDDDDDDDDDDDD NNNNNNNN NNNNNNNN tttt +D::::::::::::DDD N:::::::N N::::::N ttt:::t +D:::::::::::::::DD N::::::::N N::::::N t:::::t +DDD:::::DDDDD:::::D N:::::::::N N::::::N t:::::t + D:::::D D:::::D ooooooooooo N::::::::::N N::::::N ooooooooooo ttttttt:::::ttttttt + D:::::D D:::::D oo:::::::::::oo N:::::::::::N N::::::N oo:::::::::::oo t:::::::::::::::::t + D:::::D D:::::Do:::::::::::::::o N:::::::N::::N N::::::No:::::::::::::::ot:::::::::::::::::t + D:::::D D:::::Do:::::ooooo:::::o N::::::N N::::N N::::::No:::::ooooo:::::otttttt:::::::tttttt + D:::::D D:::::Do::::o o::::o N::::::N N::::N:::::::No::::o o::::o t:::::t + D:::::D D:::::Do::::o o::::o N::::::N N:::::::::::No::::o o::::o t:::::t + D:::::D D:::::Do::::o o::::o N::::::N N::::::::::No::::o o::::o t:::::t + D:::::D D:::::D o::::o o::::o N::::::N N:::::::::No::::o o::::o t:::::t tttttt +DDD:::::DDDDD:::::D o:::::ooooo:::::o N::::::N N::::::::No:::::ooooo:::::o t::::::tttt:::::t +D:::::::::::::::DD o:::::::::::::::o N::::::N N:::::::No:::::::::::::::o tt::::::::::::::t +D::::::::::::DDD oo:::::::::::oo N::::::N N::::::N oo:::::::::::oo tt:::::::::::tt +DDDDDDDDDDDDD ooooooooooo NNNNNNNN NNNNNNN ooooooooooo ttttttttttt + + + + + + + + + dddddddd +EEEEEEEEEEEEEEEEEEEEEE d::::::d iiii tttt BBBBBBBBBBBBBBBBB +E::::::::::::::::::::E d::::::d i::::i ttt:::t B::::::::::::::::B +E::::::::::::::::::::E d::::::d iiii t:::::t B::::::BBBBBB:::::B +EE::::::EEEEEEEEE::::E d:::::d t:::::t BB:::::B B:::::B E:::::E EEEEEE ddddddddd:::::d iiiiiiittttttt:::::ttttttt B::::B B:::::Byyyyyyy yyyyyyy - E:::::E dd::::::::::::::d i:::::it:::::::::::::::::t B::::B B:::::B y:::::y y:::::y - E::::::EEEEEEEEEE d::::::::::::::::d i::::it:::::::::::::::::t B::::BBBBBB:::::B y:::::y y:::::y - E:::::::::::::::E d:::::::ddddd:::::d i::::itttttt:::::::tttttt B:::::::::::::BB y:::::y y:::::y - E:::::::::::::::E d::::::d d:::::d i::::i t:::::t B::::BBBBBB:::::B y:::::y y:::::y - E::::::EEEEEEEEEE d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y y:::::y - E:::::E d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y:::::y - E:::::E EEEEEEd:::::d d:::::d i::::i t:::::t tttttt B::::B B:::::B y:::::::::y -EE::::::EEEEEEEE:::::Ed::::::ddddd::::::ddi::::::i t::::::tttt:::::t BB:::::BBBBBB::::::B y:::::::y -E::::::::::::::::::::E d:::::::::::::::::di::::::i tt::::::::::::::t B:::::::::::::::::B y:::::y -E::::::::::::::::::::E d:::::::::ddd::::di::::::i tt:::::::::::tt B::::::::::::::::B y:::::y -EEEEEEEEEEEEEEEEEEEEEE ddddddddd dddddiiiiiiii ttttttttttt BBBBBBBBBBBBBBBBB y:::::y - y:::::y - y:::::y - y:::::y - y:::::y - yyyyyyy - - - - dddddddd -HHHHHHHHH HHHHHHHHH d::::::d -H:::::::H H:::::::H d::::::d -H:::::::H H:::::::H d::::::d -HH::::::H H::::::HH d:::::d - H:::::H H:::::H aaaaaaaaaaaaa nnnn nnnnnnnn ddddddddd:::::d - H:::::H H:::::H a::::::::::::a n:::nn::::::::nn dd::::::::::::::d - H::::::HHHHH::::::H aaaaaaaaa:::::an::::::::::::::nn d::::::::::::::::d - H:::::::::::::::::H a::::ann:::::::::::::::nd:::::::ddddd:::::d - H:::::::::::::::::H aaaaaaa:::::a n:::::nnnn:::::nd::::::d d:::::d - H::::::HHHHH::::::H aa::::::::::::a n::::n n::::nd:::::d d:::::d - H:::::H H:::::H a::::aaaa::::::a n::::n n::::nd:::::d d:::::d - H:::::H H:::::H a::::a a:::::a n::::n n::::nd:::::d d:::::d -HH::::::H H::::::HHa::::a a:::::a n::::n n::::nd::::::ddddd::::::dd -H:::::::H H:::::::Ha:::::aaaa::::::a n::::n n::::n d:::::::::::::::::d -H:::::::H H:::::::H a::::::::::aa:::a n::::n n::::n d:::::::::ddd::::d -HHHHHHHHH HHHHHHHHH aaaaaaaaaa aaaa nnnnnn nnnnnn ddddddddd ddddd - + E:::::E dd::::::::::::::d i:::::it:::::::::::::::::t B::::B B:::::B y:::::y y:::::y + E::::::EEEEEEEEEE d::::::::::::::::d i::::it:::::::::::::::::t B::::BBBBBB:::::B y:::::y y:::::y + E:::::::::::::::E d:::::::ddddd:::::d i::::itttttt:::::::tttttt B:::::::::::::BB y:::::y y:::::y + E:::::::::::::::E d::::::d d:::::d i::::i t:::::t B::::BBBBBB:::::B y:::::y y:::::y + E::::::EEEEEEEEEE d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y y:::::y + E:::::E d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y:::::y + E:::::E EEEEEEd:::::d d:::::d i::::i t:::::t tttttt B::::B B:::::B y:::::::::y +EE::::::EEEEEEEE:::::Ed::::::ddddd::::::ddi::::::i t::::::tttt:::::t BB:::::BBBBBB::::::B y:::::::y +E::::::::::::::::::::E d:::::::::::::::::di::::::i tt::::::::::::::t B:::::::::::::::::B y:::::y +E::::::::::::::::::::E d:::::::::ddd::::di::::::i tt:::::::::::tt B::::::::::::::::B y:::::y +EEEEEEEEEEEEEEEEEEEEEE ddddddddd dddddiiiiiiii ttttttttttt BBBBBBBBBBBBBBBBB y:::::y + y:::::y + y:::::y + y:::::y + y:::::y + yyyyyyy + + + + dddddddd +HHHHHHHHH HHHHHHHHH d::::::d +H:::::::H H:::::::H d::::::d +H:::::::H H:::::::H d::::::d +HH::::::H H::::::HH d:::::d + H:::::H H:::::H aaaaaaaaaaaaa nnnn nnnnnnnn ddddddddd:::::d + H:::::H H:::::H a::::::::::::a n:::nn::::::::nn dd::::::::::::::d + H::::::HHHHH::::::H aaaaaaaaa:::::an::::::::::::::nn d::::::::::::::::d + H:::::::::::::::::H a::::ann:::::::::::::::nd:::::::ddddd:::::d + H:::::::::::::::::H aaaaaaa:::::a n:::::nnnn:::::nd::::::d d:::::d + H::::::HHHHH::::::H aa::::::::::::a n::::n n::::nd:::::d d:::::d + H:::::H H:::::H a::::aaaa::::::a n::::n n::::nd:::::d d:::::d + H:::::H H:::::H a::::a a:::::a n::::n n::::nd:::::d d:::::d +HH::::::H H::::::HHa::::a a:::::a n::::n n::::nd::::::ddddd::::::dd +H:::::::H H:::::::Ha:::::aaaa::::::a n::::n n::::n d:::::::::::::::::d +H:::::::H H:::::::H a::::::::::aa:::a n::::n n::::n d:::::::::ddd::::d +HHHHHHHHH HHHHHHHHH aaaaaaaaaa aaaa nnnnnn nnnnnn ddddddddd ddddd + """ -from sasdata.quantities.units import ( - amperes, - coulombs, - farads, - henry, - hertz, - joules, - kelvin, - kilograms, - meters, - newtons, - ohms, - pascals, - seconds, - siemens, - tesla, - volts, - watts, - webers, -) - -all_si = [ - amperes, - coulombs, - farads, - henry, - hertz, - joules, - kelvin, - kilograms, - meters, - newtons, - ohms, - pascals, - seconds, - siemens, - tesla, - volts, - watts, - webers, -] +from sasdata.quantities.units import meters +from sasdata.quantities.units import seconds +from sasdata.quantities.units import amperes +from sasdata.quantities.units import kelvin +from sasdata.quantities.units import hertz +from sasdata.quantities.units import newtons +from sasdata.quantities.units import pascals +from sasdata.quantities.units import joules +from sasdata.quantities.units import watts +from sasdata.quantities.units import coulombs +from sasdata.quantities.units import volts +from sasdata.quantities.units import ohms +from sasdata.quantities.units import farads +from sasdata.quantities.units import siemens +from sasdata.quantities.units import webers +from sasdata.quantities.units import tesla +from sasdata.quantities.units import henry +from sasdata.quantities.units import kilograms diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 24ca20761..04907bfc6 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -571,7 +571,6 @@ def __init__(self, name: str, units: list[Unit]): picohenry = Unit(1e-12, Dimensions(2, -2, 1, -2, 0),name='picohenry',ascii_symbol='pH',symbol='pH') femtohenry = Unit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') attohenry = Unit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') -degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') minutes = Unit(60, Dimensions(0, 1, 0, 0, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') hours = Unit(360, Dimensions(0, 1, 0, 0, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') @@ -604,6 +603,7 @@ def __init__(self, name: str, units: list[Unit]): femtomoles = Unit(602214076.0, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') attomoles = Unit(602214.076, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') kg_force = Unit(9.80665, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kg_force',ascii_symbol='kgForce',symbol='kgForce') +degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') square_meters = Unit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = Unit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = Unit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') From fa5e853afb6a458f15877320b9cadf71ee935415 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 15:21:34 +0100 Subject: [PATCH 013/675] si unit list --- sasdata/quantities/_build_tables.py | 5 ++++- sasdata/quantities/si.py | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index a7f9c1ab3..f5d25e828 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -353,4 +353,7 @@ def format_name(name: str): for name in si_unit_names: - fid.write(f"from sasdata.quantities.units import {name}\n") \ No newline at end of file + fid.write(f"from sasdata.quantities.units import {name}\n") fid.write("\nall_si = [\n") + for name in si_unit_names: + fid.write(f" {name},\n") + fid.write("]\n") \ No newline at end of file diff --git a/sasdata/quantities/si.py b/sasdata/quantities/si.py index 039cdb8e8..871b6ee26 100644 --- a/sasdata/quantities/si.py +++ b/sasdata/quantities/si.py @@ -96,3 +96,24 @@ from sasdata.quantities.units import tesla from sasdata.quantities.units import henry from sasdata.quantities.units import kilograms + +all_si = [ + meters, + seconds, + amperes, + kelvin, + hertz, + newtons, + pascals, + joules, + watts, + coulombs, + volts, + ohms, + farads, + siemens, + webers, + tesla, + henry, + kilograms, +] From 0fc377c956bf6e10b718d2db34b4e2b105a93c14 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 16:37:17 +0100 Subject: [PATCH 014/675] More tests, added names --- sasdata/quantities/_build_tables.py | 34 +- sasdata/quantities/_units_base.py | 57 +- sasdata/quantities/accessors.py | 948 +++++++++ sasdata/quantities/quantities_tests.py | 58 +- sasdata/quantities/quantity.py | 11 +- sasdata/quantities/units.py | 2580 ++++++++++++++---------- 6 files changed, 2592 insertions(+), 1096 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index f5d25e828..f660d7dcd 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -54,7 +54,7 @@ ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), ] -non_si_units = [ +non_si_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, 0, 0, []), ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, 0, 0, []), ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, 0, 0, []), @@ -69,7 +69,14 @@ ("au", None, "atomic mass unit", "atomic mass units", 1.660538921e-27, 0, 0, 1, 0, 0, 0, 0, []), ("mol", None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes), ("kgForce", None, "kg force", "kg force", 9.80665, 1, -2, 1, 0, 0, 0, 0, []), - ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []) + ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []), + ("miles", None, "mile", "miles", 1760*3*0.3048, 1, 0, 0, 0, 0, 0, 0, []), + ("yrd", None, "yard", "yards", 3*0.3048, 1, 0, 0, 0, 0, 0, 0, []), + ("ft", None, "foot", "feet", 0.3048, 1, 0, 0, 0, 0, 0, 0, []), + ("in", None, "inch", "inches", 0.0254, 1, 0, 0, 0, 0, 0, 0, []), + ("lb", None, "pound", "pounds", 0.45359237, 0, 0, 1, 0, 0, 0, 0, []), + ("oz", None, "ounce", "ounces", 0.45359237/16, 0, 0, 1, 0, 0, 0, 0, []), + ("psi", None, "pound force per square inch", "pound force per square inch", 6.894757e3, -1, -2, 1, 0, 0, 0, 0, []), ] aliases = { @@ -113,13 +120,20 @@ def format_name(name: str): unit_types_temp = defaultdict(list) # Keep track of unit types unit_types = defaultdict(list) - for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, moles_hint, angle_hint, magnitudes in all_units: + for unit_def in all_units: + + try: + symbol, special_symbol, singular, plural, scale, length, time, \ + mass, current, temperature, moles_hint, angle_hint, magnitudes = unit_def + except Exception as e: + print(unit_def) + raise e formatted_plural = format_name(plural) formatted_singular = format_name(singular) dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) - fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," + fid.write(f"{formatted_plural} = NamedUnit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," f"name='{formatted_plural}'," f"ascii_symbol='{symbol}'," f"symbol='{symbol if special_symbol is None else special_symbol}')\n") @@ -149,7 +163,7 @@ def format_name(name: str): # Units dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) - fid.write(f"{combined_name_plural} = Unit({combined_scale}, " + fid.write(f"{combined_name_plural} = NamedUnit({combined_scale}, " f"Dimensions({length}, {time}, {mass}, {current}, {temperature})," f"name='{combined_name_plural}'," f"ascii_symbol='{combined_symbol}'," @@ -186,7 +200,7 @@ def format_name(name: str): unit_name = prefix + name unit_special_symbol = (symbol if special_symbol is None else special_symbol) + unicode_suffix unit_symbol = symbol + f"^{power}" - fid.write(f"{unit_name} = Unit({scale**power}, Dimensions(length={power}), " + fid.write(f"{unit_name} = NamedUnit({scale**power}, Dimensions(length={power}), " f"name='{unit_name}', " f"ascii_symbol='{unit_symbol}', " f"symbol='{unit_special_symbol}')\n") @@ -203,13 +217,13 @@ def format_name(name: str): accel_dimensions = Dimensions(length=1, time=-2) fid.write(f"{speed_name} " - f"= Unit({length_scale / time_scale}, " + f"= NamedUnit({length_scale / time_scale}, " f"Dimensions(length=1, time=-1), " f"name='{speed_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}', " f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") - fid.write(f"{accel_name} = Unit({length_scale / time_scale}, " + fid.write(f"{accel_name} = NamedUnit({length_scale / time_scale}, " f"Dimensions(length=1, time=-2), " f"name='{accel_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}^2', " @@ -227,7 +241,7 @@ def format_name(name: str): dimensions = Dimensions(length=-3, mass=1) fid.write(f"{name} " - f"= Unit({mass_scale / length_scale**3}, " + f"= NamedUnit({mass_scale / length_scale**3}, " f"Dimensions(length=-3, mass=1), " f"name='{name}', " f"ascii_symbol='{mass_symbol} {length_symbol}^-3', " @@ -244,7 +258,7 @@ def format_name(name: str): dimensions = Dimensions(length=-3, moles_hint=1) fid.write(f"{name} " - f"= Unit({amount_scale / length_scale**3}, " + f"= NamedUnit({amount_scale / length_scale**3}, " f"Dimensions(length=-3, moles_hint=1), " f"name='{name}', " f"ascii_symbol='{amount_symbol} {length_symbol}^-3', " diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index adbf86399..b008932d7 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -143,50 +143,27 @@ def __repr__(self): 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 - -class Unit: - def __init__(self, - si_scaling_factor: float, - dimensions: Dimensions, - name: str | None = None, - ascii_symbol: str | None = None, - symbol: str | None = None): + dimensions: Dimensions): 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): + def __mul__(self: Self, other: "Unit"): if not isinstance(other, Unit): return NotImplemented return Unit(self.scale * other.scale, self.dimensions * other.dimensions) - def __truediv__(self: Self, other: Self): + def __truediv__(self: Self, other: "Unit"): if not isinstance(other, Unit): return NotImplemented return Unit(self.scale / other.scale, self.dimensions / other.dimensions) - def __rtruediv__(self: Self, other: Self): + def __rtruediv__(self: Self, other: "Unit"): if isinstance(other, Unit): return Unit(other.scale / self.scale, other.dimensions / self.dimensions) elif isinstance(other, (int, float)): @@ -200,10 +177,10 @@ def __pow__(self, power: int): return Unit(self.scale**power, self.dimensions**power) - def equivalent(self: Self, other: Self): + def equivalent(self: Self, other: "Unit"): return self.dimensions == other.dimensions - def __eq__(self: Self, other: Self): + def __eq__(self: Self, other: "Unit"): return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 def si_equivalent(self): @@ -213,6 +190,9 @@ def si_equivalent(self): def _format_unit(self, format_process: list["UnitFormatProcessor"]): for processor in format_process: pass + + def __repr__(self): + return f"Unit[{self.scale}, {self.dimensions}]" class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): """ This processor minimises the dimensionality of the unit by multiplying by as many units of the specified type as needed """ @@ -226,9 +206,22 @@ class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): pass -class NamedUnit: - # TODO: Add named unit class - pass +class NamedUnit(Unit): + """ Units, but they have a name, and a symbol""" + def __init__(self, + si_scaling_factor: float, + dimensions: Dimensions, + name: str | None = None, + ascii_symbol: str | None = None, + symbol: str | None = None): + + super().__init__(si_scaling_factor, dimensions) + self.name = name + self.ascii_symbol = ascii_symbol + self.symbol = symbol + + def __repr__(self): + return self.name # # Parsing plan: diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 53cfca732..5882a9795 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -163,6 +163,22 @@ def centimeters(self) -> T: def angstroms(self) -> T: return self.quantity.in_units_of(units.angstroms) + @property + def miles(self) -> T: + return self.quantity.in_units_of(units.miles) + + @property + def yards(self) -> T: + return self.quantity.in_units_of(units.yards) + + @property + def feet(self) -> T: + return self.quantity.in_units_of(units.feet) + + @property + def inches(self) -> T: + return self.quantity.in_units_of(units.inches) + class AreaAccessor[T](Accessor[T]): @@ -232,6 +248,22 @@ def square_centimeters(self) -> T: def square_angstroms(self) -> T: return self.quantity.in_units_of(units.square_angstroms) + @property + def square_miles(self) -> T: + return self.quantity.in_units_of(units.square_miles) + + @property + def square_yards(self) -> T: + return self.quantity.in_units_of(units.square_yards) + + @property + def square_feet(self) -> T: + return self.quantity.in_units_of(units.square_feet) + + @property + def square_inches(self) -> T: + return self.quantity.in_units_of(units.square_inches) + class VolumeAccessor[T](Accessor[T]): @@ -305,6 +337,22 @@ def cubic_centimeters(self) -> T: def cubic_angstroms(self) -> T: return self.quantity.in_units_of(units.cubic_angstroms) + @property + def cubic_miles(self) -> T: + return self.quantity.in_units_of(units.cubic_miles) + + @property + def cubic_yards(self) -> T: + return self.quantity.in_units_of(units.cubic_yards) + + @property + def cubic_feet(self) -> T: + return self.quantity.in_units_of(units.cubic_feet) + + @property + def cubic_inches(self) -> T: + return self.quantity.in_units_of(units.cubic_inches) + class InverselengthAccessor[T](Accessor[T]): @@ -374,6 +422,22 @@ def per_centimeter(self) -> T: def per_angstrom(self) -> T: return self.quantity.in_units_of(units.per_angstrom) + @property + def per_mile(self) -> T: + return self.quantity.in_units_of(units.per_mile) + + @property + def per_yard(self) -> T: + return self.quantity.in_units_of(units.per_yard) + + @property + def per_foot(self) -> T: + return self.quantity.in_units_of(units.per_foot) + + @property + def per_inch(self) -> T: + return self.quantity.in_units_of(units.per_inch) + class InverseareaAccessor[T](Accessor[T]): @@ -443,6 +507,22 @@ def per_square_centimeter(self) -> T: def per_square_angstrom(self) -> T: return self.quantity.in_units_of(units.per_square_angstrom) + @property + def per_square_mile(self) -> T: + return self.quantity.in_units_of(units.per_square_mile) + + @property + def per_square_yard(self) -> T: + return self.quantity.in_units_of(units.per_square_yard) + + @property + def per_square_foot(self) -> T: + return self.quantity.in_units_of(units.per_square_foot) + + @property + def per_square_inch(self) -> T: + return self.quantity.in_units_of(units.per_square_inch) + class InversevolumeAccessor[T](Accessor[T]): @@ -512,6 +592,22 @@ def per_cubic_centimeter(self) -> T: def per_cubic_angstrom(self) -> T: return self.quantity.in_units_of(units.per_cubic_angstrom) + @property + def per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.per_cubic_mile) + + @property + def per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.per_cubic_yard) + + @property + def per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.per_cubic_foot) + + @property + def per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.per_cubic_inch) + class TimeAccessor[T](Accessor[T]): @@ -1327,6 +1423,182 @@ def angstroms_per_day(self) -> T: def angstroms_per_year(self) -> T: return self.quantity.in_units_of(units.angstroms_per_year) + @property + def miles_per_second(self) -> T: + return self.quantity.in_units_of(units.miles_per_second) + + @property + def miles_per_millisecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_millisecond) + + @property + def miles_per_microsecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_microsecond) + + @property + def miles_per_nanosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_nanosecond) + + @property + def miles_per_picosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_picosecond) + + @property + def miles_per_femtosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_femtosecond) + + @property + def miles_per_attosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_attosecond) + + @property + def miles_per_minute(self) -> T: + return self.quantity.in_units_of(units.miles_per_minute) + + @property + def miles_per_hour(self) -> T: + return self.quantity.in_units_of(units.miles_per_hour) + + @property + def miles_per_day(self) -> T: + return self.quantity.in_units_of(units.miles_per_day) + + @property + def miles_per_year(self) -> T: + return self.quantity.in_units_of(units.miles_per_year) + + @property + def yards_per_second(self) -> T: + return self.quantity.in_units_of(units.yards_per_second) + + @property + def yards_per_millisecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_millisecond) + + @property + def yards_per_microsecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_microsecond) + + @property + def yards_per_nanosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_nanosecond) + + @property + def yards_per_picosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_picosecond) + + @property + def yards_per_femtosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_femtosecond) + + @property + def yards_per_attosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_attosecond) + + @property + def yards_per_minute(self) -> T: + return self.quantity.in_units_of(units.yards_per_minute) + + @property + def yards_per_hour(self) -> T: + return self.quantity.in_units_of(units.yards_per_hour) + + @property + def yards_per_day(self) -> T: + return self.quantity.in_units_of(units.yards_per_day) + + @property + def yards_per_year(self) -> T: + return self.quantity.in_units_of(units.yards_per_year) + + @property + def feet_per_second(self) -> T: + return self.quantity.in_units_of(units.feet_per_second) + + @property + def feet_per_millisecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_millisecond) + + @property + def feet_per_microsecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_microsecond) + + @property + def feet_per_nanosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_nanosecond) + + @property + def feet_per_picosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_picosecond) + + @property + def feet_per_femtosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_femtosecond) + + @property + def feet_per_attosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_attosecond) + + @property + def feet_per_minute(self) -> T: + return self.quantity.in_units_of(units.feet_per_minute) + + @property + def feet_per_hour(self) -> T: + return self.quantity.in_units_of(units.feet_per_hour) + + @property + def feet_per_day(self) -> T: + return self.quantity.in_units_of(units.feet_per_day) + + @property + def feet_per_year(self) -> T: + return self.quantity.in_units_of(units.feet_per_year) + + @property + def inches_per_second(self) -> T: + return self.quantity.in_units_of(units.inches_per_second) + + @property + def inches_per_millisecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_millisecond) + + @property + def inches_per_microsecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_microsecond) + + @property + def inches_per_nanosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_nanosecond) + + @property + def inches_per_picosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_picosecond) + + @property + def inches_per_femtosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_femtosecond) + + @property + def inches_per_attosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_attosecond) + + @property + def inches_per_minute(self) -> T: + return self.quantity.in_units_of(units.inches_per_minute) + + @property + def inches_per_hour(self) -> T: + return self.quantity.in_units_of(units.inches_per_hour) + + @property + def inches_per_day(self) -> T: + return self.quantity.in_units_of(units.inches_per_day) + + @property + def inches_per_year(self) -> T: + return self.quantity.in_units_of(units.inches_per_year) + class AccelerationAccessor[T](Accessor[T]): @@ -2036,6 +2308,182 @@ def angstroms_per_square_day(self) -> T: def angstroms_per_square_year(self) -> T: return self.quantity.in_units_of(units.angstroms_per_square_year) + @property + def miles_per_square_second(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_second) + + @property + def miles_per_square_millisecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_millisecond) + + @property + def miles_per_square_microsecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_microsecond) + + @property + def miles_per_square_nanosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_nanosecond) + + @property + def miles_per_square_picosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_picosecond) + + @property + def miles_per_square_femtosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_femtosecond) + + @property + def miles_per_square_attosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_attosecond) + + @property + def miles_per_square_minute(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_minute) + + @property + def miles_per_square_hour(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_hour) + + @property + def miles_per_square_day(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_day) + + @property + def miles_per_square_year(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_year) + + @property + def yards_per_square_second(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_second) + + @property + def yards_per_square_millisecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_millisecond) + + @property + def yards_per_square_microsecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_microsecond) + + @property + def yards_per_square_nanosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_nanosecond) + + @property + def yards_per_square_picosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_picosecond) + + @property + def yards_per_square_femtosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_femtosecond) + + @property + def yards_per_square_attosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_attosecond) + + @property + def yards_per_square_minute(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_minute) + + @property + def yards_per_square_hour(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_hour) + + @property + def yards_per_square_day(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_day) + + @property + def yards_per_square_year(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_year) + + @property + def feet_per_square_second(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_second) + + @property + def feet_per_square_millisecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_millisecond) + + @property + def feet_per_square_microsecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_microsecond) + + @property + def feet_per_square_nanosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_nanosecond) + + @property + def feet_per_square_picosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_picosecond) + + @property + def feet_per_square_femtosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_femtosecond) + + @property + def feet_per_square_attosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_attosecond) + + @property + def feet_per_square_minute(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_minute) + + @property + def feet_per_square_hour(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_hour) + + @property + def feet_per_square_day(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_day) + + @property + def feet_per_square_year(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_year) + + @property + def inches_per_square_second(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_second) + + @property + def inches_per_square_millisecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_millisecond) + + @property + def inches_per_square_microsecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_microsecond) + + @property + def inches_per_square_nanosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_nanosecond) + + @property + def inches_per_square_picosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_picosecond) + + @property + def inches_per_square_femtosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_femtosecond) + + @property + def inches_per_square_attosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_attosecond) + + @property + def inches_per_square_minute(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_minute) + + @property + def inches_per_square_hour(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_hour) + + @property + def inches_per_square_day(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_day) + + @property + def inches_per_square_year(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_year) + class DensityAccessor[T](Accessor[T]): @@ -2097,6 +2545,14 @@ def attograms_per_cubic_meter(self) -> T: def atomic_mass_units_per_cubic_meter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_meter) + @property + def pounds_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_meter) + + @property + def ounces_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_meter) + @property def grams_per_cubic_exameter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_exameter) @@ -2153,6 +2609,14 @@ def attograms_per_cubic_exameter(self) -> T: def atomic_mass_units_per_cubic_exameter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_exameter) + @property + def pounds_per_cubic_exameter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_exameter) + + @property + def ounces_per_cubic_exameter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_exameter) + @property def grams_per_cubic_petameter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_petameter) @@ -2209,6 +2673,14 @@ def attograms_per_cubic_petameter(self) -> T: def atomic_mass_units_per_cubic_petameter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_petameter) + @property + def pounds_per_cubic_petameter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_petameter) + + @property + def ounces_per_cubic_petameter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_petameter) + @property def grams_per_cubic_terameter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_terameter) @@ -2265,6 +2737,14 @@ def attograms_per_cubic_terameter(self) -> T: def atomic_mass_units_per_cubic_terameter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_terameter) + @property + def pounds_per_cubic_terameter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_terameter) + + @property + def ounces_per_cubic_terameter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_terameter) + @property def grams_per_cubic_gigameter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_gigameter) @@ -2321,6 +2801,14 @@ def attograms_per_cubic_gigameter(self) -> T: def atomic_mass_units_per_cubic_gigameter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_gigameter) + @property + def pounds_per_cubic_gigameter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_gigameter) + + @property + def ounces_per_cubic_gigameter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_gigameter) + @property def grams_per_cubic_megameter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_megameter) @@ -2377,6 +2865,14 @@ def attograms_per_cubic_megameter(self) -> T: def atomic_mass_units_per_cubic_megameter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_megameter) + @property + def pounds_per_cubic_megameter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_megameter) + + @property + def ounces_per_cubic_megameter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_megameter) + @property def grams_per_cubic_kilometer(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_kilometer) @@ -2433,6 +2929,14 @@ def attograms_per_cubic_kilometer(self) -> T: def atomic_mass_units_per_cubic_kilometer(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_kilometer) + @property + def pounds_per_cubic_kilometer(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_kilometer) + + @property + def ounces_per_cubic_kilometer(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_kilometer) + @property def grams_per_cubic_millimeter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_millimeter) @@ -2489,6 +2993,14 @@ def attograms_per_cubic_millimeter(self) -> T: def atomic_mass_units_per_cubic_millimeter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_millimeter) + @property + def pounds_per_cubic_millimeter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_millimeter) + + @property + def ounces_per_cubic_millimeter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_millimeter) + @property def grams_per_cubic_micrometer(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_micrometer) @@ -2545,6 +3057,14 @@ def attograms_per_cubic_micrometer(self) -> T: def atomic_mass_units_per_cubic_micrometer(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_micrometer) + @property + def pounds_per_cubic_micrometer(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_micrometer) + + @property + def ounces_per_cubic_micrometer(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_micrometer) + @property def grams_per_cubic_nanometer(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_nanometer) @@ -2601,6 +3121,14 @@ def attograms_per_cubic_nanometer(self) -> T: def atomic_mass_units_per_cubic_nanometer(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_nanometer) + @property + def pounds_per_cubic_nanometer(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_nanometer) + + @property + def ounces_per_cubic_nanometer(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_nanometer) + @property def grams_per_cubic_picometer(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_picometer) @@ -2657,6 +3185,14 @@ def attograms_per_cubic_picometer(self) -> T: def atomic_mass_units_per_cubic_picometer(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_picometer) + @property + def pounds_per_cubic_picometer(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_picometer) + + @property + def ounces_per_cubic_picometer(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_picometer) + @property def grams_per_cubic_femtometer(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_femtometer) @@ -2713,6 +3249,14 @@ def attograms_per_cubic_femtometer(self) -> T: def atomic_mass_units_per_cubic_femtometer(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_femtometer) + @property + def pounds_per_cubic_femtometer(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_femtometer) + + @property + def ounces_per_cubic_femtometer(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_femtometer) + @property def grams_per_cubic_attometer(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_attometer) @@ -2769,6 +3313,14 @@ def attograms_per_cubic_attometer(self) -> T: def atomic_mass_units_per_cubic_attometer(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_attometer) + @property + def pounds_per_cubic_attometer(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_attometer) + + @property + def ounces_per_cubic_attometer(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_attometer) + @property def grams_per_cubic_decimeter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_decimeter) @@ -2825,6 +3377,14 @@ def attograms_per_cubic_decimeter(self) -> T: def atomic_mass_units_per_cubic_decimeter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_decimeter) + @property + def pounds_per_cubic_decimeter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_decimeter) + + @property + def ounces_per_cubic_decimeter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_decimeter) + @property def grams_per_cubic_centimeter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_centimeter) @@ -2881,6 +3441,14 @@ def attograms_per_cubic_centimeter(self) -> T: def atomic_mass_units_per_cubic_centimeter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_centimeter) + @property + def pounds_per_cubic_centimeter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_centimeter) + + @property + def ounces_per_cubic_centimeter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_centimeter) + @property def grams_per_cubic_angstrom(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_angstrom) @@ -2937,6 +3505,270 @@ def attograms_per_cubic_angstrom(self) -> T: def atomic_mass_units_per_cubic_angstrom(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_angstrom) + @property + def pounds_per_cubic_angstrom(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_angstrom) + + @property + def ounces_per_cubic_angstrom(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_angstrom) + + @property + def grams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.grams_per_cubic_mile) + + @property + def exagrams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.exagrams_per_cubic_mile) + + @property + def petagrams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.petagrams_per_cubic_mile) + + @property + def teragrams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.teragrams_per_cubic_mile) + + @property + def gigagrams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.gigagrams_per_cubic_mile) + + @property + def megagrams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.megagrams_per_cubic_mile) + + @property + def kilograms_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.kilograms_per_cubic_mile) + + @property + def milligrams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.milligrams_per_cubic_mile) + + @property + def micrograms_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.micrograms_per_cubic_mile) + + @property + def nanograms_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.nanograms_per_cubic_mile) + + @property + def picograms_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.picograms_per_cubic_mile) + + @property + def femtograms_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.femtograms_per_cubic_mile) + + @property + def attograms_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.attograms_per_cubic_mile) + + @property + def atomic_mass_units_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_mile) + + @property + def pounds_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_mile) + + @property + def ounces_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_mile) + + @property + def grams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.grams_per_cubic_yard) + + @property + def exagrams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.exagrams_per_cubic_yard) + + @property + def petagrams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.petagrams_per_cubic_yard) + + @property + def teragrams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.teragrams_per_cubic_yard) + + @property + def gigagrams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.gigagrams_per_cubic_yard) + + @property + def megagrams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.megagrams_per_cubic_yard) + + @property + def kilograms_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.kilograms_per_cubic_yard) + + @property + def milligrams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.milligrams_per_cubic_yard) + + @property + def micrograms_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.micrograms_per_cubic_yard) + + @property + def nanograms_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.nanograms_per_cubic_yard) + + @property + def picograms_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.picograms_per_cubic_yard) + + @property + def femtograms_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.femtograms_per_cubic_yard) + + @property + def attograms_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.attograms_per_cubic_yard) + + @property + def atomic_mass_units_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_yard) + + @property + def pounds_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_yard) + + @property + def ounces_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_yard) + + @property + def grams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.grams_per_cubic_foot) + + @property + def exagrams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.exagrams_per_cubic_foot) + + @property + def petagrams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.petagrams_per_cubic_foot) + + @property + def teragrams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.teragrams_per_cubic_foot) + + @property + def gigagrams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.gigagrams_per_cubic_foot) + + @property + def megagrams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.megagrams_per_cubic_foot) + + @property + def kilograms_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.kilograms_per_cubic_foot) + + @property + def milligrams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.milligrams_per_cubic_foot) + + @property + def micrograms_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.micrograms_per_cubic_foot) + + @property + def nanograms_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.nanograms_per_cubic_foot) + + @property + def picograms_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.picograms_per_cubic_foot) + + @property + def femtograms_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.femtograms_per_cubic_foot) + + @property + def attograms_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.attograms_per_cubic_foot) + + @property + def atomic_mass_units_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_foot) + + @property + def pounds_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_foot) + + @property + def ounces_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_foot) + + @property + def grams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.grams_per_cubic_inch) + + @property + def exagrams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.exagrams_per_cubic_inch) + + @property + def petagrams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.petagrams_per_cubic_inch) + + @property + def teragrams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.teragrams_per_cubic_inch) + + @property + def gigagrams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.gigagrams_per_cubic_inch) + + @property + def megagrams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.megagrams_per_cubic_inch) + + @property + def kilograms_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.kilograms_per_cubic_inch) + + @property + def milligrams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.milligrams_per_cubic_inch) + + @property + def micrograms_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.micrograms_per_cubic_inch) + + @property + def nanograms_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.nanograms_per_cubic_inch) + + @property + def picograms_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.picograms_per_cubic_inch) + + @property + def femtograms_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.femtograms_per_cubic_inch) + + @property + def attograms_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.attograms_per_cubic_inch) + + @property + def atomic_mass_units_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_inch) + + @property + def pounds_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_inch) + + @property + def ounces_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_inch) + class ForceAccessor[T](Accessor[T]): @@ -3055,6 +3887,10 @@ def femtopascals(self) -> T: def attopascals(self) -> T: return self.quantity.in_units_of(units.attopascals) + @property + def pound_force_per_square_inch(self) -> T: + return self.quantity.in_units_of(units.pound_force_per_square_inch) + class EnergyAccessor[T](Accessor[T]): @@ -4255,4 +5091,116 @@ def femtomoles_per_cubic_angstrom(self) -> T: def attomoles_per_cubic_angstrom(self) -> T: return self.quantity.in_units_of(units.attomoles_per_cubic_angstrom) + @property + def moles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.moles_per_cubic_mile) + + @property + def millimoles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.millimoles_per_cubic_mile) + + @property + def micromoles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.micromoles_per_cubic_mile) + + @property + def nanomoles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.nanomoles_per_cubic_mile) + + @property + def picomoles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.picomoles_per_cubic_mile) + + @property + def femtomoles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.femtomoles_per_cubic_mile) + + @property + def attomoles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.attomoles_per_cubic_mile) + + @property + def moles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.moles_per_cubic_yard) + + @property + def millimoles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.millimoles_per_cubic_yard) + + @property + def micromoles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.micromoles_per_cubic_yard) + + @property + def nanomoles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.nanomoles_per_cubic_yard) + + @property + def picomoles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.picomoles_per_cubic_yard) + + @property + def femtomoles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.femtomoles_per_cubic_yard) + + @property + def attomoles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.attomoles_per_cubic_yard) + + @property + def moles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.moles_per_cubic_foot) + + @property + def millimoles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.millimoles_per_cubic_foot) + + @property + def micromoles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.micromoles_per_cubic_foot) + + @property + def nanomoles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.nanomoles_per_cubic_foot) + + @property + def picomoles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.picomoles_per_cubic_foot) + + @property + def femtomoles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.femtomoles_per_cubic_foot) + + @property + def attomoles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.attomoles_per_cubic_foot) + + @property + def moles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.moles_per_cubic_inch) + + @property + def millimoles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.millimoles_per_cubic_inch) + + @property + def micromoles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.micromoles_per_cubic_inch) + + @property + def nanomoles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.nanomoles_per_cubic_inch) + + @property + def picomoles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.picomoles_per_cubic_inch) + + @property + def femtomoles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.femtomoles_per_cubic_inch) + + @property + def attomoles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.attomoles_per_cubic_inch) + diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index 6c8a2d728..d5d83bef1 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -2,6 +2,7 @@ from sasdata.quantities.quantity import Quantity, UnitError import sasdata.quantities.units as units +import sasdata.quantities.si as si import pytest def test_in_units_of_calculation(): """ Just a couple of unit conversions """ @@ -12,26 +13,77 @@ def test_in_units_of_calculation(): def test_unit_compounding_pow(): + """ Test units compound correctly when __pow__ is used""" assert (Quantity(1, units.millimeters)**2).in_units_of(units.square_meters) == 1e-6 assert (Quantity(1, units.minutes)**3).in_units_of(units.seconds**3) == 60**3 def test_unit_compounding_mul(): + """ Test units compound correctly when __mul__ is used""" assert (Quantity(4, units.minutes) * Quantity(0.25, units.hertz)).in_units_of(units.none) == 60 assert (Quantity(250, units.volts) * Quantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 def test_unit_compounding_div(): + """ Test units compound correctly when __truediv__ is used""" assert (Quantity(10, units.kilometers) / Quantity(2, units.minutes) ).in_units_of(units.meters_per_second) == pytest.approx(250/3, abs=1e-6) assert (Quantity(1, units.nanowebers) / (Quantity(1, units.millimeters)**2)).in_units_of(units.millitesla) == 1 def test_value_mul(): + """ Test value part of quantities multiply correctly""" assert (Quantity(1j, units.seconds) * Quantity(1j, units.watts)).in_units_of(units.joules) == -1 +def test_scalar_mul(): + assert (Quantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 + assert (10 * Quantity(1, units.seconds)).in_units_of(units.seconds) == 10 + assert (1000 * Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 -def test_conversion_errors(): +def test_scalar_div(): + assert (Quantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 + assert (10 / Quantity(1, units.seconds)).in_units_of(units.hertz) == 10 + assert (0.001 / Quantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 +def test_good_add_sub(): + """ Test that adding and subtracting units works """ + assert (Quantity(1, units.seconds) + Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 + assert (Quantity(1, units.seconds) - Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 + + assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == 13 + + +@pytest.mark.parametrize("unit_1", si.all_si) +@pytest.mark.parametrize("unit_2", si.all_si) +def test_mixed_quantity_add_sub(unit_1, unit_2): + if unit_1.equivalent(unit_2): + assert (Quantity(0, unit_1) + Quantity(0, unit_2)).in_units_of(unit_1) == 0 + + else: + with pytest.raises(UnitError): + Quantity(1, unit_1) + Quantity(1, unit_2) + +def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float): + """ Helper function for testing units that are multiples of each other """ + + assert u1.equivalent(u2), "Units should be compatible for this test" + assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=1e-9) + + +def test_american_units(): + assert_unit_ratio(units.feet, units.inches, 12) + assert_unit_ratio(units.yards, units.inches, 36) + assert_unit_ratio(units.miles, units.inches, 63360) + + +@pytest.mark.parametrize("unit_1", si.all_si) +@pytest.mark.parametrize("unit_2", si.all_si) +def test_conversion_errors(unit_1, unit_2): + """ Test conversion errors are thrown when units are not compatible """ + + if unit_1 == unit_2: + assert Quantity(1, unit_1).in_units_of(unit_2) == 1 + + else: + with pytest.raises(UnitError): + Quantity(1, units.seconds).in_units_of(units.meters) - with pytest.raises(UnitError): - Quantity(1, units.seconds).in_units_of(units.meters) \ No newline at end of file diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 7d42dccef..1ecc2ad9d 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -55,13 +55,14 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: def __add__(self: Self, other: Self | ArrayLike) -> Self: if isinstance(other, Quantity): if self.units.equivalent(other.units): - return Quantity - - elif self.units.dimensions.is_dimensionless: - return Quantity(other/self.units.scale, self.units) + return Quantity(self.value + (other.value * other.scale)/self.scale, self.units) + else: + raise UnitError(f"Units do not have the same dimensionality: {self.units} vs {other.units}") else: - raise UnitError(f"Cannot combine type {type(other)} with quantity") + raise UnitError(f"Cannot perform addition/subtraction non-quantity {type(other)} with quantity") + + # Don't need __radd__ because only quantity/quantity operations should be allowed def __neg__(self): return Quantity(-self.value, self.units) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 04907bfc6..ba3d8ae18 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -227,33 +227,27 @@ def __repr__(self): class Unit: def __init__(self, si_scaling_factor: float, - dimensions: Dimensions, - name: str | None = None, - ascii_symbol: str | None = None, - symbol: str | None = None): + dimensions: Dimensions): 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): + def __mul__(self: Self, other: "Unit"): if not isinstance(other, Unit): return NotImplemented return Unit(self.scale * other.scale, self.dimensions * other.dimensions) - def __truediv__(self: Self, other: Self): + def __truediv__(self: Self, other: "Unit"): if not isinstance(other, Unit): return NotImplemented return Unit(self.scale / other.scale, self.dimensions / other.dimensions) - def __rtruediv__(self: Self, other: Self): + def __rtruediv__(self: Self, other: "Unit"): if isinstance(other, Unit): return Unit(other.scale / self.scale, other.dimensions / self.dimensions) elif isinstance(other, (int, float)): @@ -267,10 +261,10 @@ def __pow__(self, power: int): return Unit(self.scale**power, self.dimensions**power) - def equivalent(self: Self, other: Self): + def equivalent(self: Self, other: "Unit"): return self.dimensions == other.dimensions - def __eq__(self: Self, other: Self): + def __eq__(self: Self, other: "Unit"): return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 def si_equivalent(self): @@ -281,10 +275,21 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): for processor in format_process: pass + def __repr__(self): + return f"Unit[{self.scale}, {self.dimensions}]" + +class NamedUnit(Unit): + def __init__(self, + si_scaling_factor: float, + dimensions: Dimensions, + name: str | None = None, + ascii_symbol: str | None = None, + symbol: str | None = None): -class NamedUnit: - # TODO: Add named unit class - pass + super().__init__(si_scaling_factor, dimensions) + self.name = name + self.ascii_symbol = ascii_symbol + self.symbol = symbol # # Parsing plan: @@ -341,1037 +346,1276 @@ def __init__(self, name: str, units: list[Unit]): # Specific units # -meters = Unit(1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') -exameters = Unit(1e+18, Dimensions(1, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') -petameters = Unit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') -terameters = Unit(1000000000000.0, Dimensions(1, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') -gigameters = Unit(1000000000.0, Dimensions(1, 0, 0, 0, 0),name='gigameters',ascii_symbol='Gm',symbol='Gm') -megameters = Unit(1000000.0, Dimensions(1, 0, 0, 0, 0),name='megameters',ascii_symbol='Mm',symbol='Mm') -kilometers = Unit(1000.0, Dimensions(1, 0, 0, 0, 0),name='kilometers',ascii_symbol='km',symbol='km') -millimeters = Unit(0.001, Dimensions(1, 0, 0, 0, 0),name='millimeters',ascii_symbol='mm',symbol='mm') -micrometers = Unit(1e-06, Dimensions(1, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',symbol='µm') -nanometers = Unit(1e-09, Dimensions(1, 0, 0, 0, 0),name='nanometers',ascii_symbol='nm',symbol='nm') -picometers = Unit(1e-12, Dimensions(1, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') -femtometers = Unit(1e-15, Dimensions(1, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') -attometers = Unit(1e-18, Dimensions(1, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') -decimeters = Unit(0.1, Dimensions(1, 0, 0, 0, 0),name='decimeters',ascii_symbol='dm',symbol='dm') -centimeters = Unit(0.01, Dimensions(1, 0, 0, 0, 0),name='centimeters',ascii_symbol='cm',symbol='cm') -seconds = Unit(1, Dimensions(0, 1, 0, 0, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') -milliseconds = Unit(0.001, Dimensions(0, 1, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') -microseconds = Unit(1e-06, Dimensions(0, 1, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') -nanoseconds = Unit(1e-09, Dimensions(0, 1, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') -picoseconds = Unit(1e-12, Dimensions(0, 1, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') -femtoseconds = Unit(1e-15, Dimensions(0, 1, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') -attoseconds = Unit(1e-18, Dimensions(0, 1, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') -grams = Unit(0.001, Dimensions(0, 0, 1, 0, 0, 0, 0),name='grams',ascii_symbol='g',symbol='g') -exagrams = Unit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') -petagrams = Unit(1000000000000.0, Dimensions(0, 0, 1, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') -teragrams = Unit(1000000000.0, Dimensions(0, 0, 1, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') -gigagrams = Unit(1000000.0, Dimensions(0, 0, 1, 0, 0),name='gigagrams',ascii_symbol='Gg',symbol='Gg') -megagrams = Unit(1000.0, Dimensions(0, 0, 1, 0, 0),name='megagrams',ascii_symbol='Mg',symbol='Mg') -kilograms = Unit(1.0, Dimensions(0, 0, 1, 0, 0),name='kilograms',ascii_symbol='kg',symbol='kg') -milligrams = Unit(1e-06, Dimensions(0, 0, 1, 0, 0),name='milligrams',ascii_symbol='mg',symbol='mg') -micrograms = Unit(1e-09, Dimensions(0, 0, 1, 0, 0),name='micrograms',ascii_symbol='ug',symbol='µg') -nanograms = Unit(1.0000000000000002e-12, Dimensions(0, 0, 1, 0, 0),name='nanograms',ascii_symbol='ng',symbol='ng') -picograms = Unit(1e-15, Dimensions(0, 0, 1, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') -femtograms = Unit(1e-18, Dimensions(0, 0, 1, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') -attograms = Unit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') -amperes = Unit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amperes',ascii_symbol='A',symbol='A') -exaamperes = Unit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamperes',ascii_symbol='EA',symbol='EA') -petaamperes = Unit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamperes',ascii_symbol='PA',symbol='PA') -teraamperes = Unit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamperes',ascii_symbol='TA',symbol='TA') -gigaamperes = Unit(1000000000.0, Dimensions(0, 0, 0, 1, 0),name='gigaamperes',ascii_symbol='GA',symbol='GA') -megaamperes = Unit(1000000.0, Dimensions(0, 0, 0, 1, 0),name='megaamperes',ascii_symbol='MA',symbol='MA') -kiloamperes = Unit(1000.0, Dimensions(0, 0, 0, 1, 0),name='kiloamperes',ascii_symbol='kA',symbol='kA') -milliamperes = Unit(0.001, Dimensions(0, 0, 0, 1, 0),name='milliamperes',ascii_symbol='mA',symbol='mA') -microamperes = Unit(1e-06, Dimensions(0, 0, 0, 1, 0),name='microamperes',ascii_symbol='uA',symbol='µA') -nanoamperes = Unit(1e-09, Dimensions(0, 0, 0, 1, 0),name='nanoamperes',ascii_symbol='nA',symbol='nA') -picoamperes = Unit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamperes',ascii_symbol='pA',symbol='pA') -femtoamperes = Unit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamperes',ascii_symbol='fA',symbol='fA') -attoamperes = Unit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamperes',ascii_symbol='aA',symbol='aA') -kelvin = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kelvin',ascii_symbol='K',symbol='K') -exakelvin = Unit(1e+18, Dimensions(0, 0, 0, 0, 1),name='exakelvin',ascii_symbol='EK',symbol='EK') -petakelvin = Unit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1),name='petakelvin',ascii_symbol='PK',symbol='PK') -terakelvin = Unit(1000000000000.0, Dimensions(0, 0, 0, 0, 1),name='terakelvin',ascii_symbol='TK',symbol='TK') -gigakelvin = Unit(1000000000.0, Dimensions(0, 0, 0, 0, 1),name='gigakelvin',ascii_symbol='GK',symbol='GK') -megakelvin = Unit(1000000.0, Dimensions(0, 0, 0, 0, 1),name='megakelvin',ascii_symbol='MK',symbol='MK') -kilokelvin = Unit(1000.0, Dimensions(0, 0, 0, 0, 1),name='kilokelvin',ascii_symbol='kK',symbol='kK') -millikelvin = Unit(0.001, Dimensions(0, 0, 0, 0, 1),name='millikelvin',ascii_symbol='mK',symbol='mK') -microkelvin = Unit(1e-06, Dimensions(0, 0, 0, 0, 1),name='microkelvin',ascii_symbol='uK',symbol='µK') -nanokelvin = Unit(1e-09, Dimensions(0, 0, 0, 0, 1),name='nanokelvin',ascii_symbol='nK',symbol='nK') -picokelvin = Unit(1e-12, Dimensions(0, 0, 0, 0, 1),name='picokelvin',ascii_symbol='pK',symbol='pK') -femtokelvin = Unit(1e-15, Dimensions(0, 0, 0, 0, 1),name='femtokelvin',ascii_symbol='fK',symbol='fK') -attokelvin = Unit(1e-18, Dimensions(0, 0, 0, 0, 1),name='attokelvin',ascii_symbol='aK',symbol='aK') -hertz = Unit(1, Dimensions(0, -1, 0, 0, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') -exahertz = Unit(1e+18, Dimensions(0, -1, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') -petahertz = Unit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') -terahertz = Unit(1000000000000.0, Dimensions(0, -1, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') -gigahertz = Unit(1000000000.0, Dimensions(0, -1, 0, 0, 0),name='gigahertz',ascii_symbol='GHz',symbol='GHz') -megahertz = Unit(1000000.0, Dimensions(0, -1, 0, 0, 0),name='megahertz',ascii_symbol='MHz',symbol='MHz') -kilohertz = Unit(1000.0, Dimensions(0, -1, 0, 0, 0),name='kilohertz',ascii_symbol='kHz',symbol='kHz') -millihertz = Unit(0.001, Dimensions(0, -1, 0, 0, 0),name='millihertz',ascii_symbol='mHz',symbol='mHz') -microhertz = Unit(1e-06, Dimensions(0, -1, 0, 0, 0),name='microhertz',ascii_symbol='uHz',symbol='µHz') -nanohertz = Unit(1e-09, Dimensions(0, -1, 0, 0, 0),name='nanohertz',ascii_symbol='nHz',symbol='nHz') -picohertz = Unit(1e-12, Dimensions(0, -1, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') -femtohertz = Unit(1e-15, Dimensions(0, -1, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') -attohertz = Unit(1e-18, Dimensions(0, -1, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') -newtons = Unit(1, Dimensions(1, -2, 1, 0, 0, 0, 0),name='newtons',ascii_symbol='N',symbol='N') -exanewtons = Unit(1e+18, Dimensions(1, -2, 1, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') -petanewtons = Unit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') -teranewtons = Unit(1000000000000.0, Dimensions(1, -2, 1, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') -giganewtons = Unit(1000000000.0, Dimensions(1, -2, 1, 0, 0),name='giganewtons',ascii_symbol='GN',symbol='GN') -meganewtons = Unit(1000000.0, Dimensions(1, -2, 1, 0, 0),name='meganewtons',ascii_symbol='MN',symbol='MN') -kilonewtons = Unit(1000.0, Dimensions(1, -2, 1, 0, 0),name='kilonewtons',ascii_symbol='kN',symbol='kN') -millinewtons = Unit(0.001, Dimensions(1, -2, 1, 0, 0),name='millinewtons',ascii_symbol='mN',symbol='mN') -micronewtons = Unit(1e-06, Dimensions(1, -2, 1, 0, 0),name='micronewtons',ascii_symbol='uN',symbol='µN') -nanonewtons = Unit(1e-09, Dimensions(1, -2, 1, 0, 0),name='nanonewtons',ascii_symbol='nN',symbol='nN') -piconewtons = Unit(1e-12, Dimensions(1, -2, 1, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') -femtonewtons = Unit(1e-15, Dimensions(1, -2, 1, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') -attonewtons = Unit(1e-18, Dimensions(1, -2, 1, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') -pascals = Unit(1, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') -exapascals = Unit(1e+18, Dimensions(-1, -2, 1, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') -petapascals = Unit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') -terapascals = Unit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') -gigapascals = Unit(1000000000.0, Dimensions(-1, -2, 1, 0, 0),name='gigapascals',ascii_symbol='GPa',symbol='GPa') -megapascals = Unit(1000000.0, Dimensions(-1, -2, 1, 0, 0),name='megapascals',ascii_symbol='MPa',symbol='MPa') -kilopascals = Unit(1000.0, Dimensions(-1, -2, 1, 0, 0),name='kilopascals',ascii_symbol='kPa',symbol='kPa') -millipascals = Unit(0.001, Dimensions(-1, -2, 1, 0, 0),name='millipascals',ascii_symbol='mPa',symbol='mPa') -micropascals = Unit(1e-06, Dimensions(-1, -2, 1, 0, 0),name='micropascals',ascii_symbol='uPa',symbol='µPa') -nanopascals = Unit(1e-09, Dimensions(-1, -2, 1, 0, 0),name='nanopascals',ascii_symbol='nPa',symbol='nPa') -picopascals = Unit(1e-12, Dimensions(-1, -2, 1, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') -femtopascals = Unit(1e-15, Dimensions(-1, -2, 1, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') -attopascals = Unit(1e-18, Dimensions(-1, -2, 1, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') -joules = Unit(1, Dimensions(2, -2, 1, 0, 0, 0, 0),name='joules',ascii_symbol='J',symbol='J') -exajoules = Unit(1e+18, Dimensions(2, -2, 1, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') -petajoules = Unit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') -terajoules = Unit(1000000000000.0, Dimensions(2, -2, 1, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') -gigajoules = Unit(1000000000.0, Dimensions(2, -2, 1, 0, 0),name='gigajoules',ascii_symbol='GJ',symbol='GJ') -megajoules = Unit(1000000.0, Dimensions(2, -2, 1, 0, 0),name='megajoules',ascii_symbol='MJ',symbol='MJ') -kilojoules = Unit(1000.0, Dimensions(2, -2, 1, 0, 0),name='kilojoules',ascii_symbol='kJ',symbol='kJ') -millijoules = Unit(0.001, Dimensions(2, -2, 1, 0, 0),name='millijoules',ascii_symbol='mJ',symbol='mJ') -microjoules = Unit(1e-06, Dimensions(2, -2, 1, 0, 0),name='microjoules',ascii_symbol='uJ',symbol='µJ') -nanojoules = Unit(1e-09, Dimensions(2, -2, 1, 0, 0),name='nanojoules',ascii_symbol='nJ',symbol='nJ') -picojoules = Unit(1e-12, Dimensions(2, -2, 1, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') -femtojoules = Unit(1e-15, Dimensions(2, -2, 1, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') -attojoules = Unit(1e-18, Dimensions(2, -2, 1, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') -watts = Unit(1, Dimensions(2, -3, 1, 0, 0, 0, 0),name='watts',ascii_symbol='W',symbol='W') -exawatts = Unit(1e+18, Dimensions(2, -3, 1, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') -petawatts = Unit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') -terawatts = Unit(1000000000000.0, Dimensions(2, -3, 1, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') -gigawatts = Unit(1000000000.0, Dimensions(2, -3, 1, 0, 0),name='gigawatts',ascii_symbol='GW',symbol='GW') -megawatts = Unit(1000000.0, Dimensions(2, -3, 1, 0, 0),name='megawatts',ascii_symbol='MW',symbol='MW') -kilowatts = Unit(1000.0, Dimensions(2, -3, 1, 0, 0),name='kilowatts',ascii_symbol='kW',symbol='kW') -milliwatts = Unit(0.001, Dimensions(2, -3, 1, 0, 0),name='milliwatts',ascii_symbol='mW',symbol='mW') -microwatts = Unit(1e-06, Dimensions(2, -3, 1, 0, 0),name='microwatts',ascii_symbol='uW',symbol='µW') -nanowatts = Unit(1e-09, Dimensions(2, -3, 1, 0, 0),name='nanowatts',ascii_symbol='nW',symbol='nW') -picowatts = Unit(1e-12, Dimensions(2, -3, 1, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') -femtowatts = Unit(1e-15, Dimensions(2, -3, 1, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') -attowatts = Unit(1e-18, Dimensions(2, -3, 1, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') -coulombs = Unit(1, Dimensions(0, 1, 0, 1, 0, 0, 0),name='coulombs',ascii_symbol='C',symbol='C') -exacoulombs = Unit(1e+18, Dimensions(0, 1, 0, 1, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') -petacoulombs = Unit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') -teracoulombs = Unit(1000000000000.0, Dimensions(0, 1, 0, 1, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') -gigacoulombs = Unit(1000000000.0, Dimensions(0, 1, 0, 1, 0),name='gigacoulombs',ascii_symbol='GC',symbol='GC') -megacoulombs = Unit(1000000.0, Dimensions(0, 1, 0, 1, 0),name='megacoulombs',ascii_symbol='MC',symbol='MC') -kilocoulombs = Unit(1000.0, Dimensions(0, 1, 0, 1, 0),name='kilocoulombs',ascii_symbol='kC',symbol='kC') -millicoulombs = Unit(0.001, Dimensions(0, 1, 0, 1, 0),name='millicoulombs',ascii_symbol='mC',symbol='mC') -microcoulombs = Unit(1e-06, Dimensions(0, 1, 0, 1, 0),name='microcoulombs',ascii_symbol='uC',symbol='µC') -nanocoulombs = Unit(1e-09, Dimensions(0, 1, 0, 1, 0),name='nanocoulombs',ascii_symbol='nC',symbol='nC') -picocoulombs = Unit(1e-12, Dimensions(0, 1, 0, 1, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') -femtocoulombs = Unit(1e-15, Dimensions(0, 1, 0, 1, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') -attocoulombs = Unit(1e-18, Dimensions(0, 1, 0, 1, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') -volts = Unit(1, Dimensions(2, -3, 1, -1, 0, 0, 0),name='volts',ascii_symbol='V',symbol='V') -exavolts = Unit(1e+18, Dimensions(2, -3, 1, -1, 0),name='exavolts',ascii_symbol='EV',symbol='EV') -petavolts = Unit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0),name='petavolts',ascii_symbol='PV',symbol='PV') -teravolts = Unit(1000000000000.0, Dimensions(2, -3, 1, -1, 0),name='teravolts',ascii_symbol='TV',symbol='TV') -gigavolts = Unit(1000000000.0, Dimensions(2, -3, 1, -1, 0),name='gigavolts',ascii_symbol='GV',symbol='GV') -megavolts = Unit(1000000.0, Dimensions(2, -3, 1, -1, 0),name='megavolts',ascii_symbol='MV',symbol='MV') -kilovolts = Unit(1000.0, Dimensions(2, -3, 1, -1, 0),name='kilovolts',ascii_symbol='kV',symbol='kV') -millivolts = Unit(0.001, Dimensions(2, -3, 1, -1, 0),name='millivolts',ascii_symbol='mV',symbol='mV') -microvolts = Unit(1e-06, Dimensions(2, -3, 1, -1, 0),name='microvolts',ascii_symbol='uV',symbol='µV') -nanovolts = Unit(1e-09, Dimensions(2, -3, 1, -1, 0),name='nanovolts',ascii_symbol='nV',symbol='nV') -picovolts = Unit(1e-12, Dimensions(2, -3, 1, -1, 0),name='picovolts',ascii_symbol='pV',symbol='pV') -femtovolts = Unit(1e-15, Dimensions(2, -3, 1, -1, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') -attovolts = Unit(1e-18, Dimensions(2, -3, 1, -1, 0),name='attovolts',ascii_symbol='aV',symbol='aV') -ohms = Unit(1, Dimensions(2, -3, 1, -2, 0, 0, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') -exaohms = Unit(1e+18, Dimensions(2, -3, 1, -2, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') -petaohms = Unit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') -teraohms = Unit(1000000000000.0, Dimensions(2, -3, 1, -2, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') -gigaohms = Unit(1000000000.0, Dimensions(2, -3, 1, -2, 0),name='gigaohms',ascii_symbol='GOhm',symbol='GΩ') -megaohms = Unit(1000000.0, Dimensions(2, -3, 1, -2, 0),name='megaohms',ascii_symbol='MOhm',symbol='MΩ') -kiloohms = Unit(1000.0, Dimensions(2, -3, 1, -2, 0),name='kiloohms',ascii_symbol='kOhm',symbol='kΩ') -milliohms = Unit(0.001, Dimensions(2, -3, 1, -2, 0),name='milliohms',ascii_symbol='mOhm',symbol='mΩ') -microohms = Unit(1e-06, Dimensions(2, -3, 1, -2, 0),name='microohms',ascii_symbol='uOhm',symbol='µΩ') -nanoohms = Unit(1e-09, Dimensions(2, -3, 1, -2, 0),name='nanoohms',ascii_symbol='nOhm',symbol='nΩ') -picoohms = Unit(1e-12, Dimensions(2, -3, 1, -2, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') -femtoohms = Unit(1e-15, Dimensions(2, -3, 1, -2, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') -attoohms = Unit(1e-18, Dimensions(2, -3, 1, -2, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') -farads = Unit(1, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='farads',ascii_symbol='F',symbol='F') -exafarads = Unit(1e+18, Dimensions(-2, 4, -1, 2, 0),name='exafarads',ascii_symbol='EF',symbol='EF') -petafarads = Unit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='petafarads',ascii_symbol='PF',symbol='PF') -terafarads = Unit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='terafarads',ascii_symbol='TF',symbol='TF') -gigafarads = Unit(1000000000.0, Dimensions(-2, 4, -1, 2, 0),name='gigafarads',ascii_symbol='GF',symbol='GF') -megafarads = Unit(1000000.0, Dimensions(-2, 4, -1, 2, 0),name='megafarads',ascii_symbol='MF',symbol='MF') -kilofarads = Unit(1000.0, Dimensions(-2, 4, -1, 2, 0),name='kilofarads',ascii_symbol='kF',symbol='kF') -millifarads = Unit(0.001, Dimensions(-2, 4, -1, 2, 0),name='millifarads',ascii_symbol='mF',symbol='mF') -microfarads = Unit(1e-06, Dimensions(-2, 4, -1, 2, 0),name='microfarads',ascii_symbol='uF',symbol='µF') -nanofarads = Unit(1e-09, Dimensions(-2, 4, -1, 2, 0),name='nanofarads',ascii_symbol='nF',symbol='nF') -picofarads = Unit(1e-12, Dimensions(-2, 4, -1, 2, 0),name='picofarads',ascii_symbol='pF',symbol='pF') -femtofarads = Unit(1e-15, Dimensions(-2, 4, -1, 2, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') -attofarads = Unit(1e-18, Dimensions(-2, 4, -1, 2, 0),name='attofarads',ascii_symbol='aF',symbol='aF') -siemens = Unit(1, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='siemens',ascii_symbol='S',symbol='S') -exasiemens = Unit(1e+18, Dimensions(-2, 3, -1, 2, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') -petasiemens = Unit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') -terasiemens = Unit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') -gigasiemens = Unit(1000000000.0, Dimensions(-2, 3, -1, 2, 0),name='gigasiemens',ascii_symbol='GS',symbol='GS') -megasiemens = Unit(1000000.0, Dimensions(-2, 3, -1, 2, 0),name='megasiemens',ascii_symbol='MS',symbol='MS') -kilosiemens = Unit(1000.0, Dimensions(-2, 3, -1, 2, 0),name='kilosiemens',ascii_symbol='kS',symbol='kS') -millisiemens = Unit(0.001, Dimensions(-2, 3, -1, 2, 0),name='millisiemens',ascii_symbol='mS',symbol='mS') -microsiemens = Unit(1e-06, Dimensions(-2, 3, -1, 2, 0),name='microsiemens',ascii_symbol='uS',symbol='µS') -nanosiemens = Unit(1e-09, Dimensions(-2, 3, -1, 2, 0),name='nanosiemens',ascii_symbol='nS',symbol='nS') -picosiemens = Unit(1e-12, Dimensions(-2, 3, -1, 2, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') -femtosiemens = Unit(1e-15, Dimensions(-2, 3, -1, 2, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') -attosiemens = Unit(1e-18, Dimensions(-2, 3, -1, 2, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') -webers = Unit(1, Dimensions(2, -2, 1, -1, 0, 0, 0),name='webers',ascii_symbol='Wb',symbol='Wb') -exawebers = Unit(1e+18, Dimensions(2, -2, 1, -1, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') -petawebers = Unit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') -terawebers = Unit(1000000000000.0, Dimensions(2, -2, 1, -1, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') -gigawebers = Unit(1000000000.0, Dimensions(2, -2, 1, -1, 0),name='gigawebers',ascii_symbol='GWb',symbol='GWb') -megawebers = Unit(1000000.0, Dimensions(2, -2, 1, -1, 0),name='megawebers',ascii_symbol='MWb',symbol='MWb') -kilowebers = Unit(1000.0, Dimensions(2, -2, 1, -1, 0),name='kilowebers',ascii_symbol='kWb',symbol='kWb') -milliwebers = Unit(0.001, Dimensions(2, -2, 1, -1, 0),name='milliwebers',ascii_symbol='mWb',symbol='mWb') -microwebers = Unit(1e-06, Dimensions(2, -2, 1, -1, 0),name='microwebers',ascii_symbol='uWb',symbol='µWb') -nanowebers = Unit(1e-09, Dimensions(2, -2, 1, -1, 0),name='nanowebers',ascii_symbol='nWb',symbol='nWb') -picowebers = Unit(1e-12, Dimensions(2, -2, 1, -1, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') -femtowebers = Unit(1e-15, Dimensions(2, -2, 1, -1, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') -attowebers = Unit(1e-18, Dimensions(2, -2, 1, -1, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') -tesla = Unit(1, Dimensions(0, -2, 1, -1, 0, 0, 0),name='tesla',ascii_symbol='T',symbol='T') -exatesla = Unit(1e+18, Dimensions(0, -2, 1, -1, 0),name='exatesla',ascii_symbol='ET',symbol='ET') -petatesla = Unit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0),name='petatesla',ascii_symbol='PT',symbol='PT') -teratesla = Unit(1000000000000.0, Dimensions(0, -2, 1, -1, 0),name='teratesla',ascii_symbol='TT',symbol='TT') -gigatesla = Unit(1000000000.0, Dimensions(0, -2, 1, -1, 0),name='gigatesla',ascii_symbol='GT',symbol='GT') -megatesla = Unit(1000000.0, Dimensions(0, -2, 1, -1, 0),name='megatesla',ascii_symbol='MT',symbol='MT') -kilotesla = Unit(1000.0, Dimensions(0, -2, 1, -1, 0),name='kilotesla',ascii_symbol='kT',symbol='kT') -millitesla = Unit(0.001, Dimensions(0, -2, 1, -1, 0),name='millitesla',ascii_symbol='mT',symbol='mT') -microtesla = Unit(1e-06, Dimensions(0, -2, 1, -1, 0),name='microtesla',ascii_symbol='uT',symbol='µT') -nanotesla = Unit(1e-09, Dimensions(0, -2, 1, -1, 0),name='nanotesla',ascii_symbol='nT',symbol='nT') -picotesla = Unit(1e-12, Dimensions(0, -2, 1, -1, 0),name='picotesla',ascii_symbol='pT',symbol='pT') -femtotesla = Unit(1e-15, Dimensions(0, -2, 1, -1, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') -attotesla = Unit(1e-18, Dimensions(0, -2, 1, -1, 0),name='attotesla',ascii_symbol='aT',symbol='aT') -henry = Unit(1, Dimensions(2, -2, 1, -2, 0, 0, 0),name='henry',ascii_symbol='H',symbol='H') -exahenry = Unit(1e+18, Dimensions(2, -2, 1, -2, 0),name='exahenry',ascii_symbol='EH',symbol='EH') -petahenry = Unit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0),name='petahenry',ascii_symbol='PH',symbol='PH') -terahenry = Unit(1000000000000.0, Dimensions(2, -2, 1, -2, 0),name='terahenry',ascii_symbol='TH',symbol='TH') -gigahenry = Unit(1000000000.0, Dimensions(2, -2, 1, -2, 0),name='gigahenry',ascii_symbol='GH',symbol='GH') -megahenry = Unit(1000000.0, Dimensions(2, -2, 1, -2, 0),name='megahenry',ascii_symbol='MH',symbol='MH') -kilohenry = Unit(1000.0, Dimensions(2, -2, 1, -2, 0),name='kilohenry',ascii_symbol='kH',symbol='kH') -millihenry = Unit(0.001, Dimensions(2, -2, 1, -2, 0),name='millihenry',ascii_symbol='mH',symbol='mH') -microhenry = Unit(1e-06, Dimensions(2, -2, 1, -2, 0),name='microhenry',ascii_symbol='uH',symbol='µH') -nanohenry = Unit(1e-09, Dimensions(2, -2, 1, -2, 0),name='nanohenry',ascii_symbol='nH',symbol='nH') -picohenry = Unit(1e-12, Dimensions(2, -2, 1, -2, 0),name='picohenry',ascii_symbol='pH',symbol='pH') -femtohenry = Unit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') -attohenry = Unit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') -angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') -minutes = Unit(60, Dimensions(0, 1, 0, 0, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') -hours = Unit(360, Dimensions(0, 1, 0, 0, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') -days = Unit(8640, Dimensions(0, 1, 0, 0, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') -years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') -degrees = Unit(57.29577951308232, Dimensions(0, 0, 0, 0, 0, 0, 1),name='degrees',ascii_symbol='deg',symbol='deg') -radians = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 1),name='radians',ascii_symbol='rad',symbol='rad') -stradians = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') -none = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') -litres = Unit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') -electronvolts = Unit(1.602176634e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') -exaelectronvolts = Unit(0.1602176634, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') -petaelectronvolts = Unit(0.0001602176634, Dimensions(2, -2, 1, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') -teraelectronvolts = Unit(1.602176634e-07, Dimensions(2, -2, 1, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') -gigaelectronvolts = Unit(1.6021766339999998e-10, Dimensions(2, -2, 1, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') -megaelectronvolts = Unit(1.6021766339999998e-13, Dimensions(2, -2, 1, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') -kiloelectronvolts = Unit(1.602176634e-16, Dimensions(2, -2, 1, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') -millielectronvolts = Unit(1.6021766339999998e-22, Dimensions(2, -2, 1, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') -microelectronvolts = Unit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') -nanoelectronvolts = Unit(1.602176634e-28, Dimensions(2, -2, 1, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') -picoelectronvolts = Unit(1.6021766339999998e-31, Dimensions(2, -2, 1, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') -femtoelectronvolts = Unit(1.602176634e-34, Dimensions(2, -2, 1, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') -attoelectronvolts = Unit(1.602176634e-37, Dimensions(2, -2, 1, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') -atomic_mass_units = Unit(1.660538921e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') -moles = Unit(6.02214076e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') -millimoles = Unit(6.02214076e+20, Dimensions(0, 0, 0, 0, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') -micromoles = Unit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') -nanomoles = Unit(602214076000000.0, Dimensions(0, 0, 0, 0, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') -picomoles = Unit(602214076000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') -femtomoles = Unit(602214076.0, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') -attomoles = Unit(602214.076, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') -kg_force = Unit(9.80665, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kg_force',ascii_symbol='kgForce',symbol='kgForce') -degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') -square_meters = Unit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') -cubic_meters = Unit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') -per_meter = Unit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') -per_square_meter = Unit(1.0, Dimensions(length=-2), name='per_square_meter', ascii_symbol='m^-2', symbol='m⁻²') -per_cubic_meter = Unit(1.0, Dimensions(length=-3), name='per_cubic_meter', ascii_symbol='m^-3', symbol='m⁻³') -square_exameters = Unit(1e+36, Dimensions(length=2), name='square_exameters', ascii_symbol='Em^2', symbol='Em²') -cubic_exameters = Unit(1e+54, Dimensions(length=3), name='cubic_exameters', ascii_symbol='Em^3', symbol='Em³') -per_exameter = Unit(1e-18, Dimensions(length=-1), name='per_exameter', ascii_symbol='Em^-1', symbol='Em⁻¹') -per_square_exameter = Unit(1e-36, Dimensions(length=-2), name='per_square_exameter', ascii_symbol='Em^-2', symbol='Em⁻²') -per_cubic_exameter = Unit(1e-54, Dimensions(length=-3), name='per_cubic_exameter', ascii_symbol='Em^-3', symbol='Em⁻³') -square_petameters = Unit(1e+30, Dimensions(length=2), name='square_petameters', ascii_symbol='Pm^2', symbol='Pm²') -cubic_petameters = Unit(1e+45, Dimensions(length=3), name='cubic_petameters', ascii_symbol='Pm^3', symbol='Pm³') -per_petameter = Unit(1e-15, Dimensions(length=-1), name='per_petameter', ascii_symbol='Pm^-1', symbol='Pm⁻¹') -per_square_petameter = Unit(1e-30, Dimensions(length=-2), name='per_square_petameter', ascii_symbol='Pm^-2', symbol='Pm⁻²') -per_cubic_petameter = Unit(1e-45, Dimensions(length=-3), name='per_cubic_petameter', ascii_symbol='Pm^-3', symbol='Pm⁻³') -square_terameters = Unit(1e+24, Dimensions(length=2), name='square_terameters', ascii_symbol='Tm^2', symbol='Tm²') -cubic_terameters = Unit(1e+36, Dimensions(length=3), name='cubic_terameters', ascii_symbol='Tm^3', symbol='Tm³') -per_terameter = Unit(1e-12, Dimensions(length=-1), name='per_terameter', ascii_symbol='Tm^-1', symbol='Tm⁻¹') -per_square_terameter = Unit(1e-24, Dimensions(length=-2), name='per_square_terameter', ascii_symbol='Tm^-2', symbol='Tm⁻²') -per_cubic_terameter = Unit(1e-36, Dimensions(length=-3), name='per_cubic_terameter', ascii_symbol='Tm^-3', symbol='Tm⁻³') -square_gigameters = Unit(1e+18, Dimensions(length=2), name='square_gigameters', ascii_symbol='Gm^2', symbol='Gm²') -cubic_gigameters = Unit(1e+27, Dimensions(length=3), name='cubic_gigameters', ascii_symbol='Gm^3', symbol='Gm³') -per_gigameter = Unit(1e-09, Dimensions(length=-1), name='per_gigameter', ascii_symbol='Gm^-1', symbol='Gm⁻¹') -per_square_gigameter = Unit(1e-18, Dimensions(length=-2), name='per_square_gigameter', ascii_symbol='Gm^-2', symbol='Gm⁻²') -per_cubic_gigameter = Unit(1e-27, Dimensions(length=-3), name='per_cubic_gigameter', ascii_symbol='Gm^-3', symbol='Gm⁻³') -square_megameters = Unit(1000000000000.0, Dimensions(length=2), name='square_megameters', ascii_symbol='Mm^2', symbol='Mm²') -cubic_megameters = Unit(1e+18, Dimensions(length=3), name='cubic_megameters', ascii_symbol='Mm^3', symbol='Mm³') -per_megameter = Unit(1e-06, Dimensions(length=-1), name='per_megameter', ascii_symbol='Mm^-1', symbol='Mm⁻¹') -per_square_megameter = Unit(1e-12, Dimensions(length=-2), name='per_square_megameter', ascii_symbol='Mm^-2', symbol='Mm⁻²') -per_cubic_megameter = Unit(1e-18, Dimensions(length=-3), name='per_cubic_megameter', ascii_symbol='Mm^-3', symbol='Mm⁻³') -square_kilometers = Unit(1000000.0, Dimensions(length=2), name='square_kilometers', ascii_symbol='km^2', symbol='km²') -cubic_kilometers = Unit(1000000000.0, Dimensions(length=3), name='cubic_kilometers', ascii_symbol='km^3', symbol='km³') -per_kilometer = Unit(0.001, Dimensions(length=-1), name='per_kilometer', ascii_symbol='km^-1', symbol='km⁻¹') -per_square_kilometer = Unit(1e-06, Dimensions(length=-2), name='per_square_kilometer', ascii_symbol='km^-2', symbol='km⁻²') -per_cubic_kilometer = Unit(1e-09, Dimensions(length=-3), name='per_cubic_kilometer', ascii_symbol='km^-3', symbol='km⁻³') -square_millimeters = Unit(1e-06, Dimensions(length=2), name='square_millimeters', ascii_symbol='mm^2', symbol='mm²') -cubic_millimeters = Unit(1e-09, Dimensions(length=3), name='cubic_millimeters', ascii_symbol='mm^3', symbol='mm³') -per_millimeter = Unit(1000.0, Dimensions(length=-1), name='per_millimeter', ascii_symbol='mm^-1', symbol='mm⁻¹') -per_square_millimeter = Unit(1000000.0, Dimensions(length=-2), name='per_square_millimeter', ascii_symbol='mm^-2', symbol='mm⁻²') -per_cubic_millimeter = Unit(999999999.9999999, Dimensions(length=-3), name='per_cubic_millimeter', ascii_symbol='mm^-3', symbol='mm⁻³') -square_micrometers = Unit(1e-12, Dimensions(length=2), name='square_micrometers', ascii_symbol='um^2', symbol='µm²') -cubic_micrometers = Unit(9.999999999999999e-19, Dimensions(length=3), name='cubic_micrometers', ascii_symbol='um^3', symbol='µm³') -per_micrometer = Unit(1000000.0, Dimensions(length=-1), name='per_micrometer', ascii_symbol='um^-1', symbol='µm⁻¹') -per_square_micrometer = Unit(1000000000000.0001, Dimensions(length=-2), name='per_square_micrometer', ascii_symbol='um^-2', symbol='µm⁻²') -per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(length=-3), name='per_cubic_micrometer', ascii_symbol='um^-3', symbol='µm⁻³') -square_nanometers = Unit(1e-18, Dimensions(length=2), name='square_nanometers', ascii_symbol='nm^2', symbol='nm²') -cubic_nanometers = Unit(1.0000000000000002e-27, Dimensions(length=3), name='cubic_nanometers', ascii_symbol='nm^3', symbol='nm³') -per_nanometer = Unit(999999999.9999999, Dimensions(length=-1), name='per_nanometer', ascii_symbol='nm^-1', symbol='nm⁻¹') -per_square_nanometer = Unit(9.999999999999999e+17, Dimensions(length=-2), name='per_square_nanometer', ascii_symbol='nm^-2', symbol='nm⁻²') -per_cubic_nanometer = Unit(9.999999999999999e+26, Dimensions(length=-3), name='per_cubic_nanometer', ascii_symbol='nm^-3', symbol='nm⁻³') -square_picometers = Unit(1e-24, Dimensions(length=2), name='square_picometers', ascii_symbol='pm^2', symbol='pm²') -cubic_picometers = Unit(1e-36, Dimensions(length=3), name='cubic_picometers', ascii_symbol='pm^3', symbol='pm³') -per_picometer = Unit(1000000000000.0, Dimensions(length=-1), name='per_picometer', ascii_symbol='pm^-1', symbol='pm⁻¹') -per_square_picometer = Unit(1e+24, Dimensions(length=-2), name='per_square_picometer', ascii_symbol='pm^-2', symbol='pm⁻²') -per_cubic_picometer = Unit(1e+36, Dimensions(length=-3), name='per_cubic_picometer', ascii_symbol='pm^-3', symbol='pm⁻³') -square_femtometers = Unit(1e-30, Dimensions(length=2), name='square_femtometers', ascii_symbol='fm^2', symbol='fm²') -cubic_femtometers = Unit(1.0000000000000003e-45, Dimensions(length=3), name='cubic_femtometers', ascii_symbol='fm^3', symbol='fm³') -per_femtometer = Unit(999999999999999.9, Dimensions(length=-1), name='per_femtometer', ascii_symbol='fm^-1', symbol='fm⁻¹') -per_square_femtometer = Unit(9.999999999999999e+29, Dimensions(length=-2), name='per_square_femtometer', ascii_symbol='fm^-2', symbol='fm⁻²') -per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(length=-3), name='per_cubic_femtometer', ascii_symbol='fm^-3', symbol='fm⁻³') -square_attometers = Unit(1.0000000000000001e-36, Dimensions(length=2), name='square_attometers', ascii_symbol='am^2', symbol='am²') -cubic_attometers = Unit(1.0000000000000002e-54, Dimensions(length=3), name='cubic_attometers', ascii_symbol='am^3', symbol='am³') -per_attometer = Unit(9.999999999999999e+17, Dimensions(length=-1), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') -per_square_attometer = Unit(9.999999999999999e+35, Dimensions(length=-2), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') -per_cubic_attometer = Unit(9.999999999999997e+53, Dimensions(length=-3), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') -square_decimeters = Unit(0.010000000000000002, Dimensions(length=2), name='square_decimeters', ascii_symbol='dm^2', symbol='dm²') -cubic_decimeters = Unit(0.0010000000000000002, Dimensions(length=3), name='cubic_decimeters', ascii_symbol='dm^3', symbol='dm³') -per_decimeter = Unit(10.0, Dimensions(length=-1), name='per_decimeter', ascii_symbol='dm^-1', symbol='dm⁻¹') -per_square_decimeter = Unit(99.99999999999999, Dimensions(length=-2), name='per_square_decimeter', ascii_symbol='dm^-2', symbol='dm⁻²') -per_cubic_decimeter = Unit(999.9999999999999, Dimensions(length=-3), name='per_cubic_decimeter', ascii_symbol='dm^-3', symbol='dm⁻³') -square_centimeters = Unit(0.0001, Dimensions(length=2), name='square_centimeters', ascii_symbol='cm^2', symbol='cm²') -cubic_centimeters = Unit(1.0000000000000002e-06, Dimensions(length=3), name='cubic_centimeters', ascii_symbol='cm^3', symbol='cm³') -per_centimeter = Unit(100.0, Dimensions(length=-1), name='per_centimeter', ascii_symbol='cm^-1', symbol='cm⁻¹') -per_square_centimeter = Unit(10000.0, Dimensions(length=-2), name='per_square_centimeter', ascii_symbol='cm^-2', symbol='cm⁻²') -per_cubic_centimeter = Unit(999999.9999999999, Dimensions(length=-3), name='per_cubic_centimeter', ascii_symbol='cm^-3', symbol='cm⁻³') -square_angstroms = Unit(1.0000000000000001e-20, Dimensions(length=2), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ų') -cubic_angstroms = Unit(1e-30, Dimensions(length=3), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='ų') -per_angstrom = Unit(10000000000.0, Dimensions(length=-1), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Å⁻¹') -per_square_angstrom = Unit(1e+20, Dimensions(length=-2), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Å⁻²') -per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(length=-3), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Å⁻³') -meters_per_second = Unit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') -meters_per_square_second = Unit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') -meters_per_millisecond = Unit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') -meters_per_square_millisecond = Unit(1000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') -meters_per_microsecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') -meters_per_square_microsecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') -meters_per_nanosecond = Unit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') -meters_per_square_nanosecond = Unit(999999999.9999999, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') -meters_per_picosecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') -meters_per_square_picosecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') -meters_per_femtosecond = Unit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') -meters_per_square_femtosecond = Unit(999999999999999.9, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') -meters_per_attosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') -meters_per_square_attosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') -meters_per_minute = Unit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') -meters_per_square_minute = Unit(0.016666666666666666, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') -meters_per_hour = Unit(0.002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') -meters_per_square_hour = Unit(0.002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') -meters_per_day = Unit(0.00011574074074074075, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') -meters_per_square_day = Unit(0.00011574074074074075, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') -meters_per_year = Unit(3.168873850681143e-07, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') -meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') -exameters_per_second = Unit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') -exameters_per_square_second = Unit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') -exameters_per_millisecond = Unit(1e+21, Dimensions(length=1, time=-1), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') -exameters_per_square_millisecond = Unit(1e+21, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') -exameters_per_microsecond = Unit(1e+24, Dimensions(length=1, time=-1), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') -exameters_per_square_microsecond = Unit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') -exameters_per_nanosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') -exameters_per_square_nanosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') -exameters_per_picosecond = Unit(1e+30, Dimensions(length=1, time=-1), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') -exameters_per_square_picosecond = Unit(1e+30, Dimensions(length=1, time=-2), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') -exameters_per_femtosecond = Unit(1e+33, Dimensions(length=1, time=-1), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') -exameters_per_square_femtosecond = Unit(1e+33, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') -exameters_per_attosecond = Unit(9.999999999999999e+35, Dimensions(length=1, time=-1), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') -exameters_per_square_attosecond = Unit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') -exameters_per_minute = Unit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') -exameters_per_square_minute = Unit(1.6666666666666666e+16, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') -exameters_per_hour = Unit(2777777777777778.0, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') -exameters_per_square_hour = Unit(2777777777777778.0, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') -exameters_per_day = Unit(115740740740740.73, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') -exameters_per_square_day = Unit(115740740740740.73, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') -exameters_per_year = Unit(316887385068.1143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') -exameters_per_square_year = Unit(316887385068.1143, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') -petameters_per_second = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') -petameters_per_square_second = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') -petameters_per_millisecond = Unit(1e+18, Dimensions(length=1, time=-1), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') -petameters_per_square_millisecond = Unit(1e+18, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') -petameters_per_microsecond = Unit(1e+21, Dimensions(length=1, time=-1), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') -petameters_per_square_microsecond = Unit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') -petameters_per_nanosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') -petameters_per_square_nanosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') -petameters_per_picosecond = Unit(1e+27, Dimensions(length=1, time=-1), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') -petameters_per_square_picosecond = Unit(1e+27, Dimensions(length=1, time=-2), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') -petameters_per_femtosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') -petameters_per_square_femtosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') -petameters_per_attosecond = Unit(1e+33, Dimensions(length=1, time=-1), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') -petameters_per_square_attosecond = Unit(1e+33, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') -petameters_per_minute = Unit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') -petameters_per_square_minute = Unit(16666666666666.666, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') -petameters_per_hour = Unit(2777777777777.778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') -petameters_per_square_hour = Unit(2777777777777.778, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') -petameters_per_day = Unit(115740740740.74074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') -petameters_per_square_day = Unit(115740740740.74074, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') -petameters_per_year = Unit(316887385.0681143, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') -petameters_per_square_year = Unit(316887385.0681143, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') -terameters_per_second = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') -terameters_per_square_second = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') -terameters_per_millisecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') -terameters_per_square_millisecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') -terameters_per_microsecond = Unit(1e+18, Dimensions(length=1, time=-1), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') -terameters_per_square_microsecond = Unit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') -terameters_per_nanosecond = Unit(1e+21, Dimensions(length=1, time=-1), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') -terameters_per_square_nanosecond = Unit(1e+21, Dimensions(length=1, time=-2), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') -terameters_per_picosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') -terameters_per_square_picosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') -terameters_per_femtosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') -terameters_per_square_femtosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') -terameters_per_attosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') -terameters_per_square_attosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') -terameters_per_minute = Unit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') -terameters_per_square_minute = Unit(16666666666.666666, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') -terameters_per_hour = Unit(2777777777.7777777, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') -terameters_per_square_hour = Unit(2777777777.7777777, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') -terameters_per_day = Unit(115740740.74074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') -terameters_per_square_day = Unit(115740740.74074075, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') -terameters_per_year = Unit(316887.38506811426, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') -terameters_per_square_year = Unit(316887.38506811426, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') -gigameters_per_second = Unit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') -gigameters_per_square_second = Unit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') -gigameters_per_millisecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') -gigameters_per_square_millisecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') -gigameters_per_microsecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') -gigameters_per_square_microsecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') -gigameters_per_nanosecond = Unit(1e+18, Dimensions(length=1, time=-1), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') -gigameters_per_square_nanosecond = Unit(1e+18, Dimensions(length=1, time=-2), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') -gigameters_per_picosecond = Unit(1e+21, Dimensions(length=1, time=-1), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') -gigameters_per_square_picosecond = Unit(1e+21, Dimensions(length=1, time=-2), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') -gigameters_per_femtosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') -gigameters_per_square_femtosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') -gigameters_per_attosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') -gigameters_per_square_attosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') -gigameters_per_minute = Unit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') -gigameters_per_square_minute = Unit(16666666.666666666, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') -gigameters_per_hour = Unit(2777777.777777778, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') -gigameters_per_square_hour = Unit(2777777.777777778, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') -gigameters_per_day = Unit(115740.74074074074, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') -gigameters_per_square_day = Unit(115740.74074074074, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') -gigameters_per_year = Unit(316.88738506811427, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') -gigameters_per_square_year = Unit(316.88738506811427, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') -megameters_per_second = Unit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') -megameters_per_square_second = Unit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') -megameters_per_millisecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') -megameters_per_square_millisecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') -megameters_per_microsecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') -megameters_per_square_microsecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') -megameters_per_nanosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') -megameters_per_square_nanosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') -megameters_per_picosecond = Unit(1e+18, Dimensions(length=1, time=-1), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') -megameters_per_square_picosecond = Unit(1e+18, Dimensions(length=1, time=-2), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') -megameters_per_femtosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') -megameters_per_square_femtosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') -megameters_per_attosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') -megameters_per_square_attosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') -megameters_per_minute = Unit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') -megameters_per_square_minute = Unit(16666.666666666668, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') -megameters_per_hour = Unit(2777.777777777778, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') -megameters_per_square_hour = Unit(2777.777777777778, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') -megameters_per_day = Unit(115.74074074074075, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') -megameters_per_square_day = Unit(115.74074074074075, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') -megameters_per_year = Unit(0.3168873850681143, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') -megameters_per_square_year = Unit(0.3168873850681143, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') -kilometers_per_second = Unit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') -kilometers_per_square_second = Unit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') -kilometers_per_millisecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') -kilometers_per_square_millisecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') -kilometers_per_microsecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') -kilometers_per_square_microsecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') -kilometers_per_nanosecond = Unit(999999999999.9999, Dimensions(length=1, time=-1), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') -kilometers_per_square_nanosecond = Unit(999999999999.9999, Dimensions(length=1, time=-2), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') -kilometers_per_picosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') -kilometers_per_square_picosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') -kilometers_per_femtosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') -kilometers_per_square_femtosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') -kilometers_per_attosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') -kilometers_per_square_attosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') -kilometers_per_minute = Unit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') -kilometers_per_square_minute = Unit(16.666666666666668, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') -kilometers_per_hour = Unit(2.7777777777777777, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') -kilometers_per_square_hour = Unit(2.7777777777777777, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') -kilometers_per_day = Unit(0.11574074074074074, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') -kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') -kilometers_per_year = Unit(0.0003168873850681143, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') -kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') -millimeters_per_second = Unit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') -millimeters_per_square_second = Unit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') -millimeters_per_millisecond = Unit(1.0, Dimensions(length=1, time=-1), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') -millimeters_per_square_millisecond = Unit(1.0, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') -millimeters_per_microsecond = Unit(1000.0000000000001, Dimensions(length=1, time=-1), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') -millimeters_per_square_microsecond = Unit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') -millimeters_per_nanosecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') -millimeters_per_square_nanosecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') -millimeters_per_picosecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') -millimeters_per_square_picosecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') -millimeters_per_femtosecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') -millimeters_per_square_femtosecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') -millimeters_per_attosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') -millimeters_per_square_attosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') -millimeters_per_minute = Unit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') -millimeters_per_square_minute = Unit(1.6666666666666667e-05, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') -millimeters_per_hour = Unit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') -millimeters_per_square_hour = Unit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') -millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') -millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') -millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') -millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') -micrometers_per_second = Unit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') -micrometers_per_square_second = Unit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') -micrometers_per_millisecond = Unit(0.001, Dimensions(length=1, time=-1), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') -micrometers_per_square_millisecond = Unit(0.001, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') -micrometers_per_microsecond = Unit(1.0, Dimensions(length=1, time=-1), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') -micrometers_per_square_microsecond = Unit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') -micrometers_per_nanosecond = Unit(999.9999999999999, Dimensions(length=1, time=-1), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') -micrometers_per_square_nanosecond = Unit(999.9999999999999, Dimensions(length=1, time=-2), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') -micrometers_per_picosecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') -micrometers_per_square_picosecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') -micrometers_per_femtosecond = Unit(999999999.9999999, Dimensions(length=1, time=-1), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') -micrometers_per_square_femtosecond = Unit(999999999.9999999, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') -micrometers_per_attosecond = Unit(999999999999.9999, Dimensions(length=1, time=-1), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') -micrometers_per_square_attosecond = Unit(999999999999.9999, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') -micrometers_per_minute = Unit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') -micrometers_per_square_minute = Unit(1.6666666666666667e-08, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') -micrometers_per_hour = Unit(2.7777777777777776e-09, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') -micrometers_per_square_hour = Unit(2.7777777777777776e-09, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') -micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') -micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') -micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') -micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') -nanometers_per_second = Unit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') -nanometers_per_square_second = Unit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') -nanometers_per_millisecond = Unit(1e-06, Dimensions(length=1, time=-1), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') -nanometers_per_square_millisecond = Unit(1e-06, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') -nanometers_per_microsecond = Unit(0.001, Dimensions(length=1, time=-1), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') -nanometers_per_square_microsecond = Unit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') -nanometers_per_nanosecond = Unit(1.0, Dimensions(length=1, time=-1), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') -nanometers_per_square_nanosecond = Unit(1.0, Dimensions(length=1, time=-2), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') -nanometers_per_picosecond = Unit(1000.0000000000001, Dimensions(length=1, time=-1), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') -nanometers_per_square_picosecond = Unit(1000.0000000000001, Dimensions(length=1, time=-2), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') -nanometers_per_femtosecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') -nanometers_per_square_femtosecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') -nanometers_per_attosecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') -nanometers_per_square_attosecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') -nanometers_per_minute = Unit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') -nanometers_per_square_minute = Unit(1.6666666666666667e-11, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') -nanometers_per_hour = Unit(2.777777777777778e-12, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') -nanometers_per_square_hour = Unit(2.777777777777778e-12, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') -nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') -nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') -nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') -nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') -picometers_per_second = Unit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') -picometers_per_square_second = Unit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') -picometers_per_millisecond = Unit(1e-09, Dimensions(length=1, time=-1), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') -picometers_per_square_millisecond = Unit(1e-09, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') -picometers_per_microsecond = Unit(1e-06, Dimensions(length=1, time=-1), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') -picometers_per_square_microsecond = Unit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') -picometers_per_nanosecond = Unit(0.001, Dimensions(length=1, time=-1), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') -picometers_per_square_nanosecond = Unit(0.001, Dimensions(length=1, time=-2), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') -picometers_per_picosecond = Unit(1.0, Dimensions(length=1, time=-1), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') -picometers_per_square_picosecond = Unit(1.0, Dimensions(length=1, time=-2), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') -picometers_per_femtosecond = Unit(999.9999999999999, Dimensions(length=1, time=-1), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') -picometers_per_square_femtosecond = Unit(999.9999999999999, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') -picometers_per_attosecond = Unit(999999.9999999999, Dimensions(length=1, time=-1), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') -picometers_per_square_attosecond = Unit(999999.9999999999, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') -picometers_per_minute = Unit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') -picometers_per_square_minute = Unit(1.6666666666666667e-14, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') -picometers_per_hour = Unit(2.7777777777777776e-15, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') -picometers_per_square_hour = Unit(2.7777777777777776e-15, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') -picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') -picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') -picometers_per_year = Unit(3.168873850681143e-19, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') -picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') -femtometers_per_second = Unit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') -femtometers_per_square_second = Unit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') -femtometers_per_millisecond = Unit(1e-12, Dimensions(length=1, time=-1), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') -femtometers_per_square_millisecond = Unit(1e-12, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') -femtometers_per_microsecond = Unit(1e-09, Dimensions(length=1, time=-1), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') -femtometers_per_square_microsecond = Unit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') -femtometers_per_nanosecond = Unit(1e-06, Dimensions(length=1, time=-1), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') -femtometers_per_square_nanosecond = Unit(1e-06, Dimensions(length=1, time=-2), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') -femtometers_per_picosecond = Unit(0.001, Dimensions(length=1, time=-1), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') -femtometers_per_square_picosecond = Unit(0.001, Dimensions(length=1, time=-2), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') -femtometers_per_femtosecond = Unit(1.0, Dimensions(length=1, time=-1), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') -femtometers_per_square_femtosecond = Unit(1.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') -femtometers_per_attosecond = Unit(1000.0, Dimensions(length=1, time=-1), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') -femtometers_per_square_attosecond = Unit(1000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') -femtometers_per_minute = Unit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') -femtometers_per_square_minute = Unit(1.6666666666666667e-17, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') -femtometers_per_hour = Unit(2.777777777777778e-18, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') -femtometers_per_square_hour = Unit(2.777777777777778e-18, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') -femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') -femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') -femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') -femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') -attometers_per_second = Unit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') -attometers_per_square_second = Unit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') -attometers_per_millisecond = Unit(1e-15, Dimensions(length=1, time=-1), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') -attometers_per_square_millisecond = Unit(1e-15, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') -attometers_per_microsecond = Unit(1.0000000000000002e-12, Dimensions(length=1, time=-1), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') -attometers_per_square_microsecond = Unit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') -attometers_per_nanosecond = Unit(1e-09, Dimensions(length=1, time=-1), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') -attometers_per_square_nanosecond = Unit(1e-09, Dimensions(length=1, time=-2), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') -attometers_per_picosecond = Unit(1.0000000000000002e-06, Dimensions(length=1, time=-1), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') -attometers_per_square_picosecond = Unit(1.0000000000000002e-06, Dimensions(length=1, time=-2), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') -attometers_per_femtosecond = Unit(0.001, Dimensions(length=1, time=-1), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') -attometers_per_square_femtosecond = Unit(0.001, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') -attometers_per_attosecond = Unit(1.0, Dimensions(length=1, time=-1), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') -attometers_per_square_attosecond = Unit(1.0, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') -attometers_per_minute = Unit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') -attometers_per_square_minute = Unit(1.6666666666666668e-20, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') -attometers_per_hour = Unit(2.7777777777777778e-21, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') -attometers_per_square_hour = Unit(2.7777777777777778e-21, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') -attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') -attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') -attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') -attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') -decimeters_per_second = Unit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dmNone⁻¹') -decimeters_per_square_second = Unit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dmNone⁻²') -decimeters_per_millisecond = Unit(100.0, Dimensions(length=1, time=-1), name='decimeters_per_millisecond', ascii_symbol='dm/ms', symbol='dmms⁻¹') -decimeters_per_square_millisecond = Unit(100.0, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') -decimeters_per_microsecond = Unit(100000.00000000001, Dimensions(length=1, time=-1), name='decimeters_per_microsecond', ascii_symbol='dm/us', symbol='dmµs⁻¹') -decimeters_per_square_microsecond = Unit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_microsecond', ascii_symbol='dm/us^2', symbol='dmµs⁻²') -decimeters_per_nanosecond = Unit(100000000.0, Dimensions(length=1, time=-1), name='decimeters_per_nanosecond', ascii_symbol='dm/ns', symbol='dmns⁻¹') -decimeters_per_square_nanosecond = Unit(100000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_nanosecond', ascii_symbol='dm/ns^2', symbol='dmns⁻²') -decimeters_per_picosecond = Unit(100000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_picosecond', ascii_symbol='dm/ps', symbol='dmps⁻¹') -decimeters_per_square_picosecond = Unit(100000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_picosecond', ascii_symbol='dm/ps^2', symbol='dmps⁻²') -decimeters_per_femtosecond = Unit(100000000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_femtosecond', ascii_symbol='dm/fs', symbol='dmfs⁻¹') -decimeters_per_square_femtosecond = Unit(100000000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') -decimeters_per_attosecond = Unit(1e+17, Dimensions(length=1, time=-1), name='decimeters_per_attosecond', ascii_symbol='dm/as', symbol='dmas⁻¹') -decimeters_per_square_attosecond = Unit(1e+17, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') -decimeters_per_minute = Unit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmNone⁻¹') -decimeters_per_square_minute = Unit(0.0016666666666666668, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmNone⁻²') -decimeters_per_hour = Unit(0.0002777777777777778, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmNone⁻¹') -decimeters_per_square_hour = Unit(0.0002777777777777778, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmNone⁻²') -decimeters_per_day = Unit(1.1574074074074075e-05, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmNone⁻¹') -decimeters_per_square_day = Unit(1.1574074074074075e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmNone⁻²') -decimeters_per_year = Unit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmNone⁻¹') -decimeters_per_square_year = Unit(3.168873850681143e-08, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmNone⁻²') -centimeters_per_second = Unit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cmNone⁻¹') -centimeters_per_square_second = Unit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cmNone⁻²') -centimeters_per_millisecond = Unit(10.0, Dimensions(length=1, time=-1), name='centimeters_per_millisecond', ascii_symbol='cm/ms', symbol='cmms⁻¹') -centimeters_per_square_millisecond = Unit(10.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') -centimeters_per_microsecond = Unit(10000.0, Dimensions(length=1, time=-1), name='centimeters_per_microsecond', ascii_symbol='cm/us', symbol='cmµs⁻¹') -centimeters_per_square_microsecond = Unit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_microsecond', ascii_symbol='cm/us^2', symbol='cmµs⁻²') -centimeters_per_nanosecond = Unit(10000000.0, Dimensions(length=1, time=-1), name='centimeters_per_nanosecond', ascii_symbol='cm/ns', symbol='cmns⁻¹') -centimeters_per_square_nanosecond = Unit(10000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_nanosecond', ascii_symbol='cm/ns^2', symbol='cmns⁻²') -centimeters_per_picosecond = Unit(10000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_picosecond', ascii_symbol='cm/ps', symbol='cmps⁻¹') -centimeters_per_square_picosecond = Unit(10000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_picosecond', ascii_symbol='cm/ps^2', symbol='cmps⁻²') -centimeters_per_femtosecond = Unit(10000000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_femtosecond', ascii_symbol='cm/fs', symbol='cmfs⁻¹') -centimeters_per_square_femtosecond = Unit(10000000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') -centimeters_per_attosecond = Unit(1e+16, Dimensions(length=1, time=-1), name='centimeters_per_attosecond', ascii_symbol='cm/as', symbol='cmas⁻¹') -centimeters_per_square_attosecond = Unit(1e+16, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') -centimeters_per_minute = Unit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmNone⁻¹') -centimeters_per_square_minute = Unit(0.00016666666666666666, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmNone⁻²') -centimeters_per_hour = Unit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmNone⁻¹') -centimeters_per_square_hour = Unit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmNone⁻²') -centimeters_per_day = Unit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmNone⁻¹') -centimeters_per_square_day = Unit(1.1574074074074074e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmNone⁻²') -centimeters_per_year = Unit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmNone⁻¹') -centimeters_per_square_year = Unit(3.168873850681143e-09, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmNone⁻²') -angstroms_per_second = Unit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') -angstroms_per_square_second = Unit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') -angstroms_per_millisecond = Unit(1e-07, Dimensions(length=1, time=-1), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') -angstroms_per_square_millisecond = Unit(1e-07, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') -angstroms_per_microsecond = Unit(0.0001, Dimensions(length=1, time=-1), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') -angstroms_per_square_microsecond = Unit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') -angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(length=1, time=-1), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') -angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(length=1, time=-2), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') -angstroms_per_picosecond = Unit(100.0, Dimensions(length=1, time=-1), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') -angstroms_per_square_picosecond = Unit(100.0, Dimensions(length=1, time=-2), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') -angstroms_per_femtosecond = Unit(100000.0, Dimensions(length=1, time=-1), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') -angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') -angstroms_per_attosecond = Unit(100000000.0, Dimensions(length=1, time=-1), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') -angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') -angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') -angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') -angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') -angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') -angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') -angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') -angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') -angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') -grams_per_cubic_meter = Unit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_meter = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') -petagrams_per_cubic_meter = Unit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') -teragrams_per_cubic_meter = Unit(1000000000.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_meter = Unit(1000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') -megagrams_per_cubic_meter = Unit(1000.0, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') -kilograms_per_cubic_meter = Unit(1.0, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgNone⁻³') -milligrams_per_cubic_meter = Unit(1e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgNone⁻³') -micrograms_per_cubic_meter = Unit(1e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgNone⁻³') -nanograms_per_cubic_meter = Unit(1.0000000000000002e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngNone⁻³') -picograms_per_cubic_meter = Unit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') -femtograms_per_cubic_meter = Unit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') -attograms_per_cubic_meter = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_meter = Unit(1.660538921e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='NoneNone⁻³') -grams_per_cubic_exameter = Unit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') -exagrams_per_cubic_exameter = Unit(1e-39, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') -petagrams_per_cubic_exameter = Unit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') -teragrams_per_cubic_exameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') -gigagrams_per_cubic_exameter = Unit(1e-48, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_exameter', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') -megagrams_per_cubic_exameter = Unit(9.999999999999999e-52, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_exameter', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') -kilograms_per_cubic_exameter = Unit(9.999999999999999e-55, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_exameter', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') -milligrams_per_cubic_exameter = Unit(9.999999999999998e-61, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_exameter', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') -micrograms_per_cubic_exameter = Unit(9.999999999999999e-64, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_exameter', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') -nanograms_per_cubic_exameter = Unit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_exameter', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') -picograms_per_cubic_exameter = Unit(1e-69, Dimensions(length=-3, mass=1), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') -femtograms_per_cubic_exameter = Unit(1e-72, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') -attograms_per_cubic_exameter = Unit(1e-75, Dimensions(length=-3, mass=1), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -atomic_mass_units_per_cubic_exameter = Unit(1.6605389209999996e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='NoneEm⁻³') -grams_per_cubic_petameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') -exagrams_per_cubic_petameter = Unit(1e-30, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') -petagrams_per_cubic_petameter = Unit(1e-33, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') -teragrams_per_cubic_petameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') -gigagrams_per_cubic_petameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_petameter', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') -megagrams_per_cubic_petameter = Unit(1e-42, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_petameter', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') -kilograms_per_cubic_petameter = Unit(1.0000000000000001e-45, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_petameter', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') -milligrams_per_cubic_petameter = Unit(1e-51, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_petameter', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') -micrograms_per_cubic_petameter = Unit(1.0000000000000002e-54, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_petameter', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') -nanograms_per_cubic_petameter = Unit(1.0000000000000002e-57, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_petameter', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') -picograms_per_cubic_petameter = Unit(1.0000000000000001e-60, Dimensions(length=-3, mass=1), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') -femtograms_per_cubic_petameter = Unit(1.0000000000000002e-63, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') -attograms_per_cubic_petameter = Unit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -atomic_mass_units_per_cubic_petameter = Unit(1.660538921e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='NonePm⁻³') -grams_per_cubic_terameter = Unit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') -exagrams_per_cubic_terameter = Unit(1e-21, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') -petagrams_per_cubic_terameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') -teragrams_per_cubic_terameter = Unit(1e-27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') -gigagrams_per_cubic_terameter = Unit(9.999999999999999e-31, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_terameter', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') -megagrams_per_cubic_terameter = Unit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_terameter', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') -kilograms_per_cubic_terameter = Unit(1e-36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_terameter', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') -milligrams_per_cubic_terameter = Unit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_terameter', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') -micrograms_per_cubic_terameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_terameter', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') -nanograms_per_cubic_terameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_terameter', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') -picograms_per_cubic_terameter = Unit(1e-51, Dimensions(length=-3, mass=1), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') -femtograms_per_cubic_terameter = Unit(1e-54, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') -attograms_per_cubic_terameter = Unit(1.0000000000000001e-57, Dimensions(length=-3, mass=1), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -atomic_mass_units_per_cubic_terameter = Unit(1.6605389209999997e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='NoneTm⁻³') -grams_per_cubic_gigameter = Unit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') -exagrams_per_cubic_gigameter = Unit(1e-12, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') -petagrams_per_cubic_gigameter = Unit(1e-15, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') -teragrams_per_cubic_gigameter = Unit(1e-18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') -gigagrams_per_cubic_gigameter = Unit(1e-21, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_gigameter', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') -megagrams_per_cubic_gigameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_gigameter', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') -kilograms_per_cubic_gigameter = Unit(1e-27, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_gigameter', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') -milligrams_per_cubic_gigameter = Unit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_gigameter', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') -micrograms_per_cubic_gigameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_gigameter', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') -nanograms_per_cubic_gigameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_gigameter', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') -picograms_per_cubic_gigameter = Unit(1e-42, Dimensions(length=-3, mass=1), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') -femtograms_per_cubic_gigameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') -attograms_per_cubic_gigameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -atomic_mass_units_per_cubic_gigameter = Unit(1.660538921e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='NoneGm⁻³') -grams_per_cubic_megameter = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') -exagrams_per_cubic_megameter = Unit(0.001, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') -petagrams_per_cubic_megameter = Unit(1e-06, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') -teragrams_per_cubic_megameter = Unit(1e-09, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') -gigagrams_per_cubic_megameter = Unit(1e-12, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_megameter', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') -megagrams_per_cubic_megameter = Unit(1e-15, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_megameter', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') -kilograms_per_cubic_megameter = Unit(1e-18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_megameter', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') -milligrams_per_cubic_megameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_megameter', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') -micrograms_per_cubic_megameter = Unit(1e-27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_megameter', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') -nanograms_per_cubic_megameter = Unit(1.0000000000000003e-30, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_megameter', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') -picograms_per_cubic_megameter = Unit(1e-33, Dimensions(length=-3, mass=1), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') -femtograms_per_cubic_megameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') -attograms_per_cubic_megameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -atomic_mass_units_per_cubic_megameter = Unit(1.6605389209999997e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='NoneMm⁻³') -grams_per_cubic_kilometer = Unit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') -exagrams_per_cubic_kilometer = Unit(1000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') -petagrams_per_cubic_kilometer = Unit(1000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') -teragrams_per_cubic_kilometer = Unit(1.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') -gigagrams_per_cubic_kilometer = Unit(0.001, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_kilometer', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') -megagrams_per_cubic_kilometer = Unit(1e-06, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_kilometer', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') -kilograms_per_cubic_kilometer = Unit(1e-09, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_kilometer', ascii_symbol='kg km^-3', symbol='kgkm⁻³') -milligrams_per_cubic_kilometer = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_kilometer', ascii_symbol='mg km^-3', symbol='mgkm⁻³') -micrograms_per_cubic_kilometer = Unit(1e-18, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_kilometer', ascii_symbol='ug km^-3', symbol='µgkm⁻³') -nanograms_per_cubic_kilometer = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_kilometer', ascii_symbol='ng km^-3', symbol='ngkm⁻³') -picograms_per_cubic_kilometer = Unit(1.0000000000000001e-24, Dimensions(length=-3, mass=1), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') -femtograms_per_cubic_kilometer = Unit(1e-27, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') -attograms_per_cubic_kilometer = Unit(1e-30, Dimensions(length=-3, mass=1), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') -atomic_mass_units_per_cubic_kilometer = Unit(1.6605389209999997e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='Nonekm⁻³') -grams_per_cubic_millimeter = Unit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') -exagrams_per_cubic_millimeter = Unit(1e+24, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') -petagrams_per_cubic_millimeter = Unit(1e+21, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') -teragrams_per_cubic_millimeter = Unit(1e+18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') -gigagrams_per_cubic_millimeter = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_millimeter', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') -megagrams_per_cubic_millimeter = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_millimeter', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') -kilograms_per_cubic_millimeter = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_millimeter', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') -milligrams_per_cubic_millimeter = Unit(999.9999999999999, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_millimeter', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') -micrograms_per_cubic_millimeter = Unit(1.0, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_millimeter', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') -nanograms_per_cubic_millimeter = Unit(0.001, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_millimeter', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') -picograms_per_cubic_millimeter = Unit(1e-06, Dimensions(length=-3, mass=1), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') -femtograms_per_cubic_millimeter = Unit(1e-09, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') -attograms_per_cubic_millimeter = Unit(1e-12, Dimensions(length=-3, mass=1), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -atomic_mass_units_per_cubic_millimeter = Unit(1.6605389209999997e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='Nonemm⁻³') -grams_per_cubic_micrometer = Unit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') -exagrams_per_cubic_micrometer = Unit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') -petagrams_per_cubic_micrometer = Unit(1.0000000000000002e+30, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') -teragrams_per_cubic_micrometer = Unit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') -gigagrams_per_cubic_micrometer = Unit(1.0000000000000001e+24, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_micrometer', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') -megagrams_per_cubic_micrometer = Unit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_micrometer', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') -kilograms_per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_micrometer', ascii_symbol='kg um^-3', symbol='kgµm⁻³') -milligrams_per_cubic_micrometer = Unit(1000000000000.0001, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_micrometer', ascii_symbol='mg um^-3', symbol='mgµm⁻³') -micrograms_per_cubic_micrometer = Unit(1000000000.0000002, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_micrometer', ascii_symbol='ug um^-3', symbol='µgµm⁻³') -nanograms_per_cubic_micrometer = Unit(1000000.0000000003, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_micrometer', ascii_symbol='ng um^-3', symbol='ngµm⁻³') -picograms_per_cubic_micrometer = Unit(1000.0000000000002, Dimensions(length=-3, mass=1), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') -femtograms_per_cubic_micrometer = Unit(1.0000000000000002, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') -attograms_per_cubic_micrometer = Unit(0.0010000000000000002, Dimensions(length=-3, mass=1), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') -atomic_mass_units_per_cubic_micrometer = Unit(1.660538921e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='Noneµm⁻³') -grams_per_cubic_nanometer = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') -exagrams_per_cubic_nanometer = Unit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') -petagrams_per_cubic_nanometer = Unit(9.999999999999998e+38, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') -teragrams_per_cubic_nanometer = Unit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') -gigagrams_per_cubic_nanometer = Unit(9.999999999999998e+32, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_nanometer', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') -megagrams_per_cubic_nanometer = Unit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_nanometer', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') -kilograms_per_cubic_nanometer = Unit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_nanometer', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') -milligrams_per_cubic_nanometer = Unit(9.999999999999997e+20, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_nanometer', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') -micrograms_per_cubic_nanometer = Unit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_nanometer', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') -nanograms_per_cubic_nanometer = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_nanometer', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') -picograms_per_cubic_nanometer = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') -femtograms_per_cubic_nanometer = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') -attograms_per_cubic_nanometer = Unit(999999.9999999999, Dimensions(length=-3, mass=1), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -atomic_mass_units_per_cubic_nanometer = Unit(1.6605389209999994, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='Nonenm⁻³') -grams_per_cubic_picometer = Unit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') -exagrams_per_cubic_picometer = Unit(1e+51, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') -petagrams_per_cubic_picometer = Unit(1e+48, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') -teragrams_per_cubic_picometer = Unit(1.0000000000000001e+45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') -gigagrams_per_cubic_picometer = Unit(1e+42, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_picometer', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') -megagrams_per_cubic_picometer = Unit(1.0000000000000001e+39, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_picometer', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') -kilograms_per_cubic_picometer = Unit(1e+36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_picometer', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') -milligrams_per_cubic_picometer = Unit(1e+30, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_picometer', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') -micrograms_per_cubic_picometer = Unit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_picometer', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') -nanograms_per_cubic_picometer = Unit(1.0000000000000003e+24, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_picometer', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') -picograms_per_cubic_picometer = Unit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') -femtograms_per_cubic_picometer = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') -attograms_per_cubic_picometer = Unit(1000000000000000.1, Dimensions(length=-3, mass=1), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -atomic_mass_units_per_cubic_picometer = Unit(1660538921.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='Nonepm⁻³') -grams_per_cubic_femtometer = Unit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') -exagrams_per_cubic_femtometer = Unit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') -petagrams_per_cubic_femtometer = Unit(9.999999999999997e+56, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') -teragrams_per_cubic_femtometer = Unit(9.999999999999997e+53, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') -gigagrams_per_cubic_femtometer = Unit(9.999999999999997e+50, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_femtometer', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') -megagrams_per_cubic_femtometer = Unit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_femtometer', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') -kilograms_per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_femtometer', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') -milligrams_per_cubic_femtometer = Unit(9.999999999999996e+38, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_femtometer', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') -micrograms_per_cubic_femtometer = Unit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_femtometer', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') -nanograms_per_cubic_femtometer = Unit(1e+33, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_femtometer', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') -picograms_per_cubic_femtometer = Unit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') -femtograms_per_cubic_femtometer = Unit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') -attograms_per_cubic_femtometer = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -atomic_mass_units_per_cubic_femtometer = Unit(1.6605389209999992e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='Nonefm⁻³') -grams_per_cubic_attometer = Unit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') -exagrams_per_cubic_attometer = Unit(9.999999999999999e+68, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') -petagrams_per_cubic_attometer = Unit(9.999999999999998e+65, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') -teragrams_per_cubic_attometer = Unit(9.999999999999999e+62, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') -gigagrams_per_cubic_attometer = Unit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_attometer', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') -megagrams_per_cubic_attometer = Unit(9.999999999999999e+56, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_attometer', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') -kilograms_per_cubic_attometer = Unit(9.999999999999999e+53, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_attometer', ascii_symbol='kg am^-3', symbol='kgam⁻³') -milligrams_per_cubic_attometer = Unit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_attometer', ascii_symbol='mg am^-3', symbol='mgam⁻³') -micrograms_per_cubic_attometer = Unit(1e+45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_attometer', ascii_symbol='ug am^-3', symbol='µgam⁻³') -nanograms_per_cubic_attometer = Unit(1e+42, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_attometer', ascii_symbol='ng am^-3', symbol='ngam⁻³') -picograms_per_cubic_attometer = Unit(1e+39, Dimensions(length=-3, mass=1), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') -femtograms_per_cubic_attometer = Unit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') -attograms_per_cubic_attometer = Unit(1e+33, Dimensions(length=-3, mass=1), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') -atomic_mass_units_per_cubic_attometer = Unit(1.6605389209999997e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='Noneam⁻³') -grams_per_cubic_decimeter = Unit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='Nonedm⁻³') -exagrams_per_cubic_decimeter = Unit(9.999999999999997e+17, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_decimeter', ascii_symbol='Eg dm^-3', symbol='Egdm⁻³') -petagrams_per_cubic_decimeter = Unit(999999999999999.8, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_decimeter', ascii_symbol='Pg dm^-3', symbol='Pgdm⁻³') -teragrams_per_cubic_decimeter = Unit(999999999999.9998, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_decimeter', ascii_symbol='Tg dm^-3', symbol='Tgdm⁻³') -gigagrams_per_cubic_decimeter = Unit(999999999.9999998, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_decimeter', ascii_symbol='Gg dm^-3', symbol='Ggdm⁻³') -megagrams_per_cubic_decimeter = Unit(999999.9999999998, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_decimeter', ascii_symbol='Mg dm^-3', symbol='Mgdm⁻³') -kilograms_per_cubic_decimeter = Unit(999.9999999999998, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_decimeter', ascii_symbol='kg dm^-3', symbol='kgdm⁻³') -milligrams_per_cubic_decimeter = Unit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_decimeter', ascii_symbol='mg dm^-3', symbol='mgdm⁻³') -micrograms_per_cubic_decimeter = Unit(9.999999999999997e-07, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_decimeter', ascii_symbol='ug dm^-3', symbol='µgdm⁻³') -nanograms_per_cubic_decimeter = Unit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_decimeter', ascii_symbol='ng dm^-3', symbol='ngdm⁻³') -picograms_per_cubic_decimeter = Unit(9.999999999999998e-13, Dimensions(length=-3, mass=1), name='picograms_per_cubic_decimeter', ascii_symbol='pg dm^-3', symbol='pgdm⁻³') -femtograms_per_cubic_decimeter = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_decimeter', ascii_symbol='fg dm^-3', symbol='fgdm⁻³') -attograms_per_cubic_decimeter = Unit(9.999999999999999e-19, Dimensions(length=-3, mass=1), name='attograms_per_cubic_decimeter', ascii_symbol='ag dm^-3', symbol='agdm⁻³') -atomic_mass_units_per_cubic_decimeter = Unit(1.6605389209999993e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='Nonedm⁻³') -grams_per_cubic_centimeter = Unit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='Nonecm⁻³') -exagrams_per_cubic_centimeter = Unit(9.999999999999999e+20, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_centimeter', ascii_symbol='Eg cm^-3', symbol='Egcm⁻³') -petagrams_per_cubic_centimeter = Unit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_centimeter', ascii_symbol='Pg cm^-3', symbol='Pgcm⁻³') -teragrams_per_cubic_centimeter = Unit(999999999999999.9, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_centimeter', ascii_symbol='Tg cm^-3', symbol='Tgcm⁻³') -gigagrams_per_cubic_centimeter = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_centimeter', ascii_symbol='Gg cm^-3', symbol='Ggcm⁻³') -megagrams_per_cubic_centimeter = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_centimeter', ascii_symbol='Mg cm^-3', symbol='Mgcm⁻³') -kilograms_per_cubic_centimeter = Unit(999999.9999999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_centimeter', ascii_symbol='kg cm^-3', symbol='kgcm⁻³') -milligrams_per_cubic_centimeter = Unit(0.9999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_centimeter', ascii_symbol='mg cm^-3', symbol='mgcm⁻³') -micrograms_per_cubic_centimeter = Unit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_centimeter', ascii_symbol='ug cm^-3', symbol='µgcm⁻³') -nanograms_per_cubic_centimeter = Unit(1e-06, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_centimeter', ascii_symbol='ng cm^-3', symbol='ngcm⁻³') -picograms_per_cubic_centimeter = Unit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='picograms_per_cubic_centimeter', ascii_symbol='pg cm^-3', symbol='pgcm⁻³') -femtograms_per_cubic_centimeter = Unit(1e-12, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_centimeter', ascii_symbol='fg cm^-3', symbol='fgcm⁻³') -attograms_per_cubic_centimeter = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='attograms_per_cubic_centimeter', ascii_symbol='ag cm^-3', symbol='agcm⁻³') -atomic_mass_units_per_cubic_centimeter = Unit(1.6605389209999996e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='Nonecm⁻³') -grams_per_cubic_angstrom = Unit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') -exagrams_per_cubic_angstrom = Unit(1e+45, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') -petagrams_per_cubic_angstrom = Unit(9.999999999999999e+41, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') -teragrams_per_cubic_angstrom = Unit(1e+39, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') -gigagrams_per_cubic_angstrom = Unit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_angstrom', ascii_symbol='Gg Ang^-3', symbol='GgÅ⁻³') -megagrams_per_cubic_angstrom = Unit(1e+33, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_angstrom', ascii_symbol='Mg Ang^-3', symbol='MgÅ⁻³') -kilograms_per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_angstrom', ascii_symbol='kg Ang^-3', symbol='kgÅ⁻³') -milligrams_per_cubic_angstrom = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_angstrom', ascii_symbol='mg Ang^-3', symbol='mgÅ⁻³') -micrograms_per_cubic_angstrom = Unit(1e+21, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_angstrom', ascii_symbol='ug Ang^-3', symbol='µgÅ⁻³') -nanograms_per_cubic_angstrom = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_angstrom', ascii_symbol='ng Ang^-3', symbol='ngÅ⁻³') -picograms_per_cubic_angstrom = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') -femtograms_per_cubic_angstrom = Unit(1000000000000.0, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') -attograms_per_cubic_angstrom = Unit(1000000000.0, Dimensions(length=-3, mass=1), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') -atomic_mass_units_per_cubic_angstrom = Unit(1660.5389209999996, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='NoneÅ⁻³') -moles_per_cubic_meter = Unit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_meter = Unit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_meter = Unit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_meter = Unit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_meter = Unit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_meter = Unit(602214076.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_meter = Unit(602214.076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolNone⁻³') -moles_per_cubic_exameter = Unit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='NoneEm⁻³') -millimoles_per_cubic_exameter = Unit(6.02214076e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') -micromoles_per_cubic_exameter = Unit(6.02214076e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') -nanomoles_per_cubic_exameter = Unit(6.022140759999999e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') -picomoles_per_cubic_exameter = Unit(6.022140759999999e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') -femtomoles_per_cubic_exameter = Unit(6.02214076e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') -attomoles_per_cubic_exameter = Unit(6.022140759999999e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') -moles_per_cubic_petameter = Unit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='NonePm⁻³') -millimoles_per_cubic_petameter = Unit(6.0221407600000005e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') -micromoles_per_cubic_petameter = Unit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') -nanomoles_per_cubic_petameter = Unit(6.02214076e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') -picomoles_per_cubic_petameter = Unit(6.0221407600000005e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') -femtomoles_per_cubic_petameter = Unit(6.022140760000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') -attomoles_per_cubic_petameter = Unit(6.022140760000001e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') -moles_per_cubic_terameter = Unit(6.022140759999999e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='NoneTm⁻³') -millimoles_per_cubic_terameter = Unit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') -micromoles_per_cubic_terameter = Unit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') -nanomoles_per_cubic_terameter = Unit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') -picomoles_per_cubic_terameter = Unit(6.02214076e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') -femtomoles_per_cubic_terameter = Unit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') -attomoles_per_cubic_terameter = Unit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') -moles_per_cubic_gigameter = Unit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='NoneGm⁻³') -millimoles_per_cubic_gigameter = Unit(6.022140760000001e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') -micromoles_per_cubic_gigameter = Unit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') -nanomoles_per_cubic_gigameter = Unit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') -picomoles_per_cubic_gigameter = Unit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') -femtomoles_per_cubic_gigameter = Unit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') -attomoles_per_cubic_gigameter = Unit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') -moles_per_cubic_megameter = Unit(602214.076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='NoneMm⁻³') -millimoles_per_cubic_megameter = Unit(602.214076, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') -micromoles_per_cubic_megameter = Unit(0.602214076, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') -nanomoles_per_cubic_megameter = Unit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') -picomoles_per_cubic_megameter = Unit(6.02214076e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') -femtomoles_per_cubic_megameter = Unit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') -attomoles_per_cubic_megameter = Unit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') -moles_per_cubic_kilometer = Unit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='Nonekm⁻³') -millimoles_per_cubic_kilometer = Unit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') -micromoles_per_cubic_kilometer = Unit(602214076.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') -nanomoles_per_cubic_kilometer = Unit(602214.076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') -picomoles_per_cubic_kilometer = Unit(602.214076, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') -femtomoles_per_cubic_kilometer = Unit(0.602214076, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') -attomoles_per_cubic_kilometer = Unit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') -moles_per_cubic_millimeter = Unit(6.0221407599999995e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='Nonemm⁻³') -millimoles_per_cubic_millimeter = Unit(6.02214076e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') -micromoles_per_cubic_millimeter = Unit(6.0221407599999996e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') -nanomoles_per_cubic_millimeter = Unit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') -picomoles_per_cubic_millimeter = Unit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') -femtomoles_per_cubic_millimeter = Unit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') -attomoles_per_cubic_millimeter = Unit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') -moles_per_cubic_micrometer = Unit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='Noneµm⁻³') -millimoles_per_cubic_micrometer = Unit(6.022140760000001e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') -micromoles_per_cubic_micrometer = Unit(6.0221407600000004e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') -nanomoles_per_cubic_micrometer = Unit(6.022140760000001e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') -picomoles_per_cubic_micrometer = Unit(6.022140760000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') -femtomoles_per_cubic_micrometer = Unit(6.022140760000001e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') -attomoles_per_cubic_micrometer = Unit(6.0221407600000005e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') -moles_per_cubic_nanometer = Unit(6.022140759999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='Nonenm⁻³') -millimoles_per_cubic_nanometer = Unit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') -micromoles_per_cubic_nanometer = Unit(6.022140759999999e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') -nanomoles_per_cubic_nanometer = Unit(6.022140759999998e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') -picomoles_per_cubic_nanometer = Unit(6.0221407599999985e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') -femtomoles_per_cubic_nanometer = Unit(6.022140759999999e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') -attomoles_per_cubic_nanometer = Unit(6.022140759999999e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') -moles_per_cubic_picometer = Unit(6.0221407600000005e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='Nonepm⁻³') -millimoles_per_cubic_picometer = Unit(6.0221407600000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') -micromoles_per_cubic_picometer = Unit(6.022140760000001e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') -nanomoles_per_cubic_picometer = Unit(6.0221407600000005e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') -picomoles_per_cubic_picometer = Unit(6.02214076e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') -femtomoles_per_cubic_picometer = Unit(6.022140760000001e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') -attomoles_per_cubic_picometer = Unit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') -moles_per_cubic_femtometer = Unit(6.022140759999998e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='Nonefm⁻³') -millimoles_per_cubic_femtometer = Unit(6.022140759999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') -micromoles_per_cubic_femtometer = Unit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') -nanomoles_per_cubic_femtometer = Unit(6.022140759999998e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') -picomoles_per_cubic_femtometer = Unit(6.022140759999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') -femtomoles_per_cubic_femtometer = Unit(6.022140759999998e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') -attomoles_per_cubic_femtometer = Unit(6.022140759999998e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') -moles_per_cubic_attometer = Unit(6.022140759999998e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='Noneam⁻³') -millimoles_per_cubic_attometer = Unit(6.022140759999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') -micromoles_per_cubic_attometer = Unit(6.022140759999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') -nanomoles_per_cubic_attometer = Unit(6.022140759999999e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') -picomoles_per_cubic_attometer = Unit(6.022140759999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') -femtomoles_per_cubic_attometer = Unit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') -attomoles_per_cubic_attometer = Unit(6.022140759999999e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') -moles_per_cubic_decimeter = Unit(6.022140759999998e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='Nonedm⁻³') -millimoles_per_cubic_decimeter = Unit(6.0221407599999985e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') -micromoles_per_cubic_decimeter = Unit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') -nanomoles_per_cubic_decimeter = Unit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') -picomoles_per_cubic_decimeter = Unit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') -femtomoles_per_cubic_decimeter = Unit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') -attomoles_per_cubic_decimeter = Unit(602214075.9999999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') -moles_per_cubic_centimeter = Unit(6.022140759999999e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='Nonecm⁻³') -millimoles_per_cubic_centimeter = Unit(6.022140759999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') -micromoles_per_cubic_centimeter = Unit(6.022140759999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') -nanomoles_per_cubic_centimeter = Unit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') -picomoles_per_cubic_centimeter = Unit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') -femtomoles_per_cubic_centimeter = Unit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') -attomoles_per_cubic_centimeter = Unit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') -moles_per_cubic_angstrom = Unit(6.022140759999999e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='NoneÅ⁻³') -millimoles_per_cubic_angstrom = Unit(6.02214076e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') -micromoles_per_cubic_angstrom = Unit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') -nanomoles_per_cubic_angstrom = Unit(6.02214076e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') -picomoles_per_cubic_angstrom = Unit(6.02214076e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') -femtomoles_per_cubic_angstrom = Unit(6.022140759999999e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') -attomoles_per_cubic_angstrom = Unit(6.02214076e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') +meters = NamedUnit(1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') +exameters = NamedUnit(1e+18, Dimensions(1, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') +petameters = NamedUnit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') +terameters = NamedUnit(1000000000000.0, Dimensions(1, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') +gigameters = NamedUnit(1000000000.0, Dimensions(1, 0, 0, 0, 0),name='gigameters',ascii_symbol='Gm',symbol='Gm') +megameters = NamedUnit(1000000.0, Dimensions(1, 0, 0, 0, 0),name='megameters',ascii_symbol='Mm',symbol='Mm') +kilometers = NamedUnit(1000.0, Dimensions(1, 0, 0, 0, 0),name='kilometers',ascii_symbol='km',symbol='km') +millimeters = NamedUnit(0.001, Dimensions(1, 0, 0, 0, 0),name='millimeters',ascii_symbol='mm',symbol='mm') +micrometers = NamedUnit(1e-06, Dimensions(1, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',symbol='µm') +nanometers = NamedUnit(1e-09, Dimensions(1, 0, 0, 0, 0),name='nanometers',ascii_symbol='nm',symbol='nm') +picometers = NamedUnit(1e-12, Dimensions(1, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') +femtometers = NamedUnit(1e-15, Dimensions(1, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') +attometers = NamedUnit(1e-18, Dimensions(1, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') +decimeters = NamedUnit(0.1, Dimensions(1, 0, 0, 0, 0),name='decimeters',ascii_symbol='dm',symbol='dm') +centimeters = NamedUnit(0.01, Dimensions(1, 0, 0, 0, 0),name='centimeters',ascii_symbol='cm',symbol='cm') +seconds = NamedUnit(1, Dimensions(0, 1, 0, 0, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') +milliseconds = NamedUnit(0.001, Dimensions(0, 1, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') +microseconds = NamedUnit(1e-06, Dimensions(0, 1, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') +nanoseconds = NamedUnit(1e-09, Dimensions(0, 1, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') +picoseconds = NamedUnit(1e-12, Dimensions(0, 1, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') +femtoseconds = NamedUnit(1e-15, Dimensions(0, 1, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') +attoseconds = NamedUnit(1e-18, Dimensions(0, 1, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') +grams = NamedUnit(0.001, Dimensions(0, 0, 1, 0, 0, 0, 0),name='grams',ascii_symbol='g',symbol='g') +exagrams = NamedUnit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') +petagrams = NamedUnit(1000000000000.0, Dimensions(0, 0, 1, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') +teragrams = NamedUnit(1000000000.0, Dimensions(0, 0, 1, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') +gigagrams = NamedUnit(1000000.0, Dimensions(0, 0, 1, 0, 0),name='gigagrams',ascii_symbol='Gg',symbol='Gg') +megagrams = NamedUnit(1000.0, Dimensions(0, 0, 1, 0, 0),name='megagrams',ascii_symbol='Mg',symbol='Mg') +kilograms = NamedUnit(1.0, Dimensions(0, 0, 1, 0, 0),name='kilograms',ascii_symbol='kg',symbol='kg') +milligrams = NamedUnit(1e-06, Dimensions(0, 0, 1, 0, 0),name='milligrams',ascii_symbol='mg',symbol='mg') +micrograms = NamedUnit(1e-09, Dimensions(0, 0, 1, 0, 0),name='micrograms',ascii_symbol='ug',symbol='µg') +nanograms = NamedUnit(1.0000000000000002e-12, Dimensions(0, 0, 1, 0, 0),name='nanograms',ascii_symbol='ng',symbol='ng') +picograms = NamedUnit(1e-15, Dimensions(0, 0, 1, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') +femtograms = NamedUnit(1e-18, Dimensions(0, 0, 1, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') +attograms = NamedUnit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') +amperes = NamedUnit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amperes',ascii_symbol='A',symbol='A') +exaamperes = NamedUnit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamperes',ascii_symbol='EA',symbol='EA') +petaamperes = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamperes',ascii_symbol='PA',symbol='PA') +teraamperes = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamperes',ascii_symbol='TA',symbol='TA') +gigaamperes = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 1, 0),name='gigaamperes',ascii_symbol='GA',symbol='GA') +megaamperes = NamedUnit(1000000.0, Dimensions(0, 0, 0, 1, 0),name='megaamperes',ascii_symbol='MA',symbol='MA') +kiloamperes = NamedUnit(1000.0, Dimensions(0, 0, 0, 1, 0),name='kiloamperes',ascii_symbol='kA',symbol='kA') +milliamperes = NamedUnit(0.001, Dimensions(0, 0, 0, 1, 0),name='milliamperes',ascii_symbol='mA',symbol='mA') +microamperes = NamedUnit(1e-06, Dimensions(0, 0, 0, 1, 0),name='microamperes',ascii_symbol='uA',symbol='µA') +nanoamperes = NamedUnit(1e-09, Dimensions(0, 0, 0, 1, 0),name='nanoamperes',ascii_symbol='nA',symbol='nA') +picoamperes = NamedUnit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamperes',ascii_symbol='pA',symbol='pA') +femtoamperes = NamedUnit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamperes',ascii_symbol='fA',symbol='fA') +attoamperes = NamedUnit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamperes',ascii_symbol='aA',symbol='aA') +kelvin = NamedUnit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kelvin',ascii_symbol='K',symbol='K') +exakelvin = NamedUnit(1e+18, Dimensions(0, 0, 0, 0, 1),name='exakelvin',ascii_symbol='EK',symbol='EK') +petakelvin = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1),name='petakelvin',ascii_symbol='PK',symbol='PK') +terakelvin = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 0, 1),name='terakelvin',ascii_symbol='TK',symbol='TK') +gigakelvin = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 0, 1),name='gigakelvin',ascii_symbol='GK',symbol='GK') +megakelvin = NamedUnit(1000000.0, Dimensions(0, 0, 0, 0, 1),name='megakelvin',ascii_symbol='MK',symbol='MK') +kilokelvin = NamedUnit(1000.0, Dimensions(0, 0, 0, 0, 1),name='kilokelvin',ascii_symbol='kK',symbol='kK') +millikelvin = NamedUnit(0.001, Dimensions(0, 0, 0, 0, 1),name='millikelvin',ascii_symbol='mK',symbol='mK') +microkelvin = NamedUnit(1e-06, Dimensions(0, 0, 0, 0, 1),name='microkelvin',ascii_symbol='uK',symbol='µK') +nanokelvin = NamedUnit(1e-09, Dimensions(0, 0, 0, 0, 1),name='nanokelvin',ascii_symbol='nK',symbol='nK') +picokelvin = NamedUnit(1e-12, Dimensions(0, 0, 0, 0, 1),name='picokelvin',ascii_symbol='pK',symbol='pK') +femtokelvin = NamedUnit(1e-15, Dimensions(0, 0, 0, 0, 1),name='femtokelvin',ascii_symbol='fK',symbol='fK') +attokelvin = NamedUnit(1e-18, Dimensions(0, 0, 0, 0, 1),name='attokelvin',ascii_symbol='aK',symbol='aK') +hertz = NamedUnit(1, Dimensions(0, -1, 0, 0, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') +exahertz = NamedUnit(1e+18, Dimensions(0, -1, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') +petahertz = NamedUnit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') +terahertz = NamedUnit(1000000000000.0, Dimensions(0, -1, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') +gigahertz = NamedUnit(1000000000.0, Dimensions(0, -1, 0, 0, 0),name='gigahertz',ascii_symbol='GHz',symbol='GHz') +megahertz = NamedUnit(1000000.0, Dimensions(0, -1, 0, 0, 0),name='megahertz',ascii_symbol='MHz',symbol='MHz') +kilohertz = NamedUnit(1000.0, Dimensions(0, -1, 0, 0, 0),name='kilohertz',ascii_symbol='kHz',symbol='kHz') +millihertz = NamedUnit(0.001, Dimensions(0, -1, 0, 0, 0),name='millihertz',ascii_symbol='mHz',symbol='mHz') +microhertz = NamedUnit(1e-06, Dimensions(0, -1, 0, 0, 0),name='microhertz',ascii_symbol='uHz',symbol='µHz') +nanohertz = NamedUnit(1e-09, Dimensions(0, -1, 0, 0, 0),name='nanohertz',ascii_symbol='nHz',symbol='nHz') +picohertz = NamedUnit(1e-12, Dimensions(0, -1, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') +femtohertz = NamedUnit(1e-15, Dimensions(0, -1, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') +attohertz = NamedUnit(1e-18, Dimensions(0, -1, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') +newtons = NamedUnit(1, Dimensions(1, -2, 1, 0, 0, 0, 0),name='newtons',ascii_symbol='N',symbol='N') +exanewtons = NamedUnit(1e+18, Dimensions(1, -2, 1, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') +petanewtons = NamedUnit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') +teranewtons = NamedUnit(1000000000000.0, Dimensions(1, -2, 1, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') +giganewtons = NamedUnit(1000000000.0, Dimensions(1, -2, 1, 0, 0),name='giganewtons',ascii_symbol='GN',symbol='GN') +meganewtons = NamedUnit(1000000.0, Dimensions(1, -2, 1, 0, 0),name='meganewtons',ascii_symbol='MN',symbol='MN') +kilonewtons = NamedUnit(1000.0, Dimensions(1, -2, 1, 0, 0),name='kilonewtons',ascii_symbol='kN',symbol='kN') +millinewtons = NamedUnit(0.001, Dimensions(1, -2, 1, 0, 0),name='millinewtons',ascii_symbol='mN',symbol='mN') +micronewtons = NamedUnit(1e-06, Dimensions(1, -2, 1, 0, 0),name='micronewtons',ascii_symbol='uN',symbol='µN') +nanonewtons = NamedUnit(1e-09, Dimensions(1, -2, 1, 0, 0),name='nanonewtons',ascii_symbol='nN',symbol='nN') +piconewtons = NamedUnit(1e-12, Dimensions(1, -2, 1, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') +femtonewtons = NamedUnit(1e-15, Dimensions(1, -2, 1, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') +attonewtons = NamedUnit(1e-18, Dimensions(1, -2, 1, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') +pascals = NamedUnit(1, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') +exapascals = NamedUnit(1e+18, Dimensions(-1, -2, 1, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') +petapascals = NamedUnit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') +terapascals = NamedUnit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') +gigapascals = NamedUnit(1000000000.0, Dimensions(-1, -2, 1, 0, 0),name='gigapascals',ascii_symbol='GPa',symbol='GPa') +megapascals = NamedUnit(1000000.0, Dimensions(-1, -2, 1, 0, 0),name='megapascals',ascii_symbol='MPa',symbol='MPa') +kilopascals = NamedUnit(1000.0, Dimensions(-1, -2, 1, 0, 0),name='kilopascals',ascii_symbol='kPa',symbol='kPa') +millipascals = NamedUnit(0.001, Dimensions(-1, -2, 1, 0, 0),name='millipascals',ascii_symbol='mPa',symbol='mPa') +micropascals = NamedUnit(1e-06, Dimensions(-1, -2, 1, 0, 0),name='micropascals',ascii_symbol='uPa',symbol='µPa') +nanopascals = NamedUnit(1e-09, Dimensions(-1, -2, 1, 0, 0),name='nanopascals',ascii_symbol='nPa',symbol='nPa') +picopascals = NamedUnit(1e-12, Dimensions(-1, -2, 1, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') +femtopascals = NamedUnit(1e-15, Dimensions(-1, -2, 1, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') +attopascals = NamedUnit(1e-18, Dimensions(-1, -2, 1, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') +joules = NamedUnit(1, Dimensions(2, -2, 1, 0, 0, 0, 0),name='joules',ascii_symbol='J',symbol='J') +exajoules = NamedUnit(1e+18, Dimensions(2, -2, 1, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') +petajoules = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') +terajoules = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') +gigajoules = NamedUnit(1000000000.0, Dimensions(2, -2, 1, 0, 0),name='gigajoules',ascii_symbol='GJ',symbol='GJ') +megajoules = NamedUnit(1000000.0, Dimensions(2, -2, 1, 0, 0),name='megajoules',ascii_symbol='MJ',symbol='MJ') +kilojoules = NamedUnit(1000.0, Dimensions(2, -2, 1, 0, 0),name='kilojoules',ascii_symbol='kJ',symbol='kJ') +millijoules = NamedUnit(0.001, Dimensions(2, -2, 1, 0, 0),name='millijoules',ascii_symbol='mJ',symbol='mJ') +microjoules = NamedUnit(1e-06, Dimensions(2, -2, 1, 0, 0),name='microjoules',ascii_symbol='uJ',symbol='µJ') +nanojoules = NamedUnit(1e-09, Dimensions(2, -2, 1, 0, 0),name='nanojoules',ascii_symbol='nJ',symbol='nJ') +picojoules = NamedUnit(1e-12, Dimensions(2, -2, 1, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') +femtojoules = NamedUnit(1e-15, Dimensions(2, -2, 1, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') +attojoules = NamedUnit(1e-18, Dimensions(2, -2, 1, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') +watts = NamedUnit(1, Dimensions(2, -3, 1, 0, 0, 0, 0),name='watts',ascii_symbol='W',symbol='W') +exawatts = NamedUnit(1e+18, Dimensions(2, -3, 1, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') +petawatts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') +terawatts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') +gigawatts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, 0, 0),name='gigawatts',ascii_symbol='GW',symbol='GW') +megawatts = NamedUnit(1000000.0, Dimensions(2, -3, 1, 0, 0),name='megawatts',ascii_symbol='MW',symbol='MW') +kilowatts = NamedUnit(1000.0, Dimensions(2, -3, 1, 0, 0),name='kilowatts',ascii_symbol='kW',symbol='kW') +milliwatts = NamedUnit(0.001, Dimensions(2, -3, 1, 0, 0),name='milliwatts',ascii_symbol='mW',symbol='mW') +microwatts = NamedUnit(1e-06, Dimensions(2, -3, 1, 0, 0),name='microwatts',ascii_symbol='uW',symbol='µW') +nanowatts = NamedUnit(1e-09, Dimensions(2, -3, 1, 0, 0),name='nanowatts',ascii_symbol='nW',symbol='nW') +picowatts = NamedUnit(1e-12, Dimensions(2, -3, 1, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') +femtowatts = NamedUnit(1e-15, Dimensions(2, -3, 1, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') +attowatts = NamedUnit(1e-18, Dimensions(2, -3, 1, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') +coulombs = NamedUnit(1, Dimensions(0, 1, 0, 1, 0, 0, 0),name='coulombs',ascii_symbol='C',symbol='C') +exacoulombs = NamedUnit(1e+18, Dimensions(0, 1, 0, 1, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') +petacoulombs = NamedUnit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') +teracoulombs = NamedUnit(1000000000000.0, Dimensions(0, 1, 0, 1, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') +gigacoulombs = NamedUnit(1000000000.0, Dimensions(0, 1, 0, 1, 0),name='gigacoulombs',ascii_symbol='GC',symbol='GC') +megacoulombs = NamedUnit(1000000.0, Dimensions(0, 1, 0, 1, 0),name='megacoulombs',ascii_symbol='MC',symbol='MC') +kilocoulombs = NamedUnit(1000.0, Dimensions(0, 1, 0, 1, 0),name='kilocoulombs',ascii_symbol='kC',symbol='kC') +millicoulombs = NamedUnit(0.001, Dimensions(0, 1, 0, 1, 0),name='millicoulombs',ascii_symbol='mC',symbol='mC') +microcoulombs = NamedUnit(1e-06, Dimensions(0, 1, 0, 1, 0),name='microcoulombs',ascii_symbol='uC',symbol='µC') +nanocoulombs = NamedUnit(1e-09, Dimensions(0, 1, 0, 1, 0),name='nanocoulombs',ascii_symbol='nC',symbol='nC') +picocoulombs = NamedUnit(1e-12, Dimensions(0, 1, 0, 1, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') +femtocoulombs = NamedUnit(1e-15, Dimensions(0, 1, 0, 1, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') +attocoulombs = NamedUnit(1e-18, Dimensions(0, 1, 0, 1, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') +volts = NamedUnit(1, Dimensions(2, -3, 1, -1, 0, 0, 0),name='volts',ascii_symbol='V',symbol='V') +exavolts = NamedUnit(1e+18, Dimensions(2, -3, 1, -1, 0),name='exavolts',ascii_symbol='EV',symbol='EV') +petavolts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0),name='petavolts',ascii_symbol='PV',symbol='PV') +teravolts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -1, 0),name='teravolts',ascii_symbol='TV',symbol='TV') +gigavolts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -1, 0),name='gigavolts',ascii_symbol='GV',symbol='GV') +megavolts = NamedUnit(1000000.0, Dimensions(2, -3, 1, -1, 0),name='megavolts',ascii_symbol='MV',symbol='MV') +kilovolts = NamedUnit(1000.0, Dimensions(2, -3, 1, -1, 0),name='kilovolts',ascii_symbol='kV',symbol='kV') +millivolts = NamedUnit(0.001, Dimensions(2, -3, 1, -1, 0),name='millivolts',ascii_symbol='mV',symbol='mV') +microvolts = NamedUnit(1e-06, Dimensions(2, -3, 1, -1, 0),name='microvolts',ascii_symbol='uV',symbol='µV') +nanovolts = NamedUnit(1e-09, Dimensions(2, -3, 1, -1, 0),name='nanovolts',ascii_symbol='nV',symbol='nV') +picovolts = NamedUnit(1e-12, Dimensions(2, -3, 1, -1, 0),name='picovolts',ascii_symbol='pV',symbol='pV') +femtovolts = NamedUnit(1e-15, Dimensions(2, -3, 1, -1, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') +attovolts = NamedUnit(1e-18, Dimensions(2, -3, 1, -1, 0),name='attovolts',ascii_symbol='aV',symbol='aV') +ohms = NamedUnit(1, Dimensions(2, -3, 1, -2, 0, 0, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') +exaohms = NamedUnit(1e+18, Dimensions(2, -3, 1, -2, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') +petaohms = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') +teraohms = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -2, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') +gigaohms = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -2, 0),name='gigaohms',ascii_symbol='GOhm',symbol='GΩ') +megaohms = NamedUnit(1000000.0, Dimensions(2, -3, 1, -2, 0),name='megaohms',ascii_symbol='MOhm',symbol='MΩ') +kiloohms = NamedUnit(1000.0, Dimensions(2, -3, 1, -2, 0),name='kiloohms',ascii_symbol='kOhm',symbol='kΩ') +milliohms = NamedUnit(0.001, Dimensions(2, -3, 1, -2, 0),name='milliohms',ascii_symbol='mOhm',symbol='mΩ') +microohms = NamedUnit(1e-06, Dimensions(2, -3, 1, -2, 0),name='microohms',ascii_symbol='uOhm',symbol='µΩ') +nanoohms = NamedUnit(1e-09, Dimensions(2, -3, 1, -2, 0),name='nanoohms',ascii_symbol='nOhm',symbol='nΩ') +picoohms = NamedUnit(1e-12, Dimensions(2, -3, 1, -2, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') +femtoohms = NamedUnit(1e-15, Dimensions(2, -3, 1, -2, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') +attoohms = NamedUnit(1e-18, Dimensions(2, -3, 1, -2, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') +farads = NamedUnit(1, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='farads',ascii_symbol='F',symbol='F') +exafarads = NamedUnit(1e+18, Dimensions(-2, 4, -1, 2, 0),name='exafarads',ascii_symbol='EF',symbol='EF') +petafarads = NamedUnit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='petafarads',ascii_symbol='PF',symbol='PF') +terafarads = NamedUnit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='terafarads',ascii_symbol='TF',symbol='TF') +gigafarads = NamedUnit(1000000000.0, Dimensions(-2, 4, -1, 2, 0),name='gigafarads',ascii_symbol='GF',symbol='GF') +megafarads = NamedUnit(1000000.0, Dimensions(-2, 4, -1, 2, 0),name='megafarads',ascii_symbol='MF',symbol='MF') +kilofarads = NamedUnit(1000.0, Dimensions(-2, 4, -1, 2, 0),name='kilofarads',ascii_symbol='kF',symbol='kF') +millifarads = NamedUnit(0.001, Dimensions(-2, 4, -1, 2, 0),name='millifarads',ascii_symbol='mF',symbol='mF') +microfarads = NamedUnit(1e-06, Dimensions(-2, 4, -1, 2, 0),name='microfarads',ascii_symbol='uF',symbol='µF') +nanofarads = NamedUnit(1e-09, Dimensions(-2, 4, -1, 2, 0),name='nanofarads',ascii_symbol='nF',symbol='nF') +picofarads = NamedUnit(1e-12, Dimensions(-2, 4, -1, 2, 0),name='picofarads',ascii_symbol='pF',symbol='pF') +femtofarads = NamedUnit(1e-15, Dimensions(-2, 4, -1, 2, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') +attofarads = NamedUnit(1e-18, Dimensions(-2, 4, -1, 2, 0),name='attofarads',ascii_symbol='aF',symbol='aF') +siemens = NamedUnit(1, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='siemens',ascii_symbol='S',symbol='S') +exasiemens = NamedUnit(1e+18, Dimensions(-2, 3, -1, 2, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') +petasiemens = NamedUnit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') +terasiemens = NamedUnit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') +gigasiemens = NamedUnit(1000000000.0, Dimensions(-2, 3, -1, 2, 0),name='gigasiemens',ascii_symbol='GS',symbol='GS') +megasiemens = NamedUnit(1000000.0, Dimensions(-2, 3, -1, 2, 0),name='megasiemens',ascii_symbol='MS',symbol='MS') +kilosiemens = NamedUnit(1000.0, Dimensions(-2, 3, -1, 2, 0),name='kilosiemens',ascii_symbol='kS',symbol='kS') +millisiemens = NamedUnit(0.001, Dimensions(-2, 3, -1, 2, 0),name='millisiemens',ascii_symbol='mS',symbol='mS') +microsiemens = NamedUnit(1e-06, Dimensions(-2, 3, -1, 2, 0),name='microsiemens',ascii_symbol='uS',symbol='µS') +nanosiemens = NamedUnit(1e-09, Dimensions(-2, 3, -1, 2, 0),name='nanosiemens',ascii_symbol='nS',symbol='nS') +picosiemens = NamedUnit(1e-12, Dimensions(-2, 3, -1, 2, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') +femtosiemens = NamedUnit(1e-15, Dimensions(-2, 3, -1, 2, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') +attosiemens = NamedUnit(1e-18, Dimensions(-2, 3, -1, 2, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') +webers = NamedUnit(1, Dimensions(2, -2, 1, -1, 0, 0, 0),name='webers',ascii_symbol='Wb',symbol='Wb') +exawebers = NamedUnit(1e+18, Dimensions(2, -2, 1, -1, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') +petawebers = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') +terawebers = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -1, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') +gigawebers = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -1, 0),name='gigawebers',ascii_symbol='GWb',symbol='GWb') +megawebers = NamedUnit(1000000.0, Dimensions(2, -2, 1, -1, 0),name='megawebers',ascii_symbol='MWb',symbol='MWb') +kilowebers = NamedUnit(1000.0, Dimensions(2, -2, 1, -1, 0),name='kilowebers',ascii_symbol='kWb',symbol='kWb') +milliwebers = NamedUnit(0.001, Dimensions(2, -2, 1, -1, 0),name='milliwebers',ascii_symbol='mWb',symbol='mWb') +microwebers = NamedUnit(1e-06, Dimensions(2, -2, 1, -1, 0),name='microwebers',ascii_symbol='uWb',symbol='µWb') +nanowebers = NamedUnit(1e-09, Dimensions(2, -2, 1, -1, 0),name='nanowebers',ascii_symbol='nWb',symbol='nWb') +picowebers = NamedUnit(1e-12, Dimensions(2, -2, 1, -1, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') +femtowebers = NamedUnit(1e-15, Dimensions(2, -2, 1, -1, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') +attowebers = NamedUnit(1e-18, Dimensions(2, -2, 1, -1, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') +tesla = NamedUnit(1, Dimensions(0, -2, 1, -1, 0, 0, 0),name='tesla',ascii_symbol='T',symbol='T') +exatesla = NamedUnit(1e+18, Dimensions(0, -2, 1, -1, 0),name='exatesla',ascii_symbol='ET',symbol='ET') +petatesla = NamedUnit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0),name='petatesla',ascii_symbol='PT',symbol='PT') +teratesla = NamedUnit(1000000000000.0, Dimensions(0, -2, 1, -1, 0),name='teratesla',ascii_symbol='TT',symbol='TT') +gigatesla = NamedUnit(1000000000.0, Dimensions(0, -2, 1, -1, 0),name='gigatesla',ascii_symbol='GT',symbol='GT') +megatesla = NamedUnit(1000000.0, Dimensions(0, -2, 1, -1, 0),name='megatesla',ascii_symbol='MT',symbol='MT') +kilotesla = NamedUnit(1000.0, Dimensions(0, -2, 1, -1, 0),name='kilotesla',ascii_symbol='kT',symbol='kT') +millitesla = NamedUnit(0.001, Dimensions(0, -2, 1, -1, 0),name='millitesla',ascii_symbol='mT',symbol='mT') +microtesla = NamedUnit(1e-06, Dimensions(0, -2, 1, -1, 0),name='microtesla',ascii_symbol='uT',symbol='µT') +nanotesla = NamedUnit(1e-09, Dimensions(0, -2, 1, -1, 0),name='nanotesla',ascii_symbol='nT',symbol='nT') +picotesla = NamedUnit(1e-12, Dimensions(0, -2, 1, -1, 0),name='picotesla',ascii_symbol='pT',symbol='pT') +femtotesla = NamedUnit(1e-15, Dimensions(0, -2, 1, -1, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') +attotesla = NamedUnit(1e-18, Dimensions(0, -2, 1, -1, 0),name='attotesla',ascii_symbol='aT',symbol='aT') +henry = NamedUnit(1, Dimensions(2, -2, 1, -2, 0, 0, 0),name='henry',ascii_symbol='H',symbol='H') +exahenry = NamedUnit(1e+18, Dimensions(2, -2, 1, -2, 0),name='exahenry',ascii_symbol='EH',symbol='EH') +petahenry = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0),name='petahenry',ascii_symbol='PH',symbol='PH') +terahenry = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -2, 0),name='terahenry',ascii_symbol='TH',symbol='TH') +gigahenry = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -2, 0),name='gigahenry',ascii_symbol='GH',symbol='GH') +megahenry = NamedUnit(1000000.0, Dimensions(2, -2, 1, -2, 0),name='megahenry',ascii_symbol='MH',symbol='MH') +kilohenry = NamedUnit(1000.0, Dimensions(2, -2, 1, -2, 0),name='kilohenry',ascii_symbol='kH',symbol='kH') +millihenry = NamedUnit(0.001, Dimensions(2, -2, 1, -2, 0),name='millihenry',ascii_symbol='mH',symbol='mH') +microhenry = NamedUnit(1e-06, Dimensions(2, -2, 1, -2, 0),name='microhenry',ascii_symbol='uH',symbol='µH') +nanohenry = NamedUnit(1e-09, Dimensions(2, -2, 1, -2, 0),name='nanohenry',ascii_symbol='nH',symbol='nH') +picohenry = NamedUnit(1e-12, Dimensions(2, -2, 1, -2, 0),name='picohenry',ascii_symbol='pH',symbol='pH') +femtohenry = NamedUnit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') +attohenry = NamedUnit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') +angstroms = NamedUnit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') +minutes = NamedUnit(60, Dimensions(0, 1, 0, 0, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') +hours = NamedUnit(360, Dimensions(0, 1, 0, 0, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') +days = NamedUnit(8640, Dimensions(0, 1, 0, 0, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') +years = NamedUnit(3155695.2, Dimensions(0, 1, 0, 0, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') +degrees = NamedUnit(57.29577951308232, Dimensions(0, 0, 0, 0, 0, 0, 1),name='degrees',ascii_symbol='deg',symbol='deg') +radians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 1),name='radians',ascii_symbol='rad',symbol='rad') +stradians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') +none = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') +litres = NamedUnit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') +electronvolts = NamedUnit(1.602176634e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') +exaelectronvolts = NamedUnit(0.1602176634, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') +petaelectronvolts = NamedUnit(0.0001602176634, Dimensions(2, -2, 1, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') +teraelectronvolts = NamedUnit(1.602176634e-07, Dimensions(2, -2, 1, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') +gigaelectronvolts = NamedUnit(1.6021766339999998e-10, Dimensions(2, -2, 1, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') +megaelectronvolts = NamedUnit(1.6021766339999998e-13, Dimensions(2, -2, 1, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') +kiloelectronvolts = NamedUnit(1.602176634e-16, Dimensions(2, -2, 1, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') +millielectronvolts = NamedUnit(1.6021766339999998e-22, Dimensions(2, -2, 1, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') +microelectronvolts = NamedUnit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') +nanoelectronvolts = NamedUnit(1.602176634e-28, Dimensions(2, -2, 1, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') +picoelectronvolts = NamedUnit(1.6021766339999998e-31, Dimensions(2, -2, 1, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') +femtoelectronvolts = NamedUnit(1.602176634e-34, Dimensions(2, -2, 1, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') +attoelectronvolts = NamedUnit(1.602176634e-37, Dimensions(2, -2, 1, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') +atomic_mass_units = NamedUnit(1.660538921e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') +moles = NamedUnit(6.02214076e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') +millimoles = NamedUnit(6.02214076e+20, Dimensions(0, 0, 0, 0, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') +micromoles = NamedUnit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') +nanomoles = NamedUnit(602214076000000.0, Dimensions(0, 0, 0, 0, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') +picomoles = NamedUnit(602214076000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') +femtomoles = NamedUnit(602214076.0, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') +attomoles = NamedUnit(602214.076, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') +kg_force = NamedUnit(9.80665, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kg_force',ascii_symbol='kgForce',symbol='kgForce') +degrees_celsius = NamedUnit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') +miles = NamedUnit(1609.344, Dimensions(1, 0, 0, 0, 0, 0, 0),name='miles',ascii_symbol='miles',symbol='miles') +yards = NamedUnit(0.9144000000000001, Dimensions(1, 0, 0, 0, 0, 0, 0),name='yards',ascii_symbol='yrd',symbol='yrd') +feet = NamedUnit(0.3048, Dimensions(1, 0, 0, 0, 0, 0, 0),name='feet',ascii_symbol='ft',symbol='ft') +inches = NamedUnit(0.0254, Dimensions(1, 0, 0, 0, 0, 0, 0),name='inches',ascii_symbol='in',symbol='in') +pounds = NamedUnit(0.45359237, Dimensions(0, 0, 1, 0, 0, 0, 0),name='pounds',ascii_symbol='lb',symbol='lb') +ounces = NamedUnit(0.028349523125, Dimensions(0, 0, 1, 0, 0, 0, 0),name='ounces',ascii_symbol='oz',symbol='oz') +pound_force_per_square_inch = NamedUnit(6894.757, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pound_force_per_square_inch',ascii_symbol='psi',symbol='psi') +square_meters = NamedUnit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') +cubic_meters = NamedUnit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') +per_meter = NamedUnit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') +per_square_meter = NamedUnit(1.0, Dimensions(length=-2), name='per_square_meter', ascii_symbol='m^-2', symbol='m⁻²') +per_cubic_meter = NamedUnit(1.0, Dimensions(length=-3), name='per_cubic_meter', ascii_symbol='m^-3', symbol='m⁻³') +square_exameters = NamedUnit(1e+36, Dimensions(length=2), name='square_exameters', ascii_symbol='Em^2', symbol='Em²') +cubic_exameters = NamedUnit(1e+54, Dimensions(length=3), name='cubic_exameters', ascii_symbol='Em^3', symbol='Em³') +per_exameter = NamedUnit(1e-18, Dimensions(length=-1), name='per_exameter', ascii_symbol='Em^-1', symbol='Em⁻¹') +per_square_exameter = NamedUnit(1e-36, Dimensions(length=-2), name='per_square_exameter', ascii_symbol='Em^-2', symbol='Em⁻²') +per_cubic_exameter = NamedUnit(1e-54, Dimensions(length=-3), name='per_cubic_exameter', ascii_symbol='Em^-3', symbol='Em⁻³') +square_petameters = NamedUnit(1e+30, Dimensions(length=2), name='square_petameters', ascii_symbol='Pm^2', symbol='Pm²') +cubic_petameters = NamedUnit(1e+45, Dimensions(length=3), name='cubic_petameters', ascii_symbol='Pm^3', symbol='Pm³') +per_petameter = NamedUnit(1e-15, Dimensions(length=-1), name='per_petameter', ascii_symbol='Pm^-1', symbol='Pm⁻¹') +per_square_petameter = NamedUnit(1e-30, Dimensions(length=-2), name='per_square_petameter', ascii_symbol='Pm^-2', symbol='Pm⁻²') +per_cubic_petameter = NamedUnit(1e-45, Dimensions(length=-3), name='per_cubic_petameter', ascii_symbol='Pm^-3', symbol='Pm⁻³') +square_terameters = NamedUnit(1e+24, Dimensions(length=2), name='square_terameters', ascii_symbol='Tm^2', symbol='Tm²') +cubic_terameters = NamedUnit(1e+36, Dimensions(length=3), name='cubic_terameters', ascii_symbol='Tm^3', symbol='Tm³') +per_terameter = NamedUnit(1e-12, Dimensions(length=-1), name='per_terameter', ascii_symbol='Tm^-1', symbol='Tm⁻¹') +per_square_terameter = NamedUnit(1e-24, Dimensions(length=-2), name='per_square_terameter', ascii_symbol='Tm^-2', symbol='Tm⁻²') +per_cubic_terameter = NamedUnit(1e-36, Dimensions(length=-3), name='per_cubic_terameter', ascii_symbol='Tm^-3', symbol='Tm⁻³') +square_gigameters = NamedUnit(1e+18, Dimensions(length=2), name='square_gigameters', ascii_symbol='Gm^2', symbol='Gm²') +cubic_gigameters = NamedUnit(1e+27, Dimensions(length=3), name='cubic_gigameters', ascii_symbol='Gm^3', symbol='Gm³') +per_gigameter = NamedUnit(1e-09, Dimensions(length=-1), name='per_gigameter', ascii_symbol='Gm^-1', symbol='Gm⁻¹') +per_square_gigameter = NamedUnit(1e-18, Dimensions(length=-2), name='per_square_gigameter', ascii_symbol='Gm^-2', symbol='Gm⁻²') +per_cubic_gigameter = NamedUnit(1e-27, Dimensions(length=-3), name='per_cubic_gigameter', ascii_symbol='Gm^-3', symbol='Gm⁻³') +square_megameters = NamedUnit(1000000000000.0, Dimensions(length=2), name='square_megameters', ascii_symbol='Mm^2', symbol='Mm²') +cubic_megameters = NamedUnit(1e+18, Dimensions(length=3), name='cubic_megameters', ascii_symbol='Mm^3', symbol='Mm³') +per_megameter = NamedUnit(1e-06, Dimensions(length=-1), name='per_megameter', ascii_symbol='Mm^-1', symbol='Mm⁻¹') +per_square_megameter = NamedUnit(1e-12, Dimensions(length=-2), name='per_square_megameter', ascii_symbol='Mm^-2', symbol='Mm⁻²') +per_cubic_megameter = NamedUnit(1e-18, Dimensions(length=-3), name='per_cubic_megameter', ascii_symbol='Mm^-3', symbol='Mm⁻³') +square_kilometers = NamedUnit(1000000.0, Dimensions(length=2), name='square_kilometers', ascii_symbol='km^2', symbol='km²') +cubic_kilometers = NamedUnit(1000000000.0, Dimensions(length=3), name='cubic_kilometers', ascii_symbol='km^3', symbol='km³') +per_kilometer = NamedUnit(0.001, Dimensions(length=-1), name='per_kilometer', ascii_symbol='km^-1', symbol='km⁻¹') +per_square_kilometer = NamedUnit(1e-06, Dimensions(length=-2), name='per_square_kilometer', ascii_symbol='km^-2', symbol='km⁻²') +per_cubic_kilometer = NamedUnit(1e-09, Dimensions(length=-3), name='per_cubic_kilometer', ascii_symbol='km^-3', symbol='km⁻³') +square_millimeters = NamedUnit(1e-06, Dimensions(length=2), name='square_millimeters', ascii_symbol='mm^2', symbol='mm²') +cubic_millimeters = NamedUnit(1e-09, Dimensions(length=3), name='cubic_millimeters', ascii_symbol='mm^3', symbol='mm³') +per_millimeter = NamedUnit(1000.0, Dimensions(length=-1), name='per_millimeter', ascii_symbol='mm^-1', symbol='mm⁻¹') +per_square_millimeter = NamedUnit(1000000.0, Dimensions(length=-2), name='per_square_millimeter', ascii_symbol='mm^-2', symbol='mm⁻²') +per_cubic_millimeter = NamedUnit(999999999.9999999, Dimensions(length=-3), name='per_cubic_millimeter', ascii_symbol='mm^-3', symbol='mm⁻³') +square_micrometers = NamedUnit(1e-12, Dimensions(length=2), name='square_micrometers', ascii_symbol='um^2', symbol='µm²') +cubic_micrometers = NamedUnit(9.999999999999999e-19, Dimensions(length=3), name='cubic_micrometers', ascii_symbol='um^3', symbol='µm³') +per_micrometer = NamedUnit(1000000.0, Dimensions(length=-1), name='per_micrometer', ascii_symbol='um^-1', symbol='µm⁻¹') +per_square_micrometer = NamedUnit(1000000000000.0001, Dimensions(length=-2), name='per_square_micrometer', ascii_symbol='um^-2', symbol='µm⁻²') +per_cubic_micrometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3), name='per_cubic_micrometer', ascii_symbol='um^-3', symbol='µm⁻³') +square_nanometers = NamedUnit(1e-18, Dimensions(length=2), name='square_nanometers', ascii_symbol='nm^2', symbol='nm²') +cubic_nanometers = NamedUnit(1.0000000000000002e-27, Dimensions(length=3), name='cubic_nanometers', ascii_symbol='nm^3', symbol='nm³') +per_nanometer = NamedUnit(999999999.9999999, Dimensions(length=-1), name='per_nanometer', ascii_symbol='nm^-1', symbol='nm⁻¹') +per_square_nanometer = NamedUnit(9.999999999999999e+17, Dimensions(length=-2), name='per_square_nanometer', ascii_symbol='nm^-2', symbol='nm⁻²') +per_cubic_nanometer = NamedUnit(9.999999999999999e+26, Dimensions(length=-3), name='per_cubic_nanometer', ascii_symbol='nm^-3', symbol='nm⁻³') +square_picometers = NamedUnit(1e-24, Dimensions(length=2), name='square_picometers', ascii_symbol='pm^2', symbol='pm²') +cubic_picometers = NamedUnit(1e-36, Dimensions(length=3), name='cubic_picometers', ascii_symbol='pm^3', symbol='pm³') +per_picometer = NamedUnit(1000000000000.0, Dimensions(length=-1), name='per_picometer', ascii_symbol='pm^-1', symbol='pm⁻¹') +per_square_picometer = NamedUnit(1e+24, Dimensions(length=-2), name='per_square_picometer', ascii_symbol='pm^-2', symbol='pm⁻²') +per_cubic_picometer = NamedUnit(1e+36, Dimensions(length=-3), name='per_cubic_picometer', ascii_symbol='pm^-3', symbol='pm⁻³') +square_femtometers = NamedUnit(1e-30, Dimensions(length=2), name='square_femtometers', ascii_symbol='fm^2', symbol='fm²') +cubic_femtometers = NamedUnit(1.0000000000000003e-45, Dimensions(length=3), name='cubic_femtometers', ascii_symbol='fm^3', symbol='fm³') +per_femtometer = NamedUnit(999999999999999.9, Dimensions(length=-1), name='per_femtometer', ascii_symbol='fm^-1', symbol='fm⁻¹') +per_square_femtometer = NamedUnit(9.999999999999999e+29, Dimensions(length=-2), name='per_square_femtometer', ascii_symbol='fm^-2', symbol='fm⁻²') +per_cubic_femtometer = NamedUnit(9.999999999999998e+44, Dimensions(length=-3), name='per_cubic_femtometer', ascii_symbol='fm^-3', symbol='fm⁻³') +square_attometers = NamedUnit(1.0000000000000001e-36, Dimensions(length=2), name='square_attometers', ascii_symbol='am^2', symbol='am²') +cubic_attometers = NamedUnit(1.0000000000000002e-54, Dimensions(length=3), name='cubic_attometers', ascii_symbol='am^3', symbol='am³') +per_attometer = NamedUnit(9.999999999999999e+17, Dimensions(length=-1), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') +per_square_attometer = NamedUnit(9.999999999999999e+35, Dimensions(length=-2), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') +per_cubic_attometer = NamedUnit(9.999999999999997e+53, Dimensions(length=-3), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') +square_decimeters = NamedUnit(0.010000000000000002, Dimensions(length=2), name='square_decimeters', ascii_symbol='dm^2', symbol='dm²') +cubic_decimeters = NamedUnit(0.0010000000000000002, Dimensions(length=3), name='cubic_decimeters', ascii_symbol='dm^3', symbol='dm³') +per_decimeter = NamedUnit(10.0, Dimensions(length=-1), name='per_decimeter', ascii_symbol='dm^-1', symbol='dm⁻¹') +per_square_decimeter = NamedUnit(99.99999999999999, Dimensions(length=-2), name='per_square_decimeter', ascii_symbol='dm^-2', symbol='dm⁻²') +per_cubic_decimeter = NamedUnit(999.9999999999999, Dimensions(length=-3), name='per_cubic_decimeter', ascii_symbol='dm^-3', symbol='dm⁻³') +square_centimeters = NamedUnit(0.0001, Dimensions(length=2), name='square_centimeters', ascii_symbol='cm^2', symbol='cm²') +cubic_centimeters = NamedUnit(1.0000000000000002e-06, Dimensions(length=3), name='cubic_centimeters', ascii_symbol='cm^3', symbol='cm³') +per_centimeter = NamedUnit(100.0, Dimensions(length=-1), name='per_centimeter', ascii_symbol='cm^-1', symbol='cm⁻¹') +per_square_centimeter = NamedUnit(10000.0, Dimensions(length=-2), name='per_square_centimeter', ascii_symbol='cm^-2', symbol='cm⁻²') +per_cubic_centimeter = NamedUnit(999999.9999999999, Dimensions(length=-3), name='per_cubic_centimeter', ascii_symbol='cm^-3', symbol='cm⁻³') +square_angstroms = NamedUnit(1.0000000000000001e-20, Dimensions(length=2), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ų') +cubic_angstroms = NamedUnit(1e-30, Dimensions(length=3), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='ų') +per_angstrom = NamedUnit(10000000000.0, Dimensions(length=-1), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Å⁻¹') +per_square_angstrom = NamedUnit(1e+20, Dimensions(length=-2), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Å⁻²') +per_cubic_angstrom = NamedUnit(9.999999999999999e+29, Dimensions(length=-3), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Å⁻³') +square_miles = NamedUnit(2589988.110336, Dimensions(length=2), name='square_miles', ascii_symbol='miles^2', symbol='miles²') +cubic_miles = NamedUnit(4168181825.44058, Dimensions(length=3), name='cubic_miles', ascii_symbol='miles^3', symbol='miles³') +per_mile = NamedUnit(0.0006213711922373339, Dimensions(length=-1), name='per_mile', ascii_symbol='miles^-1', symbol='miles⁻¹') +per_square_mile = NamedUnit(3.861021585424458e-07, Dimensions(length=-2), name='per_square_mile', ascii_symbol='miles^-2', symbol='miles⁻²') +per_cubic_mile = NamedUnit(2.399127585789277e-10, Dimensions(length=-3), name='per_cubic_mile', ascii_symbol='miles^-3', symbol='miles⁻³') +square_yards = NamedUnit(0.8361273600000002, Dimensions(length=2), name='square_yards', ascii_symbol='yrd^2', symbol='yrd²') +cubic_yards = NamedUnit(0.7645548579840002, Dimensions(length=3), name='cubic_yards', ascii_symbol='yrd^3', symbol='yrd³') +per_yard = NamedUnit(1.0936132983377076, Dimensions(length=-1), name='per_yard', ascii_symbol='yrd^-1', symbol='yrd⁻¹') +per_square_yard = NamedUnit(1.19599004630108, Dimensions(length=-2), name='per_square_yard', ascii_symbol='yrd^-2', symbol='yrd⁻²') +per_cubic_yard = NamedUnit(1.3079506193143917, Dimensions(length=-3), name='per_cubic_yard', ascii_symbol='yrd^-3', symbol='yrd⁻³') +square_feet = NamedUnit(0.09290304, Dimensions(length=2), name='square_feet', ascii_symbol='ft^2', symbol='ft²') +cubic_feet = NamedUnit(0.028316846592000004, Dimensions(length=3), name='cubic_feet', ascii_symbol='ft^3', symbol='ft³') +per_foot = NamedUnit(3.280839895013123, Dimensions(length=-1), name='per_foot', ascii_symbol='ft^-1', symbol='ft⁻¹') +per_square_foot = NamedUnit(10.763910416709722, Dimensions(length=-2), name='per_square_foot', ascii_symbol='ft^-2', symbol='ft⁻²') +per_cubic_foot = NamedUnit(35.314666721488585, Dimensions(length=-3), name='per_cubic_foot', ascii_symbol='ft^-3', symbol='ft⁻³') +square_inches = NamedUnit(0.00064516, Dimensions(length=2), name='square_inches', ascii_symbol='in^2', symbol='in²') +cubic_inches = NamedUnit(1.6387064e-05, Dimensions(length=3), name='cubic_inches', ascii_symbol='in^3', symbol='in³') +per_inch = NamedUnit(39.37007874015748, Dimensions(length=-1), name='per_inch', ascii_symbol='in^-1', symbol='in⁻¹') +per_square_inch = NamedUnit(1550.0031000062002, Dimensions(length=-2), name='per_square_inch', ascii_symbol='in^-2', symbol='in⁻²') +per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3), name='per_cubic_inch', ascii_symbol='in^-3', symbol='in⁻³') +meters_per_second = NamedUnit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') +meters_per_square_second = NamedUnit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') +meters_per_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') +meters_per_square_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') +meters_per_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') +meters_per_square_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') +meters_per_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') +meters_per_square_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') +meters_per_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') +meters_per_square_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') +meters_per_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') +meters_per_square_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') +meters_per_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') +meters_per_square_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') +meters_per_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') +meters_per_square_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') +meters_per_hour = NamedUnit(0.002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') +meters_per_square_hour = NamedUnit(0.002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') +meters_per_day = NamedUnit(0.00011574074074074075, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') +meters_per_square_day = NamedUnit(0.00011574074074074075, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') +meters_per_year = NamedUnit(3.168873850681143e-07, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') +meters_per_square_year = NamedUnit(3.168873850681143e-07, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') +exameters_per_second = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') +exameters_per_square_second = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') +exameters_per_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') +exameters_per_square_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') +exameters_per_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') +exameters_per_square_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') +exameters_per_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') +exameters_per_square_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') +exameters_per_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-1), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') +exameters_per_square_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-2), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') +exameters_per_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') +exameters_per_square_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') +exameters_per_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-1), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') +exameters_per_square_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') +exameters_per_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') +exameters_per_square_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') +exameters_per_hour = NamedUnit(2777777777777778.0, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') +exameters_per_square_hour = NamedUnit(2777777777777778.0, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') +exameters_per_day = NamedUnit(115740740740740.73, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') +exameters_per_square_day = NamedUnit(115740740740740.73, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') +exameters_per_year = NamedUnit(316887385068.1143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') +exameters_per_square_year = NamedUnit(316887385068.1143, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') +petameters_per_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') +petameters_per_square_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') +petameters_per_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') +petameters_per_square_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') +petameters_per_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') +petameters_per_square_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') +petameters_per_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') +petameters_per_square_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') +petameters_per_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-1), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') +petameters_per_square_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') +petameters_per_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') +petameters_per_square_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') +petameters_per_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') +petameters_per_square_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') +petameters_per_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') +petameters_per_square_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') +petameters_per_hour = NamedUnit(2777777777777.778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') +petameters_per_square_hour = NamedUnit(2777777777777.778, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') +petameters_per_day = NamedUnit(115740740740.74074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') +petameters_per_square_day = NamedUnit(115740740740.74074, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') +petameters_per_year = NamedUnit(316887385.0681143, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') +petameters_per_square_year = NamedUnit(316887385.0681143, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') +terameters_per_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') +terameters_per_square_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') +terameters_per_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') +terameters_per_square_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') +terameters_per_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') +terameters_per_square_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') +terameters_per_nanosecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') +terameters_per_square_nanosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') +terameters_per_picosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') +terameters_per_square_picosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') +terameters_per_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') +terameters_per_square_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') +terameters_per_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') +terameters_per_square_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') +terameters_per_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') +terameters_per_square_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') +terameters_per_hour = NamedUnit(2777777777.7777777, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') +terameters_per_square_hour = NamedUnit(2777777777.7777777, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') +terameters_per_day = NamedUnit(115740740.74074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') +terameters_per_square_day = NamedUnit(115740740.74074075, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') +terameters_per_year = NamedUnit(316887.38506811426, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') +terameters_per_square_year = NamedUnit(316887.38506811426, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') +gigameters_per_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') +gigameters_per_square_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') +gigameters_per_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') +gigameters_per_square_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') +gigameters_per_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') +gigameters_per_square_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') +gigameters_per_nanosecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') +gigameters_per_square_nanosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') +gigameters_per_picosecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') +gigameters_per_square_picosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') +gigameters_per_femtosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') +gigameters_per_square_femtosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') +gigameters_per_attosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') +gigameters_per_square_attosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') +gigameters_per_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') +gigameters_per_square_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') +gigameters_per_hour = NamedUnit(2777777.777777778, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') +gigameters_per_square_hour = NamedUnit(2777777.777777778, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') +gigameters_per_day = NamedUnit(115740.74074074074, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') +gigameters_per_square_day = NamedUnit(115740.74074074074, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') +gigameters_per_year = NamedUnit(316.88738506811427, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') +gigameters_per_square_year = NamedUnit(316.88738506811427, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') +megameters_per_second = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') +megameters_per_square_second = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') +megameters_per_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') +megameters_per_square_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') +megameters_per_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') +megameters_per_square_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') +megameters_per_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') +megameters_per_square_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') +megameters_per_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') +megameters_per_square_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') +megameters_per_femtosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') +megameters_per_square_femtosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') +megameters_per_attosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') +megameters_per_square_attosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') +megameters_per_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') +megameters_per_square_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') +megameters_per_hour = NamedUnit(2777.777777777778, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') +megameters_per_square_hour = NamedUnit(2777.777777777778, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') +megameters_per_day = NamedUnit(115.74074074074075, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') +megameters_per_square_day = NamedUnit(115.74074074074075, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') +megameters_per_year = NamedUnit(0.3168873850681143, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') +megameters_per_square_year = NamedUnit(0.3168873850681143, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') +kilometers_per_second = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') +kilometers_per_square_second = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') +kilometers_per_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') +kilometers_per_square_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') +kilometers_per_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') +kilometers_per_square_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') +kilometers_per_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') +kilometers_per_square_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-2), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') +kilometers_per_picosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') +kilometers_per_square_picosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') +kilometers_per_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') +kilometers_per_square_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') +kilometers_per_attosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') +kilometers_per_square_attosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') +kilometers_per_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') +kilometers_per_square_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') +kilometers_per_hour = NamedUnit(2.7777777777777777, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') +kilometers_per_square_hour = NamedUnit(2.7777777777777777, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') +kilometers_per_day = NamedUnit(0.11574074074074074, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') +kilometers_per_square_day = NamedUnit(0.11574074074074074, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') +kilometers_per_year = NamedUnit(0.0003168873850681143, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') +kilometers_per_square_year = NamedUnit(0.0003168873850681143, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') +millimeters_per_second = NamedUnit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') +millimeters_per_square_second = NamedUnit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') +millimeters_per_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') +millimeters_per_square_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') +millimeters_per_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') +millimeters_per_square_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') +millimeters_per_nanosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') +millimeters_per_square_nanosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') +millimeters_per_picosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') +millimeters_per_square_picosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') +millimeters_per_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') +millimeters_per_square_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') +millimeters_per_attosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') +millimeters_per_square_attosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') +millimeters_per_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') +millimeters_per_square_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') +millimeters_per_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') +millimeters_per_square_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') +millimeters_per_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') +millimeters_per_square_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') +millimeters_per_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') +millimeters_per_square_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') +micrometers_per_second = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') +micrometers_per_square_second = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') +micrometers_per_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') +micrometers_per_square_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') +micrometers_per_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') +micrometers_per_square_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') +micrometers_per_nanosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-1), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') +micrometers_per_square_nanosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-2), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') +micrometers_per_picosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') +micrometers_per_square_picosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') +micrometers_per_femtosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') +micrometers_per_square_femtosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') +micrometers_per_attosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') +micrometers_per_square_attosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') +micrometers_per_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') +micrometers_per_square_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') +micrometers_per_hour = NamedUnit(2.7777777777777776e-09, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') +micrometers_per_square_hour = NamedUnit(2.7777777777777776e-09, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') +micrometers_per_day = NamedUnit(1.1574074074074074e-10, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') +micrometers_per_square_day = NamedUnit(1.1574074074074074e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') +micrometers_per_year = NamedUnit(3.168873850681143e-13, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') +micrometers_per_square_year = NamedUnit(3.168873850681143e-13, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') +nanometers_per_second = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') +nanometers_per_square_second = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') +nanometers_per_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') +nanometers_per_square_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') +nanometers_per_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') +nanometers_per_square_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') +nanometers_per_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') +nanometers_per_square_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') +nanometers_per_picosecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') +nanometers_per_square_picosecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') +nanometers_per_femtosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') +nanometers_per_square_femtosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') +nanometers_per_attosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') +nanometers_per_square_attosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') +nanometers_per_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') +nanometers_per_square_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') +nanometers_per_hour = NamedUnit(2.777777777777778e-12, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') +nanometers_per_square_hour = NamedUnit(2.777777777777778e-12, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') +nanometers_per_day = NamedUnit(1.1574074074074076e-13, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') +nanometers_per_square_day = NamedUnit(1.1574074074074076e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') +nanometers_per_year = NamedUnit(3.1688738506811433e-16, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') +nanometers_per_square_year = NamedUnit(3.1688738506811433e-16, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') +picometers_per_second = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') +picometers_per_square_second = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') +picometers_per_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') +picometers_per_square_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') +picometers_per_microsecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') +picometers_per_square_microsecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') +picometers_per_nanosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') +picometers_per_square_nanosecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') +picometers_per_picosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') +picometers_per_square_picosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') +picometers_per_femtosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-1), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') +picometers_per_square_femtosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') +picometers_per_attosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-1), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') +picometers_per_square_attosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') +picometers_per_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') +picometers_per_square_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') +picometers_per_hour = NamedUnit(2.7777777777777776e-15, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') +picometers_per_square_hour = NamedUnit(2.7777777777777776e-15, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') +picometers_per_day = NamedUnit(1.1574074074074073e-16, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') +picometers_per_square_day = NamedUnit(1.1574074074074073e-16, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') +picometers_per_year = NamedUnit(3.168873850681143e-19, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') +picometers_per_square_year = NamedUnit(3.168873850681143e-19, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') +femtometers_per_second = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') +femtometers_per_square_second = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') +femtometers_per_millisecond = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') +femtometers_per_square_millisecond = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') +femtometers_per_microsecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') +femtometers_per_square_microsecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') +femtometers_per_nanosecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') +femtometers_per_square_nanosecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') +femtometers_per_picosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') +femtometers_per_square_picosecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') +femtometers_per_femtosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') +femtometers_per_square_femtosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') +femtometers_per_attosecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') +femtometers_per_square_attosecond = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') +femtometers_per_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') +femtometers_per_square_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') +femtometers_per_hour = NamedUnit(2.777777777777778e-18, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') +femtometers_per_square_hour = NamedUnit(2.777777777777778e-18, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') +femtometers_per_day = NamedUnit(1.1574074074074075e-19, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') +femtometers_per_square_day = NamedUnit(1.1574074074074075e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') +femtometers_per_year = NamedUnit(3.168873850681143e-22, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') +femtometers_per_square_year = NamedUnit(3.168873850681143e-22, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') +attometers_per_second = NamedUnit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') +attometers_per_square_second = NamedUnit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') +attometers_per_millisecond = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') +attometers_per_square_millisecond = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') +attometers_per_microsecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-1), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') +attometers_per_square_microsecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') +attometers_per_nanosecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') +attometers_per_square_nanosecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') +attometers_per_picosecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-1), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') +attometers_per_square_picosecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-2), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') +attometers_per_femtosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') +attometers_per_square_femtosecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') +attometers_per_attosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') +attometers_per_square_attosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') +attometers_per_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') +attometers_per_square_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') +attometers_per_hour = NamedUnit(2.7777777777777778e-21, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') +attometers_per_square_hour = NamedUnit(2.7777777777777778e-21, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') +attometers_per_day = NamedUnit(1.1574074074074074e-22, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') +attometers_per_square_day = NamedUnit(1.1574074074074074e-22, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') +attometers_per_year = NamedUnit(3.1688738506811433e-25, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') +attometers_per_square_year = NamedUnit(3.1688738506811433e-25, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') +decimeters_per_second = NamedUnit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dmNone⁻¹') +decimeters_per_square_second = NamedUnit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dmNone⁻²') +decimeters_per_millisecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='decimeters_per_millisecond', ascii_symbol='dm/ms', symbol='dmms⁻¹') +decimeters_per_square_millisecond = NamedUnit(100.0, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') +decimeters_per_microsecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-1), name='decimeters_per_microsecond', ascii_symbol='dm/us', symbol='dmµs⁻¹') +decimeters_per_square_microsecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_microsecond', ascii_symbol='dm/us^2', symbol='dmµs⁻²') +decimeters_per_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='decimeters_per_nanosecond', ascii_symbol='dm/ns', symbol='dmns⁻¹') +decimeters_per_square_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_nanosecond', ascii_symbol='dm/ns^2', symbol='dmns⁻²') +decimeters_per_picosecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_picosecond', ascii_symbol='dm/ps', symbol='dmps⁻¹') +decimeters_per_square_picosecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_picosecond', ascii_symbol='dm/ps^2', symbol='dmps⁻²') +decimeters_per_femtosecond = NamedUnit(100000000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_femtosecond', ascii_symbol='dm/fs', symbol='dmfs⁻¹') +decimeters_per_square_femtosecond = NamedUnit(100000000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') +decimeters_per_attosecond = NamedUnit(1e+17, Dimensions(length=1, time=-1), name='decimeters_per_attosecond', ascii_symbol='dm/as', symbol='dmas⁻¹') +decimeters_per_square_attosecond = NamedUnit(1e+17, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') +decimeters_per_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmNone⁻¹') +decimeters_per_square_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmNone⁻²') +decimeters_per_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmNone⁻¹') +decimeters_per_square_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmNone⁻²') +decimeters_per_day = NamedUnit(1.1574074074074075e-05, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmNone⁻¹') +decimeters_per_square_day = NamedUnit(1.1574074074074075e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmNone⁻²') +decimeters_per_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmNone⁻¹') +decimeters_per_square_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmNone⁻²') +centimeters_per_second = NamedUnit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cmNone⁻¹') +centimeters_per_square_second = NamedUnit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cmNone⁻²') +centimeters_per_millisecond = NamedUnit(10.0, Dimensions(length=1, time=-1), name='centimeters_per_millisecond', ascii_symbol='cm/ms', symbol='cmms⁻¹') +centimeters_per_square_millisecond = NamedUnit(10.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') +centimeters_per_microsecond = NamedUnit(10000.0, Dimensions(length=1, time=-1), name='centimeters_per_microsecond', ascii_symbol='cm/us', symbol='cmµs⁻¹') +centimeters_per_square_microsecond = NamedUnit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_microsecond', ascii_symbol='cm/us^2', symbol='cmµs⁻²') +centimeters_per_nanosecond = NamedUnit(10000000.0, Dimensions(length=1, time=-1), name='centimeters_per_nanosecond', ascii_symbol='cm/ns', symbol='cmns⁻¹') +centimeters_per_square_nanosecond = NamedUnit(10000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_nanosecond', ascii_symbol='cm/ns^2', symbol='cmns⁻²') +centimeters_per_picosecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_picosecond', ascii_symbol='cm/ps', symbol='cmps⁻¹') +centimeters_per_square_picosecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_picosecond', ascii_symbol='cm/ps^2', symbol='cmps⁻²') +centimeters_per_femtosecond = NamedUnit(10000000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_femtosecond', ascii_symbol='cm/fs', symbol='cmfs⁻¹') +centimeters_per_square_femtosecond = NamedUnit(10000000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') +centimeters_per_attosecond = NamedUnit(1e+16, Dimensions(length=1, time=-1), name='centimeters_per_attosecond', ascii_symbol='cm/as', symbol='cmas⁻¹') +centimeters_per_square_attosecond = NamedUnit(1e+16, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') +centimeters_per_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmNone⁻¹') +centimeters_per_square_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmNone⁻²') +centimeters_per_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmNone⁻¹') +centimeters_per_square_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmNone⁻²') +centimeters_per_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmNone⁻¹') +centimeters_per_square_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmNone⁻²') +centimeters_per_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmNone⁻¹') +centimeters_per_square_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmNone⁻²') +angstroms_per_second = NamedUnit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') +angstroms_per_square_second = NamedUnit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') +angstroms_per_millisecond = NamedUnit(1e-07, Dimensions(length=1, time=-1), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') +angstroms_per_square_millisecond = NamedUnit(1e-07, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') +angstroms_per_microsecond = NamedUnit(0.0001, Dimensions(length=1, time=-1), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') +angstroms_per_square_microsecond = NamedUnit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') +angstroms_per_nanosecond = NamedUnit(0.09999999999999999, Dimensions(length=1, time=-1), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') +angstroms_per_square_nanosecond = NamedUnit(0.09999999999999999, Dimensions(length=1, time=-2), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') +angstroms_per_picosecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') +angstroms_per_square_picosecond = NamedUnit(100.0, Dimensions(length=1, time=-2), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') +angstroms_per_femtosecond = NamedUnit(100000.0, Dimensions(length=1, time=-1), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') +angstroms_per_square_femtosecond = NamedUnit(100000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') +angstroms_per_attosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') +angstroms_per_square_attosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') +angstroms_per_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') +angstroms_per_square_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') +angstroms_per_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') +angstroms_per_square_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') +angstroms_per_day = NamedUnit(1.1574074074074074e-14, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') +angstroms_per_square_day = NamedUnit(1.1574074074074074e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') +angstroms_per_year = NamedUnit(3.168873850681143e-17, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') +angstroms_per_square_year = NamedUnit(3.168873850681143e-17, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') +miles_per_second = NamedUnit(1609.344, Dimensions(length=1, time=-1), name='miles_per_second', ascii_symbol='miles/s', symbol='NoneNone⁻¹') +miles_per_square_second = NamedUnit(1609.344, Dimensions(length=1, time=-2), name='miles_per_square_second', ascii_symbol='miles/s^2', symbol='NoneNone⁻²') +miles_per_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-1), name='miles_per_millisecond', ascii_symbol='miles/ms', symbol='Nonems⁻¹') +miles_per_square_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-2), name='miles_per_square_millisecond', ascii_symbol='miles/ms^2', symbol='Nonems⁻²') +miles_per_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-1), name='miles_per_microsecond', ascii_symbol='miles/us', symbol='Noneµs⁻¹') +miles_per_square_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-2), name='miles_per_square_microsecond', ascii_symbol='miles/us^2', symbol='Noneµs⁻²') +miles_per_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-1), name='miles_per_nanosecond', ascii_symbol='miles/ns', symbol='Nonens⁻¹') +miles_per_square_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-2), name='miles_per_square_nanosecond', ascii_symbol='miles/ns^2', symbol='Nonens⁻²') +miles_per_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-1), name='miles_per_picosecond', ascii_symbol='miles/ps', symbol='Noneps⁻¹') +miles_per_square_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-2), name='miles_per_square_picosecond', ascii_symbol='miles/ps^2', symbol='Noneps⁻²') +miles_per_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-1), name='miles_per_femtosecond', ascii_symbol='miles/fs', symbol='Nonefs⁻¹') +miles_per_square_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-2), name='miles_per_square_femtosecond', ascii_symbol='miles/fs^2', symbol='Nonefs⁻²') +miles_per_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-1), name='miles_per_attosecond', ascii_symbol='miles/as', symbol='Noneas⁻¹') +miles_per_square_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-2), name='miles_per_square_attosecond', ascii_symbol='miles/as^2', symbol='Noneas⁻²') +miles_per_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-1), name='miles_per_minute', ascii_symbol='miles/min', symbol='NoneNone⁻¹') +miles_per_square_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-2), name='miles_per_square_minute', ascii_symbol='miles/min^2', symbol='NoneNone⁻²') +miles_per_hour = NamedUnit(4.4704, Dimensions(length=1, time=-1), name='miles_per_hour', ascii_symbol='miles/h', symbol='NoneNone⁻¹') +miles_per_square_hour = NamedUnit(4.4704, Dimensions(length=1, time=-2), name='miles_per_square_hour', ascii_symbol='miles/h^2', symbol='NoneNone⁻²') +miles_per_day = NamedUnit(0.18626666666666666, Dimensions(length=1, time=-1), name='miles_per_day', ascii_symbol='miles/d', symbol='NoneNone⁻¹') +miles_per_square_day = NamedUnit(0.18626666666666666, Dimensions(length=1, time=-2), name='miles_per_square_day', ascii_symbol='miles/d^2', symbol='NoneNone⁻²') +miles_per_year = NamedUnit(0.0005099808118350593, Dimensions(length=1, time=-1), name='miles_per_year', ascii_symbol='miles/y', symbol='NoneNone⁻¹') +miles_per_square_year = NamedUnit(0.0005099808118350593, Dimensions(length=1, time=-2), name='miles_per_square_year', ascii_symbol='miles/y^2', symbol='NoneNone⁻²') +yards_per_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-1), name='yards_per_second', ascii_symbol='yrd/s', symbol='NoneNone⁻¹') +yards_per_square_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-2), name='yards_per_square_second', ascii_symbol='yrd/s^2', symbol='NoneNone⁻²') +yards_per_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-1), name='yards_per_millisecond', ascii_symbol='yrd/ms', symbol='Nonems⁻¹') +yards_per_square_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-2), name='yards_per_square_millisecond', ascii_symbol='yrd/ms^2', symbol='Nonems⁻²') +yards_per_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-1), name='yards_per_microsecond', ascii_symbol='yrd/us', symbol='Noneµs⁻¹') +yards_per_square_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-2), name='yards_per_square_microsecond', ascii_symbol='yrd/us^2', symbol='Noneµs⁻²') +yards_per_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-1), name='yards_per_nanosecond', ascii_symbol='yrd/ns', symbol='Nonens⁻¹') +yards_per_square_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-2), name='yards_per_square_nanosecond', ascii_symbol='yrd/ns^2', symbol='Nonens⁻²') +yards_per_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-1), name='yards_per_picosecond', ascii_symbol='yrd/ps', symbol='Noneps⁻¹') +yards_per_square_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-2), name='yards_per_square_picosecond', ascii_symbol='yrd/ps^2', symbol='Noneps⁻²') +yards_per_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-1), name='yards_per_femtosecond', ascii_symbol='yrd/fs', symbol='Nonefs⁻¹') +yards_per_square_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-2), name='yards_per_square_femtosecond', ascii_symbol='yrd/fs^2', symbol='Nonefs⁻²') +yards_per_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-1), name='yards_per_attosecond', ascii_symbol='yrd/as', symbol='Noneas⁻¹') +yards_per_square_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-2), name='yards_per_square_attosecond', ascii_symbol='yrd/as^2', symbol='Noneas⁻²') +yards_per_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-1), name='yards_per_minute', ascii_symbol='yrd/min', symbol='NoneNone⁻¹') +yards_per_square_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-2), name='yards_per_square_minute', ascii_symbol='yrd/min^2', symbol='NoneNone⁻²') +yards_per_hour = NamedUnit(0.00254, Dimensions(length=1, time=-1), name='yards_per_hour', ascii_symbol='yrd/h', symbol='NoneNone⁻¹') +yards_per_square_hour = NamedUnit(0.00254, Dimensions(length=1, time=-2), name='yards_per_square_hour', ascii_symbol='yrd/h^2', symbol='NoneNone⁻²') +yards_per_day = NamedUnit(0.00010583333333333335, Dimensions(length=1, time=-1), name='yards_per_day', ascii_symbol='yrd/d', symbol='NoneNone⁻¹') +yards_per_square_day = NamedUnit(0.00010583333333333335, Dimensions(length=1, time=-2), name='yards_per_square_day', ascii_symbol='yrd/d^2', symbol='NoneNone⁻²') +yards_per_year = NamedUnit(2.897618249062837e-07, Dimensions(length=1, time=-1), name='yards_per_year', ascii_symbol='yrd/y', symbol='NoneNone⁻¹') +yards_per_square_year = NamedUnit(2.897618249062837e-07, Dimensions(length=1, time=-2), name='yards_per_square_year', ascii_symbol='yrd/y^2', symbol='NoneNone⁻²') +feet_per_second = NamedUnit(0.3048, Dimensions(length=1, time=-1), name='feet_per_second', ascii_symbol='ft/s', symbol='NoneNone⁻¹') +feet_per_square_second = NamedUnit(0.3048, Dimensions(length=1, time=-2), name='feet_per_square_second', ascii_symbol='ft/s^2', symbol='NoneNone⁻²') +feet_per_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-1), name='feet_per_millisecond', ascii_symbol='ft/ms', symbol='Nonems⁻¹') +feet_per_square_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-2), name='feet_per_square_millisecond', ascii_symbol='ft/ms^2', symbol='Nonems⁻²') +feet_per_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-1), name='feet_per_microsecond', ascii_symbol='ft/us', symbol='Noneµs⁻¹') +feet_per_square_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-2), name='feet_per_square_microsecond', ascii_symbol='ft/us^2', symbol='Noneµs⁻²') +feet_per_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-1), name='feet_per_nanosecond', ascii_symbol='ft/ns', symbol='Nonens⁻¹') +feet_per_square_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-2), name='feet_per_square_nanosecond', ascii_symbol='ft/ns^2', symbol='Nonens⁻²') +feet_per_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-1), name='feet_per_picosecond', ascii_symbol='ft/ps', symbol='Noneps⁻¹') +feet_per_square_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-2), name='feet_per_square_picosecond', ascii_symbol='ft/ps^2', symbol='Noneps⁻²') +feet_per_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-1), name='feet_per_femtosecond', ascii_symbol='ft/fs', symbol='Nonefs⁻¹') +feet_per_square_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-2), name='feet_per_square_femtosecond', ascii_symbol='ft/fs^2', symbol='Nonefs⁻²') +feet_per_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-1), name='feet_per_attosecond', ascii_symbol='ft/as', symbol='Noneas⁻¹') +feet_per_square_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-2), name='feet_per_square_attosecond', ascii_symbol='ft/as^2', symbol='Noneas⁻²') +feet_per_minute = NamedUnit(0.00508, Dimensions(length=1, time=-1), name='feet_per_minute', ascii_symbol='ft/min', symbol='NoneNone⁻¹') +feet_per_square_minute = NamedUnit(0.00508, Dimensions(length=1, time=-2), name='feet_per_square_minute', ascii_symbol='ft/min^2', symbol='NoneNone⁻²') +feet_per_hour = NamedUnit(0.0008466666666666667, Dimensions(length=1, time=-1), name='feet_per_hour', ascii_symbol='ft/h', symbol='NoneNone⁻¹') +feet_per_square_hour = NamedUnit(0.0008466666666666667, Dimensions(length=1, time=-2), name='feet_per_square_hour', ascii_symbol='ft/h^2', symbol='NoneNone⁻²') +feet_per_day = NamedUnit(3.527777777777778e-05, Dimensions(length=1, time=-1), name='feet_per_day', ascii_symbol='ft/d', symbol='NoneNone⁻¹') +feet_per_square_day = NamedUnit(3.527777777777778e-05, Dimensions(length=1, time=-2), name='feet_per_square_day', ascii_symbol='ft/d^2', symbol='NoneNone⁻²') +feet_per_year = NamedUnit(9.658727496876123e-08, Dimensions(length=1, time=-1), name='feet_per_year', ascii_symbol='ft/y', symbol='NoneNone⁻¹') +feet_per_square_year = NamedUnit(9.658727496876123e-08, Dimensions(length=1, time=-2), name='feet_per_square_year', ascii_symbol='ft/y^2', symbol='NoneNone⁻²') +inches_per_second = NamedUnit(0.0254, Dimensions(length=1, time=-1), name='inches_per_second', ascii_symbol='in/s', symbol='NoneNone⁻¹') +inches_per_square_second = NamedUnit(0.0254, Dimensions(length=1, time=-2), name='inches_per_square_second', ascii_symbol='in/s^2', symbol='NoneNone⁻²') +inches_per_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-1), name='inches_per_millisecond', ascii_symbol='in/ms', symbol='Nonems⁻¹') +inches_per_square_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-2), name='inches_per_square_millisecond', ascii_symbol='in/ms^2', symbol='Nonems⁻²') +inches_per_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-1), name='inches_per_microsecond', ascii_symbol='in/us', symbol='Noneµs⁻¹') +inches_per_square_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-2), name='inches_per_square_microsecond', ascii_symbol='in/us^2', symbol='Noneµs⁻²') +inches_per_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-1), name='inches_per_nanosecond', ascii_symbol='in/ns', symbol='Nonens⁻¹') +inches_per_square_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-2), name='inches_per_square_nanosecond', ascii_symbol='in/ns^2', symbol='Nonens⁻²') +inches_per_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-1), name='inches_per_picosecond', ascii_symbol='in/ps', symbol='Noneps⁻¹') +inches_per_square_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-2), name='inches_per_square_picosecond', ascii_symbol='in/ps^2', symbol='Noneps⁻²') +inches_per_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-1), name='inches_per_femtosecond', ascii_symbol='in/fs', symbol='Nonefs⁻¹') +inches_per_square_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-2), name='inches_per_square_femtosecond', ascii_symbol='in/fs^2', symbol='Nonefs⁻²') +inches_per_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-1), name='inches_per_attosecond', ascii_symbol='in/as', symbol='Noneas⁻¹') +inches_per_square_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-2), name='inches_per_square_attosecond', ascii_symbol='in/as^2', symbol='Noneas⁻²') +inches_per_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-1), name='inches_per_minute', ascii_symbol='in/min', symbol='NoneNone⁻¹') +inches_per_square_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-2), name='inches_per_square_minute', ascii_symbol='in/min^2', symbol='NoneNone⁻²') +inches_per_hour = NamedUnit(7.055555555555556e-05, Dimensions(length=1, time=-1), name='inches_per_hour', ascii_symbol='in/h', symbol='NoneNone⁻¹') +inches_per_square_hour = NamedUnit(7.055555555555556e-05, Dimensions(length=1, time=-2), name='inches_per_square_hour', ascii_symbol='in/h^2', symbol='NoneNone⁻²') +inches_per_day = NamedUnit(2.9398148148148147e-06, Dimensions(length=1, time=-1), name='inches_per_day', ascii_symbol='in/d', symbol='NoneNone⁻¹') +inches_per_square_day = NamedUnit(2.9398148148148147e-06, Dimensions(length=1, time=-2), name='inches_per_square_day', ascii_symbol='in/d^2', symbol='NoneNone⁻²') +inches_per_year = NamedUnit(8.048939580730103e-09, Dimensions(length=1, time=-1), name='inches_per_year', ascii_symbol='in/y', symbol='NoneNone⁻¹') +inches_per_square_year = NamedUnit(8.048939580730103e-09, Dimensions(length=1, time=-2), name='inches_per_square_year', ascii_symbol='in/y^2', symbol='NoneNone⁻²') +grams_per_cubic_meter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_meter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') +petagrams_per_cubic_meter = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') +teragrams_per_cubic_meter = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_meter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') +megagrams_per_cubic_meter = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') +kilograms_per_cubic_meter = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgNone⁻³') +milligrams_per_cubic_meter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgNone⁻³') +micrograms_per_cubic_meter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgNone⁻³') +nanograms_per_cubic_meter = NamedUnit(1.0000000000000002e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngNone⁻³') +picograms_per_cubic_meter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') +femtograms_per_cubic_meter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') +attograms_per_cubic_meter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') +atomic_mass_units_per_cubic_meter = NamedUnit(1.660538921e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='NoneNone⁻³') +pounds_per_cubic_meter = NamedUnit(0.45359237, Dimensions(length=-3, mass=1), name='pounds_per_cubic_meter', ascii_symbol='lb m^-3', symbol='NoneNone⁻³') +ounces_per_cubic_meter = NamedUnit(0.028349523125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_meter', ascii_symbol='oz m^-3', symbol='NoneNone⁻³') +grams_per_cubic_exameter = NamedUnit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') +exagrams_per_cubic_exameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') +petagrams_per_cubic_exameter = NamedUnit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') +teragrams_per_cubic_exameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') +gigagrams_per_cubic_exameter = NamedUnit(1e-48, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_exameter', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') +megagrams_per_cubic_exameter = NamedUnit(9.999999999999999e-52, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_exameter', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') +kilograms_per_cubic_exameter = NamedUnit(9.999999999999999e-55, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_exameter', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') +milligrams_per_cubic_exameter = NamedUnit(9.999999999999998e-61, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_exameter', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') +micrograms_per_cubic_exameter = NamedUnit(9.999999999999999e-64, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_exameter', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') +nanograms_per_cubic_exameter = NamedUnit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_exameter', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') +picograms_per_cubic_exameter = NamedUnit(1e-69, Dimensions(length=-3, mass=1), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') +femtograms_per_cubic_exameter = NamedUnit(1e-72, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') +attograms_per_cubic_exameter = NamedUnit(1e-75, Dimensions(length=-3, mass=1), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') +atomic_mass_units_per_cubic_exameter = NamedUnit(1.6605389209999996e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='NoneEm⁻³') +pounds_per_cubic_exameter = NamedUnit(4.5359237e-55, Dimensions(length=-3, mass=1), name='pounds_per_cubic_exameter', ascii_symbol='lb Em^-3', symbol='NoneEm⁻³') +ounces_per_cubic_exameter = NamedUnit(2.8349523125e-56, Dimensions(length=-3, mass=1), name='ounces_per_cubic_exameter', ascii_symbol='oz Em^-3', symbol='NoneEm⁻³') +grams_per_cubic_petameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') +exagrams_per_cubic_petameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') +petagrams_per_cubic_petameter = NamedUnit(1e-33, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') +teragrams_per_cubic_petameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') +gigagrams_per_cubic_petameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_petameter', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') +megagrams_per_cubic_petameter = NamedUnit(1e-42, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_petameter', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') +kilograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-45, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_petameter', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') +milligrams_per_cubic_petameter = NamedUnit(1e-51, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_petameter', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') +micrograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-54, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_petameter', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') +nanograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-57, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_petameter', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') +picograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-60, Dimensions(length=-3, mass=1), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') +femtograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-63, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') +attograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') +atomic_mass_units_per_cubic_petameter = NamedUnit(1.660538921e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='NonePm⁻³') +pounds_per_cubic_petameter = NamedUnit(4.5359237000000005e-46, Dimensions(length=-3, mass=1), name='pounds_per_cubic_petameter', ascii_symbol='lb Pm^-3', symbol='NonePm⁻³') +ounces_per_cubic_petameter = NamedUnit(2.8349523125000003e-47, Dimensions(length=-3, mass=1), name='ounces_per_cubic_petameter', ascii_symbol='oz Pm^-3', symbol='NonePm⁻³') +grams_per_cubic_terameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') +exagrams_per_cubic_terameter = NamedUnit(1e-21, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') +petagrams_per_cubic_terameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') +teragrams_per_cubic_terameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') +gigagrams_per_cubic_terameter = NamedUnit(9.999999999999999e-31, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_terameter', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') +megagrams_per_cubic_terameter = NamedUnit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_terameter', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') +kilograms_per_cubic_terameter = NamedUnit(1e-36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_terameter', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') +milligrams_per_cubic_terameter = NamedUnit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_terameter', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') +micrograms_per_cubic_terameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_terameter', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') +nanograms_per_cubic_terameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_terameter', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') +picograms_per_cubic_terameter = NamedUnit(1e-51, Dimensions(length=-3, mass=1), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') +femtograms_per_cubic_terameter = NamedUnit(1e-54, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') +attograms_per_cubic_terameter = NamedUnit(1.0000000000000001e-57, Dimensions(length=-3, mass=1), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') +atomic_mass_units_per_cubic_terameter = NamedUnit(1.6605389209999997e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='NoneTm⁻³') +pounds_per_cubic_terameter = NamedUnit(4.5359237e-37, Dimensions(length=-3, mass=1), name='pounds_per_cubic_terameter', ascii_symbol='lb Tm^-3', symbol='NoneTm⁻³') +ounces_per_cubic_terameter = NamedUnit(2.8349523125e-38, Dimensions(length=-3, mass=1), name='ounces_per_cubic_terameter', ascii_symbol='oz Tm^-3', symbol='NoneTm⁻³') +grams_per_cubic_gigameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') +exagrams_per_cubic_gigameter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') +petagrams_per_cubic_gigameter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') +teragrams_per_cubic_gigameter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') +gigagrams_per_cubic_gigameter = NamedUnit(1e-21, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_gigameter', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') +megagrams_per_cubic_gigameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_gigameter', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') +kilograms_per_cubic_gigameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_gigameter', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') +milligrams_per_cubic_gigameter = NamedUnit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_gigameter', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') +micrograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_gigameter', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') +nanograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_gigameter', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') +picograms_per_cubic_gigameter = NamedUnit(1e-42, Dimensions(length=-3, mass=1), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') +femtograms_per_cubic_gigameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') +attograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') +atomic_mass_units_per_cubic_gigameter = NamedUnit(1.660538921e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='NoneGm⁻³') +pounds_per_cubic_gigameter = NamedUnit(4.5359237e-28, Dimensions(length=-3, mass=1), name='pounds_per_cubic_gigameter', ascii_symbol='lb Gm^-3', symbol='NoneGm⁻³') +ounces_per_cubic_gigameter = NamedUnit(2.8349523125e-29, Dimensions(length=-3, mass=1), name='ounces_per_cubic_gigameter', ascii_symbol='oz Gm^-3', symbol='NoneGm⁻³') +grams_per_cubic_megameter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') +exagrams_per_cubic_megameter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') +petagrams_per_cubic_megameter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') +teragrams_per_cubic_megameter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') +gigagrams_per_cubic_megameter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_megameter', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') +megagrams_per_cubic_megameter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_megameter', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') +kilograms_per_cubic_megameter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_megameter', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') +milligrams_per_cubic_megameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_megameter', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') +micrograms_per_cubic_megameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_megameter', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') +nanograms_per_cubic_megameter = NamedUnit(1.0000000000000003e-30, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_megameter', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') +picograms_per_cubic_megameter = NamedUnit(1e-33, Dimensions(length=-3, mass=1), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') +femtograms_per_cubic_megameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') +attograms_per_cubic_megameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') +atomic_mass_units_per_cubic_megameter = NamedUnit(1.6605389209999997e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='NoneMm⁻³') +pounds_per_cubic_megameter = NamedUnit(4.535923700000001e-19, Dimensions(length=-3, mass=1), name='pounds_per_cubic_megameter', ascii_symbol='lb Mm^-3', symbol='NoneMm⁻³') +ounces_per_cubic_megameter = NamedUnit(2.8349523125000004e-20, Dimensions(length=-3, mass=1), name='ounces_per_cubic_megameter', ascii_symbol='oz Mm^-3', symbol='NoneMm⁻³') +grams_per_cubic_kilometer = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') +exagrams_per_cubic_kilometer = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') +petagrams_per_cubic_kilometer = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') +teragrams_per_cubic_kilometer = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') +gigagrams_per_cubic_kilometer = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_kilometer', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') +megagrams_per_cubic_kilometer = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_kilometer', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') +kilograms_per_cubic_kilometer = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_kilometer', ascii_symbol='kg km^-3', symbol='kgkm⁻³') +milligrams_per_cubic_kilometer = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_kilometer', ascii_symbol='mg km^-3', symbol='mgkm⁻³') +micrograms_per_cubic_kilometer = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_kilometer', ascii_symbol='ug km^-3', symbol='µgkm⁻³') +nanograms_per_cubic_kilometer = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_kilometer', ascii_symbol='ng km^-3', symbol='ngkm⁻³') +picograms_per_cubic_kilometer = NamedUnit(1.0000000000000001e-24, Dimensions(length=-3, mass=1), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') +femtograms_per_cubic_kilometer = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') +attograms_per_cubic_kilometer = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') +atomic_mass_units_per_cubic_kilometer = NamedUnit(1.6605389209999997e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='Nonekm⁻³') +pounds_per_cubic_kilometer = NamedUnit(4.5359237000000004e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_kilometer', ascii_symbol='lb km^-3', symbol='Nonekm⁻³') +ounces_per_cubic_kilometer = NamedUnit(2.8349523125000003e-11, Dimensions(length=-3, mass=1), name='ounces_per_cubic_kilometer', ascii_symbol='oz km^-3', symbol='Nonekm⁻³') +grams_per_cubic_millimeter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') +exagrams_per_cubic_millimeter = NamedUnit(1e+24, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') +petagrams_per_cubic_millimeter = NamedUnit(1e+21, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') +teragrams_per_cubic_millimeter = NamedUnit(1e+18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') +gigagrams_per_cubic_millimeter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_millimeter', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') +megagrams_per_cubic_millimeter = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_millimeter', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') +kilograms_per_cubic_millimeter = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_millimeter', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') +milligrams_per_cubic_millimeter = NamedUnit(999.9999999999999, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_millimeter', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') +micrograms_per_cubic_millimeter = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_millimeter', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') +nanograms_per_cubic_millimeter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_millimeter', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') +picograms_per_cubic_millimeter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') +femtograms_per_cubic_millimeter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') +attograms_per_cubic_millimeter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') +atomic_mass_units_per_cubic_millimeter = NamedUnit(1.6605389209999997e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='Nonemm⁻³') +pounds_per_cubic_millimeter = NamedUnit(453592370.0, Dimensions(length=-3, mass=1), name='pounds_per_cubic_millimeter', ascii_symbol='lb mm^-3', symbol='Nonemm⁻³') +ounces_per_cubic_millimeter = NamedUnit(28349523.125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_millimeter', ascii_symbol='oz mm^-3', symbol='Nonemm⁻³') +grams_per_cubic_micrometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') +exagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') +petagrams_per_cubic_micrometer = NamedUnit(1.0000000000000002e+30, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') +teragrams_per_cubic_micrometer = NamedUnit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') +gigagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+24, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_micrometer', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') +megagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_micrometer', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') +kilograms_per_cubic_micrometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_micrometer', ascii_symbol='kg um^-3', symbol='kgµm⁻³') +milligrams_per_cubic_micrometer = NamedUnit(1000000000000.0001, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_micrometer', ascii_symbol='mg um^-3', symbol='mgµm⁻³') +micrograms_per_cubic_micrometer = NamedUnit(1000000000.0000002, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_micrometer', ascii_symbol='ug um^-3', symbol='µgµm⁻³') +nanograms_per_cubic_micrometer = NamedUnit(1000000.0000000003, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_micrometer', ascii_symbol='ng um^-3', symbol='ngµm⁻³') +picograms_per_cubic_micrometer = NamedUnit(1000.0000000000002, Dimensions(length=-3, mass=1), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') +femtograms_per_cubic_micrometer = NamedUnit(1.0000000000000002, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') +attograms_per_cubic_micrometer = NamedUnit(0.0010000000000000002, Dimensions(length=-3, mass=1), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') +atomic_mass_units_per_cubic_micrometer = NamedUnit(1.660538921e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='Noneµm⁻³') +pounds_per_cubic_micrometer = NamedUnit(4.5359237000000006e+17, Dimensions(length=-3, mass=1), name='pounds_per_cubic_micrometer', ascii_symbol='lb um^-3', symbol='Noneµm⁻³') +ounces_per_cubic_micrometer = NamedUnit(2.8349523125000004e+16, Dimensions(length=-3, mass=1), name='ounces_per_cubic_micrometer', ascii_symbol='oz um^-3', symbol='Noneµm⁻³') +grams_per_cubic_nanometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') +exagrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') +petagrams_per_cubic_nanometer = NamedUnit(9.999999999999998e+38, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') +teragrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') +gigagrams_per_cubic_nanometer = NamedUnit(9.999999999999998e+32, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_nanometer', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') +megagrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_nanometer', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') +kilograms_per_cubic_nanometer = NamedUnit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_nanometer', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') +milligrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+20, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_nanometer', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') +micrograms_per_cubic_nanometer = NamedUnit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_nanometer', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') +nanograms_per_cubic_nanometer = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_nanometer', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') +picograms_per_cubic_nanometer = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') +femtograms_per_cubic_nanometer = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') +attograms_per_cubic_nanometer = NamedUnit(999999.9999999999, Dimensions(length=-3, mass=1), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') +atomic_mass_units_per_cubic_nanometer = NamedUnit(1.6605389209999994, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='Nonenm⁻³') +pounds_per_cubic_nanometer = NamedUnit(4.535923699999999e+26, Dimensions(length=-3, mass=1), name='pounds_per_cubic_nanometer', ascii_symbol='lb nm^-3', symbol='Nonenm⁻³') +ounces_per_cubic_nanometer = NamedUnit(2.8349523124999993e+25, Dimensions(length=-3, mass=1), name='ounces_per_cubic_nanometer', ascii_symbol='oz nm^-3', symbol='Nonenm⁻³') +grams_per_cubic_picometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') +exagrams_per_cubic_picometer = NamedUnit(1e+51, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') +petagrams_per_cubic_picometer = NamedUnit(1e+48, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') +teragrams_per_cubic_picometer = NamedUnit(1.0000000000000001e+45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') +gigagrams_per_cubic_picometer = NamedUnit(1e+42, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_picometer', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') +megagrams_per_cubic_picometer = NamedUnit(1.0000000000000001e+39, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_picometer', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') +kilograms_per_cubic_picometer = NamedUnit(1e+36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_picometer', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') +milligrams_per_cubic_picometer = NamedUnit(1e+30, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_picometer', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') +micrograms_per_cubic_picometer = NamedUnit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_picometer', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') +nanograms_per_cubic_picometer = NamedUnit(1.0000000000000003e+24, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_picometer', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') +picograms_per_cubic_picometer = NamedUnit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') +femtograms_per_cubic_picometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') +attograms_per_cubic_picometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') +atomic_mass_units_per_cubic_picometer = NamedUnit(1660538921.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='Nonepm⁻³') +pounds_per_cubic_picometer = NamedUnit(4.5359237000000005e+35, Dimensions(length=-3, mass=1), name='pounds_per_cubic_picometer', ascii_symbol='lb pm^-3', symbol='Nonepm⁻³') +ounces_per_cubic_picometer = NamedUnit(2.8349523125000003e+34, Dimensions(length=-3, mass=1), name='ounces_per_cubic_picometer', ascii_symbol='oz pm^-3', symbol='Nonepm⁻³') +grams_per_cubic_femtometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') +exagrams_per_cubic_femtometer = NamedUnit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') +petagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+56, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') +teragrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+53, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') +gigagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+50, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_femtometer', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') +megagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_femtometer', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') +kilograms_per_cubic_femtometer = NamedUnit(9.999999999999998e+44, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_femtometer', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') +milligrams_per_cubic_femtometer = NamedUnit(9.999999999999996e+38, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_femtometer', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') +micrograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_femtometer', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') +nanograms_per_cubic_femtometer = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_femtometer', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') +picograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') +femtograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') +attograms_per_cubic_femtometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') +atomic_mass_units_per_cubic_femtometer = NamedUnit(1.6605389209999992e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='Nonefm⁻³') +pounds_per_cubic_femtometer = NamedUnit(4.5359236999999985e+44, Dimensions(length=-3, mass=1), name='pounds_per_cubic_femtometer', ascii_symbol='lb fm^-3', symbol='Nonefm⁻³') +ounces_per_cubic_femtometer = NamedUnit(2.834952312499999e+43, Dimensions(length=-3, mass=1), name='ounces_per_cubic_femtometer', ascii_symbol='oz fm^-3', symbol='Nonefm⁻³') +grams_per_cubic_attometer = NamedUnit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') +exagrams_per_cubic_attometer = NamedUnit(9.999999999999999e+68, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') +petagrams_per_cubic_attometer = NamedUnit(9.999999999999998e+65, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') +teragrams_per_cubic_attometer = NamedUnit(9.999999999999999e+62, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') +gigagrams_per_cubic_attometer = NamedUnit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_attometer', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') +megagrams_per_cubic_attometer = NamedUnit(9.999999999999999e+56, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_attometer', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') +kilograms_per_cubic_attometer = NamedUnit(9.999999999999999e+53, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_attometer', ascii_symbol='kg am^-3', symbol='kgam⁻³') +milligrams_per_cubic_attometer = NamedUnit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_attometer', ascii_symbol='mg am^-3', symbol='mgam⁻³') +micrograms_per_cubic_attometer = NamedUnit(1e+45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_attometer', ascii_symbol='ug am^-3', symbol='µgam⁻³') +nanograms_per_cubic_attometer = NamedUnit(1e+42, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_attometer', ascii_symbol='ng am^-3', symbol='ngam⁻³') +picograms_per_cubic_attometer = NamedUnit(1e+39, Dimensions(length=-3, mass=1), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') +femtograms_per_cubic_attometer = NamedUnit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') +attograms_per_cubic_attometer = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') +atomic_mass_units_per_cubic_attometer = NamedUnit(1.6605389209999997e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='Noneam⁻³') +pounds_per_cubic_attometer = NamedUnit(4.5359237e+53, Dimensions(length=-3, mass=1), name='pounds_per_cubic_attometer', ascii_symbol='lb am^-3', symbol='Noneam⁻³') +ounces_per_cubic_attometer = NamedUnit(2.8349523125e+52, Dimensions(length=-3, mass=1), name='ounces_per_cubic_attometer', ascii_symbol='oz am^-3', symbol='Noneam⁻³') +grams_per_cubic_decimeter = NamedUnit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='Nonedm⁻³') +exagrams_per_cubic_decimeter = NamedUnit(9.999999999999997e+17, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_decimeter', ascii_symbol='Eg dm^-3', symbol='Egdm⁻³') +petagrams_per_cubic_decimeter = NamedUnit(999999999999999.8, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_decimeter', ascii_symbol='Pg dm^-3', symbol='Pgdm⁻³') +teragrams_per_cubic_decimeter = NamedUnit(999999999999.9998, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_decimeter', ascii_symbol='Tg dm^-3', symbol='Tgdm⁻³') +gigagrams_per_cubic_decimeter = NamedUnit(999999999.9999998, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_decimeter', ascii_symbol='Gg dm^-3', symbol='Ggdm⁻³') +megagrams_per_cubic_decimeter = NamedUnit(999999.9999999998, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_decimeter', ascii_symbol='Mg dm^-3', symbol='Mgdm⁻³') +kilograms_per_cubic_decimeter = NamedUnit(999.9999999999998, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_decimeter', ascii_symbol='kg dm^-3', symbol='kgdm⁻³') +milligrams_per_cubic_decimeter = NamedUnit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_decimeter', ascii_symbol='mg dm^-3', symbol='mgdm⁻³') +micrograms_per_cubic_decimeter = NamedUnit(9.999999999999997e-07, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_decimeter', ascii_symbol='ug dm^-3', symbol='µgdm⁻³') +nanograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_decimeter', ascii_symbol='ng dm^-3', symbol='ngdm⁻³') +picograms_per_cubic_decimeter = NamedUnit(9.999999999999998e-13, Dimensions(length=-3, mass=1), name='picograms_per_cubic_decimeter', ascii_symbol='pg dm^-3', symbol='pgdm⁻³') +femtograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_decimeter', ascii_symbol='fg dm^-3', symbol='fgdm⁻³') +attograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-19, Dimensions(length=-3, mass=1), name='attograms_per_cubic_decimeter', ascii_symbol='ag dm^-3', symbol='agdm⁻³') +atomic_mass_units_per_cubic_decimeter = NamedUnit(1.6605389209999993e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='Nonedm⁻³') +pounds_per_cubic_decimeter = NamedUnit(453.5923699999999, Dimensions(length=-3, mass=1), name='pounds_per_cubic_decimeter', ascii_symbol='lb dm^-3', symbol='Nonedm⁻³') +ounces_per_cubic_decimeter = NamedUnit(28.349523124999994, Dimensions(length=-3, mass=1), name='ounces_per_cubic_decimeter', ascii_symbol='oz dm^-3', symbol='Nonedm⁻³') +grams_per_cubic_centimeter = NamedUnit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='Nonecm⁻³') +exagrams_per_cubic_centimeter = NamedUnit(9.999999999999999e+20, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_centimeter', ascii_symbol='Eg cm^-3', symbol='Egcm⁻³') +petagrams_per_cubic_centimeter = NamedUnit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_centimeter', ascii_symbol='Pg cm^-3', symbol='Pgcm⁻³') +teragrams_per_cubic_centimeter = NamedUnit(999999999999999.9, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_centimeter', ascii_symbol='Tg cm^-3', symbol='Tgcm⁻³') +gigagrams_per_cubic_centimeter = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_centimeter', ascii_symbol='Gg cm^-3', symbol='Ggcm⁻³') +megagrams_per_cubic_centimeter = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_centimeter', ascii_symbol='Mg cm^-3', symbol='Mgcm⁻³') +kilograms_per_cubic_centimeter = NamedUnit(999999.9999999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_centimeter', ascii_symbol='kg cm^-3', symbol='kgcm⁻³') +milligrams_per_cubic_centimeter = NamedUnit(0.9999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_centimeter', ascii_symbol='mg cm^-3', symbol='mgcm⁻³') +micrograms_per_cubic_centimeter = NamedUnit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_centimeter', ascii_symbol='ug cm^-3', symbol='µgcm⁻³') +nanograms_per_cubic_centimeter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_centimeter', ascii_symbol='ng cm^-3', symbol='ngcm⁻³') +picograms_per_cubic_centimeter = NamedUnit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='picograms_per_cubic_centimeter', ascii_symbol='pg cm^-3', symbol='pgcm⁻³') +femtograms_per_cubic_centimeter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_centimeter', ascii_symbol='fg cm^-3', symbol='fgcm⁻³') +attograms_per_cubic_centimeter = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='attograms_per_cubic_centimeter', ascii_symbol='ag cm^-3', symbol='agcm⁻³') +atomic_mass_units_per_cubic_centimeter = NamedUnit(1.6605389209999996e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='Nonecm⁻³') +pounds_per_cubic_centimeter = NamedUnit(453592.36999999994, Dimensions(length=-3, mass=1), name='pounds_per_cubic_centimeter', ascii_symbol='lb cm^-3', symbol='Nonecm⁻³') +ounces_per_cubic_centimeter = NamedUnit(28349.523124999996, Dimensions(length=-3, mass=1), name='ounces_per_cubic_centimeter', ascii_symbol='oz cm^-3', symbol='Nonecm⁻³') +grams_per_cubic_angstrom = NamedUnit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') +exagrams_per_cubic_angstrom = NamedUnit(1e+45, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') +petagrams_per_cubic_angstrom = NamedUnit(9.999999999999999e+41, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') +teragrams_per_cubic_angstrom = NamedUnit(1e+39, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') +gigagrams_per_cubic_angstrom = NamedUnit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_angstrom', ascii_symbol='Gg Ang^-3', symbol='GgÅ⁻³') +megagrams_per_cubic_angstrom = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_angstrom', ascii_symbol='Mg Ang^-3', symbol='MgÅ⁻³') +kilograms_per_cubic_angstrom = NamedUnit(9.999999999999999e+29, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_angstrom', ascii_symbol='kg Ang^-3', symbol='kgÅ⁻³') +milligrams_per_cubic_angstrom = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_angstrom', ascii_symbol='mg Ang^-3', symbol='mgÅ⁻³') +micrograms_per_cubic_angstrom = NamedUnit(1e+21, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_angstrom', ascii_symbol='ug Ang^-3', symbol='µgÅ⁻³') +nanograms_per_cubic_angstrom = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_angstrom', ascii_symbol='ng Ang^-3', symbol='ngÅ⁻³') +picograms_per_cubic_angstrom = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') +femtograms_per_cubic_angstrom = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') +attograms_per_cubic_angstrom = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') +atomic_mass_units_per_cubic_angstrom = NamedUnit(1660.5389209999996, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='NoneÅ⁻³') +pounds_per_cubic_angstrom = NamedUnit(4.5359237e+29, Dimensions(length=-3, mass=1), name='pounds_per_cubic_angstrom', ascii_symbol='lb Ang^-3', symbol='NoneÅ⁻³') +ounces_per_cubic_angstrom = NamedUnit(2.8349523125e+28, Dimensions(length=-3, mass=1), name='ounces_per_cubic_angstrom', ascii_symbol='oz Ang^-3', symbol='NoneÅ⁻³') +grams_per_cubic_mile = NamedUnit(2.399127585789277e-13, Dimensions(length=-3, mass=1), name='grams_per_cubic_mile', ascii_symbol='g miles^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_mile = NamedUnit(239912.7585789277, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_mile', ascii_symbol='Eg miles^-3', symbol='EgNone⁻³') +petagrams_per_cubic_mile = NamedUnit(239.9127585789277, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_mile', ascii_symbol='Pg miles^-3', symbol='PgNone⁻³') +teragrams_per_cubic_mile = NamedUnit(0.2399127585789277, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_mile', ascii_symbol='Tg miles^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_mile = NamedUnit(0.0002399127585789277, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_mile', ascii_symbol='Gg miles^-3', symbol='GgNone⁻³') +megagrams_per_cubic_mile = NamedUnit(2.399127585789277e-07, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_mile', ascii_symbol='Mg miles^-3', symbol='MgNone⁻³') +kilograms_per_cubic_mile = NamedUnit(2.399127585789277e-10, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_mile', ascii_symbol='kg miles^-3', symbol='kgNone⁻³') +milligrams_per_cubic_mile = NamedUnit(2.399127585789277e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_mile', ascii_symbol='mg miles^-3', symbol='mgNone⁻³') +micrograms_per_cubic_mile = NamedUnit(2.3991275857892774e-19, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_mile', ascii_symbol='ug miles^-3', symbol='µgNone⁻³') +nanograms_per_cubic_mile = NamedUnit(2.3991275857892774e-22, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_mile', ascii_symbol='ng miles^-3', symbol='ngNone⁻³') +picograms_per_cubic_mile = NamedUnit(2.399127585789277e-25, Dimensions(length=-3, mass=1), name='picograms_per_cubic_mile', ascii_symbol='pg miles^-3', symbol='pgNone⁻³') +femtograms_per_cubic_mile = NamedUnit(2.3991275857892772e-28, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_mile', ascii_symbol='fg miles^-3', symbol='fgNone⁻³') +attograms_per_cubic_mile = NamedUnit(2.399127585789277e-31, Dimensions(length=-3, mass=1), name='attograms_per_cubic_mile', ascii_symbol='ag miles^-3', symbol='agNone⁻³') +atomic_mass_units_per_cubic_mile = NamedUnit(3.98384473264786e-37, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_mile', ascii_symbol='au miles^-3', symbol='NoneNone⁻³') +pounds_per_cubic_mile = NamedUnit(1.0882259675705365e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_mile', ascii_symbol='lb miles^-3', symbol='NoneNone⁻³') +ounces_per_cubic_mile = NamedUnit(6.801412297315853e-12, Dimensions(length=-3, mass=1), name='ounces_per_cubic_mile', ascii_symbol='oz miles^-3', symbol='NoneNone⁻³') +grams_per_cubic_yard = NamedUnit(0.0013079506193143919, Dimensions(length=-3, mass=1), name='grams_per_cubic_yard', ascii_symbol='g yrd^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_yard = NamedUnit(1307950619314391.8, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_yard', ascii_symbol='Eg yrd^-3', symbol='EgNone⁻³') +petagrams_per_cubic_yard = NamedUnit(1307950619314.3918, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_yard', ascii_symbol='Pg yrd^-3', symbol='PgNone⁻³') +teragrams_per_cubic_yard = NamedUnit(1307950619.3143919, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_yard', ascii_symbol='Tg yrd^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_yard = NamedUnit(1307950.6193143919, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_yard', ascii_symbol='Gg yrd^-3', symbol='GgNone⁻³') +megagrams_per_cubic_yard = NamedUnit(1307.9506193143918, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_yard', ascii_symbol='Mg yrd^-3', symbol='MgNone⁻³') +kilograms_per_cubic_yard = NamedUnit(1.3079506193143917, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_yard', ascii_symbol='kg yrd^-3', symbol='kgNone⁻³') +milligrams_per_cubic_yard = NamedUnit(1.3079506193143917e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_yard', ascii_symbol='mg yrd^-3', symbol='mgNone⁻³') +micrograms_per_cubic_yard = NamedUnit(1.3079506193143919e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_yard', ascii_symbol='ug yrd^-3', symbol='µgNone⁻³') +nanograms_per_cubic_yard = NamedUnit(1.307950619314392e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_yard', ascii_symbol='ng yrd^-3', symbol='ngNone⁻³') +picograms_per_cubic_yard = NamedUnit(1.3079506193143919e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_yard', ascii_symbol='pg yrd^-3', symbol='pgNone⁻³') +femtograms_per_cubic_yard = NamedUnit(1.3079506193143918e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_yard', ascii_symbol='fg yrd^-3', symbol='fgNone⁻³') +attograms_per_cubic_yard = NamedUnit(1.307950619314392e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_yard', ascii_symbol='ag yrd^-3', symbol='agNone⁻³') +atomic_mass_units_per_cubic_yard = NamedUnit(2.1719029101176016e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_yard', ascii_symbol='au yrd^-3', symbol='NoneNone⁻³') +pounds_per_cubic_yard = NamedUnit(0.5932764212577828, Dimensions(length=-3, mass=1), name='pounds_per_cubic_yard', ascii_symbol='lb yrd^-3', symbol='NoneNone⁻³') +ounces_per_cubic_yard = NamedUnit(0.037079776328611425, Dimensions(length=-3, mass=1), name='ounces_per_cubic_yard', ascii_symbol='oz yrd^-3', symbol='NoneNone⁻³') +grams_per_cubic_foot = NamedUnit(0.035314666721488586, Dimensions(length=-3, mass=1), name='grams_per_cubic_foot', ascii_symbol='g ft^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_foot = NamedUnit(3.5314666721488584e+16, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_foot', ascii_symbol='Eg ft^-3', symbol='EgNone⁻³') +petagrams_per_cubic_foot = NamedUnit(35314666721488.586, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_foot', ascii_symbol='Pg ft^-3', symbol='PgNone⁻³') +teragrams_per_cubic_foot = NamedUnit(35314666721.48859, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_foot', ascii_symbol='Tg ft^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_foot = NamedUnit(35314666.72148859, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_foot', ascii_symbol='Gg ft^-3', symbol='GgNone⁻³') +megagrams_per_cubic_foot = NamedUnit(35314.66672148858, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_foot', ascii_symbol='Mg ft^-3', symbol='MgNone⁻³') +kilograms_per_cubic_foot = NamedUnit(35.314666721488585, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_foot', ascii_symbol='kg ft^-3', symbol='kgNone⁻³') +milligrams_per_cubic_foot = NamedUnit(3.5314666721488586e-05, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_foot', ascii_symbol='mg ft^-3', symbol='mgNone⁻³') +micrograms_per_cubic_foot = NamedUnit(3.5314666721488584e-08, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_foot', ascii_symbol='ug ft^-3', symbol='µgNone⁻³') +nanograms_per_cubic_foot = NamedUnit(3.531466672148859e-11, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_foot', ascii_symbol='ng ft^-3', symbol='ngNone⁻³') +picograms_per_cubic_foot = NamedUnit(3.531466672148859e-14, Dimensions(length=-3, mass=1), name='picograms_per_cubic_foot', ascii_symbol='pg ft^-3', symbol='pgNone⁻³') +femtograms_per_cubic_foot = NamedUnit(3.5314666721488585e-17, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_foot', ascii_symbol='fg ft^-3', symbol='fgNone⁻³') +attograms_per_cubic_foot = NamedUnit(3.531466672148859e-20, Dimensions(length=-3, mass=1), name='attograms_per_cubic_foot', ascii_symbol='ag ft^-3', symbol='agNone⁻³') +atomic_mass_units_per_cubic_foot = NamedUnit(5.864137857317526e-26, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_foot', ascii_symbol='au ft^-3', symbol='NoneNone⁻³') +pounds_per_cubic_foot = NamedUnit(16.018463373960138, Dimensions(length=-3, mass=1), name='pounds_per_cubic_foot', ascii_symbol='lb ft^-3', symbol='NoneNone⁻³') +ounces_per_cubic_foot = NamedUnit(1.0011539608725086, Dimensions(length=-3, mass=1), name='ounces_per_cubic_foot', ascii_symbol='oz ft^-3', symbol='NoneNone⁻³') +grams_per_cubic_inch = NamedUnit(61.02374409473229, Dimensions(length=-3, mass=1), name='grams_per_cubic_inch', ascii_symbol='g in^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_inch = NamedUnit(6.102374409473229e+19, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_inch', ascii_symbol='Eg in^-3', symbol='EgNone⁻³') +petagrams_per_cubic_inch = NamedUnit(6.102374409473229e+16, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_inch', ascii_symbol='Pg in^-3', symbol='PgNone⁻³') +teragrams_per_cubic_inch = NamedUnit(61023744094732.29, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_inch', ascii_symbol='Tg in^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_inch = NamedUnit(61023744094.732285, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_inch', ascii_symbol='Gg in^-3', symbol='GgNone⁻³') +megagrams_per_cubic_inch = NamedUnit(61023744.094732285, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_inch', ascii_symbol='Mg in^-3', symbol='MgNone⁻³') +kilograms_per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_inch', ascii_symbol='kg in^-3', symbol='kgNone⁻³') +milligrams_per_cubic_inch = NamedUnit(0.06102374409473228, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_inch', ascii_symbol='mg in^-3', symbol='mgNone⁻³') +micrograms_per_cubic_inch = NamedUnit(6.102374409473229e-05, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_inch', ascii_symbol='ug in^-3', symbol='µgNone⁻³') +nanograms_per_cubic_inch = NamedUnit(6.10237440947323e-08, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_inch', ascii_symbol='ng in^-3', symbol='ngNone⁻³') +picograms_per_cubic_inch = NamedUnit(6.102374409473229e-11, Dimensions(length=-3, mass=1), name='picograms_per_cubic_inch', ascii_symbol='pg in^-3', symbol='pgNone⁻³') +femtograms_per_cubic_inch = NamedUnit(6.10237440947323e-14, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_inch', ascii_symbol='fg in^-3', symbol='fgNone⁻³') +attograms_per_cubic_inch = NamedUnit(6.10237440947323e-17, Dimensions(length=-3, mass=1), name='attograms_per_cubic_inch', ascii_symbol='ag in^-3', symbol='agNone⁻³') +atomic_mass_units_per_cubic_inch = NamedUnit(1.0133230217444687e-22, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_inch', ascii_symbol='au in^-3', symbol='NoneNone⁻³') +pounds_per_cubic_inch = NamedUnit(27679.904710203125, Dimensions(length=-3, mass=1), name='pounds_per_cubic_inch', ascii_symbol='lb in^-3', symbol='NoneNone⁻³') +ounces_per_cubic_inch = NamedUnit(1729.9940443876953, Dimensions(length=-3, mass=1), name='ounces_per_cubic_inch', ascii_symbol='oz in^-3', symbol='NoneNone⁻³') +moles_per_cubic_meter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_meter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_meter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_meter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_meter = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_meter = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_meter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolNone⁻³') +moles_per_cubic_exameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='NoneEm⁻³') +millimoles_per_cubic_exameter = NamedUnit(6.02214076e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') +micromoles_per_cubic_exameter = NamedUnit(6.02214076e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') +nanomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') +picomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') +femtomoles_per_cubic_exameter = NamedUnit(6.02214076e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') +attomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') +moles_per_cubic_petameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='NonePm⁻³') +millimoles_per_cubic_petameter = NamedUnit(6.0221407600000005e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') +micromoles_per_cubic_petameter = NamedUnit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') +nanomoles_per_cubic_petameter = NamedUnit(6.02214076e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') +picomoles_per_cubic_petameter = NamedUnit(6.0221407600000005e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') +femtomoles_per_cubic_petameter = NamedUnit(6.022140760000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') +attomoles_per_cubic_petameter = NamedUnit(6.022140760000001e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') +moles_per_cubic_terameter = NamedUnit(6.022140759999999e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='NoneTm⁻³') +millimoles_per_cubic_terameter = NamedUnit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') +micromoles_per_cubic_terameter = NamedUnit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') +nanomoles_per_cubic_terameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') +picomoles_per_cubic_terameter = NamedUnit(6.02214076e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') +femtomoles_per_cubic_terameter = NamedUnit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') +attomoles_per_cubic_terameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') +moles_per_cubic_gigameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='NoneGm⁻³') +millimoles_per_cubic_gigameter = NamedUnit(6.022140760000001e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') +micromoles_per_cubic_gigameter = NamedUnit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') +nanomoles_per_cubic_gigameter = NamedUnit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') +picomoles_per_cubic_gigameter = NamedUnit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') +femtomoles_per_cubic_gigameter = NamedUnit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') +attomoles_per_cubic_gigameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') +moles_per_cubic_megameter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='NoneMm⁻³') +millimoles_per_cubic_megameter = NamedUnit(602.214076, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') +micromoles_per_cubic_megameter = NamedUnit(0.602214076, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') +nanomoles_per_cubic_megameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') +picomoles_per_cubic_megameter = NamedUnit(6.02214076e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') +femtomoles_per_cubic_megameter = NamedUnit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') +attomoles_per_cubic_megameter = NamedUnit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') +moles_per_cubic_kilometer = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='Nonekm⁻³') +millimoles_per_cubic_kilometer = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') +micromoles_per_cubic_kilometer = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') +nanomoles_per_cubic_kilometer = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') +picomoles_per_cubic_kilometer = NamedUnit(602.214076, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') +femtomoles_per_cubic_kilometer = NamedUnit(0.602214076, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') +attomoles_per_cubic_kilometer = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') +moles_per_cubic_millimeter = NamedUnit(6.0221407599999995e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='Nonemm⁻³') +millimoles_per_cubic_millimeter = NamedUnit(6.02214076e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') +micromoles_per_cubic_millimeter = NamedUnit(6.0221407599999996e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') +nanomoles_per_cubic_millimeter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') +picomoles_per_cubic_millimeter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') +femtomoles_per_cubic_millimeter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') +attomoles_per_cubic_millimeter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') +moles_per_cubic_micrometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='Noneµm⁻³') +millimoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') +micromoles_per_cubic_micrometer = NamedUnit(6.0221407600000004e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') +nanomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') +picomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') +femtomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') +attomoles_per_cubic_micrometer = NamedUnit(6.0221407600000005e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') +moles_per_cubic_nanometer = NamedUnit(6.022140759999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='Nonenm⁻³') +millimoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') +micromoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') +nanomoles_per_cubic_nanometer = NamedUnit(6.022140759999998e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') +picomoles_per_cubic_nanometer = NamedUnit(6.0221407599999985e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') +femtomoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') +attomoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') +moles_per_cubic_picometer = NamedUnit(6.0221407600000005e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='Nonepm⁻³') +millimoles_per_cubic_picometer = NamedUnit(6.0221407600000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') +micromoles_per_cubic_picometer = NamedUnit(6.022140760000001e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') +nanomoles_per_cubic_picometer = NamedUnit(6.0221407600000005e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') +picomoles_per_cubic_picometer = NamedUnit(6.02214076e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') +femtomoles_per_cubic_picometer = NamedUnit(6.022140760000001e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') +attomoles_per_cubic_picometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') +moles_per_cubic_femtometer = NamedUnit(6.022140759999998e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='Nonefm⁻³') +millimoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') +micromoles_per_cubic_femtometer = NamedUnit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') +nanomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') +picomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') +femtomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') +attomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') +moles_per_cubic_attometer = NamedUnit(6.022140759999998e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='Noneam⁻³') +millimoles_per_cubic_attometer = NamedUnit(6.022140759999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') +micromoles_per_cubic_attometer = NamedUnit(6.022140759999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') +nanomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') +picomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') +femtomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') +attomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') +moles_per_cubic_decimeter = NamedUnit(6.022140759999998e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='Nonedm⁻³') +millimoles_per_cubic_decimeter = NamedUnit(6.0221407599999985e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') +micromoles_per_cubic_decimeter = NamedUnit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') +nanomoles_per_cubic_decimeter = NamedUnit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') +picomoles_per_cubic_decimeter = NamedUnit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') +femtomoles_per_cubic_decimeter = NamedUnit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') +attomoles_per_cubic_decimeter = NamedUnit(602214075.9999999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') +moles_per_cubic_centimeter = NamedUnit(6.022140759999999e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='Nonecm⁻³') +millimoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') +micromoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') +nanomoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') +picomoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') +femtomoles_per_cubic_centimeter = NamedUnit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') +attomoles_per_cubic_centimeter = NamedUnit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') +moles_per_cubic_angstrom = NamedUnit(6.022140759999999e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='NoneÅ⁻³') +millimoles_per_cubic_angstrom = NamedUnit(6.02214076e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') +micromoles_per_cubic_angstrom = NamedUnit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') +nanomoles_per_cubic_angstrom = NamedUnit(6.02214076e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') +picomoles_per_cubic_angstrom = NamedUnit(6.02214076e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') +femtomoles_per_cubic_angstrom = NamedUnit(6.022140759999999e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') +attomoles_per_cubic_angstrom = NamedUnit(6.02214076e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') +moles_per_cubic_mile = NamedUnit(144478840228220.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_mile', ascii_symbol='mol miles^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_mile = NamedUnit(144478840228.22003, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_mile', ascii_symbol='mmol miles^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_mile = NamedUnit(144478840.22822002, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_mile', ascii_symbol='umol miles^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_mile = NamedUnit(144478.84022822, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_mile', ascii_symbol='nmol miles^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_mile = NamedUnit(144.47884022822, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_mile', ascii_symbol='pmol miles^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_mile = NamedUnit(0.14447884022822002, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_mile', ascii_symbol='fmol miles^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_mile = NamedUnit(0.00014447884022822003, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_mile', ascii_symbol='amol miles^-3', symbol='amolNone⁻³') +moles_per_cubic_yard = NamedUnit(7.876662736640442e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_yard', ascii_symbol='mol yrd^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_yard = NamedUnit(7.876662736640442e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_yard', ascii_symbol='mmol yrd^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_yard = NamedUnit(7.876662736640442e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_yard', ascii_symbol='umol yrd^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_yard = NamedUnit(787666273664044.2, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_yard', ascii_symbol='nmol yrd^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_yard = NamedUnit(787666273664.0442, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_yard', ascii_symbol='pmol yrd^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_yard = NamedUnit(787666273.6640443, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_yard', ascii_symbol='fmol yrd^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_yard = NamedUnit(787666.2736640442, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_yard', ascii_symbol='amol yrd^-3', symbol='amolNone⁻³') +moles_per_cubic_foot = NamedUnit(2.1266989388929195e+25, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_foot', ascii_symbol='mol ft^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_foot = NamedUnit(2.1266989388929197e+22, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_foot', ascii_symbol='mmol ft^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_foot = NamedUnit(2.1266989388929196e+19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_foot', ascii_symbol='umol ft^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_foot = NamedUnit(2.1266989388929196e+16, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_foot', ascii_symbol='nmol ft^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_foot = NamedUnit(21266989388929.2, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_foot', ascii_symbol='pmol ft^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_foot = NamedUnit(21266989388.9292, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_foot', ascii_symbol='fmol ft^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_foot = NamedUnit(21266989.388929196, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_foot', ascii_symbol='amol ft^-3', symbol='amolNone⁻³') +moles_per_cubic_inch = NamedUnit(3.6749357664069658e+28, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_inch', ascii_symbol='mol in^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_inch = NamedUnit(3.674935766406966e+25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_inch', ascii_symbol='mmol in^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_inch = NamedUnit(3.674935766406966e+22, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_inch', ascii_symbol='umol in^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_inch = NamedUnit(3.674935766406966e+19, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_inch', ascii_symbol='nmol in^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_inch = NamedUnit(3.674935766406966e+16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_inch', ascii_symbol='pmol in^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_inch = NamedUnit(36749357664069.664, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_inch', ascii_symbol='fmol in^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_inch = NamedUnit(36749357664.069664, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_inch', ascii_symbol='amol in^-3', symbol='amolNone⁻³') # # Lookup table from symbols to units @@ -1673,6 +1917,13 @@ def __init__(self, name: str, units: list[Unit]): "fmol": femtomoles, "amol": attomoles, "kgForce": kg_force, + "miles": miles, + "yrd": yards, + "ft": feet, + "in": inches, + "lb": pounds, + "oz": ounces, + "psi": pound_force_per_square_inch, "yr": years, "year": years, "day": days, @@ -1707,6 +1958,10 @@ def __init__(self, name: str, units: list[Unit]): decimeters, centimeters, angstroms, + miles, + yards, + feet, + inches, ]) area = UnitGroup( @@ -1728,6 +1983,10 @@ def __init__(self, name: str, units: list[Unit]): square_decimeters, square_centimeters, square_angstroms, + square_miles, + square_yards, + square_feet, + square_inches, ]) volume = UnitGroup( @@ -1750,6 +2009,10 @@ def __init__(self, name: str, units: list[Unit]): cubic_decimeters, cubic_centimeters, cubic_angstroms, + cubic_miles, + cubic_yards, + cubic_feet, + cubic_inches, ]) inverse_length = UnitGroup( @@ -1771,6 +2034,10 @@ def __init__(self, name: str, units: list[Unit]): per_decimeter, per_centimeter, per_angstrom, + per_mile, + per_yard, + per_foot, + per_inch, ]) inverse_area = UnitGroup( @@ -1792,6 +2059,10 @@ def __init__(self, name: str, units: list[Unit]): per_square_decimeter, per_square_centimeter, per_square_angstrom, + per_square_mile, + per_square_yard, + per_square_foot, + per_square_inch, ]) inverse_volume = UnitGroup( @@ -1813,6 +2084,10 @@ def __init__(self, name: str, units: list[Unit]): per_cubic_decimeter, per_cubic_centimeter, per_cubic_angstrom, + per_cubic_mile, + per_cubic_yard, + per_cubic_foot, + per_cubic_inch, ]) time = UnitGroup( @@ -2029,6 +2304,50 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_hour, angstroms_per_day, angstroms_per_year, + miles_per_second, + miles_per_millisecond, + miles_per_microsecond, + miles_per_nanosecond, + miles_per_picosecond, + miles_per_femtosecond, + miles_per_attosecond, + miles_per_minute, + miles_per_hour, + miles_per_day, + miles_per_year, + yards_per_second, + yards_per_millisecond, + yards_per_microsecond, + yards_per_nanosecond, + yards_per_picosecond, + yards_per_femtosecond, + yards_per_attosecond, + yards_per_minute, + yards_per_hour, + yards_per_day, + yards_per_year, + feet_per_second, + feet_per_millisecond, + feet_per_microsecond, + feet_per_nanosecond, + feet_per_picosecond, + feet_per_femtosecond, + feet_per_attosecond, + feet_per_minute, + feet_per_hour, + feet_per_day, + feet_per_year, + inches_per_second, + inches_per_millisecond, + inches_per_microsecond, + inches_per_nanosecond, + inches_per_picosecond, + inches_per_femtosecond, + inches_per_attosecond, + inches_per_minute, + inches_per_hour, + inches_per_day, + inches_per_year, ]) acceleration = UnitGroup( @@ -2210,6 +2529,50 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_square_hour, angstroms_per_square_day, angstroms_per_square_year, + miles_per_square_second, + miles_per_square_millisecond, + miles_per_square_microsecond, + miles_per_square_nanosecond, + miles_per_square_picosecond, + miles_per_square_femtosecond, + miles_per_square_attosecond, + miles_per_square_minute, + miles_per_square_hour, + miles_per_square_day, + miles_per_square_year, + yards_per_square_second, + yards_per_square_millisecond, + yards_per_square_microsecond, + yards_per_square_nanosecond, + yards_per_square_picosecond, + yards_per_square_femtosecond, + yards_per_square_attosecond, + yards_per_square_minute, + yards_per_square_hour, + yards_per_square_day, + yards_per_square_year, + feet_per_square_second, + feet_per_square_millisecond, + feet_per_square_microsecond, + feet_per_square_nanosecond, + feet_per_square_picosecond, + feet_per_square_femtosecond, + feet_per_square_attosecond, + feet_per_square_minute, + feet_per_square_hour, + feet_per_square_day, + feet_per_square_year, + inches_per_square_second, + inches_per_square_millisecond, + inches_per_square_microsecond, + inches_per_square_nanosecond, + inches_per_square_picosecond, + inches_per_square_femtosecond, + inches_per_square_attosecond, + inches_per_square_minute, + inches_per_square_hour, + inches_per_square_day, + inches_per_square_year, ]) density = UnitGroup( @@ -2229,6 +2592,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_meter, attograms_per_cubic_meter, atomic_mass_units_per_cubic_meter, + pounds_per_cubic_meter, + ounces_per_cubic_meter, grams_per_cubic_exameter, exagrams_per_cubic_exameter, petagrams_per_cubic_exameter, @@ -2243,6 +2608,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_exameter, attograms_per_cubic_exameter, atomic_mass_units_per_cubic_exameter, + pounds_per_cubic_exameter, + ounces_per_cubic_exameter, grams_per_cubic_petameter, exagrams_per_cubic_petameter, petagrams_per_cubic_petameter, @@ -2257,6 +2624,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_petameter, attograms_per_cubic_petameter, atomic_mass_units_per_cubic_petameter, + pounds_per_cubic_petameter, + ounces_per_cubic_petameter, grams_per_cubic_terameter, exagrams_per_cubic_terameter, petagrams_per_cubic_terameter, @@ -2271,6 +2640,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_terameter, attograms_per_cubic_terameter, atomic_mass_units_per_cubic_terameter, + pounds_per_cubic_terameter, + ounces_per_cubic_terameter, grams_per_cubic_gigameter, exagrams_per_cubic_gigameter, petagrams_per_cubic_gigameter, @@ -2285,6 +2656,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_gigameter, attograms_per_cubic_gigameter, atomic_mass_units_per_cubic_gigameter, + pounds_per_cubic_gigameter, + ounces_per_cubic_gigameter, grams_per_cubic_megameter, exagrams_per_cubic_megameter, petagrams_per_cubic_megameter, @@ -2299,6 +2672,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_megameter, attograms_per_cubic_megameter, atomic_mass_units_per_cubic_megameter, + pounds_per_cubic_megameter, + ounces_per_cubic_megameter, grams_per_cubic_kilometer, exagrams_per_cubic_kilometer, petagrams_per_cubic_kilometer, @@ -2313,6 +2688,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_kilometer, attograms_per_cubic_kilometer, atomic_mass_units_per_cubic_kilometer, + pounds_per_cubic_kilometer, + ounces_per_cubic_kilometer, grams_per_cubic_millimeter, exagrams_per_cubic_millimeter, petagrams_per_cubic_millimeter, @@ -2327,6 +2704,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_millimeter, attograms_per_cubic_millimeter, atomic_mass_units_per_cubic_millimeter, + pounds_per_cubic_millimeter, + ounces_per_cubic_millimeter, grams_per_cubic_micrometer, exagrams_per_cubic_micrometer, petagrams_per_cubic_micrometer, @@ -2341,6 +2720,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_micrometer, attograms_per_cubic_micrometer, atomic_mass_units_per_cubic_micrometer, + pounds_per_cubic_micrometer, + ounces_per_cubic_micrometer, grams_per_cubic_nanometer, exagrams_per_cubic_nanometer, petagrams_per_cubic_nanometer, @@ -2355,6 +2736,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_nanometer, attograms_per_cubic_nanometer, atomic_mass_units_per_cubic_nanometer, + pounds_per_cubic_nanometer, + ounces_per_cubic_nanometer, grams_per_cubic_picometer, exagrams_per_cubic_picometer, petagrams_per_cubic_picometer, @@ -2369,6 +2752,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_picometer, attograms_per_cubic_picometer, atomic_mass_units_per_cubic_picometer, + pounds_per_cubic_picometer, + ounces_per_cubic_picometer, grams_per_cubic_femtometer, exagrams_per_cubic_femtometer, petagrams_per_cubic_femtometer, @@ -2383,6 +2768,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_femtometer, attograms_per_cubic_femtometer, atomic_mass_units_per_cubic_femtometer, + pounds_per_cubic_femtometer, + ounces_per_cubic_femtometer, grams_per_cubic_attometer, exagrams_per_cubic_attometer, petagrams_per_cubic_attometer, @@ -2397,6 +2784,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_attometer, attograms_per_cubic_attometer, atomic_mass_units_per_cubic_attometer, + pounds_per_cubic_attometer, + ounces_per_cubic_attometer, grams_per_cubic_decimeter, exagrams_per_cubic_decimeter, petagrams_per_cubic_decimeter, @@ -2411,6 +2800,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_decimeter, attograms_per_cubic_decimeter, atomic_mass_units_per_cubic_decimeter, + pounds_per_cubic_decimeter, + ounces_per_cubic_decimeter, grams_per_cubic_centimeter, exagrams_per_cubic_centimeter, petagrams_per_cubic_centimeter, @@ -2425,6 +2816,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_centimeter, attograms_per_cubic_centimeter, atomic_mass_units_per_cubic_centimeter, + pounds_per_cubic_centimeter, + ounces_per_cubic_centimeter, grams_per_cubic_angstrom, exagrams_per_cubic_angstrom, petagrams_per_cubic_angstrom, @@ -2439,6 +2832,72 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_angstrom, attograms_per_cubic_angstrom, atomic_mass_units_per_cubic_angstrom, + pounds_per_cubic_angstrom, + ounces_per_cubic_angstrom, + grams_per_cubic_mile, + exagrams_per_cubic_mile, + petagrams_per_cubic_mile, + teragrams_per_cubic_mile, + gigagrams_per_cubic_mile, + megagrams_per_cubic_mile, + kilograms_per_cubic_mile, + milligrams_per_cubic_mile, + micrograms_per_cubic_mile, + nanograms_per_cubic_mile, + picograms_per_cubic_mile, + femtograms_per_cubic_mile, + attograms_per_cubic_mile, + atomic_mass_units_per_cubic_mile, + pounds_per_cubic_mile, + ounces_per_cubic_mile, + grams_per_cubic_yard, + exagrams_per_cubic_yard, + petagrams_per_cubic_yard, + teragrams_per_cubic_yard, + gigagrams_per_cubic_yard, + megagrams_per_cubic_yard, + kilograms_per_cubic_yard, + milligrams_per_cubic_yard, + micrograms_per_cubic_yard, + nanograms_per_cubic_yard, + picograms_per_cubic_yard, + femtograms_per_cubic_yard, + attograms_per_cubic_yard, + atomic_mass_units_per_cubic_yard, + pounds_per_cubic_yard, + ounces_per_cubic_yard, + grams_per_cubic_foot, + exagrams_per_cubic_foot, + petagrams_per_cubic_foot, + teragrams_per_cubic_foot, + gigagrams_per_cubic_foot, + megagrams_per_cubic_foot, + kilograms_per_cubic_foot, + milligrams_per_cubic_foot, + micrograms_per_cubic_foot, + nanograms_per_cubic_foot, + picograms_per_cubic_foot, + femtograms_per_cubic_foot, + attograms_per_cubic_foot, + atomic_mass_units_per_cubic_foot, + pounds_per_cubic_foot, + ounces_per_cubic_foot, + grams_per_cubic_inch, + exagrams_per_cubic_inch, + petagrams_per_cubic_inch, + teragrams_per_cubic_inch, + gigagrams_per_cubic_inch, + megagrams_per_cubic_inch, + kilograms_per_cubic_inch, + milligrams_per_cubic_inch, + micrograms_per_cubic_inch, + nanograms_per_cubic_inch, + picograms_per_cubic_inch, + femtograms_per_cubic_inch, + attograms_per_cubic_inch, + atomic_mass_units_per_cubic_inch, + pounds_per_cubic_inch, + ounces_per_cubic_inch, ]) force = UnitGroup( @@ -2476,6 +2935,7 @@ def __init__(self, name: str, units: list[Unit]): picopascals, femtopascals, attopascals, + pound_force_per_square_inch, ]) energy = UnitGroup( @@ -2836,4 +3296,32 @@ def __init__(self, name: str, units: list[Unit]): picomoles_per_cubic_angstrom, femtomoles_per_cubic_angstrom, attomoles_per_cubic_angstrom, + moles_per_cubic_mile, + millimoles_per_cubic_mile, + micromoles_per_cubic_mile, + nanomoles_per_cubic_mile, + picomoles_per_cubic_mile, + femtomoles_per_cubic_mile, + attomoles_per_cubic_mile, + moles_per_cubic_yard, + millimoles_per_cubic_yard, + micromoles_per_cubic_yard, + nanomoles_per_cubic_yard, + picomoles_per_cubic_yard, + femtomoles_per_cubic_yard, + attomoles_per_cubic_yard, + moles_per_cubic_foot, + millimoles_per_cubic_foot, + micromoles_per_cubic_foot, + nanomoles_per_cubic_foot, + picomoles_per_cubic_foot, + femtomoles_per_cubic_foot, + attomoles_per_cubic_foot, + moles_per_cubic_inch, + millimoles_per_cubic_inch, + micromoles_per_cubic_inch, + nanomoles_per_cubic_inch, + picomoles_per_cubic_inch, + femtomoles_per_cubic_inch, + attomoles_per_cubic_inch, ]) From f15b7c2590f8195c60b60de3284b3f28c096c15a Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 16:58:02 +0100 Subject: [PATCH 015/675] More units --- sasdata/quantities/_build_tables.py | 3 ++- sasdata/quantities/accessors.py | 8 ++++++-- sasdata/quantities/quantities_tests.py | 5 +++-- sasdata/quantities/quantity.py | 2 +- sasdata/quantities/units.py | 13 ++++++++++--- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index f660d7dcd..a32290c96 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -75,8 +75,9 @@ ("ft", None, "foot", "feet", 0.3048, 1, 0, 0, 0, 0, 0, 0, []), ("in", None, "inch", "inches", 0.0254, 1, 0, 0, 0, 0, 0, 0, []), ("lb", None, "pound", "pounds", 0.45359237, 0, 0, 1, 0, 0, 0, 0, []), + ("lbf", None, "pound force", "pounds force", 4.448222, 1, -2, 1, 0, 0, 0, 0, []), ("oz", None, "ounce", "ounces", 0.45359237/16, 0, 0, 1, 0, 0, 0, 0, []), - ("psi", None, "pound force per square inch", "pound force per square inch", 6.894757e3, -1, -2, 1, 0, 0, 0, 0, []), + ("psi", None, "pound force per square inch", "pounds force per square inch", 4.448222/(0.0254**2), -1, -2, 1, 0, 0, 0, 0, []), ] aliases = { diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 5882a9795..945e6f3b5 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -3830,6 +3830,10 @@ def attonewtons(self) -> T: def kg_force(self) -> T: return self.quantity.in_units_of(units.kg_force) + @property + def pounds_force(self) -> T: + return self.quantity.in_units_of(units.pounds_force) + class PressureAccessor[T](Accessor[T]): @@ -3888,8 +3892,8 @@ def attopascals(self) -> T: return self.quantity.in_units_of(units.attopascals) @property - def pound_force_per_square_inch(self) -> T: - return self.quantity.in_units_of(units.pound_force_per_square_inch) + def pounds_force_per_square_inch(self) -> T: + return self.quantity.in_units_of(units.pounds_force_per_square_inch) diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index d5d83bef1..16b06aae6 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -62,17 +62,18 @@ def test_mixed_quantity_add_sub(unit_1, unit_2): with pytest.raises(UnitError): Quantity(1, unit_1) + Quantity(1, unit_2) -def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float): +def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float, abs=1e-9): """ Helper function for testing units that are multiples of each other """ assert u1.equivalent(u2), "Units should be compatible for this test" - assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=1e-9) + assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) def test_american_units(): assert_unit_ratio(units.feet, units.inches, 12) assert_unit_ratio(units.yards, units.inches, 36) assert_unit_ratio(units.miles, units.inches, 63360) + assert_unit_ratio(units.pounds_force_per_square_inch, units.pounds_force / (units.inches**2), 1, abs=1e-5) @pytest.mark.parametrize("unit_1", si.all_si) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 1ecc2ad9d..23a2e5acb 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -55,7 +55,7 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: def __add__(self: Self, other: Self | ArrayLike) -> Self: if isinstance(other, Quantity): if self.units.equivalent(other.units): - return Quantity(self.value + (other.value * other.scale)/self.scale, self.units) + return Quantity(self.value + (other.value * other.units.scale)/self.units.scale, self.units) else: raise UnitError(f"Units do not have the same dimensionality: {self.units} vs {other.units}") diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index ba3d8ae18..80fcba319 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -279,6 +279,7 @@ def __repr__(self): return f"Unit[{self.scale}, {self.dimensions}]" class NamedUnit(Unit): + """ Units, but they have a name, and a symbol""" def __init__(self, si_scaling_factor: float, dimensions: Dimensions, @@ -291,6 +292,9 @@ def __init__(self, self.ascii_symbol = ascii_symbol self.symbol = symbol + def __repr__(self): + return self.name + # # Parsing plan: # Require unknown amounts of units to be explicitly positive or negative? @@ -614,8 +618,9 @@ def __init__(self, name: str, units: list[Unit]): feet = NamedUnit(0.3048, Dimensions(1, 0, 0, 0, 0, 0, 0),name='feet',ascii_symbol='ft',symbol='ft') inches = NamedUnit(0.0254, Dimensions(1, 0, 0, 0, 0, 0, 0),name='inches',ascii_symbol='in',symbol='in') pounds = NamedUnit(0.45359237, Dimensions(0, 0, 1, 0, 0, 0, 0),name='pounds',ascii_symbol='lb',symbol='lb') +pounds_force = NamedUnit(4.448222, Dimensions(1, -2, 1, 0, 0, 0, 0),name='pounds_force',ascii_symbol='lbf',symbol='lbf') ounces = NamedUnit(0.028349523125, Dimensions(0, 0, 1, 0, 0, 0, 0),name='ounces',ascii_symbol='oz',symbol='oz') -pound_force_per_square_inch = NamedUnit(6894.757, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pound_force_per_square_inch',ascii_symbol='psi',symbol='psi') +pounds_force_per_square_inch = NamedUnit(6894.757, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pounds_force_per_square_inch',ascii_symbol='psi',symbol='psi') square_meters = NamedUnit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = NamedUnit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = NamedUnit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -1922,8 +1927,9 @@ def __init__(self, name: str, units: list[Unit]): "ft": feet, "in": inches, "lb": pounds, + "lbf": pounds_force, "oz": ounces, - "psi": pound_force_per_square_inch, + "psi": pounds_force_per_square_inch, "yr": years, "year": years, "day": days, @@ -2917,6 +2923,7 @@ def __init__(self, name: str, units: list[Unit]): femtonewtons, attonewtons, kg_force, + pounds_force, ]) pressure = UnitGroup( @@ -2935,7 +2942,7 @@ def __init__(self, name: str, units: list[Unit]): picopascals, femtopascals, attopascals, - pound_force_per_square_inch, + pounds_force_per_square_inch, ]) energy = UnitGroup( From 6ffabb184d218f624d5363269957db886bfe041e Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 17:14:36 +0100 Subject: [PATCH 016/675] Notes --- sasdata/quantities/_build_tables.py | 7 ++++++- sasdata/quantities/units.py | 3 +-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index a32290c96..9148059d9 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -80,6 +80,10 @@ ("psi", None, "pound force per square inch", "pounds force per square inch", 4.448222/(0.0254**2), -1, -2, 1, 0, 0, 0, 0, []), ] +# TODO: +# Add Hartree? Rydberg? Bohrs? +# Add CGS + aliases = { "y": ["yr", "year"], "d": ["day"], @@ -283,7 +287,8 @@ def format_name(name: str): fid.write("\n#\n# Lookup table from symbols to units\n#\n\n") fid.write("symbol_lookup = {\n") for k in symbol_lookup: - fid.write(f' "{k}": {symbol_lookup[k]},\n') + if k != "none": + fid.write(f' "{k}": {symbol_lookup[k]},\n') fid.write("}\n\n") # diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 80fcba319..3b5e6fdb4 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -620,7 +620,7 @@ def __init__(self, name: str, units: list[Unit]): pounds = NamedUnit(0.45359237, Dimensions(0, 0, 1, 0, 0, 0, 0),name='pounds',ascii_symbol='lb',symbol='lb') pounds_force = NamedUnit(4.448222, Dimensions(1, -2, 1, 0, 0, 0, 0),name='pounds_force',ascii_symbol='lbf',symbol='lbf') ounces = NamedUnit(0.028349523125, Dimensions(0, 0, 1, 0, 0, 0, 0),name='ounces',ascii_symbol='oz',symbol='oz') -pounds_force_per_square_inch = NamedUnit(6894.757, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pounds_force_per_square_inch',ascii_symbol='psi',symbol='psi') +pounds_force_per_square_inch = NamedUnit(6894.757889515779, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pounds_force_per_square_inch',ascii_symbol='psi',symbol='psi') square_meters = NamedUnit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = NamedUnit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = NamedUnit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -1896,7 +1896,6 @@ def __init__(self, name: str, units: list[Unit]): "deg": degrees, "rad": radians, "sr": stradians, - "none": none, "l": litres, "eV": electronvolts, "EeV": exaelectronvolts, From 84c6890951afa249fed2a589377e081fa0b81150 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 8 Aug 2024 10:40:39 +0100 Subject: [PATCH 017/675] Notes --- sasdata/quantities/_build_tables.py | 1 + sasdata/quantities/_units_base.py | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 9148059d9..72e15c0a0 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -271,6 +271,7 @@ def format_name(name: str): unit_types[hash(dimensions)].append(name) + # TODO: Torque, Momentum, Entropy # # Add aliases to symbol lookup table diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index b008932d7..a908135bd 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -207,7 +207,14 @@ class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): class NamedUnit(Unit): - """ Units, but they have a name, and a symbol""" + """ Units, but they have a name, and a symbol + + :si_scaling_factor: Number of these units per SI equivalent + :param dimensions: Dimensions object representing the dimensionality of these units + :param name: Name of unit - string without unicode + :param ascii_symbol: Symbol for unit without unicode + :param symbol: Unicode symbol + """ def __init__(self, si_scaling_factor: float, dimensions: Dimensions, From 46d6f9f055217cef30a2d85c6c076d64180d0e32 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 8 Aug 2024 16:20:41 +0100 Subject: [PATCH 018/675] start of metadata structure --- sasdata/metadata.py | 331 +- sasdata/quantities/_accessor_base.py | 41 +- sasdata/quantities/_build_tables.py | 10 +- sasdata/quantities/accessors.py | 7551 +++++++++++++++++++++----- sasdata/quantities/units.py | 9 +- 5 files changed, 6607 insertions(+), 1335 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index ed9396cf5..2efff3932 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -1,83 +1,312 @@ -from typing import TypeVar +import numpy as np +from numpy.typing import ArrayLike -from numpy._typing import ArrayLike +import sasdata.quantities.units as units +from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor +class Detector: + """ + Detector information + """ -from sasdata.quantities.quantity import Unit, Quantity + def __init__(self, target_object): + self.target_object = target_object + # Name of the instrument [string] + self.name = StringAccessor(self.target_object, "detector.name") -class RawMetaData: - pass + # Sample to detector distance [float] [mm] + self.distance = LengthAccessor[float](self.target_object, + "detector.distance", + "detector.distance.units", + default_unit=units.millimeters) + # Offset of this detector position in X, Y, + # (and Z if necessary) [Vector] [mm] + self.offset = LengthAccessor[ArrayLike](self.target_object, + "detector.offset", + "detector.offset.units", + default_units=units.millimeters) -FieldDataType = TypeVar("FieldDataType") -OutputDataType = TypeVar("OutputDataType") + self.orientation = AngleAccessor[ArrayLike](self.target_object, + "detector.orientation", + "detector.orientation.units", + default_units=units.degrees) -class Accessor[FieldDataType, OutputDataType]: - def __init__(self, target_field: str): - self._target_field = target_field + self.beam_center = LengthAccessor[ArrayLike](self.target_object, + "detector.beam_center", + "detector.beam_center.units", + default_units=units.millimeters) - def _raw_values(self) -> FieldDataType: - raise NotImplementedError("not implemented in base class") + # Pixel size in X, Y, (and Z if necessary) [Vector] [mm] + self.pixel_size = LengthAccessor[ArrayLike](self.target_object, + "detector.pixel_size", + "detector.pixel_size.units", + default_units=units.millimeters) - @property - def value(self) -> OutputDataType: - raise NotImplementedError("value not implemented in base class") + # Slit length of the instrument for this detector.[float] [mm] + self.slit_length = LengthAccessor[float](self.target_object, + "detector.slit_length", + "detector.slit_length.units", + default_units=units.millimeters) + def summary(self): + return (f"Detector:\n" + f" Name: {self.name.value}\n" + f" Distance: {self.distance.value}\n" + f" Offset: {self.offset.value}\n" + f" Orientation: {self.orientation.value}\n" + f" Beam center: {self.beam_center.value}\n" + f" Pixel size: {self.pixel_size.value}\n" + f" Slit length: {self.slit_length.value}\n") -class QuantityAccessor(Accessor[ArrayLike, Quantity[ArrayLike]]): - def __init__(self, target_field: str, units_field: str | None = None): - super().__init__(target_field) - self._units_field = units_field +class Aperture: - def _units(self) -> Unit: - pass + def __init__(self, target_object): + self.target_object = target_object - def _raw_values(self) -> ArrayLike: - pass + # Name + name = StringAccessor(self.target_object, "aperture.name") - @property - def value(self) -> Quantity[ArrayLike]: - return Quantity(self._raw_values(), self._units()) + # Type + type = StringAccessor(self.target_object, "aperture.type") + # Size name - TODO: What is the name of a size + size_name = StringAccessor(self.target_object, "aperture.size_name") -class StringAccessor(Accessor[str, str]): + # Aperture size [Vector] # TODO: Wat!?! + size = QuantityAccessor(self.target_object, + "aperture.size", + "aperture.size", + default_unit=units.millimeters) + size = None + size_unit = 'mm' - def _raw_values(self) -> str: + # Aperture distance [float] + distance = None + distance_unit = 'mm' + + def summary(self): pass - @property - def value(self) -> str: - return self._raw_values() +class Collimation: + """ + Class to hold collimation information + """ + # Name + name = None + # Length [float] [mm] + length = None + length_unit = 'mm' + # Aperture + aperture = None + + def __init__(self): + self.aperture = [] + + def __str__(self): + _str = "Collimation:\n" + _str += " Length: %s [%s]\n" % \ + (str(self.length), str(self.length_unit)) + for item in self.aperture: + _str += " Aperture size:%s [%s]\n" % \ + (str(item.size), str(item.size_unit)) + _str += " Aperture_dist:%s [%s]\n" % \ + (str(item.distance), str(item.distance_unit)) + return _str + + +class Source: + """ + Class to hold source information + """ + # Name + name = None + # Generic radiation type (Type and probe give more specific info) [string] + radiation = None + # Type and probe are only written to by the NXcanSAS reader + # Specific radiation type (Synchotron X-ray, Reactor neutron, etc) [string] + type = None + # Radiation probe (generic probe such as neutron, x-ray, muon, etc) [string] + probe = None + # Beam size name + beam_size_name = None + # Beam size [Vector] [mm] + beam_size = None + beam_size_unit = 'mm' + # Beam shape [string] + beam_shape = None + # Wavelength [float] [Angstrom] + wavelength = None + wavelength_unit = 'A' + # Minimum wavelength [float] [Angstrom] + wavelength_min = None + wavelength_min_unit = 'nm' + # Maximum wavelength [float] [Angstrom] + wavelength_max = None + wavelength_max_unit = 'nm' + # Wavelength spread [float] [Angstrom] + wavelength_spread = None + wavelength_spread_unit = 'percent' + + def __init__(self): + self.beam_size = None #Vector() + + def __str__(self): + _str = "Source:\n" + radiation = self.radiation + if self.radiation is None and self.type and self.probe: + radiation = self.type + " " + self.probe + _str += " Radiation: %s\n" % str(radiation) + _str += " Shape: %s\n" % str(self.beam_shape) + _str += " Wavelength: %s [%s]\n" % \ + (str(self.wavelength), str(self.wavelength_unit)) + _str += " Waveln_min: %s [%s]\n" % \ + (str(self.wavelength_min), str(self.wavelength_min_unit)) + _str += " Waveln_max: %s [%s]\n" % \ + (str(self.wavelength_max), str(self.wavelength_max_unit)) + _str += " Waveln_spread:%s [%s]\n" % \ + (str(self.wavelength_spread), str(self.wavelength_spread_unit)) + _str += " Beam_size: %s [%s]\n" % \ + (str(self.beam_size), str(self.beam_size_unit)) + return _str + + +""" +Definitions of radiation types +""" +NEUTRON = 'neutron' +XRAY = 'x-ray' +MUON = 'muon' +ELECTRON = 'electron' + + +class Sample: + """ + Class to hold the sample description + """ + # Short name for sample + name = '' + # ID + ID = '' + # Thickness [float] [mm] + thickness = None + thickness_unit = 'mm' + # Transmission [float] [fraction] + transmission = None + # Temperature [float] [No Default] + temperature = None + temperature_unit = None + # Position [Vector] [mm] + position = None + position_unit = 'mm' + # Orientation [Vector] [degrees] + orientation = None + orientation_unit = 'degree' + # Details + details = None + # SESANS zacceptance + zacceptance = (0,"") + yacceptance = (0,"") + + def __init__(self): + self.position = None # Vector() + self.orientation = None # Vector() + self.details = [] + + def __str__(self): + _str = "Sample:\n" + _str += " ID: %s\n" % str(self.ID) + _str += " Transmission: %s\n" % str(self.transmission) + _str += " Thickness: %s [%s]\n" % \ + (str(self.thickness), str(self.thickness_unit)) + _str += " Temperature: %s [%s]\n" % \ + (str(self.temperature), str(self.temperature_unit)) + _str += " Position: %s [%s]\n" % \ + (str(self.position), str(self.position_unit)) + _str += " Orientation: %s [%s]\n" % \ + (str(self.orientation), str(self.orientation_unit)) + + _str += " Details:\n" + for item in self.details: + _str += " %s\n" % item -# -# Quantity specific accessors, provides helper methods for quantities with known dimensionality -# + return _str -class LengthAccessor(QuantityAccessor): - @property - def m(self): - return self.value.in_units_of("m") +class Process: + """ + Class that holds information about the processes + performed on the data. + """ + name = '' + date = '' + description = '' + term = None + notes = None -class TimeAccessor(QuantityAccessor): - pass + def __init__(self): + self.term = [] + self.notes = [] + def is_empty(self): + """ + Return True if the object is empty + """ + return (len(self.name) == 0 and len(self.date) == 0 + and len(self.description) == 0 and len(self.term) == 0 + and len(self.notes) == 0) -class TemperatureAccessor(QuantityAccessor): - pass + def single_line_desc(self): + """ + Return a single line string representing the process + """ + return "%s %s %s" % (self.name, self.date, self.description) + def __str__(self): + _str = "Process:\n" + _str += " Name: %s\n" % self.name + _str += " Date: %s\n" % self.date + _str += " Description: %s\n" % self.description + for item in self.term: + _str += " Term: %s\n" % item + for item in self.notes: + _str += " Note: %s\n" % item + return _str -class AbsoluteTemperatureAccessor(QuantityAccessor): - pass -# -# Main metadata object -# +class TransmissionSpectrum(object): + """ + Class that holds information about transmission spectrum + for white beams and spallation sources. + """ + name = '' + timestamp = '' + # Wavelength (float) [A] + wavelength = None + wavelength_unit = 'A' + # Transmission (float) [unit less] + transmission = None + transmission_unit = '' + # Transmission Deviation (float) [unit less] + transmission_deviation = None + transmission_deviation_unit = '' + def __init__(self): + self.wavelength = [] + self.transmission = [] + self.transmission_deviation = [] -class MetaData: - def __init__(self, raw: RawMetaData): - self._raw = raw + def __str__(self): + _str = "Transmission Spectrum:\n" + _str += " Name: \t{0}\n".format(self.name) + _str += " Timestamp: \t{0}\n".format(self.timestamp) + _str += " Wavelength unit: \t{0}\n".format(self.wavelength_unit) + _str += " Transmission unit:\t{0}\n".format(self.transmission_unit) + _str += " Trans. Dev. unit: \t{0}\n".format( + self.transmission_deviation_unit) + length_list = [len(self.wavelength), len(self.transmission), + len(self.transmission_deviation)] + _str += " Number of Pts: \t{0}\n".format(max(length_list)) + return _str - # Put the structure of the metadata that should be exposed to a power-user / developer in here diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index c887b7242..a1437d170 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -4,14 +4,45 @@ import sasdata.quantities.units as units -T = TypeVar("T") +DataType = TypeVar("DataType") +OutputType = TypeVar("OutputType") -class Accessor[T]: +class Accessor[DataType, OutputType]: """ Base class """ - def __init__(self, value_target: str, unit_target: str): - self._value_target = value_target + def __init__(self, target_object, value_target: str): + self.target_object = target_object + self.value_target = value_target + + @property + def value(self) -> OutputType | None: + pass + +class StringAccessor(Accessor[str, str]): + """ String based fields """ + @property + def value(self) -> str | None: + pass + +class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): + """ Base class for accessors that work with quantities that have units """ + def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): + super().__init__(target_object, value_target) self._unit_target = unit_target + self.default_unit = default_unit + + def _lookup_unit(self) -> units.Unit | None: + # TODO: Implement + return None + + def data_unit(self): + unit = self._lookup_unit + if unit is None: + return self.default_unit + else: + return unit + @property - def quantity(self) -> Quantity[T]: + def quantity(self) -> Quantity[DataType]: raise NotImplementedError("Not implemented yet") + diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 72e15c0a0..842ef52fb 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -343,6 +343,8 @@ def format_name(name: str): fid.write("])\n") + + with open("accessors.py", 'w', encoding=encoding) as fid: @@ -357,14 +359,18 @@ def format_name(name: str): accessor_name = dimension_name.capitalize().replace("_", "") + "Accessor" fid.write(f"\n" - f"class {accessor_name}[T](Accessor[T]):\n" + f"class {accessor_name}[T](QuantityAccessor[T]):\n" f" dimension_name = '{dimension_name}'\n" f"\n") for unit_name in unit_types[hash(dimensions)]: fid.write(f" @property\n" f" def {unit_name}(self) -> T:\n" - f" return self.quantity.in_units_of(units.{unit_name})\n" + f" quantity = self.quantity\n" + f" if quantity is None:\n" + f" return None\n" + f" else:\n" + f" return quantity.in_units_of(units.{unit_name})\n" f"\n") fid.write("\n")with open("si.py", 'w') as fid: diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 945e6f3b5..eaffa5e23 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -84,5127 +84,10126 @@ import sasdata.quantities.units as units -T = TypeVar("T") +DataType = TypeVar("DataType") +OutputType = TypeVar("OutputType") -class Accessor[T]: +class Accessor[DataType, OutputType]: """ Base class """ - def __init__(self, value_target: str, unit_target: str): - self._value_target = value_target + def __init__(self, target_object, value_target: str): + self.target_object = target_object + self.value_target = value_target + + @property + def value(self) -> OutputType | None: + pass + +class StringAccessor(Accessor[str, str]): + """ String based fields """ + @property + def value(self) -> str | None: + pass + +class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): + """ Base class for accessors that work with quantities that have units """ + def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): + super().__init__(target_object, value_target) self._unit_target = unit_target + self.default_unit = default_unit + + def _lookup_unit(self) -> units.Unit | None: + # TODO: Implement + return None + + def data_unit(self): + unit = self._lookup_unit + if unit is None: + return self.default_unit + else: + return unit + @property - def quantity(self) -> Quantity[T]: + def quantity(self) -> Quantity[DataType]: raise NotImplementedError("Not implemented yet") -class LengthAccessor[T](Accessor[T]): + +class LengthAccessor[T](QuantityAccessor[T]): dimension_name = 'length' @property def meters(self) -> T: - return self.quantity.in_units_of(units.meters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters) @property def exameters(self) -> T: - return self.quantity.in_units_of(units.exameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters) @property def petameters(self) -> T: - return self.quantity.in_units_of(units.petameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters) @property def terameters(self) -> T: - return self.quantity.in_units_of(units.terameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters) @property def gigameters(self) -> T: - return self.quantity.in_units_of(units.gigameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters) @property def megameters(self) -> T: - return self.quantity.in_units_of(units.megameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters) @property def kilometers(self) -> T: - return self.quantity.in_units_of(units.kilometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers) @property def millimeters(self) -> T: - return self.quantity.in_units_of(units.millimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters) @property def micrometers(self) -> T: - return self.quantity.in_units_of(units.micrometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers) @property def nanometers(self) -> T: - return self.quantity.in_units_of(units.nanometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers) @property def picometers(self) -> T: - return self.quantity.in_units_of(units.picometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers) @property def femtometers(self) -> T: - return self.quantity.in_units_of(units.femtometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers) @property def attometers(self) -> T: - return self.quantity.in_units_of(units.attometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers) @property def decimeters(self) -> T: - return self.quantity.in_units_of(units.decimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters) @property def centimeters(self) -> T: - return self.quantity.in_units_of(units.centimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters) @property def angstroms(self) -> T: - return self.quantity.in_units_of(units.angstroms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms) @property def miles(self) -> T: - return self.quantity.in_units_of(units.miles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles) @property def yards(self) -> T: - return self.quantity.in_units_of(units.yards) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards) @property def feet(self) -> T: - return self.quantity.in_units_of(units.feet) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet) @property def inches(self) -> T: - return self.quantity.in_units_of(units.inches) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches) -class AreaAccessor[T](Accessor[T]): +class AreaAccessor[T](QuantityAccessor[T]): dimension_name = 'area' @property def square_meters(self) -> T: - return self.quantity.in_units_of(units.square_meters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_meters) @property def square_exameters(self) -> T: - return self.quantity.in_units_of(units.square_exameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_exameters) @property def square_petameters(self) -> T: - return self.quantity.in_units_of(units.square_petameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_petameters) @property def square_terameters(self) -> T: - return self.quantity.in_units_of(units.square_terameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_terameters) @property def square_gigameters(self) -> T: - return self.quantity.in_units_of(units.square_gigameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_gigameters) @property def square_megameters(self) -> T: - return self.quantity.in_units_of(units.square_megameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_megameters) @property def square_kilometers(self) -> T: - return self.quantity.in_units_of(units.square_kilometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_kilometers) @property def square_millimeters(self) -> T: - return self.quantity.in_units_of(units.square_millimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_millimeters) @property def square_micrometers(self) -> T: - return self.quantity.in_units_of(units.square_micrometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_micrometers) @property def square_nanometers(self) -> T: - return self.quantity.in_units_of(units.square_nanometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_nanometers) @property def square_picometers(self) -> T: - return self.quantity.in_units_of(units.square_picometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_picometers) @property def square_femtometers(self) -> T: - return self.quantity.in_units_of(units.square_femtometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_femtometers) @property def square_attometers(self) -> T: - return self.quantity.in_units_of(units.square_attometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_attometers) @property def square_decimeters(self) -> T: - return self.quantity.in_units_of(units.square_decimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_decimeters) @property def square_centimeters(self) -> T: - return self.quantity.in_units_of(units.square_centimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_centimeters) @property def square_angstroms(self) -> T: - return self.quantity.in_units_of(units.square_angstroms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_angstroms) @property def square_miles(self) -> T: - return self.quantity.in_units_of(units.square_miles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_miles) @property def square_yards(self) -> T: - return self.quantity.in_units_of(units.square_yards) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_yards) @property def square_feet(self) -> T: - return self.quantity.in_units_of(units.square_feet) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_feet) @property def square_inches(self) -> T: - return self.quantity.in_units_of(units.square_inches) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_inches) -class VolumeAccessor[T](Accessor[T]): +class VolumeAccessor[T](QuantityAccessor[T]): dimension_name = 'volume' @property def litres(self) -> T: - return self.quantity.in_units_of(units.litres) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.litres) @property def cubic_meters(self) -> T: - return self.quantity.in_units_of(units.cubic_meters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_meters) @property def cubic_exameters(self) -> T: - return self.quantity.in_units_of(units.cubic_exameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_exameters) @property def cubic_petameters(self) -> T: - return self.quantity.in_units_of(units.cubic_petameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_petameters) @property def cubic_terameters(self) -> T: - return self.quantity.in_units_of(units.cubic_terameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_terameters) @property def cubic_gigameters(self) -> T: - return self.quantity.in_units_of(units.cubic_gigameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_gigameters) @property def cubic_megameters(self) -> T: - return self.quantity.in_units_of(units.cubic_megameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_megameters) @property def cubic_kilometers(self) -> T: - return self.quantity.in_units_of(units.cubic_kilometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_kilometers) @property def cubic_millimeters(self) -> T: - return self.quantity.in_units_of(units.cubic_millimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_millimeters) @property def cubic_micrometers(self) -> T: - return self.quantity.in_units_of(units.cubic_micrometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_micrometers) @property def cubic_nanometers(self) -> T: - return self.quantity.in_units_of(units.cubic_nanometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_nanometers) @property def cubic_picometers(self) -> T: - return self.quantity.in_units_of(units.cubic_picometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_picometers) @property def cubic_femtometers(self) -> T: - return self.quantity.in_units_of(units.cubic_femtometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_femtometers) @property def cubic_attometers(self) -> T: - return self.quantity.in_units_of(units.cubic_attometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_attometers) @property def cubic_decimeters(self) -> T: - return self.quantity.in_units_of(units.cubic_decimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_decimeters) @property def cubic_centimeters(self) -> T: - return self.quantity.in_units_of(units.cubic_centimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_centimeters) @property def cubic_angstroms(self) -> T: - return self.quantity.in_units_of(units.cubic_angstroms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_angstroms) @property def cubic_miles(self) -> T: - return self.quantity.in_units_of(units.cubic_miles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_miles) @property def cubic_yards(self) -> T: - return self.quantity.in_units_of(units.cubic_yards) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_yards) @property def cubic_feet(self) -> T: - return self.quantity.in_units_of(units.cubic_feet) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_feet) @property def cubic_inches(self) -> T: - return self.quantity.in_units_of(units.cubic_inches) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_inches) -class InverselengthAccessor[T](Accessor[T]): +class InverselengthAccessor[T](QuantityAccessor[T]): dimension_name = 'inverse_length' @property def per_meter(self) -> T: - return self.quantity.in_units_of(units.per_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_meter) @property def per_exameter(self) -> T: - return self.quantity.in_units_of(units.per_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_exameter) @property def per_petameter(self) -> T: - return self.quantity.in_units_of(units.per_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_petameter) @property def per_terameter(self) -> T: - return self.quantity.in_units_of(units.per_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_terameter) @property def per_gigameter(self) -> T: - return self.quantity.in_units_of(units.per_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_gigameter) @property def per_megameter(self) -> T: - return self.quantity.in_units_of(units.per_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_megameter) @property def per_kilometer(self) -> T: - return self.quantity.in_units_of(units.per_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_kilometer) @property def per_millimeter(self) -> T: - return self.quantity.in_units_of(units.per_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_millimeter) @property def per_micrometer(self) -> T: - return self.quantity.in_units_of(units.per_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_micrometer) @property def per_nanometer(self) -> T: - return self.quantity.in_units_of(units.per_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_nanometer) @property def per_picometer(self) -> T: - return self.quantity.in_units_of(units.per_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_picometer) @property def per_femtometer(self) -> T: - return self.quantity.in_units_of(units.per_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_femtometer) @property def per_attometer(self) -> T: - return self.quantity.in_units_of(units.per_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_attometer) @property def per_decimeter(self) -> T: - return self.quantity.in_units_of(units.per_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_decimeter) @property def per_centimeter(self) -> T: - return self.quantity.in_units_of(units.per_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_centimeter) @property def per_angstrom(self) -> T: - return self.quantity.in_units_of(units.per_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_angstrom) @property def per_mile(self) -> T: - return self.quantity.in_units_of(units.per_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_mile) @property def per_yard(self) -> T: - return self.quantity.in_units_of(units.per_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_yard) @property def per_foot(self) -> T: - return self.quantity.in_units_of(units.per_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_foot) @property def per_inch(self) -> T: - return self.quantity.in_units_of(units.per_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_inch) -class InverseareaAccessor[T](Accessor[T]): +class InverseareaAccessor[T](QuantityAccessor[T]): dimension_name = 'inverse_area' @property def per_square_meter(self) -> T: - return self.quantity.in_units_of(units.per_square_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_meter) @property def per_square_exameter(self) -> T: - return self.quantity.in_units_of(units.per_square_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_exameter) @property def per_square_petameter(self) -> T: - return self.quantity.in_units_of(units.per_square_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_petameter) @property def per_square_terameter(self) -> T: - return self.quantity.in_units_of(units.per_square_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_terameter) @property def per_square_gigameter(self) -> T: - return self.quantity.in_units_of(units.per_square_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_gigameter) @property def per_square_megameter(self) -> T: - return self.quantity.in_units_of(units.per_square_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_megameter) @property def per_square_kilometer(self) -> T: - return self.quantity.in_units_of(units.per_square_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_kilometer) @property def per_square_millimeter(self) -> T: - return self.quantity.in_units_of(units.per_square_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_millimeter) @property def per_square_micrometer(self) -> T: - return self.quantity.in_units_of(units.per_square_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_micrometer) @property def per_square_nanometer(self) -> T: - return self.quantity.in_units_of(units.per_square_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_nanometer) @property def per_square_picometer(self) -> T: - return self.quantity.in_units_of(units.per_square_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_picometer) @property def per_square_femtometer(self) -> T: - return self.quantity.in_units_of(units.per_square_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_femtometer) @property def per_square_attometer(self) -> T: - return self.quantity.in_units_of(units.per_square_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_attometer) @property def per_square_decimeter(self) -> T: - return self.quantity.in_units_of(units.per_square_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_decimeter) @property def per_square_centimeter(self) -> T: - return self.quantity.in_units_of(units.per_square_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_centimeter) @property def per_square_angstrom(self) -> T: - return self.quantity.in_units_of(units.per_square_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_angstrom) @property def per_square_mile(self) -> T: - return self.quantity.in_units_of(units.per_square_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_mile) @property def per_square_yard(self) -> T: - return self.quantity.in_units_of(units.per_square_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_yard) @property def per_square_foot(self) -> T: - return self.quantity.in_units_of(units.per_square_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_foot) @property def per_square_inch(self) -> T: - return self.quantity.in_units_of(units.per_square_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_inch) -class InversevolumeAccessor[T](Accessor[T]): +class InversevolumeAccessor[T](QuantityAccessor[T]): dimension_name = 'inverse_volume' @property def per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_meter) @property def per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_exameter) @property def per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_petameter) @property def per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_terameter) @property def per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_gigameter) @property def per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_megameter) @property def per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_kilometer) @property def per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_millimeter) @property def per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_micrometer) @property def per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_nanometer) @property def per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_picometer) @property def per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_femtometer) @property def per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_attometer) @property def per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_decimeter) @property def per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_centimeter) @property def per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_angstrom) @property def per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_mile) @property def per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_yard) @property def per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_foot) @property def per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_inch) -class TimeAccessor[T](Accessor[T]): +class TimeAccessor[T](QuantityAccessor[T]): dimension_name = 'time' @property def seconds(self) -> T: - return self.quantity.in_units_of(units.seconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.seconds) @property def milliseconds(self) -> T: - return self.quantity.in_units_of(units.milliseconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milliseconds) @property def microseconds(self) -> T: - return self.quantity.in_units_of(units.microseconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microseconds) @property def nanoseconds(self) -> T: - return self.quantity.in_units_of(units.nanoseconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanoseconds) @property def picoseconds(self) -> T: - return self.quantity.in_units_of(units.picoseconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picoseconds) @property def femtoseconds(self) -> T: - return self.quantity.in_units_of(units.femtoseconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtoseconds) @property def attoseconds(self) -> T: - return self.quantity.in_units_of(units.attoseconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attoseconds) @property def minutes(self) -> T: - return self.quantity.in_units_of(units.minutes) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.minutes) @property def hours(self) -> T: - return self.quantity.in_units_of(units.hours) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.hours) @property def days(self) -> T: - return self.quantity.in_units_of(units.days) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.days) @property def years(self) -> T: - return self.quantity.in_units_of(units.years) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.years) -class RateAccessor[T](Accessor[T]): +class RateAccessor[T](QuantityAccessor[T]): dimension_name = 'rate' @property def hertz(self) -> T: - return self.quantity.in_units_of(units.hertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.hertz) @property def exahertz(self) -> T: - return self.quantity.in_units_of(units.exahertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exahertz) @property def petahertz(self) -> T: - return self.quantity.in_units_of(units.petahertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petahertz) @property def terahertz(self) -> T: - return self.quantity.in_units_of(units.terahertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terahertz) @property def gigahertz(self) -> T: - return self.quantity.in_units_of(units.gigahertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigahertz) @property def megahertz(self) -> T: - return self.quantity.in_units_of(units.megahertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megahertz) @property def kilohertz(self) -> T: - return self.quantity.in_units_of(units.kilohertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilohertz) @property def millihertz(self) -> T: - return self.quantity.in_units_of(units.millihertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millihertz) @property def microhertz(self) -> T: - return self.quantity.in_units_of(units.microhertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microhertz) @property def nanohertz(self) -> T: - return self.quantity.in_units_of(units.nanohertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanohertz) @property def picohertz(self) -> T: - return self.quantity.in_units_of(units.picohertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picohertz) @property def femtohertz(self) -> T: - return self.quantity.in_units_of(units.femtohertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtohertz) @property def attohertz(self) -> T: - return self.quantity.in_units_of(units.attohertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attohertz) -class SpeedAccessor[T](Accessor[T]): +class SpeedAccessor[T](QuantityAccessor[T]): dimension_name = 'speed' @property def meters_per_second(self) -> T: - return self.quantity.in_units_of(units.meters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_second) @property def meters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_millisecond) @property def meters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_microsecond) @property def meters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_nanosecond) @property def meters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_picosecond) @property def meters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_femtosecond) @property def meters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_attosecond) @property def meters_per_minute(self) -> T: - return self.quantity.in_units_of(units.meters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_minute) @property def meters_per_hour(self) -> T: - return self.quantity.in_units_of(units.meters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_hour) @property def meters_per_day(self) -> T: - return self.quantity.in_units_of(units.meters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_day) @property def meters_per_year(self) -> T: - return self.quantity.in_units_of(units.meters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_year) @property def exameters_per_second(self) -> T: - return self.quantity.in_units_of(units.exameters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_second) @property def exameters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_millisecond) @property def exameters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_microsecond) @property def exameters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_nanosecond) @property def exameters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_picosecond) @property def exameters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_femtosecond) @property def exameters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_attosecond) @property def exameters_per_minute(self) -> T: - return self.quantity.in_units_of(units.exameters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_minute) @property def exameters_per_hour(self) -> T: - return self.quantity.in_units_of(units.exameters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_hour) @property def exameters_per_day(self) -> T: - return self.quantity.in_units_of(units.exameters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_day) @property def exameters_per_year(self) -> T: - return self.quantity.in_units_of(units.exameters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_year) @property def petameters_per_second(self) -> T: - return self.quantity.in_units_of(units.petameters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_second) @property def petameters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_millisecond) @property def petameters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_microsecond) @property def petameters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_nanosecond) @property def petameters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_picosecond) @property def petameters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_femtosecond) @property def petameters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_attosecond) @property def petameters_per_minute(self) -> T: - return self.quantity.in_units_of(units.petameters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_minute) @property def petameters_per_hour(self) -> T: - return self.quantity.in_units_of(units.petameters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_hour) @property def petameters_per_day(self) -> T: - return self.quantity.in_units_of(units.petameters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_day) @property def petameters_per_year(self) -> T: - return self.quantity.in_units_of(units.petameters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_year) @property def terameters_per_second(self) -> T: - return self.quantity.in_units_of(units.terameters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_second) @property def terameters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_millisecond) @property def terameters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_microsecond) @property def terameters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_nanosecond) @property def terameters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_picosecond) @property def terameters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_femtosecond) @property def terameters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_attosecond) @property def terameters_per_minute(self) -> T: - return self.quantity.in_units_of(units.terameters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_minute) @property def terameters_per_hour(self) -> T: - return self.quantity.in_units_of(units.terameters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_hour) @property def terameters_per_day(self) -> T: - return self.quantity.in_units_of(units.terameters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_day) @property def terameters_per_year(self) -> T: - return self.quantity.in_units_of(units.terameters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_year) @property def gigameters_per_second(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_second) @property def gigameters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_millisecond) @property def gigameters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_microsecond) @property def gigameters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_nanosecond) @property def gigameters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_picosecond) @property def gigameters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_femtosecond) @property def gigameters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_attosecond) @property def gigameters_per_minute(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_minute) @property def gigameters_per_hour(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_hour) @property def gigameters_per_day(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_day) @property def gigameters_per_year(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_year) @property def megameters_per_second(self) -> T: - return self.quantity.in_units_of(units.megameters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_second) @property def megameters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_millisecond) @property def megameters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_microsecond) @property def megameters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_nanosecond) @property def megameters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_picosecond) @property def megameters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_femtosecond) @property def megameters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_attosecond) @property def megameters_per_minute(self) -> T: - return self.quantity.in_units_of(units.megameters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_minute) @property def megameters_per_hour(self) -> T: - return self.quantity.in_units_of(units.megameters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_hour) @property def megameters_per_day(self) -> T: - return self.quantity.in_units_of(units.megameters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_day) @property def megameters_per_year(self) -> T: - return self.quantity.in_units_of(units.megameters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_year) @property def kilometers_per_second(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_second) @property def kilometers_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_millisecond) @property def kilometers_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_microsecond) @property def kilometers_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_nanosecond) @property def kilometers_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_picosecond) @property def kilometers_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_femtosecond) @property def kilometers_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_attosecond) @property def kilometers_per_minute(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_minute) @property def kilometers_per_hour(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_hour) @property def kilometers_per_day(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_day) @property def kilometers_per_year(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_year) @property def millimeters_per_second(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_second) @property def millimeters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_millisecond) @property def millimeters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_microsecond) @property def millimeters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_nanosecond) @property def millimeters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_picosecond) @property def millimeters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_femtosecond) @property def millimeters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_attosecond) @property def millimeters_per_minute(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_minute) @property def millimeters_per_hour(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_hour) @property def millimeters_per_day(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_day) @property def millimeters_per_year(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_year) @property def micrometers_per_second(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_second) @property def micrometers_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_millisecond) @property def micrometers_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_microsecond) @property def micrometers_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_nanosecond) @property def micrometers_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_picosecond) @property def micrometers_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_femtosecond) @property def micrometers_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_attosecond) @property def micrometers_per_minute(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_minute) @property def micrometers_per_hour(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_hour) @property def micrometers_per_day(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_day) @property def micrometers_per_year(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_year) @property def nanometers_per_second(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_second) @property def nanometers_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_millisecond) @property def nanometers_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_microsecond) @property def nanometers_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_nanosecond) @property def nanometers_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_picosecond) @property def nanometers_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_femtosecond) @property def nanometers_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_attosecond) @property def nanometers_per_minute(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_minute) @property def nanometers_per_hour(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_hour) @property def nanometers_per_day(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_day) @property def nanometers_per_year(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_year) @property def picometers_per_second(self) -> T: - return self.quantity.in_units_of(units.picometers_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_second) @property def picometers_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_millisecond) @property def picometers_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_microsecond) @property def picometers_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_nanosecond) @property def picometers_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_picosecond) @property def picometers_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_femtosecond) @property def picometers_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_attosecond) @property def picometers_per_minute(self) -> T: - return self.quantity.in_units_of(units.picometers_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_minute) @property def picometers_per_hour(self) -> T: - return self.quantity.in_units_of(units.picometers_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_hour) @property def picometers_per_day(self) -> T: - return self.quantity.in_units_of(units.picometers_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_day) @property def picometers_per_year(self) -> T: - return self.quantity.in_units_of(units.picometers_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_year) @property def femtometers_per_second(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_second) @property def femtometers_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_millisecond) @property def femtometers_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_microsecond) @property def femtometers_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_nanosecond) @property def femtometers_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_picosecond) @property def femtometers_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_femtosecond) @property def femtometers_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_attosecond) @property def femtometers_per_minute(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_minute) @property def femtometers_per_hour(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_hour) @property def femtometers_per_day(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_day) @property def femtometers_per_year(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_year) @property def attometers_per_second(self) -> T: - return self.quantity.in_units_of(units.attometers_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_second) @property def attometers_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_millisecond) @property def attometers_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_microsecond) @property def attometers_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_nanosecond) @property def attometers_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_picosecond) @property def attometers_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_femtosecond) @property def attometers_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_attosecond) @property def attometers_per_minute(self) -> T: - return self.quantity.in_units_of(units.attometers_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_minute) @property def attometers_per_hour(self) -> T: - return self.quantity.in_units_of(units.attometers_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_hour) @property def attometers_per_day(self) -> T: - return self.quantity.in_units_of(units.attometers_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_day) @property def attometers_per_year(self) -> T: - return self.quantity.in_units_of(units.attometers_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_year) @property def decimeters_per_second(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_second) @property def decimeters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_millisecond) @property def decimeters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_microsecond) @property def decimeters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_nanosecond) @property def decimeters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_picosecond) @property def decimeters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_femtosecond) @property def decimeters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_attosecond) @property def decimeters_per_minute(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_minute) @property def decimeters_per_hour(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_hour) @property def decimeters_per_day(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_day) @property def decimeters_per_year(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_year) @property def centimeters_per_second(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_second) @property def centimeters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_millisecond) @property def centimeters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_microsecond) @property def centimeters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_nanosecond) @property def centimeters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_picosecond) @property def centimeters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_femtosecond) @property def centimeters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_attosecond) @property def centimeters_per_minute(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_minute) @property def centimeters_per_hour(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_hour) @property def centimeters_per_day(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_day) @property def centimeters_per_year(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_year) @property def angstroms_per_second(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_second) @property def angstroms_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_millisecond) @property def angstroms_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_microsecond) @property def angstroms_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_nanosecond) @property def angstroms_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_picosecond) @property def angstroms_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_femtosecond) @property def angstroms_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_attosecond) @property def angstroms_per_minute(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_minute) @property def angstroms_per_hour(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_hour) @property def angstroms_per_day(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_day) @property def angstroms_per_year(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_year) @property def miles_per_second(self) -> T: - return self.quantity.in_units_of(units.miles_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_second) @property def miles_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_millisecond) @property def miles_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_microsecond) @property def miles_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_nanosecond) @property def miles_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_picosecond) @property def miles_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_femtosecond) @property def miles_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_attosecond) @property def miles_per_minute(self) -> T: - return self.quantity.in_units_of(units.miles_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_minute) @property def miles_per_hour(self) -> T: - return self.quantity.in_units_of(units.miles_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_hour) @property def miles_per_day(self) -> T: - return self.quantity.in_units_of(units.miles_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_day) @property def miles_per_year(self) -> T: - return self.quantity.in_units_of(units.miles_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_year) @property def yards_per_second(self) -> T: - return self.quantity.in_units_of(units.yards_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_second) @property def yards_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_millisecond) @property def yards_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_microsecond) @property def yards_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_nanosecond) @property def yards_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_picosecond) @property def yards_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_femtosecond) @property def yards_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_attosecond) @property def yards_per_minute(self) -> T: - return self.quantity.in_units_of(units.yards_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_minute) @property def yards_per_hour(self) -> T: - return self.quantity.in_units_of(units.yards_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_hour) @property def yards_per_day(self) -> T: - return self.quantity.in_units_of(units.yards_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_day) @property def yards_per_year(self) -> T: - return self.quantity.in_units_of(units.yards_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_year) @property def feet_per_second(self) -> T: - return self.quantity.in_units_of(units.feet_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_second) @property def feet_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_millisecond) @property def feet_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_microsecond) @property def feet_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_nanosecond) @property def feet_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_picosecond) @property def feet_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_femtosecond) @property def feet_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_attosecond) @property def feet_per_minute(self) -> T: - return self.quantity.in_units_of(units.feet_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_minute) @property def feet_per_hour(self) -> T: - return self.quantity.in_units_of(units.feet_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_hour) @property def feet_per_day(self) -> T: - return self.quantity.in_units_of(units.feet_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_day) @property def feet_per_year(self) -> T: - return self.quantity.in_units_of(units.feet_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_year) @property def inches_per_second(self) -> T: - return self.quantity.in_units_of(units.inches_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_second) @property def inches_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_millisecond) @property def inches_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_microsecond) @property def inches_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_nanosecond) @property def inches_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_picosecond) @property def inches_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_femtosecond) @property def inches_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_attosecond) @property def inches_per_minute(self) -> T: - return self.quantity.in_units_of(units.inches_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_minute) @property def inches_per_hour(self) -> T: - return self.quantity.in_units_of(units.inches_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_hour) @property def inches_per_day(self) -> T: - return self.quantity.in_units_of(units.inches_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_day) @property def inches_per_year(self) -> T: - return self.quantity.in_units_of(units.inches_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_year) -class AccelerationAccessor[T](Accessor[T]): +class AccelerationAccessor[T](QuantityAccessor[T]): dimension_name = 'acceleration' @property def meters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_second) @property def meters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_millisecond) @property def meters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_microsecond) @property def meters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_nanosecond) @property def meters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_picosecond) @property def meters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_femtosecond) @property def meters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_attosecond) @property def meters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_minute) @property def meters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_hour) @property def meters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_day) @property def meters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_year) @property def exameters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_second) @property def exameters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_millisecond) @property def exameters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_microsecond) @property def exameters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_nanosecond) @property def exameters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_picosecond) @property def exameters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_femtosecond) @property def exameters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_attosecond) @property def exameters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_minute) @property def exameters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_hour) @property def exameters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_day) @property def exameters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_year) @property def petameters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_second) @property def petameters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_millisecond) @property def petameters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_microsecond) @property def petameters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_nanosecond) @property def petameters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_picosecond) @property def petameters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_femtosecond) @property def petameters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_attosecond) @property def petameters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_minute) @property def petameters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_hour) @property def petameters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_day) @property def petameters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_year) @property def terameters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_second) @property def terameters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_millisecond) @property def terameters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_microsecond) @property def terameters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_nanosecond) @property def terameters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_picosecond) @property def terameters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_femtosecond) @property def terameters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_attosecond) @property def terameters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_minute) @property def terameters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_hour) @property def terameters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_day) @property def terameters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_year) @property def gigameters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_second) @property def gigameters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_millisecond) @property def gigameters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_microsecond) @property def gigameters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_nanosecond) @property def gigameters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_picosecond) @property def gigameters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_femtosecond) @property def gigameters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_attosecond) @property def gigameters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_minute) @property def gigameters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_hour) @property def gigameters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_day) @property def gigameters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_year) @property def megameters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_second) @property def megameters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_millisecond) @property def megameters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_microsecond) @property def megameters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_nanosecond) @property def megameters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_picosecond) @property def megameters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_femtosecond) @property def megameters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_attosecond) @property def megameters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_minute) @property def megameters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_hour) @property def megameters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_day) @property def megameters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_year) @property def kilometers_per_square_second(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_second) @property def kilometers_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_millisecond) @property def kilometers_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_microsecond) @property def kilometers_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_nanosecond) @property def kilometers_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_picosecond) @property def kilometers_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_femtosecond) @property def kilometers_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_attosecond) @property def kilometers_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_minute) @property def kilometers_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_hour) @property def kilometers_per_square_day(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_day) @property def kilometers_per_square_year(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_year) @property def millimeters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_second) @property def millimeters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_millisecond) @property def millimeters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_microsecond) @property def millimeters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_nanosecond) @property def millimeters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_picosecond) @property def millimeters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_femtosecond) @property def millimeters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_attosecond) @property def millimeters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_minute) @property def millimeters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_hour) @property def millimeters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_day) @property def millimeters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_year) @property def micrometers_per_square_second(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_second) @property def micrometers_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_millisecond) @property def micrometers_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_microsecond) @property def micrometers_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_nanosecond) @property def micrometers_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_picosecond) @property def micrometers_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_femtosecond) @property def micrometers_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_attosecond) @property def micrometers_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_minute) @property def micrometers_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_hour) @property def micrometers_per_square_day(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_day) @property def micrometers_per_square_year(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_year) @property def nanometers_per_square_second(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_second) @property def nanometers_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_millisecond) @property def nanometers_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_microsecond) @property def nanometers_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_nanosecond) @property def nanometers_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_picosecond) @property def nanometers_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_femtosecond) @property def nanometers_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_attosecond) @property def nanometers_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_minute) @property def nanometers_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_hour) @property def nanometers_per_square_day(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_day) @property def nanometers_per_square_year(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_year) @property def picometers_per_square_second(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_second) @property def picometers_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_millisecond) @property def picometers_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_microsecond) @property def picometers_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_nanosecond) @property def picometers_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_picosecond) @property def picometers_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_femtosecond) @property def picometers_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_attosecond) @property def picometers_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_minute) @property def picometers_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_hour) @property def picometers_per_square_day(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_day) @property def picometers_per_square_year(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_year) @property def femtometers_per_square_second(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_second) @property def femtometers_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_millisecond) @property def femtometers_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_microsecond) @property def femtometers_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_nanosecond) @property def femtometers_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_picosecond) @property def femtometers_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_femtosecond) @property def femtometers_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_attosecond) @property def femtometers_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_minute) @property def femtometers_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_hour) @property def femtometers_per_square_day(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_day) @property def femtometers_per_square_year(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_year) @property def attometers_per_square_second(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_second) @property def attometers_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_millisecond) @property def attometers_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_microsecond) @property def attometers_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_nanosecond) @property def attometers_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_picosecond) @property def attometers_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_femtosecond) @property def attometers_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_attosecond) @property def attometers_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_minute) @property def attometers_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_hour) @property def attometers_per_square_day(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_day) @property def attometers_per_square_year(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_year) @property def decimeters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_second) @property def decimeters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_millisecond) @property def decimeters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_microsecond) @property def decimeters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_nanosecond) @property def decimeters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_picosecond) @property def decimeters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_femtosecond) @property def decimeters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_attosecond) @property def decimeters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_minute) @property def decimeters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_hour) @property def decimeters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_day) @property def decimeters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_year) @property def centimeters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_second) @property def centimeters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_millisecond) @property def centimeters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_microsecond) @property def centimeters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_nanosecond) @property def centimeters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_picosecond) @property def centimeters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_femtosecond) @property def centimeters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_attosecond) @property def centimeters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_minute) @property def centimeters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_hour) @property def centimeters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_day) @property def centimeters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_year) @property def angstroms_per_square_second(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_second) @property def angstroms_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_millisecond) @property def angstroms_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_microsecond) @property def angstroms_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_nanosecond) @property def angstroms_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_picosecond) @property def angstroms_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_femtosecond) @property def angstroms_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_attosecond) @property def angstroms_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_minute) @property def angstroms_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_hour) @property def angstroms_per_square_day(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_day) @property def angstroms_per_square_year(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_year) @property def miles_per_square_second(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_second) @property def miles_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_millisecond) @property def miles_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_microsecond) @property def miles_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_nanosecond) @property def miles_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_picosecond) @property def miles_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_femtosecond) @property def miles_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_attosecond) @property def miles_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_minute) @property def miles_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_hour) @property def miles_per_square_day(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_day) @property def miles_per_square_year(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_year) @property def yards_per_square_second(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_second) @property def yards_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_millisecond) @property def yards_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_microsecond) @property def yards_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_nanosecond) @property def yards_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_picosecond) @property def yards_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_femtosecond) @property def yards_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_attosecond) @property def yards_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_minute) @property def yards_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_hour) @property def yards_per_square_day(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_day) @property def yards_per_square_year(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_year) @property def feet_per_square_second(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_second) @property def feet_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_millisecond) @property def feet_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_microsecond) @property def feet_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_nanosecond) @property def feet_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_picosecond) @property def feet_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_femtosecond) @property def feet_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_attosecond) @property def feet_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_minute) @property def feet_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_hour) @property def feet_per_square_day(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_day) @property def feet_per_square_year(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_year) @property def inches_per_square_second(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_second) @property def inches_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_millisecond) @property def inches_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_microsecond) @property def inches_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_nanosecond) @property def inches_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_picosecond) @property def inches_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_femtosecond) @property def inches_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_attosecond) @property def inches_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_minute) @property def inches_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_hour) @property def inches_per_square_day(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_day) @property def inches_per_square_year(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_year) -class DensityAccessor[T](Accessor[T]): +class DensityAccessor[T](QuantityAccessor[T]): dimension_name = 'density' @property def grams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_meter) @property def exagrams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_meter) @property def petagrams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_meter) @property def teragrams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_meter) @property def gigagrams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_meter) @property def megagrams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_meter) @property def kilograms_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_meter) @property def milligrams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_meter) @property def micrograms_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_meter) @property def nanograms_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_meter) @property def picograms_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_meter) @property def femtograms_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_meter) @property def attograms_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_meter) @property def atomic_mass_units_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_meter) @property def pounds_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_meter) @property def ounces_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_meter) @property def grams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_exameter) @property def exagrams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_exameter) @property def petagrams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_exameter) @property def teragrams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_exameter) @property def gigagrams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_exameter) @property def megagrams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_exameter) @property def kilograms_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_exameter) @property def milligrams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_exameter) @property def micrograms_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_exameter) @property def nanograms_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_exameter) @property def picograms_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_exameter) @property def femtograms_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_exameter) @property def attograms_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_exameter) @property def atomic_mass_units_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_exameter) @property def pounds_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_exameter) @property def ounces_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_exameter) @property def grams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_petameter) @property def exagrams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_petameter) @property def petagrams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_petameter) @property def teragrams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_petameter) @property def gigagrams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_petameter) @property def megagrams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_petameter) @property def kilograms_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_petameter) @property def milligrams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_petameter) @property def micrograms_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_petameter) @property def nanograms_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_petameter) @property def picograms_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_petameter) @property def femtograms_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_petameter) @property def attograms_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_petameter) @property def atomic_mass_units_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_petameter) @property def pounds_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_petameter) @property def ounces_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_petameter) @property def grams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_terameter) @property def exagrams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_terameter) @property def petagrams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_terameter) @property def teragrams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_terameter) @property def gigagrams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_terameter) @property def megagrams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_terameter) @property def kilograms_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_terameter) @property def milligrams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_terameter) @property def micrograms_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_terameter) @property def nanograms_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_terameter) @property def picograms_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_terameter) @property def femtograms_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_terameter) @property def attograms_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_terameter) @property def atomic_mass_units_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_terameter) @property def pounds_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_terameter) @property def ounces_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_terameter) @property def grams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_gigameter) @property def exagrams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_gigameter) @property def petagrams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_gigameter) @property def teragrams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_gigameter) @property def gigagrams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_gigameter) @property def megagrams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_gigameter) @property def kilograms_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_gigameter) @property def milligrams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_gigameter) @property def micrograms_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_gigameter) @property def nanograms_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_gigameter) @property def picograms_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_gigameter) @property def femtograms_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_gigameter) @property def attograms_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_gigameter) @property def atomic_mass_units_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_gigameter) @property def pounds_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_gigameter) @property def ounces_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_gigameter) @property def grams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_megameter) @property def exagrams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_megameter) @property def petagrams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_megameter) @property def teragrams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_megameter) @property def gigagrams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_megameter) @property def megagrams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_megameter) @property def kilograms_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_megameter) @property def milligrams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_megameter) @property def micrograms_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_megameter) @property def nanograms_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_megameter) @property def picograms_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_megameter) @property def femtograms_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_megameter) @property def attograms_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_megameter) @property def atomic_mass_units_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_megameter) @property def pounds_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_megameter) @property def ounces_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_megameter) @property def grams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_kilometer) @property def exagrams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_kilometer) @property def petagrams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_kilometer) @property def teragrams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_kilometer) @property def gigagrams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_kilometer) @property def megagrams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_kilometer) @property def kilograms_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_kilometer) @property def milligrams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_kilometer) @property def micrograms_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_kilometer) @property def nanograms_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_kilometer) @property def picograms_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_kilometer) @property def femtograms_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_kilometer) @property def attograms_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_kilometer) @property def atomic_mass_units_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_kilometer) @property def pounds_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_kilometer) @property def ounces_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_kilometer) @property def grams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_millimeter) @property def exagrams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_millimeter) @property def petagrams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_millimeter) @property def teragrams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_millimeter) @property def gigagrams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_millimeter) @property def megagrams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_millimeter) @property def kilograms_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_millimeter) @property def milligrams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_millimeter) @property def micrograms_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_millimeter) @property def nanograms_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_millimeter) @property def picograms_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_millimeter) @property def femtograms_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_millimeter) @property def attograms_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_millimeter) @property def atomic_mass_units_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_millimeter) @property def pounds_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_millimeter) @property def ounces_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_millimeter) @property def grams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_micrometer) @property def exagrams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_micrometer) @property def petagrams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_micrometer) @property def teragrams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_micrometer) @property def gigagrams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_micrometer) @property def megagrams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_micrometer) @property def kilograms_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_micrometer) @property def milligrams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_micrometer) @property def micrograms_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_micrometer) @property def nanograms_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_micrometer) @property def picograms_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_micrometer) @property def femtograms_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_micrometer) @property def attograms_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_micrometer) @property def atomic_mass_units_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_micrometer) @property def pounds_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_micrometer) @property def ounces_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_micrometer) @property def grams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_nanometer) @property def exagrams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_nanometer) @property def petagrams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_nanometer) @property def teragrams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_nanometer) @property def gigagrams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_nanometer) @property def megagrams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_nanometer) @property def kilograms_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_nanometer) @property def milligrams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_nanometer) @property def micrograms_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_nanometer) @property def nanograms_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_nanometer) @property def picograms_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_nanometer) @property def femtograms_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_nanometer) @property def attograms_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_nanometer) @property def atomic_mass_units_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_nanometer) @property def pounds_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_nanometer) @property def ounces_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_nanometer) @property def grams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_picometer) @property def exagrams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_picometer) @property def petagrams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_picometer) @property def teragrams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_picometer) @property def gigagrams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_picometer) @property def megagrams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_picometer) @property def kilograms_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_picometer) @property def milligrams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_picometer) @property def micrograms_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_picometer) @property def nanograms_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_picometer) @property def picograms_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_picometer) @property def femtograms_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_picometer) @property def attograms_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_picometer) @property def atomic_mass_units_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_picometer) @property def pounds_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_picometer) @property def ounces_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_picometer) @property def grams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_femtometer) @property def exagrams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_femtometer) @property def petagrams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_femtometer) @property def teragrams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_femtometer) @property def gigagrams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_femtometer) @property def megagrams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_femtometer) @property def kilograms_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_femtometer) @property def milligrams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_femtometer) @property def micrograms_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_femtometer) @property def nanograms_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_femtometer) @property def picograms_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_femtometer) @property def femtograms_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_femtometer) @property def attograms_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_femtometer) @property def atomic_mass_units_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_femtometer) @property def pounds_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_femtometer) @property def ounces_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_femtometer) @property def grams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_attometer) @property def exagrams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_attometer) @property def petagrams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_attometer) @property def teragrams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_attometer) @property def gigagrams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_attometer) @property def megagrams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_attometer) @property def kilograms_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_attometer) @property def milligrams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_attometer) @property def micrograms_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_attometer) @property def nanograms_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_attometer) @property def picograms_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_attometer) @property def femtograms_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_attometer) @property def attograms_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_attometer) @property def atomic_mass_units_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_attometer) @property def pounds_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_attometer) @property def ounces_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_attometer) @property def grams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_decimeter) @property def exagrams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_decimeter) @property def petagrams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_decimeter) @property def teragrams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_decimeter) @property def gigagrams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_decimeter) @property def megagrams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_decimeter) @property def kilograms_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_decimeter) @property def milligrams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_decimeter) @property def micrograms_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_decimeter) @property def nanograms_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_decimeter) @property def picograms_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_decimeter) @property def femtograms_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_decimeter) @property def attograms_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_decimeter) @property def atomic_mass_units_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_decimeter) @property def pounds_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_decimeter) @property def ounces_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_decimeter) @property def grams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_centimeter) @property def exagrams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_centimeter) @property def petagrams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_centimeter) @property def teragrams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_centimeter) @property def gigagrams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_centimeter) @property def megagrams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_centimeter) @property def kilograms_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_centimeter) @property def milligrams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_centimeter) @property def micrograms_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_centimeter) @property def nanograms_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_centimeter) @property def picograms_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_centimeter) @property def femtograms_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_centimeter) @property def attograms_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_centimeter) @property def atomic_mass_units_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_centimeter) @property def pounds_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_centimeter) @property def ounces_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_centimeter) @property def grams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_angstrom) @property def exagrams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_angstrom) @property def petagrams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_angstrom) @property def teragrams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_angstrom) @property def gigagrams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_angstrom) @property def megagrams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_angstrom) @property def kilograms_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_angstrom) @property def milligrams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_angstrom) @property def micrograms_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_angstrom) @property def nanograms_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_angstrom) @property def picograms_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_angstrom) @property def femtograms_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_angstrom) @property def attograms_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_angstrom) @property def atomic_mass_units_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_angstrom) @property def pounds_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_angstrom) @property def ounces_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_angstrom) @property def grams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_mile) @property def exagrams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_mile) @property def petagrams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_mile) @property def teragrams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_mile) @property def gigagrams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_mile) @property def megagrams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_mile) @property def kilograms_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_mile) @property def milligrams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_mile) @property def micrograms_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_mile) @property def nanograms_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_mile) @property def picograms_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_mile) @property def femtograms_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_mile) @property def attograms_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_mile) @property def atomic_mass_units_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_mile) @property def pounds_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_mile) @property def ounces_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_mile) @property def grams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_yard) @property def exagrams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_yard) @property def petagrams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_yard) @property def teragrams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_yard) @property def gigagrams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_yard) @property def megagrams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_yard) @property def kilograms_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_yard) @property def milligrams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_yard) @property def micrograms_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_yard) @property def nanograms_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_yard) @property def picograms_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_yard) @property def femtograms_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_yard) @property def attograms_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_yard) @property def atomic_mass_units_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_yard) @property def pounds_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_yard) @property def ounces_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_yard) @property def grams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_foot) @property def exagrams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_foot) @property def petagrams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_foot) @property def teragrams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_foot) @property def gigagrams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_foot) @property def megagrams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_foot) @property def kilograms_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_foot) @property def milligrams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_foot) @property def micrograms_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_foot) @property def nanograms_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_foot) @property def picograms_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_foot) @property def femtograms_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_foot) @property def attograms_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_foot) @property def atomic_mass_units_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_foot) @property def pounds_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_foot) @property def ounces_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_foot) @property def grams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_inch) @property def exagrams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_inch) @property def petagrams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_inch) @property def teragrams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_inch) @property def gigagrams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_inch) @property def megagrams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_inch) @property def kilograms_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_inch) @property def milligrams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_inch) @property def micrograms_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_inch) @property def nanograms_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_inch) @property def picograms_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_inch) @property def femtograms_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_inch) @property def attograms_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_inch) @property def atomic_mass_units_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_inch) @property def pounds_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_inch) @property def ounces_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_inch) -class ForceAccessor[T](Accessor[T]): +class ForceAccessor[T](QuantityAccessor[T]): dimension_name = 'force' @property def newtons(self) -> T: - return self.quantity.in_units_of(units.newtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.newtons) @property def exanewtons(self) -> T: - return self.quantity.in_units_of(units.exanewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exanewtons) @property def petanewtons(self) -> T: - return self.quantity.in_units_of(units.petanewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petanewtons) @property def teranewtons(self) -> T: - return self.quantity.in_units_of(units.teranewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teranewtons) @property def giganewtons(self) -> T: - return self.quantity.in_units_of(units.giganewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.giganewtons) @property def meganewtons(self) -> T: - return self.quantity.in_units_of(units.meganewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meganewtons) @property def kilonewtons(self) -> T: - return self.quantity.in_units_of(units.kilonewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilonewtons) @property def millinewtons(self) -> T: - return self.quantity.in_units_of(units.millinewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millinewtons) @property def micronewtons(self) -> T: - return self.quantity.in_units_of(units.micronewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micronewtons) @property def nanonewtons(self) -> T: - return self.quantity.in_units_of(units.nanonewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanonewtons) @property def piconewtons(self) -> T: - return self.quantity.in_units_of(units.piconewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.piconewtons) @property def femtonewtons(self) -> T: - return self.quantity.in_units_of(units.femtonewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtonewtons) @property def attonewtons(self) -> T: - return self.quantity.in_units_of(units.attonewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attonewtons) @property def kg_force(self) -> T: - return self.quantity.in_units_of(units.kg_force) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kg_force) @property def pounds_force(self) -> T: - return self.quantity.in_units_of(units.pounds_force) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_force) -class PressureAccessor[T](Accessor[T]): +class PressureAccessor[T](QuantityAccessor[T]): dimension_name = 'pressure' @property def pascals(self) -> T: - return self.quantity.in_units_of(units.pascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pascals) @property def exapascals(self) -> T: - return self.quantity.in_units_of(units.exapascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exapascals) @property def petapascals(self) -> T: - return self.quantity.in_units_of(units.petapascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petapascals) @property def terapascals(self) -> T: - return self.quantity.in_units_of(units.terapascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terapascals) @property def gigapascals(self) -> T: - return self.quantity.in_units_of(units.gigapascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigapascals) @property def megapascals(self) -> T: - return self.quantity.in_units_of(units.megapascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megapascals) @property def kilopascals(self) -> T: - return self.quantity.in_units_of(units.kilopascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilopascals) @property def millipascals(self) -> T: - return self.quantity.in_units_of(units.millipascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millipascals) @property def micropascals(self) -> T: - return self.quantity.in_units_of(units.micropascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micropascals) @property def nanopascals(self) -> T: - return self.quantity.in_units_of(units.nanopascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanopascals) @property def picopascals(self) -> T: - return self.quantity.in_units_of(units.picopascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picopascals) @property def femtopascals(self) -> T: - return self.quantity.in_units_of(units.femtopascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtopascals) @property def attopascals(self) -> T: - return self.quantity.in_units_of(units.attopascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attopascals) @property def pounds_force_per_square_inch(self) -> T: - return self.quantity.in_units_of(units.pounds_force_per_square_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_force_per_square_inch) -class EnergyAccessor[T](Accessor[T]): +class EnergyAccessor[T](QuantityAccessor[T]): dimension_name = 'energy' @property def joules(self) -> T: - return self.quantity.in_units_of(units.joules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.joules) @property def exajoules(self) -> T: - return self.quantity.in_units_of(units.exajoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exajoules) @property def petajoules(self) -> T: - return self.quantity.in_units_of(units.petajoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petajoules) @property def terajoules(self) -> T: - return self.quantity.in_units_of(units.terajoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terajoules) @property def gigajoules(self) -> T: - return self.quantity.in_units_of(units.gigajoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigajoules) @property def megajoules(self) -> T: - return self.quantity.in_units_of(units.megajoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megajoules) @property def kilojoules(self) -> T: - return self.quantity.in_units_of(units.kilojoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilojoules) @property def millijoules(self) -> T: - return self.quantity.in_units_of(units.millijoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millijoules) @property def microjoules(self) -> T: - return self.quantity.in_units_of(units.microjoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microjoules) @property def nanojoules(self) -> T: - return self.quantity.in_units_of(units.nanojoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanojoules) @property def picojoules(self) -> T: - return self.quantity.in_units_of(units.picojoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picojoules) @property def femtojoules(self) -> T: - return self.quantity.in_units_of(units.femtojoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtojoules) @property def attojoules(self) -> T: - return self.quantity.in_units_of(units.attojoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attojoules) @property def electronvolts(self) -> T: - return self.quantity.in_units_of(units.electronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.electronvolts) @property def exaelectronvolts(self) -> T: - return self.quantity.in_units_of(units.exaelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exaelectronvolts) @property def petaelectronvolts(self) -> T: - return self.quantity.in_units_of(units.petaelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petaelectronvolts) @property def teraelectronvolts(self) -> T: - return self.quantity.in_units_of(units.teraelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teraelectronvolts) @property def gigaelectronvolts(self) -> T: - return self.quantity.in_units_of(units.gigaelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigaelectronvolts) @property def megaelectronvolts(self) -> T: - return self.quantity.in_units_of(units.megaelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megaelectronvolts) @property def kiloelectronvolts(self) -> T: - return self.quantity.in_units_of(units.kiloelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kiloelectronvolts) @property def millielectronvolts(self) -> T: - return self.quantity.in_units_of(units.millielectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millielectronvolts) @property def microelectronvolts(self) -> T: - return self.quantity.in_units_of(units.microelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microelectronvolts) @property def nanoelectronvolts(self) -> T: - return self.quantity.in_units_of(units.nanoelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanoelectronvolts) @property def picoelectronvolts(self) -> T: - return self.quantity.in_units_of(units.picoelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picoelectronvolts) @property def femtoelectronvolts(self) -> T: - return self.quantity.in_units_of(units.femtoelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtoelectronvolts) @property def attoelectronvolts(self) -> T: - return self.quantity.in_units_of(units.attoelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attoelectronvolts) -class PowerAccessor[T](Accessor[T]): +class PowerAccessor[T](QuantityAccessor[T]): dimension_name = 'power' @property def watts(self) -> T: - return self.quantity.in_units_of(units.watts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.watts) @property def exawatts(self) -> T: - return self.quantity.in_units_of(units.exawatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exawatts) @property def petawatts(self) -> T: - return self.quantity.in_units_of(units.petawatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petawatts) @property def terawatts(self) -> T: - return self.quantity.in_units_of(units.terawatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terawatts) @property def gigawatts(self) -> T: - return self.quantity.in_units_of(units.gigawatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigawatts) @property def megawatts(self) -> T: - return self.quantity.in_units_of(units.megawatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megawatts) @property def kilowatts(self) -> T: - return self.quantity.in_units_of(units.kilowatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilowatts) @property def milliwatts(self) -> T: - return self.quantity.in_units_of(units.milliwatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milliwatts) @property def microwatts(self) -> T: - return self.quantity.in_units_of(units.microwatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microwatts) @property def nanowatts(self) -> T: - return self.quantity.in_units_of(units.nanowatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanowatts) @property def picowatts(self) -> T: - return self.quantity.in_units_of(units.picowatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picowatts) @property def femtowatts(self) -> T: - return self.quantity.in_units_of(units.femtowatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtowatts) @property def attowatts(self) -> T: - return self.quantity.in_units_of(units.attowatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attowatts) -class ChargeAccessor[T](Accessor[T]): +class ChargeAccessor[T](QuantityAccessor[T]): dimension_name = 'charge' @property def coulombs(self) -> T: - return self.quantity.in_units_of(units.coulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.coulombs) @property def exacoulombs(self) -> T: - return self.quantity.in_units_of(units.exacoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exacoulombs) @property def petacoulombs(self) -> T: - return self.quantity.in_units_of(units.petacoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petacoulombs) @property def teracoulombs(self) -> T: - return self.quantity.in_units_of(units.teracoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teracoulombs) @property def gigacoulombs(self) -> T: - return self.quantity.in_units_of(units.gigacoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigacoulombs) @property def megacoulombs(self) -> T: - return self.quantity.in_units_of(units.megacoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megacoulombs) @property def kilocoulombs(self) -> T: - return self.quantity.in_units_of(units.kilocoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilocoulombs) @property def millicoulombs(self) -> T: - return self.quantity.in_units_of(units.millicoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millicoulombs) @property def microcoulombs(self) -> T: - return self.quantity.in_units_of(units.microcoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microcoulombs) @property def nanocoulombs(self) -> T: - return self.quantity.in_units_of(units.nanocoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanocoulombs) @property def picocoulombs(self) -> T: - return self.quantity.in_units_of(units.picocoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picocoulombs) @property def femtocoulombs(self) -> T: - return self.quantity.in_units_of(units.femtocoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtocoulombs) @property def attocoulombs(self) -> T: - return self.quantity.in_units_of(units.attocoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attocoulombs) -class PotentialAccessor[T](Accessor[T]): +class PotentialAccessor[T](QuantityAccessor[T]): dimension_name = 'potential' @property def volts(self) -> T: - return self.quantity.in_units_of(units.volts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.volts) @property def exavolts(self) -> T: - return self.quantity.in_units_of(units.exavolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exavolts) @property def petavolts(self) -> T: - return self.quantity.in_units_of(units.petavolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petavolts) @property def teravolts(self) -> T: - return self.quantity.in_units_of(units.teravolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teravolts) @property def gigavolts(self) -> T: - return self.quantity.in_units_of(units.gigavolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigavolts) @property def megavolts(self) -> T: - return self.quantity.in_units_of(units.megavolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megavolts) @property def kilovolts(self) -> T: - return self.quantity.in_units_of(units.kilovolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilovolts) @property def millivolts(self) -> T: - return self.quantity.in_units_of(units.millivolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millivolts) @property def microvolts(self) -> T: - return self.quantity.in_units_of(units.microvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microvolts) @property def nanovolts(self) -> T: - return self.quantity.in_units_of(units.nanovolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanovolts) @property def picovolts(self) -> T: - return self.quantity.in_units_of(units.picovolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picovolts) @property def femtovolts(self) -> T: - return self.quantity.in_units_of(units.femtovolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtovolts) @property def attovolts(self) -> T: - return self.quantity.in_units_of(units.attovolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attovolts) -class ResistanceAccessor[T](Accessor[T]): +class ResistanceAccessor[T](QuantityAccessor[T]): dimension_name = 'resistance' @property def ohms(self) -> T: - return self.quantity.in_units_of(units.ohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ohms) @property def exaohms(self) -> T: - return self.quantity.in_units_of(units.exaohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exaohms) @property def petaohms(self) -> T: - return self.quantity.in_units_of(units.petaohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petaohms) @property def teraohms(self) -> T: - return self.quantity.in_units_of(units.teraohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teraohms) @property def gigaohms(self) -> T: - return self.quantity.in_units_of(units.gigaohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigaohms) @property def megaohms(self) -> T: - return self.quantity.in_units_of(units.megaohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megaohms) @property def kiloohms(self) -> T: - return self.quantity.in_units_of(units.kiloohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kiloohms) @property def milliohms(self) -> T: - return self.quantity.in_units_of(units.milliohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milliohms) @property def microohms(self) -> T: - return self.quantity.in_units_of(units.microohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microohms) @property def nanoohms(self) -> T: - return self.quantity.in_units_of(units.nanoohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanoohms) @property def picoohms(self) -> T: - return self.quantity.in_units_of(units.picoohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picoohms) @property def femtoohms(self) -> T: - return self.quantity.in_units_of(units.femtoohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtoohms) @property def attoohms(self) -> T: - return self.quantity.in_units_of(units.attoohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attoohms) -class CapacitanceAccessor[T](Accessor[T]): +class CapacitanceAccessor[T](QuantityAccessor[T]): dimension_name = 'capacitance' @property def farads(self) -> T: - return self.quantity.in_units_of(units.farads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.farads) @property def exafarads(self) -> T: - return self.quantity.in_units_of(units.exafarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exafarads) @property def petafarads(self) -> T: - return self.quantity.in_units_of(units.petafarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petafarads) @property def terafarads(self) -> T: - return self.quantity.in_units_of(units.terafarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terafarads) @property def gigafarads(self) -> T: - return self.quantity.in_units_of(units.gigafarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigafarads) @property def megafarads(self) -> T: - return self.quantity.in_units_of(units.megafarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megafarads) @property def kilofarads(self) -> T: - return self.quantity.in_units_of(units.kilofarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilofarads) @property def millifarads(self) -> T: - return self.quantity.in_units_of(units.millifarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millifarads) @property def microfarads(self) -> T: - return self.quantity.in_units_of(units.microfarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microfarads) @property def nanofarads(self) -> T: - return self.quantity.in_units_of(units.nanofarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanofarads) @property def picofarads(self) -> T: - return self.quantity.in_units_of(units.picofarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picofarads) @property def femtofarads(self) -> T: - return self.quantity.in_units_of(units.femtofarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtofarads) @property def attofarads(self) -> T: - return self.quantity.in_units_of(units.attofarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attofarads) -class ConductanceAccessor[T](Accessor[T]): +class ConductanceAccessor[T](QuantityAccessor[T]): dimension_name = 'conductance' @property def siemens(self) -> T: - return self.quantity.in_units_of(units.siemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.siemens) @property def exasiemens(self) -> T: - return self.quantity.in_units_of(units.exasiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exasiemens) @property def petasiemens(self) -> T: - return self.quantity.in_units_of(units.petasiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petasiemens) @property def terasiemens(self) -> T: - return self.quantity.in_units_of(units.terasiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terasiemens) @property def gigasiemens(self) -> T: - return self.quantity.in_units_of(units.gigasiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigasiemens) @property def megasiemens(self) -> T: - return self.quantity.in_units_of(units.megasiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megasiemens) @property def kilosiemens(self) -> T: - return self.quantity.in_units_of(units.kilosiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilosiemens) @property def millisiemens(self) -> T: - return self.quantity.in_units_of(units.millisiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millisiemens) @property def microsiemens(self) -> T: - return self.quantity.in_units_of(units.microsiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microsiemens) @property def nanosiemens(self) -> T: - return self.quantity.in_units_of(units.nanosiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanosiemens) @property def picosiemens(self) -> T: - return self.quantity.in_units_of(units.picosiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picosiemens) @property def femtosiemens(self) -> T: - return self.quantity.in_units_of(units.femtosiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtosiemens) @property def attosiemens(self) -> T: - return self.quantity.in_units_of(units.attosiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attosiemens) -class MagneticfluxAccessor[T](Accessor[T]): +class MagneticfluxAccessor[T](QuantityAccessor[T]): dimension_name = 'magnetic_flux' @property def webers(self) -> T: - return self.quantity.in_units_of(units.webers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.webers) @property def exawebers(self) -> T: - return self.quantity.in_units_of(units.exawebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exawebers) @property def petawebers(self) -> T: - return self.quantity.in_units_of(units.petawebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petawebers) @property def terawebers(self) -> T: - return self.quantity.in_units_of(units.terawebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terawebers) @property def gigawebers(self) -> T: - return self.quantity.in_units_of(units.gigawebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigawebers) @property def megawebers(self) -> T: - return self.quantity.in_units_of(units.megawebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megawebers) @property def kilowebers(self) -> T: - return self.quantity.in_units_of(units.kilowebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilowebers) @property def milliwebers(self) -> T: - return self.quantity.in_units_of(units.milliwebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milliwebers) @property def microwebers(self) -> T: - return self.quantity.in_units_of(units.microwebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microwebers) @property def nanowebers(self) -> T: - return self.quantity.in_units_of(units.nanowebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanowebers) @property def picowebers(self) -> T: - return self.quantity.in_units_of(units.picowebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picowebers) @property def femtowebers(self) -> T: - return self.quantity.in_units_of(units.femtowebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtowebers) @property def attowebers(self) -> T: - return self.quantity.in_units_of(units.attowebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attowebers) -class MagneticfluxdensityAccessor[T](Accessor[T]): +class MagneticfluxdensityAccessor[T](QuantityAccessor[T]): dimension_name = 'magnetic_flux_density' @property def tesla(self) -> T: - return self.quantity.in_units_of(units.tesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.tesla) @property def exatesla(self) -> T: - return self.quantity.in_units_of(units.exatesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exatesla) @property def petatesla(self) -> T: - return self.quantity.in_units_of(units.petatesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petatesla) @property def teratesla(self) -> T: - return self.quantity.in_units_of(units.teratesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teratesla) @property def gigatesla(self) -> T: - return self.quantity.in_units_of(units.gigatesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigatesla) @property def megatesla(self) -> T: - return self.quantity.in_units_of(units.megatesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megatesla) @property def kilotesla(self) -> T: - return self.quantity.in_units_of(units.kilotesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilotesla) @property def millitesla(self) -> T: - return self.quantity.in_units_of(units.millitesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millitesla) @property def microtesla(self) -> T: - return self.quantity.in_units_of(units.microtesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microtesla) @property def nanotesla(self) -> T: - return self.quantity.in_units_of(units.nanotesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanotesla) @property def picotesla(self) -> T: - return self.quantity.in_units_of(units.picotesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picotesla) @property def femtotesla(self) -> T: - return self.quantity.in_units_of(units.femtotesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtotesla) @property def attotesla(self) -> T: - return self.quantity.in_units_of(units.attotesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attotesla) -class InductanceAccessor[T](Accessor[T]): +class InductanceAccessor[T](QuantityAccessor[T]): dimension_name = 'inductance' @property def henry(self) -> T: - return self.quantity.in_units_of(units.henry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.henry) @property def exahenry(self) -> T: - return self.quantity.in_units_of(units.exahenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exahenry) @property def petahenry(self) -> T: - return self.quantity.in_units_of(units.petahenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petahenry) @property def terahenry(self) -> T: - return self.quantity.in_units_of(units.terahenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terahenry) @property def gigahenry(self) -> T: - return self.quantity.in_units_of(units.gigahenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigahenry) @property def megahenry(self) -> T: - return self.quantity.in_units_of(units.megahenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megahenry) @property def kilohenry(self) -> T: - return self.quantity.in_units_of(units.kilohenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilohenry) @property def millihenry(self) -> T: - return self.quantity.in_units_of(units.millihenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millihenry) @property def microhenry(self) -> T: - return self.quantity.in_units_of(units.microhenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microhenry) @property def nanohenry(self) -> T: - return self.quantity.in_units_of(units.nanohenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanohenry) @property def picohenry(self) -> T: - return self.quantity.in_units_of(units.picohenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picohenry) @property def femtohenry(self) -> T: - return self.quantity.in_units_of(units.femtohenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtohenry) @property def attohenry(self) -> T: - return self.quantity.in_units_of(units.attohenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attohenry) -class TemperatureAccessor[T](Accessor[T]): +class TemperatureAccessor[T](QuantityAccessor[T]): dimension_name = 'temperature' @property def kelvin(self) -> T: - return self.quantity.in_units_of(units.kelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kelvin) @property def exakelvin(self) -> T: - return self.quantity.in_units_of(units.exakelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exakelvin) @property def petakelvin(self) -> T: - return self.quantity.in_units_of(units.petakelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petakelvin) @property def terakelvin(self) -> T: - return self.quantity.in_units_of(units.terakelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terakelvin) @property def gigakelvin(self) -> T: - return self.quantity.in_units_of(units.gigakelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigakelvin) @property def megakelvin(self) -> T: - return self.quantity.in_units_of(units.megakelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megakelvin) @property def kilokelvin(self) -> T: - return self.quantity.in_units_of(units.kilokelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilokelvin) @property def millikelvin(self) -> T: - return self.quantity.in_units_of(units.millikelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millikelvin) @property def microkelvin(self) -> T: - return self.quantity.in_units_of(units.microkelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microkelvin) @property def nanokelvin(self) -> T: - return self.quantity.in_units_of(units.nanokelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanokelvin) @property def picokelvin(self) -> T: - return self.quantity.in_units_of(units.picokelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picokelvin) @property def femtokelvin(self) -> T: - return self.quantity.in_units_of(units.femtokelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtokelvin) @property def attokelvin(self) -> T: - return self.quantity.in_units_of(units.attokelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attokelvin) @property def degrees_celsius(self) -> T: - return self.quantity.in_units_of(units.degrees_celsius) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.degrees_celsius) -class DimensionlessAccessor[T](Accessor[T]): +class DimensionlessAccessor[T](QuantityAccessor[T]): dimension_name = 'dimensionless' @property def none(self) -> T: - return self.quantity.in_units_of(units.none) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.none) -class AngleAccessor[T](Accessor[T]): +class AngleAccessor[T](QuantityAccessor[T]): dimension_name = 'angle' @property def degrees(self) -> T: - return self.quantity.in_units_of(units.degrees) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.degrees) @property def radians(self) -> T: - return self.quantity.in_units_of(units.radians) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.radians) -class SolidangleAccessor[T](Accessor[T]): +class SolidangleAccessor[T](QuantityAccessor[T]): dimension_name = 'solid_angle' @property def stradians(self) -> T: - return self.quantity.in_units_of(units.stradians) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.stradians) -class AmountAccessor[T](Accessor[T]): +class AmountAccessor[T](QuantityAccessor[T]): dimension_name = 'amount' @property def moles(self) -> T: - return self.quantity.in_units_of(units.moles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles) @property def millimoles(self) -> T: - return self.quantity.in_units_of(units.millimoles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles) @property def micromoles(self) -> T: - return self.quantity.in_units_of(units.micromoles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles) @property def nanomoles(self) -> T: - return self.quantity.in_units_of(units.nanomoles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles) @property def picomoles(self) -> T: - return self.quantity.in_units_of(units.picomoles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles) @property def femtomoles(self) -> T: - return self.quantity.in_units_of(units.femtomoles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles) @property def attomoles(self) -> T: - return self.quantity.in_units_of(units.attomoles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles) -class ConcentrationAccessor[T](Accessor[T]): +class ConcentrationAccessor[T](QuantityAccessor[T]): dimension_name = 'concentration' @property def moles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_meter) @property def millimoles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_meter) @property def micromoles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_meter) @property def nanomoles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_meter) @property def picomoles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_meter) @property def femtomoles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_meter) @property def attomoles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_meter) @property def moles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_exameter) @property def millimoles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_exameter) @property def micromoles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_exameter) @property def nanomoles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_exameter) @property def picomoles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_exameter) @property def femtomoles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_exameter) @property def attomoles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_exameter) @property def moles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_petameter) @property def millimoles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_petameter) @property def micromoles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_petameter) @property def nanomoles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_petameter) @property def picomoles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_petameter) @property def femtomoles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_petameter) @property def attomoles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_petameter) @property def moles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_terameter) @property def millimoles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_terameter) @property def micromoles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_terameter) @property def nanomoles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_terameter) @property def picomoles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_terameter) @property def femtomoles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_terameter) @property def attomoles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_terameter) @property def moles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_gigameter) @property def millimoles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_gigameter) @property def micromoles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_gigameter) @property def nanomoles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_gigameter) @property def picomoles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_gigameter) @property def femtomoles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_gigameter) @property def attomoles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_gigameter) @property def moles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_megameter) @property def millimoles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_megameter) @property def micromoles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_megameter) @property def nanomoles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_megameter) @property def picomoles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_megameter) @property def femtomoles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_megameter) @property def attomoles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_megameter) @property def moles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_kilometer) @property def millimoles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_kilometer) @property def micromoles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_kilometer) @property def nanomoles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_kilometer) @property def picomoles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_kilometer) @property def femtomoles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_kilometer) @property def attomoles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_kilometer) @property def moles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_millimeter) @property def millimoles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_millimeter) @property def micromoles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_millimeter) @property def nanomoles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_millimeter) @property def picomoles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_millimeter) @property def femtomoles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_millimeter) @property def attomoles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_millimeter) @property def moles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_micrometer) @property def millimoles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_micrometer) @property def micromoles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_micrometer) @property def nanomoles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_micrometer) @property def picomoles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_micrometer) @property def femtomoles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_micrometer) @property def attomoles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_micrometer) @property def moles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_nanometer) @property def millimoles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_nanometer) @property def micromoles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_nanometer) @property def nanomoles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_nanometer) @property def picomoles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_nanometer) @property def femtomoles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_nanometer) @property def attomoles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_nanometer) @property def moles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_picometer) @property def millimoles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_picometer) @property def micromoles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_picometer) @property def nanomoles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_picometer) @property def picomoles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_picometer) @property def femtomoles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_picometer) @property def attomoles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_picometer) @property def moles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_femtometer) @property def millimoles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_femtometer) @property def micromoles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_femtometer) @property def nanomoles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_femtometer) @property def picomoles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_femtometer) @property def femtomoles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_femtometer) @property def attomoles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_femtometer) @property def moles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_attometer) @property def millimoles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_attometer) @property def micromoles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_attometer) @property def nanomoles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_attometer) @property def picomoles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_attometer) @property def femtomoles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_attometer) @property def attomoles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_attometer) @property def moles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_decimeter) @property def millimoles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_decimeter) @property def micromoles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_decimeter) @property def nanomoles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_decimeter) @property def picomoles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_decimeter) @property def femtomoles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_decimeter) @property def attomoles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_decimeter) @property def moles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_centimeter) @property def millimoles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_centimeter) @property def micromoles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_centimeter) @property def nanomoles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_centimeter) @property def picomoles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_centimeter) @property def femtomoles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_centimeter) @property def attomoles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_centimeter) @property def moles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_angstrom) @property def millimoles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_angstrom) @property def micromoles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_angstrom) @property def nanomoles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_angstrom) @property def picomoles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_angstrom) @property def femtomoles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_angstrom) @property def attomoles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_angstrom) @property def moles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_mile) @property def millimoles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_mile) @property def micromoles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_mile) @property def nanomoles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_mile) @property def picomoles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_mile) @property def femtomoles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_mile) @property def attomoles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_mile) @property def moles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_yard) @property def millimoles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_yard) @property def micromoles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_yard) @property def nanomoles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_yard) @property def picomoles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_yard) @property def femtomoles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_yard) @property def attomoles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_yard) @property def moles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_foot) @property def millimoles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_foot) @property def micromoles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_foot) @property def nanomoles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_foot) @property def picomoles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_foot) @property def femtomoles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_foot) @property def attomoles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_foot) @property def moles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_inch) @property def millimoles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_inch) @property def micromoles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_inch) @property def nanomoles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_inch) @property def picomoles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_inch) @property def femtomoles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_inch) @property def attomoles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_inch) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 3b5e6fdb4..b3a88c50d 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -279,7 +279,14 @@ def __repr__(self): return f"Unit[{self.scale}, {self.dimensions}]" class NamedUnit(Unit): - """ Units, but they have a name, and a symbol""" + """ Units, but they have a name, and a symbol + + :si_scaling_factor: Number of these units per SI equivalent + :param dimensions: Dimensions object representing the dimensionality of these units + :param name: Name of unit - string without unicode + :param ascii_symbol: Symbol for unit without unicode + :param symbol: Unicode symbol + """ def __init__(self, si_scaling_factor: float, dimensions: Dimensions, From 013108a67763d319be05a8d7d130a74b9a234e49 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 13 Aug 2024 10:27:58 +0100 Subject: [PATCH 019/675] Metadata work, and unit groups --- sasdata/metadata.py | 50 +++++++++++----------- sasdata/quantities/_build_tables.py | 12 ++++++ sasdata/quantities/units.py | 66 +++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 24 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 2efff3932..56f1cdfe8 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -66,54 +66,56 @@ def __init__(self, target_object): self.target_object = target_object # Name - name = StringAccessor(self.target_object, "aperture.name") + self.name = StringAccessor(self.target_object, "aperture.name") # Type - type = StringAccessor(self.target_object, "aperture.type") + self.type = StringAccessor(self.target_object, "aperture.type") # Size name - TODO: What is the name of a size - size_name = StringAccessor(self.target_object, "aperture.size_name") + self.size_name = StringAccessor(self.target_object, "aperture.size_name") # Aperture size [Vector] # TODO: Wat!?! - size = QuantityAccessor(self.target_object, - "aperture.size", + self.size = QuantityAccessor(self.target_object, "aperture.size", + "aperture.size.units", default_unit=units.millimeters) - size = None - size_unit = 'mm' # Aperture distance [float] - distance = None - distance_unit = 'mm' + self.distance = QuantityAccessor(self.target_object, + "apature.distance", + "apature.distance.units", + default_unit=units.millimeters) + def summary(self): - pass + return (f"Aperture:" + f" Name: {self.name.value}" + f" Aperture size: {self.value}\n") + _str += " Aperture_dist:%s [%s]\n" % \ + (str(item.distance), str(item.distance_unit)) + class Collimation: """ Class to hold collimation information """ - # Name - name = None - # Length [float] [mm] - length = None - length_unit = 'mm' - # Aperture - aperture = None - def __init__(self): - self.aperture = [] + def __init__(self, target_object): + + # Name + name = None + # Length [float] [mm] + length = None + length_unit = 'mm' + # Aperture + aperture = None + def __str__(self): _str = "Collimation:\n" _str += " Length: %s [%s]\n" % \ (str(self.length), str(self.length_unit)) for item in self.aperture: - _str += " Aperture size:%s [%s]\n" % \ - (str(item.size), str(item.size_unit)) - _str += " Aperture_dist:%s [%s]\n" % \ - (str(item.distance), str(item.distance_unit)) - return _str class Source: diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 842ef52fb..7d357d469 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -344,6 +344,18 @@ def format_name(name: str): fid.write("])\n") + # List of dimensions + fid.write("\n\n") + fid.write("unit_group_names = [\n") + for dimension_name, _ in dimension_names: + fid.write(f" '{dimension_name}',\n") + fid.write("]\n\n") + + fid.write("unit_groups = {\n") + for dimension_name, _ in dimension_names: + fid.write(f" '{dimension_name}': {dimension_name},\n") + fid.write("}\n\n") + with open("accessors.py", 'w', encoding=encoding) as fid: diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index b3a88c50d..c992777cf 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -3338,3 +3338,69 @@ def __init__(self, name: str, units: list[Unit]): femtomoles_per_cubic_inch, attomoles_per_cubic_inch, ]) + + +unit_group_names = [ + 'length', + 'area', + 'volume', + 'inverse_length', + 'inverse_area', + 'inverse_volume', + 'time', + 'rate', + 'speed', + 'acceleration', + 'density', + 'force', + 'pressure', + 'energy', + 'power', + 'charge', + 'potential', + 'resistance', + 'capacitance', + 'conductance', + 'magnetic_flux', + 'magnetic_flux_density', + 'inductance', + 'temperature', + 'dimensionless', + 'angle', + 'solid_angle', + 'amount', + 'concentration', +] + +unit_groups = { + 'length': length, + 'area': area, + 'volume': volume, + 'inverse_length': inverse_length, + 'inverse_area': inverse_area, + 'inverse_volume': inverse_volume, + 'time': time, + 'rate': rate, + 'speed': speed, + 'acceleration': acceleration, + 'density': density, + 'force': force, + 'pressure': pressure, + 'energy': energy, + 'power': power, + 'charge': charge, + 'potential': potential, + 'resistance': resistance, + 'capacitance': capacitance, + 'conductance': conductance, + 'magnetic_flux': magnetic_flux, + 'magnetic_flux_density': magnetic_flux_density, + 'inductance': inductance, + 'temperature': temperature, + 'dimensionless': dimensionless, + 'angle': angle, + 'solid_angle': solid_angle, + 'amount': amount, + 'concentration': concentration, +} + From 368a5130681438d8022087432153c27d726a3ea5 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 13 Aug 2024 10:39:32 +0100 Subject: [PATCH 020/675] More metadata stuff --- sasdata/metadata.py | 49 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 56f1cdfe8..20bf43271 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -9,42 +9,41 @@ class Detector: """ def __init__(self, target_object): - self.target_object = target_object # Name of the instrument [string] - self.name = StringAccessor(self.target_object, "detector.name") + self.name = StringAccessor(target_object, "detector.name") # Sample to detector distance [float] [mm] - self.distance = LengthAccessor[float](self.target_object, + self.distance = LengthAccessor[float](target_object, "detector.distance", "detector.distance.units", default_unit=units.millimeters) # Offset of this detector position in X, Y, # (and Z if necessary) [Vector] [mm] - self.offset = LengthAccessor[ArrayLike](self.target_object, + self.offset = LengthAccessor[ArrayLike](target_object, "detector.offset", "detector.offset.units", default_units=units.millimeters) - self.orientation = AngleAccessor[ArrayLike](self.target_object, + self.orientation = AngleAccessor[ArrayLike](target_object, "detector.orientation", "detector.orientation.units", default_units=units.degrees) - self.beam_center = LengthAccessor[ArrayLike](self.target_object, + self.beam_center = LengthAccessor[ArrayLike](target_object, "detector.beam_center", "detector.beam_center.units", default_units=units.millimeters) # Pixel size in X, Y, (and Z if necessary) [Vector] [mm] - self.pixel_size = LengthAccessor[ArrayLike](self.target_object, + self.pixel_size = LengthAccessor[ArrayLike](target_object, "detector.pixel_size", "detector.pixel_size.units", default_units=units.millimeters) # Slit length of the instrument for this detector.[float] [mm] - self.slit_length = LengthAccessor[float](self.target_object, + self.slit_length = LengthAccessor[float](target_object, "detector.slit_length", "detector.slit_length.units", default_units=units.millimeters) @@ -63,37 +62,34 @@ def summary(self): class Aperture: def __init__(self, target_object): - self.target_object = target_object # Name - self.name = StringAccessor(self.target_object, "aperture.name") + self.name = StringAccessor(target_object, "aperture.name") # Type - self.type = StringAccessor(self.target_object, "aperture.type") + self.type = StringAccessor(target_object, "aperture.type") # Size name - TODO: What is the name of a size - self.size_name = StringAccessor(self.target_object, "aperture.size_name") + self.size_name = StringAccessor(target_object, "aperture.size_name") # Aperture size [Vector] # TODO: Wat!?! - self.size = QuantityAccessor(self.target_object, + self.size = QuantityAccessor[ArrayLike](target_object, "aperture.size", "aperture.size.units", default_unit=units.millimeters) # Aperture distance [float] - self.distance = QuantityAccessor(self.target_object, + self.distance = QuantityAccessor[float](self.target_object, "apature.distance", "apature.distance.units", default_unit=units.millimeters) def summary(self): - return (f"Aperture:" - f" Name: {self.name.value}" - f" Aperture size: {self.value}\n") - _str += " Aperture_dist:%s [%s]\n" % \ - (str(item.distance), str(item.distance_unit)) - + return (f"Aperture:\n" + f" Name: {self.name.value}\n" + f" Aperture size: {self.size.value}\n" + f" Aperture distance: {self.distance.value}") class Collimation: """ @@ -103,13 +99,16 @@ class Collimation: def __init__(self, target_object): # Name - name = None + self.name = StringAccessor(target_object, "collimation.name") # Length [float] [mm] - length = None - length_unit = 'mm' - # Aperture - aperture = None + self.length = QuantityAccessor[float](target_object, + "collimation.length", + "collimation.length.units", + default_units=units.millimeters) + + # Todo - how do we handle this + self.collimator = Collimation(target_object) def __str__(self): _str = "Collimation:\n" From e965c6d2a49549ffe2eda549e3f1116f1dd9ae6b Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 13 Aug 2024 10:40:46 +0100 Subject: [PATCH 021/675] Named units in unit groups --- sasdata/quantities/_units_base.py | 2 +- sasdata/quantities/units.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index a908135bd..6aa9336f2 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -265,6 +265,6 @@ def apply(self, scale, dimensions) -> tuple[float, Dimensions, ProcessedUnitToke class UnitGroup: """ A group of units that all have the same dimensionality """ - def __init__(self, name: str, units: list[Unit]): + def __init__(self, name: str, units: list[NamedUnit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index c992777cf..479bd5fec 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -348,7 +348,7 @@ class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): class UnitGroup: """ A group of units that all have the same dimensionality """ - def __init__(self, name: str, units: list[Unit]): + def __init__(self, name: str, units: list[NamedUnit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) From e2b8dc62776dbc727828f53b795aba4ae1f10637 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 14 Aug 2024 10:59:38 +0100 Subject: [PATCH 022/675] More metadata, added absolute temperature stuff --- sasdata/metadata.py | 205 ++++++++++++--------- sasdata/quantities/_accessor_base.py | 35 ++-- sasdata/quantities/_build_tables.py | 15 +- sasdata/quantities/_units_base.py | 5 + sasdata/quantities/absolute_temperature.py | 3 +- sasdata/quantities/accessors.py | 43 +++-- sasdata/quantities/quantity.py | 4 + sasdata/quantities/units.py | 11 +- sasdata/util.py | 3 +- 9 files changed, 208 insertions(+), 116 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 20bf43271..23ab7bc61 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -2,7 +2,10 @@ from numpy.typing import ArrayLike import sasdata.quantities.units as units -from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor +from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor, \ + DimensionlessAccessor, FloatAccessor, TemperatureAccessor + + class Detector: """ Detector information @@ -24,29 +27,29 @@ def __init__(self, target_object): self.offset = LengthAccessor[ArrayLike](target_object, "detector.offset", "detector.offset.units", - default_units=units.millimeters) + default_unit=units.millimeters) self.orientation = AngleAccessor[ArrayLike](target_object, "detector.orientation", "detector.orientation.units", - default_units=units.degrees) + default_unit=units.degrees) self.beam_center = LengthAccessor[ArrayLike](target_object, "detector.beam_center", "detector.beam_center.units", - default_units=units.millimeters) + default_unit=units.millimeters) # Pixel size in X, Y, (and Z if necessary) [Vector] [mm] self.pixel_size = LengthAccessor[ArrayLike](target_object, "detector.pixel_size", "detector.pixel_size.units", - default_units=units.millimeters) + default_unit=units.millimeters) # Slit length of the instrument for this detector.[float] [mm] self.slit_length = LengthAccessor[float](target_object, "detector.slit_length", "detector.slit_length.units", - default_units=units.millimeters) + default_unit=units.millimeters) def summary(self): return (f"Detector:\n" @@ -79,7 +82,7 @@ def __init__(self, target_object): default_unit=units.millimeters) # Aperture distance [float] - self.distance = QuantityAccessor[float](self.target_object, + self.distance = LengthAccessor[float](target_object, "apature.distance", "apature.distance.units", default_unit=units.millimeters) @@ -101,76 +104,97 @@ def __init__(self, target_object): # Name self.name = StringAccessor(target_object, "collimation.name") # Length [float] [mm] - self.length = QuantityAccessor[float](target_object, + self.length = LengthAccessor[float](target_object, "collimation.length", "collimation.length.units", - default_units=units.millimeters) + default_unit=units.millimeters) # Todo - how do we handle this self.collimator = Collimation(target_object) - def __str__(self): - _str = "Collimation:\n" - _str += " Length: %s [%s]\n" % \ - (str(self.length), str(self.length_unit)) - for item in self.aperture: + def summary(self): + + #TODO collimation stuff + return ( + f"Collimation:\n" + f" Length: {self.length.value}\n") + class Source: """ Class to hold source information """ - # Name - name = None - # Generic radiation type (Type and probe give more specific info) [string] - radiation = None - # Type and probe are only written to by the NXcanSAS reader - # Specific radiation type (Synchotron X-ray, Reactor neutron, etc) [string] - type = None - # Radiation probe (generic probe such as neutron, x-ray, muon, etc) [string] - probe = None - # Beam size name - beam_size_name = None - # Beam size [Vector] [mm] - beam_size = None - beam_size_unit = 'mm' - # Beam shape [string] - beam_shape = None - # Wavelength [float] [Angstrom] - wavelength = None - wavelength_unit = 'A' - # Minimum wavelength [float] [Angstrom] - wavelength_min = None - wavelength_min_unit = 'nm' - # Maximum wavelength [float] [Angstrom] - wavelength_max = None - wavelength_max_unit = 'nm' - # Wavelength spread [float] [Angstrom] - wavelength_spread = None - wavelength_spread_unit = 'percent' - def __init__(self): - self.beam_size = None #Vector() + def __init__(self, target_object): + # Name + self.name = StringAccessor(target_object, "source.name") + + # Generic radiation type (Type and probe give more specific info) [string] + self.radiation = StringAccessor(target_object, "source.radiation") + + # Type and probe are only written to by the NXcanSAS reader + # Specific radiation type (Synchotron X-ray, Reactor neutron, etc) [string] + self.type = StringAccessor(target_object, "source.type") + + # Radiation probe (generic probe such as neutron, x-ray, muon, etc) [string] + self.probe_particle = StringAccessor(target_object, "source.probe") + + # Beam size name + self.beam_size_name = StringAccessor(target_object, "source.beam_size_name") + + # Beam size [Vector] [mm] + self.beam_size = LengthAccessor[ArrayLike](target_object, + "source.beam_size", + "source.beam_size.units", + default_unit=units.millimeters) + + # Beam shape [string] + self.beam_shape = StringAccessor(target_object, "source.beam_shape") + + # Wavelength [float] [Angstrom] + self.wavelength = LengthAccessor[float](target_object, + "source.wavelength", + "source.wavelength.units", + default_unit=units.angstroms) + + # Minimum wavelength [float] [Angstrom] + self.wavelength_min = LengthAccessor[float](target_object, + "source.wavelength_min", + "source.wavelength_min.units", + default_unit=units.angstroms) + + # Maximum wavelength [float] [Angstrom] + self.wavelength_max = LengthAccessor[float](target_object, + "source.wavelength_min", + "source.wavelength_max.units", + default_unit=units.angstroms) + + # Wavelength spread [float] [Angstrom] + # Quantity because it might have other units, such as percent + self.wavelength_spread = QuantityAccessor[float](target_object, + "source.wavelength_spread", + "source.wavelength_spread.units", + default_unit=units.angstroms) + + + def summary(self): + + if self.radiation.value is None and self.type.value and self.probe_particle.value: + radiation = f"{self.type.value} {self.probe_particle.value}" + else: + radiation = f"{self.radiation.value}" + + return (f"Source:\n" + f" Radiation: {radiation}\n" + f" Shape: {self.beam_shape.value}\n" + f" Wavelength: {self.wavelength.value}\n" + f" Min. Wavelength: {self.wavelength_min.value}\n" + f" Max. Wavelength: {self.wavelength_max.value}\n" + f" Wavelength Spread: {self.wavelength_spread.value}\n" + f" Beam Size: {self.beam_size}\n") - def __str__(self): - _str = "Source:\n" - radiation = self.radiation - if self.radiation is None and self.type and self.probe: - radiation = self.type + " " + self.probe - _str += " Radiation: %s\n" % str(radiation) - _str += " Shape: %s\n" % str(self.beam_shape) - _str += " Wavelength: %s [%s]\n" % \ - (str(self.wavelength), str(self.wavelength_unit)) - _str += " Waveln_min: %s [%s]\n" % \ - (str(self.wavelength_min), str(self.wavelength_min_unit)) - _str += " Waveln_max: %s [%s]\n" % \ - (str(self.wavelength_max), str(self.wavelength_max_unit)) - _str += " Waveln_spread:%s [%s]\n" % \ - (str(self.wavelength_spread), str(self.wavelength_spread_unit)) - _str += " Beam_size: %s [%s]\n" % \ - (str(self.beam_size), str(self.beam_size_unit)) - return _str """ @@ -186,29 +210,40 @@ class Sample: """ Class to hold the sample description """ - # Short name for sample - name = '' - # ID - ID = '' - # Thickness [float] [mm] - thickness = None - thickness_unit = 'mm' - # Transmission [float] [fraction] - transmission = None - # Temperature [float] [No Default] - temperature = None - temperature_unit = None - # Position [Vector] [mm] - position = None - position_unit = 'mm' - # Orientation [Vector] [degrees] - orientation = None - orientation_unit = 'degree' - # Details - details = None - # SESANS zacceptance - zacceptance = (0,"") - yacceptance = (0,"") + def __init__(self, target_object): + + # Short name for sample + self.name = StringAccessor(target_object, "sample.name") + # ID + + self.sample_id = StringAccessor(target_object, "sample.id") + + # Thickness [float] [mm] + self.thickness = LengthAccessor(target_object, + "sample.thickness", + "sample.thickness.units", + default_unit=units.millimeters) + + # Transmission [float] [fraction] + self.transmission = FloatAccessor(target_object,"sample.transmission") + + # Temperature [float] [No Default] + self.temperature = TemperatureAccessor(target_object, + "sample.temperature", + "sample.temperature.unit") + temperature = None + temperature_unit = None + # Position [Vector] [mm] + position = None + position_unit = 'mm' + # Orientation [Vector] [degrees] + orientation = None + orientation_unit = 'degree' + # Details + details = None + # SESANS zacceptance + zacceptance = (0,"") + yacceptance = (0,"") def __init__(self): self.position = None # Vector() diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index a1437d170..42d14fc10 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -1,7 +1,8 @@ -from typing import TypeVar +from typing import TypeVar, Sequence from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units +from sasdata.quantities.units import Dimensions, Unit DataType = TypeVar("DataType") @@ -23,6 +24,15 @@ class StringAccessor(Accessor[str, str]): def value(self) -> str | None: pass +class FloatAccessor(Accessor[float, float]): + """ Float based fields """ + @property + def value(self) -> float | None: + pass + + + + class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): """ Base class for accessors that work with quantities that have units """ def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): @@ -30,19 +40,22 @@ def __init__(self, target_object, value_target: str, unit_target: str, default_u self._unit_target = unit_target self.default_unit = default_unit - def _lookup_unit(self) -> units.Unit | None: - # TODO: Implement - return None + def _numerical_part(self) -> DataType | None: + """ Numerical part of the data """ + + def _unit_part(self) -> str | None: + """ String form of units for the data """ - def data_unit(self): - unit = self._lookup_unit - if unit is None: + @property + def unit(self) -> Unit: + if self._unit_part() is None: return self.default_unit else: - return unit - + return Unit.parse(self._unit_part()) @property - def quantity(self) -> Quantity[DataType]: - raise NotImplementedError("Not implemented yet") + def value(self) -> Quantity[DataType] | None: + if self._unit_part() is not None and self._numerical_part() is not None: + return Quantity(self._numerical_part(), self.unit) + diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 7d357d469..db9f3f24c 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -54,7 +54,7 @@ ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), ] -non_si_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ +non_si_dimensioned_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, 0, 0, []), ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, 0, 0, []), ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, 0, 0, []), @@ -63,7 +63,6 @@ ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, 0, 1, []), ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, 0, 1, []), ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, 0, 2, []), - ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), ("l", None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), ("eV", None, "electronvolt", "electronvolts", 1.602176634e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), ("au", None, "atomic mass unit", "atomic mass units", 1.660538921e-27, 0, 0, 1, 0, 0, 0, 0, []), @@ -80,6 +79,13 @@ ("psi", None, "pound force per square inch", "pounds force per square inch", 4.448222/(0.0254**2), -1, -2, 1, 0, 0, 0, 0, []), ] +non_si_dimensionless_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ + ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), + ("percent", "%", "percent", "percent", 100, 0, 0, 0, 0, 0, 0, 0, []) +] + +non_si_units = non_si_dimensioned_units + non_si_dimensionless_units + # TODO: # Add Hartree? Rydberg? Bohrs? # Add CGS @@ -89,7 +95,8 @@ "d": ["day"], "h": ["hr", "hour"], "Ang": ["A", "Å"], - "au": ["a.u.", "amu"] + "au": ["a.u.", "amu"], + "percent": ["%"] } @@ -325,7 +332,7 @@ def format_name(name: str): ("angle", Dimensions(angle_hint=1)), ("solid_angle", Dimensions(angle_hint=2)), ("amount", Dimensions(moles_hint=1)), - ("concentration", Dimensions(length=-3, moles_hint=1)) + ("concentration", Dimensions(length=-3, moles_hint=1)), ] fid.write("\n#\n# Units by type\n#\n\n") diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 6aa9336f2..bab0cb546 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -193,6 +193,10 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): def __repr__(self): return f"Unit[{self.scale}, {self.dimensions}]" + + @staticmethod + def parse(unit_string: str) -> "Unit": + pass class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): """ This processor minimises the dimensionality of the unit by multiplying by as many units of the specified type as needed """ @@ -268,3 +272,4 @@ class UnitGroup: def __init__(self, name: str, units: list[NamedUnit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) + diff --git a/sasdata/quantities/absolute_temperature.py b/sasdata/quantities/absolute_temperature.py index 045c7de97..95c8982fb 100644 --- a/sasdata/quantities/absolute_temperature.py +++ b/sasdata/quantities/absolute_temperature.py @@ -1,7 +1,8 @@ from typing import TypeVar +from quantities.quantity import Quantity from sasdata.quantities.accessors import TemperatureAccessor -from sasdata.quantities.quantity import Quantity + DataType = TypeVar("DataType") class AbsoluteTemperatureAccessor(TemperatureAccessor[DataType]): diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index eaffa5e23..45f801084 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -78,10 +78,11 @@ """ -from typing import TypeVar +from typing import TypeVar, Sequence from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units +from sasdata.quantities.units import Dimensions, Unit DataType = TypeVar("DataType") @@ -103,6 +104,15 @@ class StringAccessor(Accessor[str, str]): def value(self) -> str | None: pass +class FloatAccessor(Accessor[float, float]): + """ Float based fields """ + @property + def value(self) -> float | None: + pass + + + + class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): """ Base class for accessors that work with quantities that have units """ def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): @@ -110,21 +120,22 @@ def __init__(self, target_object, value_target: str, unit_target: str, default_u self._unit_target = unit_target self.default_unit = default_unit - def _lookup_unit(self) -> units.Unit | None: - # TODO: Implement - return None + def _numerical_part(self) -> DataType | None: + pass + + def _unit_part(self) -> str | None: + pass - def data_unit(self): - unit = self._lookup_unit - if unit is None: + def unit(self) -> Unit: + if self._unit_part() is None: return self.default_unit else: - return unit - - + return Unit.parse(self._unit_part()) @property - def quantity(self) -> Quantity[DataType]: - raise NotImplementedError("Not implemented yet") + def value(self) -> Quantity[DataType] | None: + if self._unit_part() is not None and self._numerical_part() is not None: + return Quantity(self.unit()) + class LengthAccessor[T](QuantityAccessor[T]): @@ -8986,6 +8997,14 @@ def none(self) -> T: else: return quantity.in_units_of(units.none) + @property + def percent(self) -> T: + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.percent) + class AngleAccessor[T](QuantityAccessor[T]): diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 23a2e5acb..9a3093f6e 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -75,3 +75,7 @@ def __rsub__(self: Self, other: Self | ArrayLike) -> Self: def __pow__(self: Self, other: int): return Quantity(self.value**other, self.units**other) + + def parse(self, number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): + pass + diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 479bd5fec..e25dd59e6 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -278,6 +278,10 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): def __repr__(self): return f"Unit[{self.scale}, {self.dimensions}]" + @staticmethod + def parse(unit_string: str) -> "Unit": + pass + class NamedUnit(Unit): """ Units, but they have a name, and a symbol @@ -353,6 +357,7 @@ def __init__(self, name: str, units: list[NamedUnit]): self.units = sorted(units, key=lambda unit: unit.scale) + # # Specific units # @@ -595,7 +600,6 @@ def __init__(self, name: str, units: list[NamedUnit]): degrees = NamedUnit(57.29577951308232, Dimensions(0, 0, 0, 0, 0, 0, 1),name='degrees',ascii_symbol='deg',symbol='deg') radians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 1),name='radians',ascii_symbol='rad',symbol='rad') stradians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') -none = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') litres = NamedUnit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') electronvolts = NamedUnit(1.602176634e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') exaelectronvolts = NamedUnit(0.1602176634, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') @@ -628,6 +632,8 @@ def __init__(self, name: str, units: list[NamedUnit]): pounds_force = NamedUnit(4.448222, Dimensions(1, -2, 1, 0, 0, 0, 0),name='pounds_force',ascii_symbol='lbf',symbol='lbf') ounces = NamedUnit(0.028349523125, Dimensions(0, 0, 1, 0, 0, 0, 0),name='ounces',ascii_symbol='oz',symbol='oz') pounds_force_per_square_inch = NamedUnit(6894.757889515779, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pounds_force_per_square_inch',ascii_symbol='psi',symbol='psi') +none = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') +percent = NamedUnit(100, Dimensions(0, 0, 0, 0, 0, 0, 0),name='percent',ascii_symbol='percent',symbol='%') square_meters = NamedUnit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = NamedUnit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = NamedUnit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -1936,6 +1942,8 @@ def __init__(self, name: str, units: list[NamedUnit]): "lbf": pounds_force, "oz": ounces, "psi": pounds_force_per_square_inch, + "percent": percent, + "%": percent, "yr": years, "year": years, "day": days, @@ -3167,6 +3175,7 @@ def __init__(self, name: str, units: list[NamedUnit]): name = 'dimensionless', units = [ none, + percent, ]) angle = UnitGroup( diff --git a/sasdata/util.py b/sasdata/util.py index 8decc68be..d378a9908 100644 --- a/sasdata/util.py +++ b/sasdata/util.py @@ -1,5 +1,4 @@ -from collections.abc import Callable -from typing import TypeVar +from typing import TypeVar, Callable T = TypeVar("T") From 373e8c5eefae9655d470b6a7fe4e290c5fe1c033 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 14 Aug 2024 13:23:16 +0100 Subject: [PATCH 023/675] Metadata objects complete for now --- sasdata/metadata.py | 171 +++++++++++++--------------- sasdata/quantities/_build_tables.py | 3 +- 2 files changed, 81 insertions(+), 93 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 23ab7bc61..cc5e17ac4 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -2,6 +2,7 @@ from numpy.typing import ArrayLike import sasdata.quantities.units as units +from quantities.absolute_temperature import AbsoluteTemperatureAccessor from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor, \ DimensionlessAccessor, FloatAccessor, TemperatureAccessor @@ -178,8 +179,7 @@ def __init__(self, target_object): "source.wavelength_spread.units", default_unit=units.angstroms) - - def summary(self): + def summary(self) -> str: if self.radiation.value is None and self.type.value and self.probe_particle.value: radiation = f"{self.type.value} {self.probe_particle.value}" @@ -228,46 +228,44 @@ def __init__(self, target_object): self.transmission = FloatAccessor(target_object,"sample.transmission") # Temperature [float] [No Default] - self.temperature = TemperatureAccessor(target_object, - "sample.temperature", - "sample.temperature.unit") - temperature = None - temperature_unit = None + self.temperature = AbsoluteTemperatureAccessor(target_object, + "sample.temperature", + "sample.temperature.unit", + default_unit=units.kelvin) # Position [Vector] [mm] - position = None - position_unit = 'mm' + self.position = LengthAccessor[ArrayLike](target_object, + "sample.position", + "sample.position.unit", + default_unit=units.millimeters) + # Orientation [Vector] [degrees] - orientation = None - orientation_unit = 'degree' + self.orientation = AngleAccessor[ArrayLike](target_object, + "sample.orientation", + "sample.orientation.unit", + default_unit=units.degrees) + # Details - details = None + self.details = StringAccessor(target_object, "sample.details") + + # SESANS zacceptance zacceptance = (0,"") yacceptance = (0,"") - def __init__(self): - self.position = None # Vector() - self.orientation = None # Vector() - self.details = [] - - def __str__(self): - _str = "Sample:\n" - _str += " ID: %s\n" % str(self.ID) - _str += " Transmission: %s\n" % str(self.transmission) - _str += " Thickness: %s [%s]\n" % \ - (str(self.thickness), str(self.thickness_unit)) - _str += " Temperature: %s [%s]\n" % \ - (str(self.temperature), str(self.temperature_unit)) - _str += " Position: %s [%s]\n" % \ - (str(self.position), str(self.position_unit)) - _str += " Orientation: %s [%s]\n" % \ - (str(self.orientation), str(self.orientation_unit)) - - _str += " Details:\n" - for item in self.details: - _str += " %s\n" % item - - return _str + def summary(self) -> str: + return (f"Sample:\n" + f" ID: {self.sample_id.value}\n" + f" Transmission: {self.transmission.value}\n" + f" Thickness: {self.thickness.value}\n" + f" Temperature: {self.temperature.value}\n" + f" Position: {self.position.value}\n" + f" Orientation: {self.orientation.value}\n") + # + # _str += " Details:\n" + # for item in self.details: + # _str += " %s\n" % item + # + # return _str class Process: @@ -275,74 +273,63 @@ class Process: Class that holds information about the processes performed on the data. """ - name = '' - date = '' - description = '' - term = None - notes = None + def __init__(self, target_object): + self.name = StringAccessor(target_object, "process.name") + self.date = StringAccessor(target_object, "process.date") + self.description = StringAccessor(target_object, "process.description") - def __init__(self): - self.term = [] - self.notes = [] + #TODO: It seems like these might be lists of strings, this should be checked - def is_empty(self): - """ - Return True if the object is empty - """ - return (len(self.name) == 0 and len(self.date) == 0 - and len(self.description) == 0 and len(self.term) == 0 - and len(self.notes) == 0) + self.term = StringAccessor(target_object, "process.term") + self.notes = StringAccessor(target_object, "process.notes") def single_line_desc(self): """ Return a single line string representing the process """ - return "%s %s %s" % (self.name, self.date, self.description) + return f"{self.name.value} {self.date.value} {self.description.value}" def __str__(self): - _str = "Process:\n" - _str += " Name: %s\n" % self.name - _str += " Date: %s\n" % self.date - _str += " Description: %s\n" % self.description - for item in self.term: - _str += " Term: %s\n" % item - for item in self.notes: - _str += " Note: %s\n" % item - return _str - - -class TransmissionSpectrum(object): + return (f"Process:\n" + f" Name: {self.name.value}\n" + f" Date: {self.date.value}\n" + f" Description: {self.description.value}\n" + f" Term: {self.term.value}\n" + f" Notes: {self.notes.value}" + ) + +class TransmissionSpectrum: """ Class that holds information about transmission spectrum for white beams and spallation sources. """ - name = '' - timestamp = '' - # Wavelength (float) [A] - wavelength = None - wavelength_unit = 'A' - # Transmission (float) [unit less] - transmission = None - transmission_unit = '' - # Transmission Deviation (float) [unit less] - transmission_deviation = None - transmission_deviation_unit = '' - - def __init__(self): - self.wavelength = [] - self.transmission = [] - self.transmission_deviation = [] - - def __str__(self): - _str = "Transmission Spectrum:\n" - _str += " Name: \t{0}\n".format(self.name) - _str += " Timestamp: \t{0}\n".format(self.timestamp) - _str += " Wavelength unit: \t{0}\n".format(self.wavelength_unit) - _str += " Transmission unit:\t{0}\n".format(self.transmission_unit) - _str += " Trans. Dev. unit: \t{0}\n".format( - self.transmission_deviation_unit) - length_list = [len(self.wavelength), len(self.transmission), - len(self.transmission_deviation)] - _str += " Number of Pts: \t{0}\n".format(max(length_list)) - return _str + def __init__(self, target_object): + # TODO: Needs to be multiple cases + self.name = StringAccessor(target_object, "transmission.") + self.timestamp = StringAccessor(target_object, "transmission.timestamp") + + # Wavelength (float) [A] + self.wavelength = LengthAccessor[ArrayLike](target_object, + "transmission.wavelength", + "transmission.wavelength.units") + + # Transmission (float) [unit less] + self.transmission = DimensionlessAccessor[ArrayLike](target_object, + "transmission.transmission", + "transmission.units", + default_unit=units.none) + + # Transmission Deviation (float) [unit less] + self.transmission_deviation = DimensionlessAccessor[ArrayLike](target_object, + "transmission.transmission_deviation", + "transmission.transmission_deviation.units", + default_units=units.none) + + + def summary(self) -> str: + return (f"Transmission Spectrum:\n" + f" Name: {self.name.value}\n" + f" Timestamp: {self.timestamp.value}\n" + f" Wavelengths: {self.wavelength.value}\n" + f" Transmission: {self.transmission.value}\n") diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index db9f3f24c..bf4cda7b5 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -96,7 +96,8 @@ "h": ["hr", "hour"], "Ang": ["A", "Å"], "au": ["a.u.", "amu"], - "percent": ["%"] + "percent": ["%"], + "deg": ["degr"], } From d2faf39d79b7da14842954bffa409ce881e60646 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 14 Aug 2024 13:29:22 +0100 Subject: [PATCH 024/675] Percent test and fix --- sasdata/quantities/_build_tables.py | 2 +- sasdata/quantities/accessors.py | 8 +++++--- sasdata/quantities/quantities_tests.py | 2 ++ sasdata/quantities/units.py | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index bf4cda7b5..750509349 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -81,7 +81,7 @@ non_si_dimensionless_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), - ("percent", "%", "percent", "percent", 100, 0, 0, 0, 0, 0, 0, 0, []) + ("percent", "%", "percent", "percent", 0.01, 0, 0, 0, 0, 0, 0, 0, []) ] non_si_units = non_si_dimensioned_units + non_si_dimensionless_units diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 45f801084..92ef82bd1 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -121,20 +121,22 @@ def __init__(self, target_object, value_target: str, unit_target: str, default_u self.default_unit = default_unit def _numerical_part(self) -> DataType | None: - pass + """ Numerical part of the data """ def _unit_part(self) -> str | None: - pass + """ String form of units for the data """ + @property def unit(self) -> Unit: if self._unit_part() is None: return self.default_unit else: return Unit.parse(self._unit_part()) + @property def value(self) -> Quantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self.unit()) + return Quantity(self._numerical_part(), self.unit) diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index 16b06aae6..19237cfac 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -75,6 +75,8 @@ def test_american_units(): assert_unit_ratio(units.miles, units.inches, 63360) assert_unit_ratio(units.pounds_force_per_square_inch, units.pounds_force / (units.inches**2), 1, abs=1e-5) +def test_percent(): + assert Quantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) @pytest.mark.parametrize("unit_1", si.all_si) @pytest.mark.parametrize("unit_2", si.all_si) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index e25dd59e6..2da7b4537 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -633,7 +633,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ounces = NamedUnit(0.028349523125, Dimensions(0, 0, 1, 0, 0, 0, 0),name='ounces',ascii_symbol='oz',symbol='oz') pounds_force_per_square_inch = NamedUnit(6894.757889515779, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pounds_force_per_square_inch',ascii_symbol='psi',symbol='psi') none = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') -percent = NamedUnit(100, Dimensions(0, 0, 0, 0, 0, 0, 0),name='percent',ascii_symbol='percent',symbol='%') +percent = NamedUnit(0.01, Dimensions(0, 0, 0, 0, 0, 0, 0),name='percent',ascii_symbol='percent',symbol='%') square_meters = NamedUnit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = NamedUnit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = NamedUnit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -1951,6 +1951,7 @@ def __init__(self, name: str, units: list[NamedUnit]): "hour": hours, "a.u.": atomic_mass_units, "amu": atomic_mass_units, + "degr": degrees, } From fcf7ebe16a9503bf8f399a2ea3576d17b3bafeaa Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 21 Aug 2024 10:38:51 +0100 Subject: [PATCH 025/675] Work towards new data object --- sasdata/data.py | 8 +- sasdata/metadata.py | 4 +- sasdata/model_requirements.py | 7 +- sasdata/quantities/quantity.py | 6 + sasdata/raw_form.py | 63 +++++ sasdata/temp_hdf5_reader.py | 456 ++++----------------------------- 6 files changed, 125 insertions(+), 419 deletions(-) create mode 100644 sasdata/raw_form.py diff --git a/sasdata/data.py b/sasdata/data.py index f6ca6260d..b8daddce3 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,6 +1,6 @@ from dataclasses import dataclass -from quantities.quantity import Quantity, NamedQuantity -from sasdata.metadata import MetaData +from sasdata.quantities.quantity import Quantity, NamedQuantity +from sasdata.metadata import Metadata import numpy as np @@ -10,10 +10,10 @@ @dataclass -class SASData: +class DataSet: abscissae: list[NamedQuantity[np.ndarray]] ordinate: NamedQuantity[np.ndarray] other: list[NamedQuantity[np.ndarray]] - metadata: MetaData + metadata: Metadata model_requirements: ModellingRequirements diff --git a/sasdata/metadata.py b/sasdata/metadata.py index cc5e17ac4..316e99409 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -304,7 +304,7 @@ class TransmissionSpectrum: for white beams and spallation sources. """ def __init__(self, target_object): - # TODO: Needs to be multiple cases + # TODO: Needs to be multiple instances self.name = StringAccessor(target_object, "transmission.") self.timestamp = StringAccessor(target_object, "transmission.timestamp") @@ -333,3 +333,5 @@ def summary(self) -> str: f" Wavelengths: {self.wavelength.value}\n" f" Transmission: {self.transmission.value}\n") +class Metadata: + pass \ No newline at end of file diff --git a/sasdata/model_requirements.py b/sasdata/model_requirements.py index a35759bd2..5d68ad1b4 100644 --- a/sasdata/model_requirements.py +++ b/sasdata/model_requirements.py @@ -2,6 +2,7 @@ import numpy as np +from sasdata.metadata import Metadata from transforms.operation import Operation @@ -11,12 +12,12 @@ class ModellingRequirements: dimensionality: int operation: Operation - - def from_qi_transformation(self, data: np.ndarray) -> np.ndaarray: + def from_qi_transformation(self, data: np.ndarray, metadata: Metadata) -> np.ndarray: + """ Transformation for going from qi to this data""" pass def guess_requirements(abscissae, ordinate) -> ModellingRequirements: - """ Use names of axes and units to guess what kind of processing needs to be done """ + """ Use names of axes and units to guess what kind of processing needs to be done """ \ No newline at end of file diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 9a3093f6e..eec06609d 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -79,3 +79,9 @@ def __pow__(self: Self, other: int): def parse(self, number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass + +class NamedQuantity[QuantityType](Quantity[QuantityType]): + def __init__(self, value: QuantityType, units: Unit, name: str): + super().__init__(value, units) + self.name = name + diff --git a/sasdata/raw_form.py b/sasdata/raw_form.py new file mode 100644 index 000000000..37736d37a --- /dev/null +++ b/sasdata/raw_form.py @@ -0,0 +1,63 @@ +from typing import TypeVar, Any, Self +from dataclasses import dataclass + +from quantities.quantity import NamedQuantity + +DataType = TypeVar("DataType") + +def shorten_string(string): + lines = string.split("\n") + if len(lines) <= 1: + return string + else: + return lines[0][:30] + " ... " + lines[-1][-30:] + +@dataclass +class Dataset[DataType]: + name: str + data: DataType + attributes: dict[str, Self] + + def summary(self, indent_amount: int = 0, indent: str = " ") -> str: + + s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" + s += f"{indent*(indent_amount+1)}{shorten_string(str(self.data))}\n" + for key in self.attributes: + value = self.attributes[key] + if isinstance(value, (Group, Dataset)): + value_string = value.summary(indent_amount+1, indent) + else: + value_string = f"{indent * indent_amount}{self.data}\n" + + s += value_string + + + return s + +@dataclass +class Group: + name: str + children: dict[str, Self | Dataset] + + + def summary(self, indent_amount: int=0, indent=" "): + s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" + for key in self.children: + s += self.children[key].summary(indent_amount+1, indent) + + return s + +@dataclass +class RawData: + name: str + data_contents: list[NamedQuantity] + raw_metadata: dict[str, Dataset | Group] + + def __repr__(self): + indent = " " + + s = f"{self.name}\n" + for key in self.raw_metadata: + s += self.raw_metadata[key].summary(1, indent) + + return s \ No newline at end of file diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index e439486ad..b7194b008 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -1,457 +1,91 @@ +import os +import h5py + + import logging -from collections.abc import Callable -import h5py import numpy as np + + from h5py._hl.dataset import Dataset as HDF5Dataset from h5py._hl.group import Group as HDF5Group -from sasdata.data import SasData -from sasdata.data_backing import Dataset as SASDataDataset -from sasdata.data_backing import Group as SASDataGroup -from sasdata.dataset_types import one_dim, three_dim, two_dim -from sasdata.metadata import ( - Aperture, - BeamSize, - Collimation, - Detector, - Instrument, - Metadata, - MetaNode, - Process, - Rot3, - Sample, - Source, - Vec3, -) -from sasdata.quantities import units -from sasdata.quantities.quantity import NamedQuantity, Quantity -from sasdata.quantities.unit_parser import parse - -# test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" -# test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" -test_file = "./example_data/2d_data/BAM_2D.h5" -# test_file = "./example_data/2d_data/14250_2D_NoDetInfo_NXcanSAS_v3.h5" -# test_file = "./example_data/2d_data/33837rear_2D_1.75_16.5_NXcanSAS_v3.h5" -test_file = "./test/sasdataloader/data/nxcansas_1Dand2D_multisasdata.h5" + +from sasdata.data import DataSet +from sasdata.raw_form import RawData +from sasdata.raw_form import Dataset as SASDataDataset, Group as SASDataGroup + +test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" +test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" logger = logging.getLogger(__name__) +def hdf5_attr(entry): + return entry def recurse_hdf5(hdf5_entry): if isinstance(hdf5_entry, HDF5Dataset): - attributes = {name: hdf5_entry.attrs[name] for name in hdf5_entry.attrs} - if isinstance(hdf5_entry.dtype, np.dtypes.BytesDType): data = hdf5_entry[()][0].decode("utf-8") - - return SASDataDataset[str]( - name=hdf5_entry.name, data=data, attributes=attributes - ) - else: data = np.array(hdf5_entry, dtype=hdf5_entry.dtype) - return SASDataDataset[np.ndarray]( - name=hdf5_entry.name, data=data, attributes=attributes - ) + attributes = {name: hdf5_attr(hdf5_entry.attrs[name]) for name in hdf5_entry.attrs} + + return SASDataDataset( + name=hdf5_entry.name, + data=data, + attributes=attributes) elif isinstance(hdf5_entry, HDF5Group): return SASDataGroup( name=hdf5_entry.name, - children={key: recurse_hdf5(hdf5_entry[key]) for key in hdf5_entry.keys()}, - ) + children={key: recurse_hdf5(hdf5_entry[key]) for key in hdf5_entry.keys()}) else: - raise TypeError( - f"Unknown type found during HDF5 parsing: {type(hdf5_entry)} ({hdf5_entry})" - ) - + raise TypeError(f"Unknown type found during HDF5 parsing: {type(hdf5_entry)} ({hdf5_entry})") -GET_UNITS_FROM_ELSEWHERE = units.meters +def load_data(filename) -> list[RawData]: + with h5py.File(filename, 'r') as f: + loaded_data: list[RawData] = [] -def connected_data(node: SASDataGroup, name_prefix="", metadata=None) -> dict[str, Quantity]: - """In the context of NeXus files, load a group of data entries that are organised together - match up the units and errors with their values""" - # Gather together data with its error terms - - uncertainty_map = {} - uncertainties = set() - entries = {} + for root_key in f.keys(): - for name in node.children: - child = node.children[name] + print(root_key) - if "units" in child.attributes and child.attributes["units"]: - units = parse(child.attributes["units"]) - else: - units = GET_UNITS_FROM_ELSEWHERE - - quantity = NamedQuantity(name=child.name, value=child.data, units=units, id_header=metadata.id_header) - - # Turns out people can't be trusted to use the same keys here - if "uncertainty" in child.attributes or "uncertainties" in child.attributes: - try: - uncertainty_name = child.attributes["uncertainty"] - except: - uncertainty_name = child.attributes["uncertainties"] - uncertainty_map[name] = uncertainty_name - uncertainties.add(uncertainty_name) - - entries[name] = quantity - - output : dict[str, Quantity] = {} - - for name, entry in entries.items(): - if name not in uncertainties: - if name in uncertainty_map: - uncertainty = entries[uncertainty_map[name]] - new_entry = entry.with_standard_error(uncertainty) - output[name] = new_entry - else: - output[name] = entry - - return output - -### Begin metadata parsing code - -def get_canSAS_class(node : HDF5Group) -> str | None: - # Check if attribute exists - if "canSAS_class" in node.attrs: - cls = node.attrs["canSAS_class"] - return cls - elif "NX_class" in node.attrs: - cls = node.attrs["NX_class"] - cls = NX2SAS_class(cls) - # note that sastransmission groups have a - # NX_class of NXdata but a canSAS_class of SAStransmission_spectrum - # which is ambiguous because then how can one tell if it is a SASdata - # or a SAStransmission_spectrum object from the NX_class? - if node.name.lower().startswith("sastransmission"): - cls = 'SAStransmission_spectrum' - return cls - - return None - -def NX2SAS_class(cls : str) -> str | None: - # converts NX class names to canSAS class names - mapping = { - "NXentry": "SASentry", - "NXdata": "SASdata", - "NXdetector": "SASdetector", - "NXinstrument": "SASinstrument", - "NXnote": "SASnote", - "NXprocess": "SASprocess", - "NXcollection": "SASprocessnote", - "NXsample": "SASsample", - "NXsource": "SASsource", - "NXaperture": "SASaperture", - "NXcollimator": "SAScollimation", - - "SASentry": "SASentry", - "SASdata": "SASdata", - "SASdetector": "SASdetector", - "SASinstrument": "SASinstrument", - "SASnote": "SASnote", - "SASprocess": "SASprocess", - "SASprocessnote": "SASprocessnote", - "SAStransmission_spectrum": "SAStransmission_spectrum", - "SASsample": "SASsample", - "SASsource": "SASsource", - "SASaperture": "SASaperture", - "SAScollimation": "SAScollimation", - } - if isinstance(cls, bytes): - cls = cls.decode() - return mapping.get(cls, None) - -def find_canSAS_key(node: HDF5Group, canSAS_class: str): - matches = [] - - for key, item in node.items(): - if item.attrs.get("canSAS_class") == canSAS_class: - matches.append(key) - - return matches - -def parse_quantity(node : HDF5Group) -> Quantity[float]: - """Pull a single quantity with units out of an HDF5 node""" - magnitude = parse_float(node) - unit = node.attrs["units"] - return Quantity(magnitude, parse(unit)) - -def parse_string(node : HDF5Group) -> str: - """Access string data from a node""" - if node.shape == (): # scalar dataset - return node.asstr()[()] - else: # vector dataset - return node.asstr()[0] - -def parse_float(node: HDF5Group) -> float: - """Return the first element (or scalar) of a numeric dataset as float.""" - if node.shape == (): - return float(node[()].astype(str)) - else: - return float(node[0].astype(str)) - -def opt_parse[T](node: HDF5Group, key: str, subparser: Callable[[HDF5Group], T], ignore_case=False) -> T | None: - """Parse a subnode if it is present""" - if ignore_case: # ignore the case of the key - key = next((k for k in node.keys() if k.lower() == key.lower()), None) - if key in node: - return subparser(node[key]) - return None - -def attr_parse(node: HDF5Group, key: str) -> str | None: - """Parse an attribute if it is present""" - if key in node.attrs: - return node.attrs[key] - return None - - -def parse_aperture(node : HDF5Group) -> Aperture: - distance = opt_parse(node, "distance", parse_quantity) - name = attr_parse(node, "name") - size = opt_parse(node, "size", parse_vec3) - size_name = None - type_ = attr_parse(node, "type") - if size: - size_name = attr_parse(node["size"], "name") - else: - size_name = None - return Aperture(distance=distance, size=size, size_name=size_name, name=name, type_=type_) - -def parse_beam_size(node : HDF5Group) -> BeamSize: - name = attr_parse(node, "name") - size = parse_vec3(node) - return BeamSize(name=name, size=size) - -def parse_source(node : HDF5Group) -> Source: - radiation = opt_parse(node, "radiation", parse_string) - beam_shape = opt_parse(node, "beam_shape", parse_string) - beam_size = opt_parse(node, "beam_size", parse_beam_size) - wavelength = opt_parse(node, "wavelength", parse_quantity) - if wavelength is None: - wavelength = opt_parse(node, "incident_wavelength", parse_quantity) - wavelength_min = opt_parse(node, "wavelength_min", parse_quantity) - wavelength_max = opt_parse(node, "wavelength_max", parse_quantity) - wavelength_spread = opt_parse(node, "wavelength_spread", parse_quantity) - if wavelength_spread is None: - wavelength_spread = opt_parse(node, "incident_wavelength_spread", parse_quantity) - return Source( - radiation=radiation, - beam_shape=beam_shape, - beam_size=beam_size, - wavelength=wavelength, - wavelength_min=wavelength_min, - wavelength_max=wavelength_max, - wavelength_spread=wavelength_spread, - ) - -def parse_vec3(node : HDF5Group) -> Vec3: - """Parse a measured 3-vector""" - x = opt_parse(node, "x", parse_quantity) - y = opt_parse(node, "y", parse_quantity) - z = opt_parse(node, "z", parse_quantity) - return Vec3(x=x, y=y, z=z) - -def parse_rot3(node : HDF5Group) -> Rot3: - """Parse a measured rotation""" - roll = opt_parse(node, "roll", parse_quantity) - pitch = opt_parse(node, "pitch", parse_quantity) - yaw = opt_parse(node, "yaw", parse_quantity) - return Rot3(roll=roll, pitch=pitch, yaw=yaw) - -def parse_detector(node : HDF5Group) -> Detector: - name = opt_parse(node, "name", parse_string) - distance = opt_parse(node, "SDD", parse_quantity) - offset = opt_parse(node, "offset", parse_vec3) - orientation = opt_parse(node, "orientation", parse_rot3) - beam_center = opt_parse(node, "beam_center", parse_vec3) - pixel_size = opt_parse(node, "pixel_size", parse_vec3) - slit_length = opt_parse(node, "slit_length", parse_quantity) - - return Detector(name=name, - distance=distance, - offset=offset, - orientation=orientation, - beam_center=beam_center, - pixel_size=pixel_size, - slit_length=slit_length) - - - -def parse_collimation(node : HDF5Group) -> Collimation: - length = opt_parse(node, "length", parse_quantity) - - keys = find_canSAS_key(node, "SASaperture") - keys = list(keys) if keys is not None else [] # list([1,2,3]) returns [1,2,3] and list("string") returns ["string"] - apertures = [parse_aperture(node[p]) for p in keys] # Empty list of keys will give an empty collimations list - - return Collimation(length=length, apertures=apertures) - - -def parse_instrument(node : HDF5Group) -> Instrument: - keys = find_canSAS_key(node, "SAScollimation") - keys = list(keys) if keys is not None else [] # list([1,2,3]) returns [1,2,3] and list("string") returns ["string"] - collimations = [parse_collimation(node[p]) for p in keys] # Empty list of keys will give an empty collimations list - - keys = find_canSAS_key(node, "SASdetector") - keys = list(keys) if keys is not None else [] # list([1,2,3]) returns [1,2,3] and list("string") returns ["string"] - detector = [parse_detector(node[p]) for p in keys] # Empty list of keys will give an empty collimations list - - keys = find_canSAS_key(node, "SASsource") - source = parse_source(node[keys[0]]) if keys is not None else None - - return Instrument( - collimations=collimations, - detector=detector, - source=source, - ) - -def parse_sample(node : HDF5Group) -> Sample: - name = attr_parse(node, "name") - sample_id = opt_parse(node, "ID", parse_string) - thickness = opt_parse(node, "thickness", parse_quantity) - transmission = opt_parse(node, "transmission", parse_float) - temperature = opt_parse(node, "temperature", parse_quantity) - position = opt_parse(node, "position", parse_vec3) - orientation = opt_parse(node, "orientation", parse_rot3) - details : list[str] = sum([list(node[d].asstr()[()]) for d in node if "details" in d], []) - return Sample(name=name, - sample_id=sample_id, - thickness=thickness, - transmission=transmission, - temperature=temperature, - position=position, - orientation=orientation, - details=details) - -def parse_term(node : HDF5Group) -> tuple[str, str | Quantity[float]] | None: - name = attr_parse(node, "name") - unit = attr_parse(node, "unit") - value = attr_parse(node, "value") - if name is None or value is None: - return None - if unit and unit.strip(): - return (name, Quantity(float(value), units.symbol_lookup[unit])) - return (name, value) - - -def parse_process(node : HDF5Group) -> Process: - name = opt_parse(node, "name", parse_string) - date = opt_parse(node, "date", parse_string) - description = opt_parse(node, "description", parse_string) - term_values = [parse_term(node[n]) for n in node if "term" in n] - terms = {tup[0]: tup[1] for tup in term_values if tup is not None} - notes = [parse_string(node[n]) for n in node if "note" in n] - return Process(name=name, date=date, description=description, terms=terms, notes=notes) - -def load_raw(node: HDF5Group | HDF5Dataset) -> MetaNode: - name = node.name.split("/")[-1] - match node: - case HDF5Group(): - attrib = {a: node.attrs[a] for a in node.attrs} - contents = [load_raw(node[v]) for v in node] - return MetaNode(name=name, attrs=attrib, contents=contents) - case HDF5Dataset(dtype=dt): - attrib = {a: node.attrs[a] for a in node.attrs} - if (str(dt).startswith("|S")): - if "units" in attrib: - contents = parse_string(node) - else: - contents = parse_string(node) - else: - if "units" in attrib and attrib["units"]: - data = node[()] if node.shape == () else node[:] - contents = Quantity(data, parse(attrib["units"]), id_header=node.name) - else: - contents = node[()] if node.shape == () else node[:] - return MetaNode(name=name, attrs=attrib, contents=contents) - case _: - raise RuntimeError(f"Cannot load raw data of type {type(node)}") - -def parse_metadata(node : HDF5Group) -> Metadata: - # parse the metadata groups - keys = find_canSAS_key(node, "SASinstrument") - keys = list(keys) if keys else [] # list([1,2,3]) returns [1,2,3] and list("string") returns ["string"] - instrument = parse_instrument(node[keys[0]]) if keys else None - - keys = find_canSAS_key(node, "SASsample") - keys = list(keys) if keys else [] # list([1,2,3]) returns [1,2,3] and list("string") returns ["string"] - sample = parse_sample(node[keys[0]]) if keys else None - - keys = find_canSAS_key(node, "SASprocess") - keys = list(keys) if keys else [] # list([1,2,3]) returns [1,2,3] and list("string") returns ["string"] - process = [parse_process(node[p]) for p in keys] # Empty list of keys will give an empty collimations list - - # parse the datasets - title = opt_parse(node, "title", parse_string) - run = [parse_string(node[r]) for r in node if "run" in r] - definition = opt_parse(node, "definition", parse_string) - - # load the entire node recursively into a raw object - raw = load_raw(node) - - return Metadata(process=process, - instrument=instrument, - sample=sample, - title=title, - run=run, - definition=definition, - raw=raw) - - -def load_data(filename: str) -> dict[str, SasData]: - with h5py.File(filename, "r") as f: - loaded_data: dict[str, SasData] = {} - - for root_key in f.keys(): entry = f[root_key] - # if this is actually a SASentry - if not get_canSAS_class(entry) == 'SASentry': - continue - data_contents : dict[str, Quantity] = {} - - entry_keys = entry.keys() + data_contents = [] + raw_metadata = {} - if not [k for k in entry_keys if get_canSAS_class(entry[k])=='SASdata']: - logger.warning("No sasdata or data key") - logger.warning(f"Known keys: {[k for k in entry_keys]}") + entry_keys = [key for key in entry.keys()] - metadata = parse_metadata(f[root_key]) + if "sasdata" not in entry_keys: + logger.warning("") for key in entry_keys: component = entry[key] - if get_canSAS_class(entry[key])=='SASdata': - datum = recurse_hdf5(component) - data_contents = connected_data(datum, str(filename), metadata) + if key.lower() == "sasdata": + print("found sasdata, skipping for now") - if "Qz" in data_contents: - dataset_type = three_dim - elif "Qy" in data_contents: - dataset_type = two_dim - else: - dataset_type = one_dim + else: + raw_metadata[key] = recurse_hdf5(component) - entry_key = entry.attrs["sasview_key"] if "sasview_key" in entry.attrs else root_key - loaded_data[entry_key] = SasData( + loaded_data.append( + RawData( name=root_key, - dataset_type=dataset_type, data_contents=data_contents, - metadata=metadata, - verbose=False, - ) + raw_metadata=raw_metadata)) return loaded_data -if __name__ == "__main__": - data = load_data(test_file) - for dataset in data.values(): - print(dataset.summary()) + +data = load_data(test_file) + +for dataset in data: + print(dataset) From 9daab878c9dc0aa204543373674f9b9c4456634a Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 28 Aug 2024 15:54:50 +0100 Subject: [PATCH 026/675] Basic reading --- sasdata/raw_form.py | 3 +-- sasdata/temp_hdf5_reader.py | 15 ++++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sasdata/raw_form.py b/sasdata/raw_form.py index 37736d37a..a58c09ce5 100644 --- a/sasdata/raw_form.py +++ b/sasdata/raw_form.py @@ -27,11 +27,10 @@ def summary(self, indent_amount: int = 0, indent: str = " ") -> str: if isinstance(value, (Group, Dataset)): value_string = value.summary(indent_amount+1, indent) else: - value_string = f"{indent * indent_amount}{self.data}\n" + value_string = f"{indent * (indent_amount+1)}{key}: {shorten_string(repr(value))}\n" s += value_string - return s @dataclass diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index b7194b008..e72e6a11c 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -53,8 +53,6 @@ def load_data(filename) -> list[RawData]: for root_key in f.keys(): - print(root_key) - entry = f[root_key] data_contents = [] @@ -63,15 +61,18 @@ def load_data(filename) -> list[RawData]: entry_keys = [key for key in entry.keys()] if "sasdata" not in entry_keys: - logger.warning("") + logger.warning("No sasdata key") for key in entry_keys: component = entry[key] - if key.lower() == "sasdata": - print("found sasdata, skipping for now") + # if key.lower() == "sasdata": + # datum = recurse_hdf5(component) + # data_contents.append(datum) + # + # else: + # raw_metadata[key] = recurse_hdf5(component) + raw_metadata[key] = recurse_hdf5(component) - else: - raw_metadata[key] = recurse_hdf5(component) loaded_data.append( From b49d03ec01406be2c1170ace3f06774cfdd4657e Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 29 Aug 2024 14:57:53 +0100 Subject: [PATCH 027/675] Work towards structuring inputs with uncertainties --- sasdata/quantities/quantity.py | 12 ++++++++++++ sasdata/temp_hdf5_reader.py | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index eec06609d..0388cdddb 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -85,3 +85,15 @@ def __init__(self, value: QuantityType, units: Unit, name: str): super().__init__(value, units) self.name = name + + +class UncertainQuantity[QuantityType](Quantity[QuantityType]): + def __init__(self, value: QuantityType, units: Unit, uncertainty: Quantity[QuantityType]): + super().__init__(value, units) + self.uncertainty = uncertainty + +class UncertainNamedQuantity[QuantityType](Quantity[QuantityType]): + def __init__(self, value: QuantityType, units: Unit, uncertainty: Quantity[QuantityType], name: str): + super().__init__(value, units) + self.uncertainty = uncertainty + self.name = name diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index e72e6a11c..51bd86695 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -11,12 +11,11 @@ from h5py._hl.group import Group as HDF5Group -from sasdata.data import DataSet from sasdata.raw_form import RawData from sasdata.raw_form import Dataset as SASDataDataset, Group as SASDataGroup test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" -test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" +# test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" logger = logging.getLogger(__name__) @@ -28,6 +27,7 @@ def recurse_hdf5(hdf5_entry): if isinstance(hdf5_entry.dtype, np.dtypes.BytesDType): data = hdf5_entry[()][0].decode("utf-8") + else: data = np.array(hdf5_entry, dtype=hdf5_entry.dtype) From 41957eccb697bbf272c006b39ec62e18052dc37b Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 9 Sep 2024 14:35:08 +0100 Subject: [PATCH 028/675] Work on uncertainty propagation --- sasdata/data.py | 2 +- sasdata/quantities/_accessor_base.py | 8 +- sasdata/quantities/absolute_temperature.py | 6 +- sasdata/quantities/accessors.py | 8 +- sasdata/quantities/operations.py | 712 +++++++++++++++++++++ sasdata/quantities/operations_examples.py | 2 +- sasdata/quantities/operations_test.py | 68 ++ sasdata/quantities/quantities_tests.py | 54 +- sasdata/quantities/quantity.py | 66 +- sasdata/transforms/operation.py | 4 +- 10 files changed, 864 insertions(+), 66 deletions(-) create mode 100644 sasdata/quantities/operations.py create mode 100644 sasdata/quantities/operations_test.py diff --git a/sasdata/data.py b/sasdata/data.py index b8daddce3..18fd296d5 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from sasdata.quantities.quantity import Quantity, NamedQuantity +from sasdata.quantities.quantity import BaseQuantity, NamedQuantity from sasdata.metadata import Metadata import numpy as np diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 42d14fc10..4ec7bf8cb 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -1,6 +1,6 @@ from typing import TypeVar, Sequence -from sasdata.quantities.quantity import Quantity +from sasdata.quantities.quantity import BaseQuantity import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit @@ -33,7 +33,7 @@ def value(self) -> float | None: -class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): +class QuantityAccessor[DataType](Accessor[DataType, BaseQuantity[DataType]]): """ Base class for accessors that work with quantities that have units """ def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): super().__init__(target_object, value_target) @@ -54,8 +54,8 @@ def unit(self) -> Unit: return Unit.parse(self._unit_part()) @property - def value(self) -> Quantity[DataType] | None: + def value(self) -> BaseQuantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self._numerical_part(), self.unit) + return BaseQuantity(self._numerical_part(), self.unit) diff --git a/sasdata/quantities/absolute_temperature.py b/sasdata/quantities/absolute_temperature.py index 95c8982fb..7bcccce4e 100644 --- a/sasdata/quantities/absolute_temperature.py +++ b/sasdata/quantities/absolute_temperature.py @@ -1,6 +1,6 @@ from typing import TypeVar -from quantities.quantity import Quantity +from quantities.quantity import BaseQuantity from sasdata.quantities.accessors import TemperatureAccessor @@ -8,8 +8,8 @@ class AbsoluteTemperatureAccessor(TemperatureAccessor[DataType]): """ Parsing for absolute temperatures """ @property - def value(self) -> Quantity[DataType] | None: + def value(self) -> BaseQuantity[DataType] | None: if self._numerical_part() is None: return None else: - return Quantity.parse(self._numerical_part(), self._unit_part(), absolute_temperature=True) + return BaseQuantity.parse(self._numerical_part(), self._unit_part(), absolute_temperature=True) diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 92ef82bd1..caaeb0022 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -80,7 +80,7 @@ from typing import TypeVar, Sequence -from sasdata.quantities.quantity import Quantity +from sasdata.quantities.quantity import BaseQuantity import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit @@ -113,7 +113,7 @@ def value(self) -> float | None: -class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): +class QuantityAccessor[DataType](Accessor[DataType, BaseQuantity[DataType]]): """ Base class for accessors that work with quantities that have units """ def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): super().__init__(target_object, value_target) @@ -134,9 +134,9 @@ def unit(self) -> Unit: return Unit.parse(self._unit_part()) @property - def value(self) -> Quantity[DataType] | None: + def value(self) -> BaseQuantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self._numerical_part(), self.unit) + return BaseQuantity(self._numerical_part(), self.unit) diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py new file mode 100644 index 000000000..60590333d --- /dev/null +++ b/sasdata/quantities/operations.py @@ -0,0 +1,712 @@ +from typing import Any, TypeVar, Union + +import json + +from sasdata.quantities.quantity import BaseQuantity + +T = TypeVar("T") + +def hash_and_name(hash_or_name: int | str): + """ Infer the name of a variable from a hash, or the hash from the name + + Note: hash_and_name(hash_and_name(number)[1]) is not the identity + however: hash_and_name(hash_and_name(number)) is + """ + + if isinstance(hash_or_name, str): + hash_value = hash(hash_or_name) + name = hash_or_name + + return hash_value, name + + elif isinstance(hash_or_name, int): + hash_value = hash_or_name + name = f"#{hash_or_name}" + + return hash_value, name + + elif isinstance(hash_or_name, tuple): + return hash_or_name + + else: + raise TypeError("Variable name_or_hash_value must be either str or int") + + +class Operation: + + serialisation_name = "unknown" + def summary(self, indent_amount: int = 0, indent: str=" "): + """ Summary of the operation tree""" + + s = f"{indent_amount*indent}{self._summary_open()}(\n" + + for chunk in self._summary_components(): + s += chunk.summary(indent_amount+1, indent) + "\n" + + s += f"{indent_amount*indent})" + + return s + def _summary_open(self): + """ First line of summary """ + + def _summary_components(self) -> list["Operation"]: + return [] + def evaluate(self, variables: dict[int, T]) -> T: + + """ Evaluate this operation """ + + def _derivative(self, hash_value: int) -> "Operation": + """ Get the derivative of this operation """ + + def _clean(self): + """ Clean up this operation - i.e. remove silly things like 1*x """ + return self + + def derivative(self, variable: Union[str, int, "Variable"], simplify=True): + if isinstance(variable, Variable): + hash_value = variable.hash_value + else: + hash_value, _ = hash_and_name(variable) + + derivative = self._derivative(hash_value) + + if not simplify: + return derivative + + derivative_string = derivative.serialise() + + print("---------------") + print("Base") + print("---------------") + print(derivative.summary()) + + # Inefficient way of doing repeated simplification, but it will work + for i in range(100): # set max iterations + + derivative = derivative._clean() + + print("-------------------") + print("Iteration", i+1) + print("-------------------") + print(derivative.summary()) + print("-------------------") + + new_derivative_string = derivative.serialise() + + if derivative_string == new_derivative_string: + break + + derivative_string = new_derivative_string + + return derivative + + @staticmethod + def deserialise(data: str) -> "Operation": + json_data = json.loads(data) + return Operation.deserialise_json(json_data) + + @staticmethod + def deserialise_json(json_data: dict) -> "Operation": + + operation = json_data["operation"] + parameters = json_data["parameters"] + cls = _serialisation_lookup[operation] + + try: + return cls._deserialise(parameters) + + except NotImplementedError: + raise NotImplementedError(f"No method to deserialise {operation} with {parameters} (cls={cls})") + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + raise NotImplementedError(f"Deserialise not implemented for this class") + + def serialise(self) -> str: + return json.dumps(self._serialise_json()) + + def _serialise_json(self) -> dict[str, Any]: + return {"operation": self.serialisation_name, + "parameters": self._serialise_parameters()} + + def _serialise_parameters(self) -> dict[str, Any]: + raise NotImplementedError("_serialise_parameters not implemented") + + def __eq__(self, other: "Operation"): + return NotImplemented + +class ConstantBase(Operation): + pass + +class AdditiveIdentity(ConstantBase): + + serialisation_name = "zero" + def evaluate(self, variables: dict[int, T]) -> T: + return 0 + + def _derivative(self, hash_value: int) -> Operation: + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return AdditiveIdentity() + + def _serialise_parameters(self) -> dict[str, Any]: + return {} + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}0 [Add.Id.]" + + def __eq__(self, other): + if isinstance(other, AdditiveIdentity): + return True + elif isinstance(other, Constant): + if other.value == 0: + return True + + return False + + + +class MultiplicativeIdentity(ConstantBase): + + serialisation_name = "one" + + def evaluate(self, variables: dict[int, T]) -> T: + return 1 + + def _derivative(self, hash_value: int): + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return MultiplicativeIdentity() + + + def _serialise_parameters(self) -> dict[str, Any]: + return {} + + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}1 [Mul.Id.]" + + def __eq__(self, other): + if isinstance(other, MultiplicativeIdentity): + return True + elif isinstance(other, Constant): + if other.value == 1: + return True + + return False + + +class Constant(ConstantBase): + + serialisation_name = "constant" + def __init__(self, value): + self.value = value + + def summary(self, indent_amount: int = 0, indent: str=" "): + pass + + def evaluate(self, variables: dict[int, T]) -> T: + return self.value + + def _derivative(self, hash_value: int): + return AdditiveIdentity() + + def _clean(self): + + if self.value == 0: + return AdditiveIdentity() + + elif self.value == 1: + return MultiplicativeIdentity() + + else: + return self + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + value = parameters["value"] + return Constant(value) + + + def _serialise_parameters(self) -> dict[str, Any]: + return {"value": self.value} + + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}{self.value}" + + def __eq__(self, other): + if isinstance(other, AdditiveIdentity): + return self.value == 0 + + elif isinstance(other, MultiplicativeIdentity): + return self.value == 1 + + elif isinstance(other, Constant): + if other.value == self.value: + return True + + return False + + +class Variable(Operation): + + serialisation_name = "variable" + def __init__(self, name_or_hash_value: int | str | tuple[int, str]): + self.hash_value, self.name = hash_and_name(name_or_hash_value) + + def evaluate(self, variables: dict[int, T]) -> T: + try: + return variables[self.hash_value] + except KeyError: + raise ValueError(f"Variable dictionary didn't have an entry for {self.name} (hash={self.hash_value})") + + def _derivative(self, hash_value: int) -> Operation: + if hash_value == self.hash_value: + return MultiplicativeIdentity() + else: + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + hash_value = parameters["hash_value"] + name = parameters["name"] + + return Variable((hash_value, name)) + + def _serialise_parameters(self) -> dict[str, Any]: + return {"hash_value": self.hash_value, + "name": self.name} + + def summary(self, indent_amount: int = 0, indent: str=" "): + return f"{indent_amount*indent}{self.name}" + + def __eq__(self, other): + if isinstance(other, Variable): + return self.hash_value == other.hash_value + + return False + +class UnaryOperation(Operation): + + def __init__(self, a: Operation): + self.a = a + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": self.a._serialise_json()} + + def _summary_components(self) -> list["Operation"]: + return [self.a] + + + + +class Neg(UnaryOperation): + + serialisation_name = "neg" + def evaluate(self, variables: dict[int, T]) -> T: + return -self.a.evaluate(variables) + + def _derivative(self, hash_value: int): + return Neg(self.a._derivative(hash_value)) + + def _clean(self): + + clean_a = self.a._clean() + + if isinstance(clean_a, Neg): + # Removes double negations + return clean_a.a + + elif isinstance(clean_a, Constant): + return Constant(-clean_a.value)._clean() + + else: + return Neg(clean_a) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Neg(Operation.deserialise_json(parameters["a"])) + + + def _summary_open(self): + return "Neg" + + def __eq__(self, other): + if isinstance(other, Neg): + return other.a == self.a + + +class Inv(UnaryOperation): + + serialisation_name = "reciprocal" + + def evaluate(self, variables: dict[int, T]) -> T: + return 1/self.a.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Neg(Div(self.a._derivative(hash_value), Mul(self.a, self.a))) + + def _clean(self): + clean_a = self.a._clean() + + if isinstance(clean_a, Inv): + # Removes double negations + return clean_a.a + + elif isinstance(clean_a, Neg): + # cannonicalise 1/-a to -(1/a) + # over multiple iterations this should have the effect of ordering and gathering Neg and Inv + return Neg(Inv(clean_a.a)) + + elif isinstance(clean_a, Constant): + return Constant(1/clean_a.value)._clean() + + else: + return Inv(clean_a) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Inv(Operation.deserialise_json(parameters["a"])) + + def _summary_open(self): + return "Inv" + + + def __eq__(self, other): + if isinstance(other, Inv): + return other.a == self.a + +class BinaryOperation(Operation): + def __init__(self, a: Operation, b: Operation): + self.a = a + self.b = b + + def _clean(self): + return self._clean_ab(self.a._clean(), self.b._clean()) + + def _clean_ab(self, a, b): + raise NotImplementedError("_clean_ab not implemented") + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": self.a._serialise_json(), + "b": self.b._serialise_json()} + + @staticmethod + def _deserialise_ab(parameters) -> tuple[Operation, Operation]: + return (Operation.deserialise_json(parameters["a"]), + Operation.deserialise_json(parameters["b"])) + + + def _summary_components(self) -> list["Operation"]: + return [self.a, self.b] + + def _self_cls(self) -> type: + """ Own class""" + def __eq__(self, other): + if isinstance(other, self._self_cls()): + return other.a == self.a and self.b == other.b + +class Add(BinaryOperation): + + serialisation_name = "add" + + def _self_cls(self) -> type: + return Add + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) + self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add(self.a._derivative(hash_value), self.b._derivative(hash_value)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity): + # Convert 0 + b to b + return b + + elif isinstance(b, AdditiveIdentity): + # Convert a + 0 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"+"b" to "a+b" + return Constant(a.evaluate({}) + b.evaluate({}))._clean() + + elif isinstance(a, Neg): + if isinstance(b, Neg): + # Convert (-a)+(-b) to -(a+b) + return Neg(Add(a.a, b.a)) + else: + # Convert (-a) + b to b-a + return Sub(b, a.a) + + elif isinstance(b, Neg): + # Convert a+(-b) to a-b + return Sub(a, b.a) + + elif a == b: + return Mul(Constant(2), a) + + else: + return Add(a, b) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Add(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Add" + +class Sub(BinaryOperation): + + serialisation_name = "sub" + + + def _self_cls(self) -> type: + return Sub + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) - self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Sub(self.a._derivative(hash_value), self.b._derivative(hash_value)) + + def _clean_ab(self, a, b): + if isinstance(a, AdditiveIdentity): + # Convert 0 - b to -b + return Neg(b) + + elif isinstance(b, AdditiveIdentity): + # Convert a - 0 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant pair "a" - "b" to "a-b" + return Constant(a.evaluate({}) - b.evaluate({}))._clean() + + elif isinstance(a, Neg): + if isinstance(b, Neg): + # Convert (-a)-(-b) to b-a + return Sub(b.a, a.a) + else: + # Convert (-a)-b to -(a+b) + return Neg(Add(a.a, b)) + + elif isinstance(b, Neg): + # Convert a-(-b) to a+b + return Add(a, b.a) + + elif a == b: + return AdditiveIdentity() + + else: + return Sub(a, b) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Sub(*BinaryOperation._deserialise_ab(parameters)) + + + def _summary_open(self): + return "Sub" + +class Mul(BinaryOperation): + + serialisation_name = "mul" + + + def _self_cls(self) -> type: + return Mul + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) * self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add(Mul(self.a, self.b._derivative(hash_value)), Mul(self.a._derivative(hash_value), self.b)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): + # Convert 0*b or a*0 to 0 + return AdditiveIdentity() + + elif isinstance(a, MultiplicativeIdentity): + # Convert 1*b to b + return b + + elif isinstance(b, MultiplicativeIdentity): + # Convert a*1 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"*"b" to "a*b" + return Constant(a.evaluate({}) * b.evaluate({}))._clean() + + elif isinstance(a, Inv) and isinstance(b, Inv): + return Inv(Mul(a.a, b.a)) + + elif isinstance(a, Inv) and not isinstance(b, Inv): + return Div(b, a.a) + + elif not isinstance(a, Inv) and isinstance(b, Inv): + return Div(a, b.a) + + elif isinstance(a, Neg): + return Neg(Mul(a.a, b)) + + elif isinstance(b, Neg): + return Neg(Mul(a, b.a)) + + elif a == b: + return Pow(a, 2) + + elif isinstance(a, Pow) and a.a == b: + return Pow(b, a.power + 1) + + elif isinstance(b, Pow) and b.a == a: + return Pow(a, b.power + 1) + + elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: + return Pow(a.a, a.power + b.power) + + else: + return Mul(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Mul(*BinaryOperation._deserialise_ab(parameters)) + + + def _summary_open(self): + return "Mul" + +class Div(BinaryOperation): + + serialisation_name = "div" + + + def _self_cls(self) -> type: + return Div + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) / self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Sub(Div(self.a.derivative(hash_value), self.b), + Div(Mul(self.a, self.b.derivative(hash_value)), Mul(self.b, self.b))) + + def _clean_ab(self, a, b): + if isinstance(a, AdditiveIdentity): + # Convert 0/b to 0 + return AdditiveIdentity() + + elif isinstance(a, MultiplicativeIdentity): + # Convert 1/b to inverse of b + return Inv(b) + + elif isinstance(b, MultiplicativeIdentity): + # Convert a/1 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constants "a"/"b" to "a/b" + return Constant(self.a.evaluate({}) / self.b.evaluate({}))._clean() + + + elif isinstance(a, Inv) and isinstance(b, Inv): + return Div(b.a, a.a) + + elif isinstance(a, Inv) and not isinstance(b, Inv): + return Inv(Mul(a.a, b)) + + elif not isinstance(a, Inv) and isinstance(b, Inv): + return Mul(a, b.a) + + elif a == b: + return MultiplicativeIdentity() + + elif isinstance(a, Pow) and a.a == b: + return Pow(b, a.power - 1) + + elif isinstance(b, Pow) and b.a == a: + return Pow(a, 1 - b.power) + + elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: + return Pow(a.a, a.power - b.power) + + else: + return Div(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Div(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Div" + +class Pow(Operation): + + serialisation_name = "pow" + + def __init__(self, a: Operation, power: float): + self.a = a + self.power = power + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) ** self.power + + def _derivative(self, hash_value: int) -> Operation: + if self.power == 0: + return AdditiveIdentity() + + elif self.power == 1: + return self.a._derivative(hash_value) + + else: + return Mul(Constant(self.power), Mul(Pow(self.a, self.power-1), self.a._derivative(hash_value))) + + def _clean(self) -> Operation: + a = self.a._clean() + + if self.power == 1: + return a + + elif self.power == 0: + return MultiplicativeIdentity() + + elif self.power == -1: + return Inv(a) + + else: + return Pow(a, self.power) + + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": Operation._serialise_json(self.a), + "power": self.power} + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Pow(Operation.deserialise_json(parameters["a"]), parameters["power"]) + + def summary(self, indent_amount: int=0, indent=" "): + return (f"{indent_amount*indent}Pow\n" + + self.a.summary(indent_amount+1, indent) + "\n" + + f"{(indent_amount+1)*indent}{self.power}\n" + + f"{indent_amount*indent})") + + def __eq__(self, other): + if isinstance(other, Pow): + return self.a == other.a and self.power == other.power + +_serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, + Variable, + Neg, Inv, + Add, Sub, Mul, Div, Pow] + +_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} \ No newline at end of file diff --git a/sasdata/quantities/operations_examples.py b/sasdata/quantities/operations_examples.py index 29b50ccc0..194980e83 100644 --- a/sasdata/quantities/operations_examples.py +++ b/sasdata/quantities/operations_examples.py @@ -1,4 +1,4 @@ -from sasdata.quantities.operations import Mul, Variable +from quantities.operations import Variable, Mul x = Variable("x") y = Variable("y") diff --git a/sasdata/quantities/operations_test.py b/sasdata/quantities/operations_test.py new file mode 100644 index 000000000..6fffb3680 --- /dev/null +++ b/sasdata/quantities/operations_test.py @@ -0,0 +1,68 @@ +import pytest + +from sasdata.quantities.operations import Operation, \ + Neg, Inv, \ + Add, Sub, Mul, Div, Pow, \ + Variable, Constant, AdditiveIdentity, MultiplicativeIdentity + +operation_with_everything = \ + Div( + Pow( + Mul( + Sub( + Add( + Neg(Inv(MultiplicativeIdentity())), + Variable("x")), + Constant(7)), + AdditiveIdentity()), + 2), + Variable("y")) + +def test_serialise_deserialise(): + print(operation_with_everything._serialise_json()) + + serialised = operation_with_everything.serialise() + deserialised = Operation.deserialise(serialised) + reserialised = deserialised.serialise() + + assert serialised == reserialised + + +@pytest.mark.parametrize("op, a, b, result", [ + (Add, 1, 1, 2), + (Add, 7, 8, 15), + (Sub, 1, 1, 0), + (Sub, 7, 8, -1), + (Mul, 1, 1, 1), + (Mul, 7, 8, 56), + (Div, 1, 1, 1), + (Div, 7, 8, 7/8), + (Pow, 1, 1, 1), + (Pow, 7, 2, 49)]) +def test_binary_evaluation(op, a, b, result): + f = op(Constant(a), b if op == Pow else Constant(b)) + assert f.evaluate({}) == result + +x = Variable("x") +y = Variable("y") +z = Variable("z") +@pytest.mark.parametrize("x_over_x", [ + Div(x,x), + Mul(Inv(x), x), + Mul(x, Inv(x)), +]) +def test_dx_over_x_by_dx_should_be_zero(x_over_x): + + + dfdx = x_over_x.derivative(x) + + print(dfdx.summary()) + + assert dfdx == AdditiveIdentity() + + +def test_d_xyz_by_components_should_be_1(): + f = Mul(Mul(x, y), z) + assert f.derivative(x).derivative(y).derivative(z) == MultiplicativeIdentity() + + diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index 19237cfac..dd7271bee 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -1,72 +1,72 @@ import numpy as np -from sasdata.quantities.quantity import Quantity, UnitError +from sasdata.quantities.quantity import BaseQuantity, UnitError import sasdata.quantities.units as units import sasdata.quantities.si as si import pytest def test_in_units_of_calculation(): """ Just a couple of unit conversions """ - assert Quantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 - assert Quantity(10, units.minutes).in_units_of(units.seconds) == 600 - assert Quantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000/9.81, abs=1) - assert Quantity(0, units.meters).in_units_of(units.exameters) == 0 + assert BaseQuantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 + assert BaseQuantity(10, units.minutes).in_units_of(units.seconds) == 600 + assert BaseQuantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000 / 9.81, abs=1) + assert BaseQuantity(0, units.meters).in_units_of(units.exameters) == 0 def test_unit_compounding_pow(): """ Test units compound correctly when __pow__ is used""" - assert (Quantity(1, units.millimeters)**2).in_units_of(units.square_meters) == 1e-6 - assert (Quantity(1, units.minutes)**3).in_units_of(units.seconds**3) == 60**3 + assert (BaseQuantity(1, units.millimeters) ** 2).in_units_of(units.square_meters) == 1e-6 + assert (BaseQuantity(1, units.minutes) ** 3).in_units_of(units.seconds ** 3) == 60 ** 3 def test_unit_compounding_mul(): """ Test units compound correctly when __mul__ is used""" - assert (Quantity(4, units.minutes) * Quantity(0.25, units.hertz)).in_units_of(units.none) == 60 - assert (Quantity(250, units.volts) * Quantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 + assert (BaseQuantity(4, units.minutes) * BaseQuantity(0.25, units.hertz)).in_units_of(units.none) == 60 + assert (BaseQuantity(250, units.volts) * BaseQuantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 def test_unit_compounding_div(): """ Test units compound correctly when __truediv__ is used""" - assert (Quantity(10, units.kilometers) / Quantity(2, units.minutes) + assert (BaseQuantity(10, units.kilometers) / BaseQuantity(2, units.minutes) ).in_units_of(units.meters_per_second) == pytest.approx(250/3, abs=1e-6) - assert (Quantity(1, units.nanowebers) / (Quantity(1, units.millimeters)**2)).in_units_of(units.millitesla) == 1 + assert (BaseQuantity(1, units.nanowebers) / (BaseQuantity(1, units.millimeters) ** 2)).in_units_of(units.millitesla) == 1 def test_value_mul(): """ Test value part of quantities multiply correctly""" - assert (Quantity(1j, units.seconds) * Quantity(1j, units.watts)).in_units_of(units.joules) == -1 + assert (BaseQuantity(1j, units.seconds) * BaseQuantity(1j, units.watts)).in_units_of(units.joules) == -1 def test_scalar_mul(): - assert (Quantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 - assert (10 * Quantity(1, units.seconds)).in_units_of(units.seconds) == 10 - assert (1000 * Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 + assert (BaseQuantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 + assert (10 * BaseQuantity(1, units.seconds)).in_units_of(units.seconds) == 10 + assert (1000 * BaseQuantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 def test_scalar_div(): - assert (Quantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 - assert (10 / Quantity(1, units.seconds)).in_units_of(units.hertz) == 10 - assert (0.001 / Quantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 + assert (BaseQuantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 + assert (10 / BaseQuantity(1, units.seconds)).in_units_of(units.hertz) == 10 + assert (0.001 / BaseQuantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 def test_good_add_sub(): """ Test that adding and subtracting units works """ - assert (Quantity(1, units.seconds) + Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 - assert (Quantity(1, units.seconds) - Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 + assert (BaseQuantity(1, units.seconds) + BaseQuantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 + assert (BaseQuantity(1, units.seconds) - BaseQuantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 - assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == 13 + assert (BaseQuantity(1, units.inches) + BaseQuantity(1, units.feet)).in_units_of(units.inches) == 13 @pytest.mark.parametrize("unit_1", si.all_si) @pytest.mark.parametrize("unit_2", si.all_si) def test_mixed_quantity_add_sub(unit_1, unit_2): if unit_1.equivalent(unit_2): - assert (Quantity(0, unit_1) + Quantity(0, unit_2)).in_units_of(unit_1) == 0 + assert (BaseQuantity(0, unit_1) + BaseQuantity(0, unit_2)).in_units_of(unit_1) == 0 else: with pytest.raises(UnitError): - Quantity(1, unit_1) + Quantity(1, unit_2) + BaseQuantity(1, unit_1) + BaseQuantity(1, unit_2) def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float, abs=1e-9): """ Helper function for testing units that are multiples of each other """ assert u1.equivalent(u2), "Units should be compatible for this test" - assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) + assert (BaseQuantity(1, u1) / BaseQuantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) def test_american_units(): @@ -76,7 +76,7 @@ def test_american_units(): assert_unit_ratio(units.pounds_force_per_square_inch, units.pounds_force / (units.inches**2), 1, abs=1e-5) def test_percent(): - assert Quantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) + assert BaseQuantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) @pytest.mark.parametrize("unit_1", si.all_si) @pytest.mark.parametrize("unit_2", si.all_si) @@ -84,9 +84,9 @@ def test_conversion_errors(unit_1, unit_2): """ Test conversion errors are thrown when units are not compatible """ if unit_1 == unit_2: - assert Quantity(1, unit_1).in_units_of(unit_2) == 1 + assert BaseQuantity(1, unit_1).in_units_of(unit_2) == 1 else: with pytest.raises(UnitError): - Quantity(1, units.seconds).in_units_of(units.meters) + BaseQuantity(1, units.seconds).in_units_of(units.meters) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 0388cdddb..41f710eff 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -3,8 +3,11 @@ from numpy._typing import ArrayLike +from quantities.operations import Operation, Variable from sasdata.quantities.units import Unit +import hashlib + class UnitError(Exception): """Errors caused by unit specification not being correct""" @@ -12,7 +15,7 @@ class UnitError(Exception): QuantityType = TypeVar("QuantityType") -class Quantity[QuantityType]: +class BaseQuantity[QuantityType]: def __init__(self, value: QuantityType, units: Unit): self.value = value self.units = units @@ -24,38 +27,37 @@ def in_units_of(self, units: Unit) -> QuantityType: raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") def __mul__(self: Self, other: ArrayLike | Self ) -> Self: - if isinstance(other, Quantity): - return Quantity(self.value * other.value, self.units * other.units) + if isinstance(other, BaseQuantity): + return BaseQuantity(self.value * other.value, self.units * other.units) else: - return Quantity(self.value * other, self.units) + return BaseQuantity(self.value * other, self.units) def __rmul__(self: Self, other: ArrayLike | Self): - if isinstance(other, Quantity): - return Quantity(other.value * self.value, other.units * self.units) + if isinstance(other, BaseQuantity): + return BaseQuantity(other.value * self.value, other.units * self.units) else: - return Quantity(other * self.value, self.units) - + return BaseQuantity(other * self.value, self.units) def __truediv__(self: Self, other: float | Self) -> Self: - if isinstance(other, Quantity): - return Quantity(self.value / other.value, self.units / other.units) + if isinstance(other, BaseQuantity): + return BaseQuantity(self.value / other.value, self.units / other.units) else: - return Quantity(self.value / other, self.units) + return BaseQuantity(self.value / other, self.units) def __rtruediv__(self: Self, other: float | Self) -> Self: - if isinstance(other, Quantity): - return Quantity(self.value / other.value, self.units / other.units) + if isinstance(other, BaseQuantity): + return BaseQuantity(self.value / other.value, self.units / other.units) else: - return Quantity(self.value / other, self.units) + return BaseQuantity(self.value / other, self.units) def __add__(self: Self, other: Self | ArrayLike) -> Self: - if isinstance(other, Quantity): + if isinstance(other, BaseQuantity): if self.units.equivalent(other.units): - return Quantity(self.value + (other.value * other.units.scale)/self.units.scale, self.units) + return BaseQuantity(self.value + (other.value * other.units.scale) / self.units.scale, self.units) else: raise UnitError(f"Units do not have the same dimensionality: {self.units} vs {other.units}") @@ -65,7 +67,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: # Don't need __radd__ because only quantity/quantity operations should be allowed def __neg__(self): - return Quantity(-self.value, self.units) + return BaseQuantity(-self.value, self.units) def __sub__(self: Self, other: Self | ArrayLike) -> Self: return self + (-other) @@ -74,26 +76,42 @@ def __rsub__(self: Self, other: Self | ArrayLike) -> Self: return (-self) + other def __pow__(self: Self, other: int): - return Quantity(self.value**other, self.units**other) + return BaseQuantity(self.value ** other, self.units ** other) - def parse(self, number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): + @staticmethod + def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass -class NamedQuantity[QuantityType](Quantity[QuantityType]): +class Quantity[QuantityType](BaseQuantity[QuantityType]): + def with_uncertainty(self, uncertainty: BaseQuantity[QuantityType]): + return UncertainQuantity(self.value, self.units, uncertainty=uncertainty) + + +class NamedQuantity[QuantityType](BaseQuantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, name: str): super().__init__(value, units) self.name = name + def with_uncertainty(self, uncertainty: BaseQuantity[QuantityType]): + return UncertainNamedQuantity(self.value, self.units, uncertainty=uncertainty, name=self.name) -class UncertainQuantity[QuantityType](Quantity[QuantityType]): - def __init__(self, value: QuantityType, units: Unit, uncertainty: Quantity[QuantityType]): +class UncertainBaseQuantity[QuantityType](BaseQuantity[QuantityType]): + pass + +class UncertainQuantity[QuantityType](BaseQuantity[QuantityType]): + def __init__(self, value: QuantityType, units: Unit, uncertainty: BaseQuantity[QuantityType]): super().__init__(value, units) self.uncertainty = uncertainty -class UncertainNamedQuantity[QuantityType](Quantity[QuantityType]): - def __init__(self, value: QuantityType, units: Unit, uncertainty: Quantity[QuantityType], name: str): + hash_value = hashlib.md5(value, uncertainty) + + +class UncertainNamedQuantity[QuantityType](BaseQuantity[QuantityType]): + def __init__(self, value: QuantityType, units: Unit, uncertainty: BaseQuantity[QuantityType], name: str): super().__init__(value, units) self.uncertainty = uncertainty self.name = name + + self.history = Variable(self.name) \ No newline at end of file diff --git a/sasdata/transforms/operation.py b/sasdata/transforms/operation.py index 59121882d..b7c54ad2b 100644 --- a/sasdata/transforms/operation.py +++ b/sasdata/transforms/operation.py @@ -1,5 +1,5 @@ import numpy as np -from sasdata.quantities.quantity import Quantity +from sasdata.quantities.quantity import BaseQuantity class Operation: """ Sketch of what model post-processing classes might look like """ @@ -11,7 +11,7 @@ class Operation: def name(self) -> str: raise NotImplementedError("No name for transform") - def evaluate(self) -> Quantity[np.ndarray]: + def evaluate(self) -> BaseQuantity[np.ndarray]: pass def __call__(self, *children, **named_children): From f3ab2405d4b801274b1e6c486be69b455b8a8281 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 09:07:23 +0100 Subject: [PATCH 029/675] Added some code to enable test driven development. --- sasdata/quantities/unit_parser.py | 214 +----------------------------- 1 file changed, 4 insertions(+), 210 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index a264489d6..97e1d0847 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,212 +1,6 @@ -from re import findall, fullmatch +from sasdata.quantities.units import NamedUnit -from sasdata.quantities.units import Dimensions, NamedUnit, Unit, UnitGroup, symbol_lookup, unit_groups -# TODO: This shouldn't be in this file but I don't want to edit Lucas' code before he is finished. - -all_units_groups = [group.units for group in unit_groups.values()] -unit_groups_by_dimension_hash = {hash(group.units[0].dimensions): group for group in unit_groups.values()} -all_units: list[NamedUnit] = [] -for group in all_units_groups: - all_units.extend(group) - - -def split_unit_str(unit_str: str) -> list[str]: - """Separate the letters from the numbers in unit_str""" - return findall(r"[A-Za-zΩ%Å]+|[-\d]+|/", unit_str) - - -def validate_unit_str(unit_str: str) -> bool: - """Validate whether unit_str is valid. This doesn't mean that the unit specified in unit_str exists but rather it - only consists of letters, and numbers as a unit string should.""" - return fullmatch(r"[A-Za-zΩµ%Å^1-9⁻¹-⁹\-\+/\ \._]+", unit_str) is not None - - -def parse_single_unit( - unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True -) -> tuple[Unit | None, str]: - """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit - cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str. - - The shortest_unit parameter specifies how to resolve ambiguities. If it is true, then it will parse the longest unit - available. Otherwise, it will stop parsing as soon as it has found any unit. - - If unit_group is set, it will only try to parse units within that group. This is useful for resolving ambiguities. - """ - current_unit = "" - string_pos = 0 - if unit_group is None: - lookup_dict = symbol_lookup - else: - lookup_dict = dict([(name, unit) for name, unit in symbol_lookup.items() if unit in unit_group.units]) - for next_char in unit_str: - potential_unit_str = current_unit + next_char - potential_symbols = [ - symbol - for symbol, unit in lookup_dict.items() - if symbol.startswith(potential_unit_str) or unit.startswith(potential_unit_str) - ] - if len(potential_symbols) == 0: - break - string_pos += 1 - current_unit = potential_unit_str - if not longest_unit and current_unit in lookup_dict: - break - if current_unit == "": - return None, unit_str - matching_types = [unit for symbol, unit in lookup_dict.items() if symbol == current_unit or unit == current_unit] - if not matching_types: - raise KeyError(f"No known type matching {current_unit}") - final_unit = matching_types[0] - remaining_str = unit_str[string_pos::] - return final_unit, remaining_str - - -def parse_unit_strs(unit_str: str, current_units: list[Unit] | None = None, longest_unit: bool = True) -> list[Unit]: - """Recursively parse units from unit_str until no more characters are present.""" - if current_units is None: - current_units = [] - if unit_str == "": - return current_units - parsed_unit, remaining_str = parse_single_unit(unit_str, longest_unit=longest_unit) - if parsed_unit is not None: - current_units += [parsed_unit] - return parse_unit_strs(remaining_str, current_units, longest_unit) - else: - raise ValueError(f"Could not interpret {remaining_str}") - - -# Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there -# are two functions. - - -def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: - """Split unit_str into a stack of parsed units.""" - unit_stack: list[Unit] = [] - split_str = split_unit_str(unit_str) - inverse_next_unit = False - for token in split_str: - try: - if token == "/": - inverse_next_unit = True - continue - power = int(token) - to_modify = unit_stack[-1] - modified = to_modify**power - # modified = unit_power(to_modify, power) - unit_stack[-1] = modified - except ValueError: - new_units = parse_unit_strs(token, None, longest_unit) - if inverse_next_unit: - # TODO: Assume the power is going to be -1. This might not be true. - power = -1 - new_units[0] = new_units[0] ** power - # new_units[0] = unit_power(new_units[0], power) - unit_stack += new_units - # This error will happen if it tries to read a modifier but there are no units on the stack. We will just have - # to ignore it. Strings being parsed shouldn't really have it anyway (e.g. -1m). - except IndexError: - pass - return unit_stack - - -def known_mistake(unit_str: str) -> Unit | None: - """Take known broken units from historical files - and give them a reasonible parse""" - import sasdata.quantities.units as units - - mistakes = {"µm": units.micrometers, "per_centimeter": units.per_centimeter, "per_angstrom": units.per_angstrom} - if unit_str in mistakes: - return mistakes[unit_str] - return None - - -def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: - """Parse unit_str into a unit.""" - if result := known_mistake(unit_str): - return result - try: - if not validate_unit_str(unit_str): - raise ValueError(f"unit_str ({unit_str}) contains forbidden characters.") - parsed_unit = Unit(1, Dimensions()) - unit_stack = parse_unit_stack(unit_str, longest_unit) - for unit in unit_stack: - # parsed_unit = combine_units(parsed_unit, unit) - parsed_unit *= unit - return parsed_unit - except KeyError as ex: - raise ValueError(f"Unit string contains an unrecognised pattern: {unit_str}") from ex - - -def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: - """Tries to use the given unit group to resolve ambiguities. Parse a unit twice with different options, and returns - whatever conforms to the unit group.""" - longest_parsed_unit = parse_unit(unit_str, True) - shortest_parsed_unit = parse_unit(unit_str, False) - if longest_parsed_unit in from_group.units: - return longest_parsed_unit - elif shortest_parsed_unit in from_group.units: - return shortest_parsed_unit - else: - return None - - -def parse_named_unit(unit_string: str, rtol: float = 1e-14) -> NamedUnit: - """Parses unit into a named unit. Parses unit into a Unit if it is not already, and then finds an equivaelent named - unit. Please note that this might not be the expected unit from the string itself. E.g. 'kgm/2' will become - newtons. - - :param unit_string: string describing the units, e.g. km/s - :param rtol: relative tolerance for matching scale factors - """ - unit = parse_unit(unit_string) - named_unit = find_named_unit(unit) - if named_unit is None: - raise ValueError(f"We don't have a for this unit: '{unit}'") - else: - return named_unit - - -def find_named_unit(unit: Unit, rtol: float = 1e-14) -> NamedUnit | None: - """Find a named unit matching the one provided""" - dimension_hash = hash(unit.dimensions) - if dimension_hash in unit_groups_by_dimension_hash: - unit_group = unit_groups_by_dimension_hash[hash(unit.dimensions)] - - for named_unit in unit_group.units: - if abs(named_unit.scale - unit.scale) < rtol * named_unit.scale: - return named_unit - - return None - - -def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUnit: - """Parses unit_str into a named unit. The named unit found must be part of from_group. If two units are found, the - unit that is present in from_group is returned. This is useful in cases of ambiguities.""" - parsed_unit = parse_unit_from_group(unit_str, from_group) - if parsed_unit is None: - raise ValueError("That unit cannot be parsed from the specified group.") - return find_named_unit(parsed_unit) - - -def parse(string: str, name_lookup: bool = True, longest_unit: bool = True, lookup_rtol: float = 1e-14): - if type(string) is not str: - string = string.decode("utf-8") - unit = parse_unit(string, longest_unit=longest_unit) - if name_lookup: - named = find_named_unit(unit, rtol=lookup_rtol) - if named is not None: - return named - - return unit - - -if __name__ == "__main__": - to_parse = input("Enter a unit to parse: ") - try: - generic_unit = parse_unit(to_parse) - print(f"Generic Unit: {generic_unit}") - named_unit = find_named_unit(generic_unit) - print(f"Named Unit: {named_unit}") - except ValueError: - print("There is no named unit available.") +def parse_unit(unit_str: str) -> NamedUnit: + # TODO: Not implemented. This is just to enable testing. + return NamedUnit() From 5740db79c849e5486f3fe8efd56e59eebe5a1c16 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 09:08:24 +0100 Subject: [PATCH 030/675] Some minor changes to stop my editor from crying. Can probably revert back later. --- sasdata/quantities/operations.py | 4 ++-- sasdata/quantities/operations_examples.py | 4 ++-- sasdata/quantities/quantity.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py index 60590333d..dcbdcf242 100644 --- a/sasdata/quantities/operations.py +++ b/sasdata/quantities/operations.py @@ -2,7 +2,7 @@ import json -from sasdata.quantities.quantity import BaseQuantity +# from sasdata.quantities.quantity import BaseQuantity T = TypeVar("T") @@ -709,4 +709,4 @@ def __eq__(self, other): Neg, Inv, Add, Sub, Mul, Div, Pow] -_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} \ No newline at end of file +_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} diff --git a/sasdata/quantities/operations_examples.py b/sasdata/quantities/operations_examples.py index 194980e83..6c484eb36 100644 --- a/sasdata/quantities/operations_examples.py +++ b/sasdata/quantities/operations_examples.py @@ -1,4 +1,4 @@ -from quantities.operations import Variable, Mul +from sasdata.quantities.operations import Variable, Mul x = Variable("x") y = Variable("y") @@ -8,4 +8,4 @@ dfdx = f.derivative(x).derivative(y).derivative(z) -print(dfdx.summary()) +print(dfdx.summary()) \ No newline at end of file diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 41f710eff..e7b91cce8 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -3,7 +3,7 @@ from numpy._typing import ArrayLike -from quantities.operations import Operation, Variable +from sasdata.quantities.operations import Operation, Variable from sasdata.quantities.units import Unit import hashlib From 1755782eb73e85f78ce74f3fe4eaf382c79a0b43 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 09:23:26 +0100 Subject: [PATCH 031/675] Pass in the dimensions so this code is correct. --- sasdata/quantities/unit_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 97e1d0847..8b9e187f0 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,6 +1,6 @@ -from sasdata.quantities.units import NamedUnit +from sasdata.quantities.units import Dimensions, NamedUnit def parse_unit(unit_str: str) -> NamedUnit: # TODO: Not implemented. This is just to enable testing. - return NamedUnit() + return NamedUnit(1, Dimensions()) From 5cea869ba5a7a4abc61e57f3c3a20f2112b1d90d Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 09:51:08 +0100 Subject: [PATCH 032/675] Wrote some tests ahead. Enables some test driven development. --- sasdata/quantities/unit_parser_test.py | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 sasdata/quantities/unit_parser_test.py diff --git a/sasdata/quantities/unit_parser_test.py b/sasdata/quantities/unit_parser_test.py new file mode 100644 index 000000000..679727f33 --- /dev/null +++ b/sasdata/quantities/unit_parser_test.py @@ -0,0 +1,32 @@ +from sasdata.quantities.unit_parser import parse_unit +from sasdata.quantities.units_tests import EqualUnits +from sasdata.quantities.units import Dimensions, Unit, meters, meters_per_second, per_angstrom, \ + kilometers_per_square_hour + +# Lets start with the straight forward ones first, and get progressivel more complex as the list goes on. +tests = [ + EqualUnits('Metres', + meters, + parse_unit('m')), + EqualUnits('Metres per second', + meters_per_second, + parse_unit('ms-1')), + EqualUnits('Inverse Test', + per_angstrom, + parse_unit('1/A'), + parse_unit('A-1')), + # This test is primarily to ensure that the 'mm' doesn't get interpreted as two separate metres. + EqualUnits('Milimetres * Centimetres', + # TODO: Not sure if this calculation is right. + Unit(0.001 * 0.01, Dimensions(length=2)), + parse_unit('mmcm')), + EqualUnits("Acceleration", + kilometers_per_square_hour, + parse_unit('kmh-2'), + parse_unit('km/h2') + ) +] + +for test in tests: + print(test.test_name) + test.run_test() From e8d1110def40d0f290b4444f6e5c339ee9cdc200 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 09:53:54 +0100 Subject: [PATCH 033/675] Parse using a slant as well. --- sasdata/quantities/unit_parser_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser_test.py b/sasdata/quantities/unit_parser_test.py index 679727f33..383c079c0 100644 --- a/sasdata/quantities/unit_parser_test.py +++ b/sasdata/quantities/unit_parser_test.py @@ -10,7 +10,8 @@ parse_unit('m')), EqualUnits('Metres per second', meters_per_second, - parse_unit('ms-1')), + parse_unit('ms-1'), + parse_unit('m/s')), EqualUnits('Inverse Test', per_angstrom, parse_unit('1/A'), From b2b6cedf3130d1a2799503694be2b67695d394f6 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 15:49:17 +0100 Subject: [PATCH 034/675] Found a regex for splitting up the string. --- sasdata/quantities/unit_parser.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 8b9e187f0..a571d0838 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,5 +1,8 @@ from sasdata.quantities.units import Dimensions, NamedUnit +from re import findall +def split_unit_str(unit_str: str) -> list[str]: + return findall(r'[A-Za-z]+|[-\d]+', unit_str) def parse_unit(unit_str: str) -> NamedUnit: # TODO: Not implemented. This is just to enable testing. From 82ab75ad1cd76d52adeed5daad0a9689cff25bef Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 16:14:06 +0100 Subject: [PATCH 035/675] Implemented the parse_single_unit function. --- sasdata/quantities/unit_parser.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index a571d0838..fbdde41b3 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,9 +1,27 @@ -from sasdata.quantities.units import Dimensions, NamedUnit +from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup from re import findall def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+', unit_str) +def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: + """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit + cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str""" + current_unit = '' + string_pos = 0 + for char in unit_str: + potential_unit_str = current_unit + char + potential_symbol = symbol_lookup.get(potential_unit_str, None) + if potential_symbol is None: + break + string_pos += 1 + current_unit= potential_unit_str + if current_unit == '': + return (None, unit_str) + remaining_str = unit_str[string_pos::] + return (symbol_lookup[current_unit], remaining_str) + + def parse_unit(unit_str: str) -> NamedUnit: # TODO: Not implemented. This is just to enable testing. return NamedUnit(1, Dimensions()) From ab5c46f6352b442b9d12b92c32c0cf77daadaa5e Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 16:17:30 +0100 Subject: [PATCH 036/675] Use two functions for parsing. --- sasdata/quantities/unit_parser.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index fbdde41b3..f23987623 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -21,7 +21,13 @@ def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: remaining_str = unit_str[string_pos::] return (symbol_lookup[current_unit], remaining_str) +# Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there +# are two functions. -def parse_unit(unit_str: str) -> NamedUnit: +def parse_unit(unit_str: str) -> Unit: # TODO: Not implemented. This is just to enable testing. + return Unit(1, Dimensions()) + +def parse_named_unit(unit_str: str) -> NamedUnit: + # TODO: Not implemented. return NamedUnit(1, Dimensions()) From a17fea5a9ba445420e484146146600e71c6b6e81 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 08:26:54 +0100 Subject: [PATCH 037/675] Use list comprehension to get potential symbols. --- sasdata/quantities/unit_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index f23987623..c53af08ac 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -11,8 +11,8 @@ def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: string_pos = 0 for char in unit_str: potential_unit_str = current_unit + char - potential_symbol = symbol_lookup.get(potential_unit_str, None) - if potential_symbol is None: + potential_symbols = [symbol for symbol in symbol_lookup.keys() if symbol.startswith(potential_unit_str)] + if len(potential_symbols) == 0: break string_pos += 1 current_unit= potential_unit_str From fa89dbe4dfe0081b64d36877480818d1621b55a7 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 10:10:44 +0100 Subject: [PATCH 038/675] parse unit strs function. --- sasdata/quantities/unit_parser.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index c53af08ac..7241e11da 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -21,6 +21,17 @@ def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: remaining_str = unit_str[string_pos::] return (symbol_lookup[current_unit], remaining_str) +def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None) -> list[Unit]: + if current_units is None: + current_units = [] + if unit_str == '': + return current_units + parsed_unit, remaining_str = parse_single_unit(unit_str) + if not parsed_unit is None: + current_units += [parsed_unit] + return parse_unit_strs(remaining_str, current_units) + + # Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there # are two functions. From a063f7f300d3758a44aec68166768f4fe8f6964e Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 10:36:55 +0100 Subject: [PATCH 039/675] Created a function to pass in a stack of units. --- sasdata/quantities/unit_parser.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 7241e11da..7dba3dde3 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -35,6 +35,34 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None) -> lis # Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there # are two functions. +def parse_unit_stack(unit_str: str) -> list[Unit]: + # TODO: This doesn't work for 1/ (or any fraction) yet. + unit_stack: list[Unit] = [] + split_str = split_unit_str(unit_str) + for token in split_str: + try: + dimension_modifier = int(token) + to_modify = unit_stack[-1] + # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. + to_modify.dimensions = Dimensions( + length=to_modify.dimensions.length * dimension_modifier, + time=to_modify.dimensions.time * dimension_modifier, + mass=to_modify.dimensions.mass * dimension_modifier, + current=to_modify.dimensions.current * dimension_modifier, + temperature=to_modify.dimensions.temperature * dimension_modifier, + moles_hint=to_modify.dimensions.moles_hint * dimension_modifier, + angle_hint=to_modify.dimensions.angle_hint * dimension_modifier + ) + + except ValueError: + new_units = parse_unit_strs(token) + unit_stack += new_units + # This error will happen if it tries to read a modifier but there are no units on the stack. We will just have + # to ignore it. Strings being parsed shouldn't really have it anyway (e.g. -1m). + except IndexError: + pass + return unit_stack + def parse_unit(unit_str: str) -> Unit: # TODO: Not implemented. This is just to enable testing. return Unit(1, Dimensions()) From 1da668a79822f5914378b15e6feeef50473745fc Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 11:16:42 +0100 Subject: [PATCH 040/675] Multiply dimensions function. --- sasdata/quantities/unit_parser.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 7dba3dde3..b4801fc7d 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,6 +1,19 @@ from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup from re import findall +# TODO: This should probably be part of the Dimensions class but I don't want to change Lucas's code without asking him +# when he gets back. +def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> Dimensions: + return Dimensions( + length=dimensions_1.length * dimensions_2.length, + time=dimensions_1.time * dimensions_2.time, + mass=dimensions_1.mass * dimensions_2.mass, + current=dimensions_1.current * dimensions_2.current, + temperature=dimensions_1.temperature * dimensions_2.temperature, + moles_hint=dimensions_1.moles_hint * dimensions_2.moles_hint, + angle_hint=dimensions_1.angle_hint * dimensions_2.angle_hint + ) + def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+', unit_str) From dc84977e6a4925a5efeeaa7a323f734b8d00c257 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 11:20:26 +0100 Subject: [PATCH 041/675] Use the new multiply function. --- sasdata/quantities/unit_parser.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index b4801fc7d..600f73068 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -57,16 +57,8 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: dimension_modifier = int(token) to_modify = unit_stack[-1] # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. - to_modify.dimensions = Dimensions( - length=to_modify.dimensions.length * dimension_modifier, - time=to_modify.dimensions.time * dimension_modifier, - mass=to_modify.dimensions.mass * dimension_modifier, - current=to_modify.dimensions.current * dimension_modifier, - temperature=to_modify.dimensions.temperature * dimension_modifier, - moles_hint=to_modify.dimensions.moles_hint * dimension_modifier, - angle_hint=to_modify.dimensions.angle_hint * dimension_modifier - ) - + multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) + to_modify.dimensions = multiply_dimensions(to_modify.dimensions, multiplier) except ValueError: new_units = parse_unit_strs(token) unit_stack += new_units From b1f08de6a6f89e940e210c42a4d235860b498594 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 11:22:51 +0100 Subject: [PATCH 042/675] Nvm I'm blind; there already was a multiply method. --- sasdata/quantities/unit_parser.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 600f73068..c6aeb2bea 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,19 +1,6 @@ from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup from re import findall -# TODO: This should probably be part of the Dimensions class but I don't want to change Lucas's code without asking him -# when he gets back. -def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> Dimensions: - return Dimensions( - length=dimensions_1.length * dimensions_2.length, - time=dimensions_1.time * dimensions_2.time, - mass=dimensions_1.mass * dimensions_2.mass, - current=dimensions_1.current * dimensions_2.current, - temperature=dimensions_1.temperature * dimensions_2.temperature, - moles_hint=dimensions_1.moles_hint * dimensions_2.moles_hint, - angle_hint=dimensions_1.angle_hint * dimensions_2.angle_hint - ) - def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+', unit_str) @@ -58,7 +45,7 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: to_modify = unit_stack[-1] # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) - to_modify.dimensions = multiply_dimensions(to_modify.dimensions, multiplier) + to_modify.dimensions *= multiplier except ValueError: new_units = parse_unit_strs(token) unit_stack += new_units From 13df920774c4a94aa0004ec0aefb89b1ab4988bf Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 11:28:05 +0100 Subject: [PATCH 043/675] Parse in a whole unit. --- sasdata/quantities/unit_parser.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index c6aeb2bea..88a9d97ca 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -57,7 +57,11 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: def parse_unit(unit_str: str) -> Unit: # TODO: Not implemented. This is just to enable testing. - return Unit(1, Dimensions()) + parsed_unit = Unit(1, Dimensions()) + unit_stack = parse_unit_stack(unit_str) + for unit in unit_stack: + parsed_unit *= unit + return parsed_unit def parse_named_unit(unit_str: str) -> NamedUnit: # TODO: Not implemented. From fcab3f06ca214613771c09a55aad76d6b9257e72 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 11:39:36 +0100 Subject: [PATCH 044/675] I still need this multply for parse_unit_stack. Since the implementation in Lucas' code is just added the dimensions together which isn't what I'm looking for here. --- sasdata/quantities/unit_parser.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 88a9d97ca..5493ef2cf 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,6 +1,17 @@ from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup from re import findall +def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> Dimensions: + return Dimensions( + length=dimensions_1.length * dimensions_2.length, + time=dimensions_1.time * dimensions_2.time, + mass=dimensions_1.mass * dimensions_2.mass, + current=dimensions_1.current * dimensions_2.current, + temperature=dimensions_1.temperature * dimensions_2.temperature, + moles_hint=dimensions_1.moles_hint * dimensions_2.moles_hint, + angle_hint=dimensions_1.angle_hint * dimensions_2.angle_hint + ) + def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+', unit_str) @@ -45,7 +56,7 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: to_modify = unit_stack[-1] # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) - to_modify.dimensions *= multiplier + to_modify.dimensions = multiply_dimensions(to_modify.dimensions, multiplier) except ValueError: new_units = parse_unit_strs(token) unit_stack += new_units From f626f41ee37fae3878d310cf850281075f6818a1 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 16 Sep 2024 14:55:58 +0100 Subject: [PATCH 045/675] System for combining units. Very dodgy. --- sasdata/quantities/unit_parser.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 5493ef2cf..daf17bc37 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -12,6 +12,27 @@ def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> D angle_hint=dimensions_1.angle_hint * dimensions_2.angle_hint ) +def sum_dimensions(dimensions: Dimensions): + return sum([ + dimensions.length, + dimensions.time, + dimensions.mass, + dimensions.current, + dimensions.temperature, + dimensions.moles_hint, + dimensions.angle_hint + ]) + +def combine_units(unit_1: Unit, unit_2: Unit): + if unit_1.dimensions.is_dimensionless or unit_2.dimensions.is_dimensionless: + unit_1_scale = unit_1.scale + unit_2_scale = unit_2.scale + else: + unit_1_scale = unit_1.scale ** sum_dimensions(unit_1.dimensions) + unit_2_scale = unit_2.scale ** sum_dimensions(unit_2.dimensions) + return Unit(unit_1_scale * unit_2_scale, unit_1.dimensions * unit_2.dimensions) + + def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+', unit_str) @@ -71,9 +92,12 @@ def parse_unit(unit_str: str) -> Unit: parsed_unit = Unit(1, Dimensions()) unit_stack = parse_unit_stack(unit_str) for unit in unit_stack: - parsed_unit *= unit + parsed_unit = combine_units(parsed_unit, unit) return parsed_unit def parse_named_unit(unit_str: str) -> NamedUnit: # TODO: Not implemented. return NamedUnit(1, Dimensions()) + +if __name__ == "__main__": + print(parse_unit('kmh-1')) From 4a0ab8ce2a48252739a887e1dd9593e4adb11d29 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 16 Sep 2024 15:03:03 +0100 Subject: [PATCH 046/675] Removed not implemented comment. --- sasdata/quantities/unit_parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index daf17bc37..4ccb5afb0 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -88,7 +88,6 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: return unit_stack def parse_unit(unit_str: str) -> Unit: - # TODO: Not implemented. This is just to enable testing. parsed_unit = Unit(1, Dimensions()) unit_stack = parse_unit_stack(unit_str) for unit in unit_stack: From 8bb28081fa37848081f5a991ac1b3b93e939a714 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 16 Sep 2024 15:57:06 +0100 Subject: [PATCH 047/675] Parse in a named unit. --- sasdata/quantities/unit_parser.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 4ccb5afb0..e99efa6e9 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,6 +1,13 @@ -from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup +from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup, unit_groups from re import findall +# TODO: This shouldn't be in this file but I don't want to edit Lucas' code before he is finished. + +all_units_groups = [group.units for group in unit_groups.values()] +all_units: list[NamedUnit] = [] +for group in all_units_groups: + all_units.extend(group) + def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> Dimensions: return Dimensions( length=dimensions_1.length * dimensions_2.length, @@ -95,8 +102,12 @@ def parse_unit(unit_str: str) -> Unit: return parsed_unit def parse_named_unit(unit_str: str) -> NamedUnit: - # TODO: Not implemented. - return NamedUnit(1, Dimensions()) + # TODO: Not actually sure if this includes all units. + generic_unit = parse_unit(unit_str) + for named_unit in all_units: + if named_unit == generic_unit: + return named_unit + raise ValueError('A named unit does not exist for this unit.') if __name__ == "__main__": - print(parse_unit('kmh-1')) + print(parse_named_unit('kmh-1')) From 2403c7f77797ad0b024ec639290d447b77c2f3fe Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 16 Sep 2024 16:23:06 +0100 Subject: [PATCH 048/675] Avoid mutating state. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index e99efa6e9..462ba8da0 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -84,7 +84,7 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: to_modify = unit_stack[-1] # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) - to_modify.dimensions = multiply_dimensions(to_modify.dimensions, multiplier) + to_modify = Unit(to_modify.scale, multiply_dimensions(to_modify.dimensions, multiplier)) except ValueError: new_units = parse_unit_strs(token) unit_stack += new_units From 929ed4576024a02bc313532df05884977cdc77d1 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 08:25:18 +0100 Subject: [PATCH 049/675] Replace the unit on the stack. --- sasdata/quantities/unit_parser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 462ba8da0..70a452640 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -85,6 +85,7 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) to_modify = Unit(to_modify.scale, multiply_dimensions(to_modify.dimensions, multiplier)) + unit_stack[-1] = to_modify except ValueError: new_units = parse_unit_strs(token) unit_stack += new_units From f7262a7736dd2cb80be8c4bfe6c0187ab6646c19 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 09:46:25 +0100 Subject: [PATCH 050/675] Fixed the logic around combining units. --- sasdata/quantities/unit_parser.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 70a452640..47d70a006 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -31,14 +31,7 @@ def sum_dimensions(dimensions: Dimensions): ]) def combine_units(unit_1: Unit, unit_2: Unit): - if unit_1.dimensions.is_dimensionless or unit_2.dimensions.is_dimensionless: - unit_1_scale = unit_1.scale - unit_2_scale = unit_2.scale - else: - unit_1_scale = unit_1.scale ** sum_dimensions(unit_1.dimensions) - unit_2_scale = unit_2.scale ** sum_dimensions(unit_2.dimensions) - return Unit(unit_1_scale * unit_2_scale, unit_1.dimensions * unit_2.dimensions) - + return Unit(unit_1.scale * unit_2.scale, unit_1.dimensions * unit_2.dimensions) def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+', unit_str) @@ -84,7 +77,7 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: to_modify = unit_stack[-1] # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) - to_modify = Unit(to_modify.scale, multiply_dimensions(to_modify.dimensions, multiplier)) + to_modify = Unit(to_modify.scale ** dimension_modifier, multiply_dimensions(to_modify.dimensions, multiplier)) unit_stack[-1] = to_modify except ValueError: new_units = parse_unit_strs(token) From 5eb3f555d34c34e7ea9d01329e54dd1cb00f3c89 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 09:54:33 +0100 Subject: [PATCH 051/675] Parse_name_unit can take in an already parsed unit. --- sasdata/quantities/unit_parser.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 47d70a006..776a8f0d6 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -95,9 +95,12 @@ def parse_unit(unit_str: str) -> Unit: parsed_unit = combine_units(parsed_unit, unit) return parsed_unit -def parse_named_unit(unit_str: str) -> NamedUnit: +def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: # TODO: Not actually sure if this includes all units. - generic_unit = parse_unit(unit_str) + if parsed_unit is None: + generic_unit = parse_unit(unit_str) + else: + generic_unit = parsed_unit for named_unit in all_units: if named_unit == generic_unit: return named_unit From aff8ecf9c8fbafeaf0bebbe8c9745bae96d35564 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 10:05:22 +0100 Subject: [PATCH 052/675] Added a todo comment. --- sasdata/quantities/unit_parser.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 776a8f0d6..48010e50f 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -95,6 +95,8 @@ def parse_unit(unit_str: str) -> Unit: parsed_unit = combine_units(parsed_unit, unit) return parsed_unit +# TODO: Just noticed that, if a parsed unit is already provided, then the unit_str is redundant. Could solve this +# through function overloading but I don't know if I can do this based on the types of parameters alone. def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: # TODO: Not actually sure if this includes all units. if parsed_unit is None: From 1852d4790d0598a53eb6f2cec1c81bb7b5bbab47 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 10:05:40 +0100 Subject: [PATCH 053/675] Take a unit from the command line. --- sasdata/quantities/unit_parser.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 48010e50f..c34aa5f78 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -109,4 +109,11 @@ def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: raise ValueError('A named unit does not exist for this unit.') if __name__ == "__main__": - print(parse_named_unit('kmh-1')) + to_parse = input('Enter a unit to parse:') + generic_unit = parse_unit(to_parse) + print(f'Generic Unit: {generic_unit}') + try: + named_unit = parse_named_unit(to_parse, generic_unit) + print(f'Named Unit: {named_unit}') + except ValueError: + print('There is no named unit available.') From 1b9de374ee901fe832a5f62f30bbb7c56b7c01a9 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 10:06:18 +0100 Subject: [PATCH 054/675] Added whitespace on input. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index c34aa5f78..a81c874f3 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -109,7 +109,7 @@ def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: raise ValueError('A named unit does not exist for this unit.') if __name__ == "__main__": - to_parse = input('Enter a unit to parse:') + to_parse = input('Enter a unit to parse: ') generic_unit = parse_unit(to_parse) print(f'Generic Unit: {generic_unit}') try: From d7a782c5408ac7c0865a46d4023d59e7321af6cf Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 10:08:04 +0100 Subject: [PATCH 055/675] Fixed typo. --- sasdata/quantities/unit_parser_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser_test.py b/sasdata/quantities/unit_parser_test.py index 383c079c0..64aa3cc47 100644 --- a/sasdata/quantities/unit_parser_test.py +++ b/sasdata/quantities/unit_parser_test.py @@ -3,7 +3,7 @@ from sasdata.quantities.units import Dimensions, Unit, meters, meters_per_second, per_angstrom, \ kilometers_per_square_hour -# Lets start with the straight forward ones first, and get progressivel more complex as the list goes on. +# Lets start with the straight forward ones first, and get progressively more complex as the list goes on. tests = [ EqualUnits('Metres', meters, From a265b207f91074b1bb7e5111c2248fb090108aa6 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 10:53:19 +0100 Subject: [PATCH 056/675] Only multiply scale by 1, or -1. --- sasdata/quantities/unit_parser.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index a81c874f3..6d4473e26 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -76,8 +76,9 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: dimension_modifier = int(token) to_modify = unit_stack[-1] # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. - multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) - to_modify = Unit(to_modify.scale ** dimension_modifier, multiply_dimensions(to_modify.dimensions, multiplier)) + dimension_multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) + scale_multiplier = 1 if dimension_modifier > 0 else -1 + to_modify = Unit(to_modify.scale ** scale_multiplier, multiply_dimensions(to_modify.dimensions, dimension_multiplier)) unit_stack[-1] = to_modify except ValueError: new_units = parse_unit_strs(token) From b7b56d14c804c19957d954631df7371e14dccfa3 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 11:04:46 +0100 Subject: [PATCH 057/675] Look for slashes in the string. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 6d4473e26..f13103488 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -34,7 +34,7 @@ def combine_units(unit_1: Unit, unit_2: Unit): return Unit(unit_1.scale * unit_2.scale, unit_1.dimensions * unit_2.dimensions) def split_unit_str(unit_str: str) -> list[str]: - return findall(r'[A-Za-z]+|[-\d]+', unit_str) + return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit From ffb9d17a74116d13b279294f3bb650453d8fa15c Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 11:22:23 +0100 Subject: [PATCH 058/675] Got fraction units working as well :) --- sasdata/quantities/unit_parser.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index f13103488..238450c29 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -63,6 +63,12 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None) -> lis current_units += [parsed_unit] return parse_unit_strs(remaining_str, current_units) +def unit_power(to_modify: Unit, power: int): + # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. + dimension_multiplier = Dimensions(power, power, power, power, power, power, power) + scale_multiplier = 1 if power > 0 else -1 + return Unit(to_modify.scale ** scale_multiplier, multiply_dimensions(to_modify.dimensions, dimension_multiplier)) + # Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there # are two functions. @@ -71,17 +77,22 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: # TODO: This doesn't work for 1/ (or any fraction) yet. unit_stack: list[Unit] = [] split_str = split_unit_str(unit_str) + inverse_next_unit = False for token in split_str: try: - dimension_modifier = int(token) + if token == '/': + inverse_next_unit = True + continue + power = int(token) to_modify = unit_stack[-1] - # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. - dimension_multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) - scale_multiplier = 1 if dimension_modifier > 0 else -1 - to_modify = Unit(to_modify.scale ** scale_multiplier, multiply_dimensions(to_modify.dimensions, dimension_multiplier)) - unit_stack[-1] = to_modify + modified = unit_power(to_modify, power) + unit_stack[-1] = modified except ValueError: new_units = parse_unit_strs(token) + if inverse_next_unit: + # TODO: Assume the power is going to be -1. This might not be true. + power = -1 + new_units[0] = unit_power(new_units[0], power) unit_stack += new_units # This error will happen if it tries to read a modifier but there are no units on the stack. We will just have # to ignore it. Strings being parsed shouldn't really have it anyway (e.g. -1m). From c91cbd021120a06aa9dab8df2f4d079d095cb431 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 18 Sep 2024 15:44:55 +0100 Subject: [PATCH 059/675] Configure how ambiguities are dealt with. --- sasdata/quantities/unit_parser.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 238450c29..cf6a272b1 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -36,9 +36,14 @@ def combine_units(unit_1: Unit, unit_2: Unit): def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) -def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: +def parse_single_unit(unit_str: str, longest_unit: bool = True) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit - cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str""" + cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str. + + The shortest_unit parameter specifies how to resolve ambiguities. If it is true, then it will parse the longest unit + available. Otherwise, it will stop parsing as soon as it has found any unit. + + """ current_unit = '' string_pos = 0 for char in unit_str: @@ -48,6 +53,8 @@ def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: break string_pos += 1 current_unit= potential_unit_str + if not longest_unit: + break if current_unit == '': return (None, unit_str) remaining_str = unit_str[string_pos::] From 8502ea046078216807711f4c16e34b881b6db684 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 18 Sep 2024 15:49:47 +0100 Subject: [PATCH 060/675] Only break if we have found a symbol. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index cf6a272b1..37d2580b0 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -53,7 +53,7 @@ def parse_single_unit(unit_str: str, longest_unit: bool = True) -> tuple[Unit | break string_pos += 1 current_unit= potential_unit_str - if not longest_unit: + if not longest_unit and current_unit in symbol_lookup.keys(): break if current_unit == '': return (None, unit_str) From aaed11db2fee8c192d566c34712ad47a97755bd1 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 18 Sep 2024 16:07:55 +0100 Subject: [PATCH 061/675] Take in longest unit across the whole file. --- sasdata/quantities/unit_parser.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 37d2580b0..b65ccd856 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -60,15 +60,15 @@ def parse_single_unit(unit_str: str, longest_unit: bool = True) -> tuple[Unit | remaining_str = unit_str[string_pos::] return (symbol_lookup[current_unit], remaining_str) -def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None) -> list[Unit]: +def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longest_unit: bool = True) -> list[Unit]: if current_units is None: current_units = [] if unit_str == '': return current_units - parsed_unit, remaining_str = parse_single_unit(unit_str) + parsed_unit, remaining_str = parse_single_unit(unit_str, longest_unit) if not parsed_unit is None: current_units += [parsed_unit] - return parse_unit_strs(remaining_str, current_units) + return parse_unit_strs(remaining_str, current_units, longest_unit) def unit_power(to_modify: Unit, power: int): # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. @@ -80,7 +80,7 @@ def unit_power(to_modify: Unit, power: int): # Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there # are two functions. -def parse_unit_stack(unit_str: str) -> list[Unit]: +def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: # TODO: This doesn't work for 1/ (or any fraction) yet. unit_stack: list[Unit] = [] split_str = split_unit_str(unit_str) @@ -95,7 +95,7 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: modified = unit_power(to_modify, power) unit_stack[-1] = modified except ValueError: - new_units = parse_unit_strs(token) + new_units = parse_unit_strs(token, None, longest_unit) if inverse_next_unit: # TODO: Assume the power is going to be -1. This might not be true. power = -1 @@ -107,9 +107,9 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: pass return unit_stack -def parse_unit(unit_str: str) -> Unit: +def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: parsed_unit = Unit(1, Dimensions()) - unit_stack = parse_unit_stack(unit_str) + unit_stack = parse_unit_stack(unit_str, longest_unit) for unit in unit_stack: parsed_unit = combine_units(parsed_unit, unit) return parsed_unit From b93951807fd910dbce9a8820ae81503490cd309d Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 10:29:45 +0100 Subject: [PATCH 062/675] Take in a unit group in parse_singe_unit. --- sasdata/quantities/unit_parser.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index b65ccd856..2f51bb044 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,4 +1,4 @@ -from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup, unit_groups +from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup, unit_groups, UnitGroup from re import findall # TODO: This shouldn't be in this file but I don't want to edit Lucas' code before he is finished. @@ -36,7 +36,7 @@ def combine_units(unit_1: Unit, unit_2: Unit): def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) -def parse_single_unit(unit_str: str, longest_unit: bool = True) -> tuple[Unit | None, str]: +def parse_single_unit(unit_str: str, longest_unit: bool = True, unit_group: UnitGroup | None = None) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str. @@ -46,19 +46,23 @@ def parse_single_unit(unit_str: str, longest_unit: bool = True) -> tuple[Unit | """ current_unit = '' string_pos = 0 + if unit_group is None: + lookup_dict = symbol_lookup + else: + lookup_dict = dict([name_unit for name_unit in symbol_lookup.items() if name_unit[1] in unit_group.units]) for char in unit_str: potential_unit_str = current_unit + char - potential_symbols = [symbol for symbol in symbol_lookup.keys() if symbol.startswith(potential_unit_str)] + potential_symbols = [symbol for symbol in lookup_dict.keys() if symbol.startswith(potential_unit_str)] if len(potential_symbols) == 0: break string_pos += 1 current_unit= potential_unit_str - if not longest_unit and current_unit in symbol_lookup.keys(): + if not longest_unit and current_unit in lookup_dict.keys(): break if current_unit == '': return (None, unit_str) remaining_str = unit_str[string_pos::] - return (symbol_lookup[current_unit], remaining_str) + return (lookup_dict[current_unit], remaining_str) def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longest_unit: bool = True) -> list[Unit]: if current_units is None: From acd2d70a74b138b90a2ebe3ce5b2037e3101f3c4 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 10:51:13 +0100 Subject: [PATCH 063/675] Parse a unit from a specific group. --- sasdata/quantities/unit_parser.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 2f51bb044..019130567 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -118,6 +118,18 @@ def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: parsed_unit = combine_units(parsed_unit, unit) return parsed_unit +def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: + """Tries to use the given unit group to resolve ambiguities. Parse a unit twice with different options, and returns + whatever conforms to the unit group.""" + longest_parsed_unit = parse_unit(unit_str, True) + shortest_parsed_unit = parse_unit(unit_str, False) + if longest_parsed_unit in from_group.units: + return longest_parsed_unit + elif shortest_parsed_unit in from_group.units: + return shortest_parsed_unit + else: + return None + # TODO: Just noticed that, if a parsed unit is already provided, then the unit_str is redundant. Could solve this # through function overloading but I don't know if I can do this based on the types of parameters alone. def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: From 994d50fee058acfa1ea41556e93bffabd3fd8d4c Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 11:18:52 +0100 Subject: [PATCH 064/675] Equivalent function for from group. --- sasdata/quantities/unit_parser.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 019130567..7a01ce487 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -143,6 +143,12 @@ def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: return named_unit raise ValueError('A named unit does not exist for this unit.') +def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUnit: + parsed_unit = parse_unit_from_group(unit_str, from_group) + if parsed_unit == None: + raise ValueError('That unit cannot be parsed from the specified group.') + return parse_named_unit('', parsed_unit) + if __name__ == "__main__": to_parse = input('Enter a unit to parse: ') generic_unit = parse_unit(to_parse) From f0eec738045b9af4bd73a0a18974eb5e967f626c Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 11:28:42 +0100 Subject: [PATCH 065/675] Is none not equal to none. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 7a01ce487..90eee7154 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -145,7 +145,7 @@ def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUnit: parsed_unit = parse_unit_from_group(unit_str, from_group) - if parsed_unit == None: + if parsed_unit is None: raise ValueError('That unit cannot be parsed from the specified group.') return parse_named_unit('', parsed_unit) From 53797e30b41628350e51681e7dbdf877ec23e16a Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 11:57:17 +0100 Subject: [PATCH 066/675] Removed old TODO comment. --- sasdata/quantities/unit_parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 90eee7154..96052ffae 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -85,7 +85,6 @@ def unit_power(to_modify: Unit, power: int): # are two functions. def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: - # TODO: This doesn't work for 1/ (or any fraction) yet. unit_stack: list[Unit] = [] split_str = split_unit_str(unit_str) inverse_next_unit = False From 3da48ab73f5b55f8c1b21cf5bea5bd3ba6cb6002 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 15:02:33 +0100 Subject: [PATCH 067/675] Catch key errors. These will happen when we try to parse a unit that doesn't exist. --- sasdata/quantities/unit_parser.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 96052ffae..b60f95cf4 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -111,11 +111,14 @@ def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: return unit_stack def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: - parsed_unit = Unit(1, Dimensions()) - unit_stack = parse_unit_stack(unit_str, longest_unit) - for unit in unit_stack: - parsed_unit = combine_units(parsed_unit, unit) - return parsed_unit + try: + parsed_unit = Unit(1, Dimensions()) + unit_stack = parse_unit_stack(unit_str, longest_unit) + for unit in unit_stack: + parsed_unit = combine_units(parsed_unit, unit) + return parsed_unit + except KeyError: + raise ValueError('Unit string contains an unrecognised pattern.') def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: """Tries to use the given unit group to resolve ambiguities. Parse a unit twice with different options, and returns From 3ef199c0549d3340418b6eaf96e80cfae375937c Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 15:05:13 +0100 Subject: [PATCH 068/675] Expand the try block. --- sasdata/quantities/unit_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index b60f95cf4..bb243e148 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -153,9 +153,9 @@ def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUn if __name__ == "__main__": to_parse = input('Enter a unit to parse: ') - generic_unit = parse_unit(to_parse) - print(f'Generic Unit: {generic_unit}') try: + generic_unit = parse_unit(to_parse) + print(f'Generic Unit: {generic_unit}') named_unit = parse_named_unit(to_parse, generic_unit) print(f'Named Unit: {named_unit}') except ValueError: From e534d9f3e75056fa85537443aadd68a272f8850e Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 15:45:18 +0100 Subject: [PATCH 069/675] Removed an old todo comment. --- sasdata/quantities/unit_parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index bb243e148..66a580cbe 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -135,7 +135,6 @@ def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: # TODO: Just noticed that, if a parsed unit is already provided, then the unit_str is redundant. Could solve this # through function overloading but I don't know if I can do this based on the types of parameters alone. def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: - # TODO: Not actually sure if this includes all units. if parsed_unit is None: generic_unit = parse_unit(unit_str) else: From 08c11aff9cfe1930d58bb51f4eef665cd7c3ad96 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 13:45:19 +0100 Subject: [PATCH 070/675] New unit test in pytest. --- test/utest_unit_parser.py | 95 ++++++--------------------------------- 1 file changed, 14 insertions(+), 81 deletions(-) diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index e757a0220..0f3e19184 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -1,81 +1,14 @@ -import re - -import pytest - -from sasdata.quantities import units -from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group, parse_unit -from sasdata.quantities.units import Unit - -named_units_for_testing = [ - ("m", units.meters), - ("A-1", units.per_angstrom), - ("1/A", units.per_angstrom), - ("1/angstroms", units.per_angstrom), - ("micrometer", units.micrometers), - ("micron", units.micrometers), - ("kmh-2", units.kilometers_per_square_hour), - ("km/h2", units.kilometers_per_square_hour), - ("kgm/s2", units.newtons), - ("m m", units.square_meters), - ("mm", units.millimeters), - ("A^-1", units.per_angstrom), - ("V/Amps", units.ohms), - ("Ω", units.ohms), - ("Å", units.angstroms), - ("%", units.percent), - ("per_centimeter", units.per_centimeter), -] - -latex_units_for_testing = [ - (r"\Omega", units.ohms), # Test omega is Ω - (r"\AA", units.angstroms), # Test angstrom is Å - (r"\%", units.percent), # Test percent is NOT a comment - (r"{\mu}A", units.microamperes), # Test µ with an ASCII unit - (r"{\mu}\Omega", units.microohms), # Test µ with LaTeX unit - (r"mm", units.millimeters), # Test that most units just use ASCII in LaTeX -] - - -unnamed_units_for_testing = [("m13", units.meters**13), ("kW/sr", units.kilowatts / units.stradians)] - - -@pytest.mark.parametrize("string, expected_units", named_units_for_testing) -def test_name_parse(string: str, expected_units: Unit): - """Test basic parsing""" - assert parse_named_unit(string) == expected_units - - -@pytest.mark.parametrize("string, expected_units", named_units_for_testing + unnamed_units_for_testing) -def test_equivalent(string: str, expected_units: Unit): - """Check dimensions of parsed units""" - assert parse_unit(string).equivalent(expected_units) - - -@pytest.mark.parametrize("string, expected_units", named_units_for_testing + unnamed_units_for_testing) -def test_scale_same(string: str, expected_units: Unit): - """Test basic parsing""" - assert parse_unit(string).scale == pytest.approx(expected_units.scale, rel=1e-14) - - -@pytest.mark.parametrize("latex_string, units", latex_units_for_testing) -def test_latex_parse(latex_string: str, units: Unit): - """Test that proper LaTeX formats for units are being generated""" - assert units.latex_symbol == latex_string - - -def test_parse_from_group(): - """Test group based disambiguation""" - parsed_metres_per_second = parse_named_unit_from_group("ms-1", units.speed) - assert parsed_metres_per_second == units.meters_per_second - - -def test_parse_errors(): - # Fails because the unit is not in that specific group. - with pytest.raises(ValueError, match="That unit cannot be parsed from the specified group."): - parse_named_unit_from_group("km", units.speed) - # Fails because part of the unit matches but there is an unknown unit '@' - with pytest.raises(ValueError, match=re.escape("unit_str (km@-1) contains forbidden characters.")): - parse_unit("km@-1") - # Fails because 'da' is not a unit. - with pytest.raises(ValueError, match="Unit string contains an unrecognised pattern."): - parse_unit("mmda2") +from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group +from sasdata.quantities.units import meters, speed, meters_per_second, per_angstrom, kilometers_per_square_hour + + +def test_parse(): + parsed_metres = parse_named_unit('m') + assert parsed_metres == meters + # Have to specify a group because this is ambigious with inverse of milliseconds. + parsed_metres_per_second = parse_named_unit_from_group('ms-1', speed) + assert parsed_metres_per_second == meters_per_second + parsed_inverse_angstroms = parse_named_unit('A-1') + assert parsed_inverse_angstroms == per_angstrom + parsed_kilometers_per_square_hour = parse_named_unit('kmh-2') + assert parsed_kilometers_per_square_hour == kilometers_per_square_hour From 728716f267b76f6b1151c81a0534aee1abf10556 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 15:26:22 +0100 Subject: [PATCH 071/675] Raise an exception if the unit can't be parsed. --- sasdata/quantities/unit_parser.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 66a580cbe..df47aa40b 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -72,7 +72,9 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longes parsed_unit, remaining_str = parse_single_unit(unit_str, longest_unit) if not parsed_unit is None: current_units += [parsed_unit] - return parse_unit_strs(remaining_str, current_units, longest_unit) + return parse_unit_strs(remaining_str, current_units, longest_unit) + else: + raise ValueError(f'Could not interpret {remaining_str}') def unit_power(to_modify: Unit, power: int): # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. From 77ca69b88e36411b4fe8e8b8bca735f52f3779ba Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 15:46:28 +0100 Subject: [PATCH 072/675] Added some unit tests that should error. --- test/utest_unit_parser.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index 0f3e19184..8fcfc0b76 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -1,5 +1,6 @@ -from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group +from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group, parse_unit from sasdata.quantities.units import meters, speed, meters_per_second, per_angstrom, kilometers_per_square_hour +from pytest import raises def test_parse(): @@ -12,3 +13,11 @@ def test_parse(): assert parsed_inverse_angstroms == per_angstrom parsed_kilometers_per_square_hour = parse_named_unit('kmh-2') assert parsed_kilometers_per_square_hour == kilometers_per_square_hour + +def test_parse_errors(): + # Fails because the unit is not in that specific group. + with raises(ValueError, match='That unit cannot be parsed from the specified group.'): + parse_named_unit_from_group('km', speed) + # Fails because part of the unit matches but there is an unknown unit '@' + with raises(ValueError, match='Unit string contains an unrecognised pattern.'): + parse_unit('km@-1') From 288590e395d4cb22b42dbe5b739af9b83a1901fb Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 16:11:15 +0100 Subject: [PATCH 073/675] Created a regex validator for the unit str. --- sasdata/quantities/unit_parser.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index df47aa40b..b461cfbd1 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,5 +1,5 @@ from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup, unit_groups, UnitGroup -from re import findall +from re import findall, fullmatch # TODO: This shouldn't be in this file but I don't want to edit Lucas' code before he is finished. @@ -36,6 +36,9 @@ def combine_units(unit_1: Unit, unit_2: Unit): def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) +def validate_unit_str(unit_str: str) -> bool: + return not fullmatch(r'[A-Za-z1-9\-\+]+', unit_str) is None + def parse_single_unit(unit_str: str, longest_unit: bool = True, unit_group: UnitGroup | None = None) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str. From ef085171f2d337a09e68466b9fd64ef37f28a939 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 16:14:44 +0100 Subject: [PATCH 074/675] Throw an exception if the validation fails. --- sasdata/quantities/unit_parser.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index b461cfbd1..52b5451ec 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -117,6 +117,8 @@ def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: try: + if not validate_unit_str(unit_str): + raise ValueError('unit_str contains forbidden characters.') parsed_unit = Unit(1, Dimensions()) unit_stack = parse_unit_stack(unit_str, longest_unit) for unit in unit_stack: From a73efa88311640feb25e53208c3b6d19a0d817c5 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 16:19:25 +0100 Subject: [PATCH 075/675] Update unit test to reflect new error. --- test/utest_unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index 8fcfc0b76..4372a4dfa 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -19,5 +19,5 @@ def test_parse_errors(): with raises(ValueError, match='That unit cannot be parsed from the specified group.'): parse_named_unit_from_group('km', speed) # Fails because part of the unit matches but there is an unknown unit '@' - with raises(ValueError, match='Unit string contains an unrecognised pattern.'): + with raises(ValueError, match='unit_str contains forbidden characters.'): parse_unit('km@-1') From deae844e003607f2790a351a346c532eb9b18e75 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 16:22:17 +0100 Subject: [PATCH 076/675] Unit test for what I was originally testing for. --- test/utest_unit_parser.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index 4372a4dfa..bf477ab48 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -21,3 +21,6 @@ def test_parse_errors(): # Fails because part of the unit matches but there is an unknown unit '@' with raises(ValueError, match='unit_str contains forbidden characters.'): parse_unit('km@-1') + # Fails because 'da' is not a unit. + with raises(ValueError, match='Unit string contains an unrecognised pattern.'): + parse_unit('mmda2') From 5334c86ecce10111f63d49c40714773c8c0d0503 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 12:05:34 +0100 Subject: [PATCH 077/675] Added more tests for slants. --- test/utest_unit_parser.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index bf477ab48..4227c8bfa 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -11,8 +11,12 @@ def test_parse(): assert parsed_metres_per_second == meters_per_second parsed_inverse_angstroms = parse_named_unit('A-1') assert parsed_inverse_angstroms == per_angstrom + parsed_inverse_angstroms_slant = parse_named_unit('1/A') + assert parsed_inverse_angstroms_slant == per_angstrom parsed_kilometers_per_square_hour = parse_named_unit('kmh-2') assert parsed_kilometers_per_square_hour == kilometers_per_square_hour + parsed_kilometers_per_square_hour_slant = parse_named_unit('km/h2') + assert parsed_kilometers_per_square_hour_slant == kilometers_per_square_hour def test_parse_errors(): # Fails because the unit is not in that specific group. From 0c15b91779534ec2ddc8ef643282e864428b8f13 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:02:41 +0100 Subject: [PATCH 078/675] Slants should be valid unit strings as well. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 52b5451ec..1a2b51737 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -37,7 +37,7 @@ def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) def validate_unit_str(unit_str: str) -> bool: - return not fullmatch(r'[A-Za-z1-9\-\+]+', unit_str) is None + return not fullmatch(r'[A-Za-z1-9\-\+/]+', unit_str) is None def parse_single_unit(unit_str: str, longest_unit: bool = True, unit_group: UnitGroup | None = None) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit From 7bae6484d7d7fea6489d8572041f9049e5deb499 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:07:06 +0100 Subject: [PATCH 079/675] Parse in newton as its defined value. --- test/utest_unit_parser.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index 4227c8bfa..d5fc0d5d1 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -1,5 +1,5 @@ from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group, parse_unit -from sasdata.quantities.units import meters, speed, meters_per_second, per_angstrom, kilometers_per_square_hour +from sasdata.quantities.units import meters, speed, meters_per_second, per_angstrom, kilometers_per_square_hour, newtons from pytest import raises @@ -17,6 +17,8 @@ def test_parse(): assert parsed_kilometers_per_square_hour == kilometers_per_square_hour parsed_kilometers_per_square_hour_slant = parse_named_unit('km/h2') assert parsed_kilometers_per_square_hour_slant == kilometers_per_square_hour + parsed_newton = parse_named_unit('kgm/s2') + assert parsed_newton == newtons def test_parse_errors(): # Fails because the unit is not in that specific group. From 6c59c77986702d3f04808220b69fa83ad898801e Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:13:43 +0100 Subject: [PATCH 080/675] Remove the old testing file. --- sasdata/quantities/unit_parser_test.py | 33 -------------------------- 1 file changed, 33 deletions(-) delete mode 100644 sasdata/quantities/unit_parser_test.py diff --git a/sasdata/quantities/unit_parser_test.py b/sasdata/quantities/unit_parser_test.py deleted file mode 100644 index 64aa3cc47..000000000 --- a/sasdata/quantities/unit_parser_test.py +++ /dev/null @@ -1,33 +0,0 @@ -from sasdata.quantities.unit_parser import parse_unit -from sasdata.quantities.units_tests import EqualUnits -from sasdata.quantities.units import Dimensions, Unit, meters, meters_per_second, per_angstrom, \ - kilometers_per_square_hour - -# Lets start with the straight forward ones first, and get progressively more complex as the list goes on. -tests = [ - EqualUnits('Metres', - meters, - parse_unit('m')), - EqualUnits('Metres per second', - meters_per_second, - parse_unit('ms-1'), - parse_unit('m/s')), - EqualUnits('Inverse Test', - per_angstrom, - parse_unit('1/A'), - parse_unit('A-1')), - # This test is primarily to ensure that the 'mm' doesn't get interpreted as two separate metres. - EqualUnits('Milimetres * Centimetres', - # TODO: Not sure if this calculation is right. - Unit(0.001 * 0.01, Dimensions(length=2)), - parse_unit('mmcm')), - EqualUnits("Acceleration", - kilometers_per_square_hour, - parse_unit('kmh-2'), - parse_unit('km/h2') - ) -] - -for test in tests: - print(test.test_name) - test.run_test() From c82bb3ef372e1c7d1878952c9825f3176a508a1e Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:21:51 +0100 Subject: [PATCH 081/675] This function isn't being used. --- sasdata/quantities/unit_parser.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 1a2b51737..5541360f8 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -19,17 +19,6 @@ def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> D angle_hint=dimensions_1.angle_hint * dimensions_2.angle_hint ) -def sum_dimensions(dimensions: Dimensions): - return sum([ - dimensions.length, - dimensions.time, - dimensions.mass, - dimensions.current, - dimensions.temperature, - dimensions.moles_hint, - dimensions.angle_hint - ]) - def combine_units(unit_1: Unit, unit_2: Unit): return Unit(unit_1.scale * unit_2.scale, unit_1.dimensions * unit_2.dimensions) From d7462ebbc4a5b7f7108462bf16c1c3630eeca17a Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:26:12 +0100 Subject: [PATCH 082/675] Added to the doc string about unit groups. --- sasdata/quantities/unit_parser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 5541360f8..528fe1317 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -35,6 +35,7 @@ def parse_single_unit(unit_str: str, longest_unit: bool = True, unit_group: Unit The shortest_unit parameter specifies how to resolve ambiguities. If it is true, then it will parse the longest unit available. Otherwise, it will stop parsing as soon as it has found any unit. + If unit_group is set, it will only try to parse units within that group. This is useful for resolving ambiguities. """ current_unit = '' string_pos = 0 From f69299cc88755edcd857c6073cf7119a58451e04 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:27:12 +0100 Subject: [PATCH 083/675] Moved the unit group to first. This is probably better as I think the caller is more likely to use longest_unit's default value when unit_group is set. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 528fe1317..2b05ce834 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -28,7 +28,7 @@ def split_unit_str(unit_str: str) -> list[str]: def validate_unit_str(unit_str: str) -> bool: return not fullmatch(r'[A-Za-z1-9\-\+/]+', unit_str) is None -def parse_single_unit(unit_str: str, longest_unit: bool = True, unit_group: UnitGroup | None = None) -> tuple[Unit | None, str]: +def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str. From c087801ff9c908a038012d22bba5a90c17fa582d Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:43:37 +0100 Subject: [PATCH 084/675] Small rename. --- sasdata/quantities/unit_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 2b05ce834..27015aabd 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -43,8 +43,8 @@ def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longes lookup_dict = symbol_lookup else: lookup_dict = dict([name_unit for name_unit in symbol_lookup.items() if name_unit[1] in unit_group.units]) - for char in unit_str: - potential_unit_str = current_unit + char + for next_char in unit_str: + potential_unit_str = current_unit + next_char potential_symbols = [symbol for symbol in lookup_dict.keys() if symbol.startswith(potential_unit_str)] if len(potential_symbols) == 0: break From a7ea91eb5a22ea1c68a6667fc6bdd1d33a01c64f Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:50:00 +0100 Subject: [PATCH 085/675] Use destructuring to make this a bit cleaner. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 27015aabd..5ca0effbe 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -42,7 +42,7 @@ def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longes if unit_group is None: lookup_dict = symbol_lookup else: - lookup_dict = dict([name_unit for name_unit in symbol_lookup.items() if name_unit[1] in unit_group.units]) + lookup_dict = dict([(name, unit) for name, unit in symbol_lookup.items() if unit in unit_group.units]) for next_char in unit_str: potential_unit_str = current_unit + next_char potential_symbols = [symbol for symbol in lookup_dict.keys() if symbol.startswith(potential_unit_str)] From 235c8564eea749091abba3fd2b5ac98e1202418f Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:51:55 +0100 Subject: [PATCH 086/675] Fixed function call. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 5ca0effbe..7db651ff2 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -62,7 +62,7 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longes current_units = [] if unit_str == '': return current_units - parsed_unit, remaining_str = parse_single_unit(unit_str, longest_unit) + parsed_unit, remaining_str = parse_single_unit(unit_str, longest_unit=longest_unit) if not parsed_unit is None: current_units += [parsed_unit] return parse_unit_strs(remaining_str, current_units, longest_unit) From 9e6fdf16e2602ba91fb2d7d6c27b61903de5f2ce Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 15:40:56 +0100 Subject: [PATCH 087/675] Added some docstrings. --- sasdata/quantities/unit_parser.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 7db651ff2..90f86633b 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -9,6 +9,7 @@ all_units.extend(group) def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> Dimensions: + """Multiply each dimension in dimensions_1 with the same dimension in dimensions_2""" return Dimensions( length=dimensions_1.length * dimensions_2.length, time=dimensions_1.time * dimensions_2.time, @@ -20,12 +21,16 @@ def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> D ) def combine_units(unit_1: Unit, unit_2: Unit): + """Combine unit_1, and unit_2 into one unit.""" return Unit(unit_1.scale * unit_2.scale, unit_1.dimensions * unit_2.dimensions) def split_unit_str(unit_str: str) -> list[str]: + """Separate the letters from the numbers in unit_str""" return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) def validate_unit_str(unit_str: str) -> bool: + """Validate whether unit_str is valid. This doesn't mean that the unit specified in unit_str exists but rather it + only consists of letters, and numbers as a unit string should.""" return not fullmatch(r'[A-Za-z1-9\-\+/]+', unit_str) is None def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: @@ -58,6 +63,7 @@ def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longes return (lookup_dict[current_unit], remaining_str) def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longest_unit: bool = True) -> list[Unit]: + """Recursively parse units from unit_str until no more characters are present.""" if current_units is None: current_units = [] if unit_str == '': @@ -70,6 +76,7 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longes raise ValueError(f'Could not interpret {remaining_str}') def unit_power(to_modify: Unit, power: int): + """Raise to_modify to power""" # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. dimension_multiplier = Dimensions(power, power, power, power, power, power, power) scale_multiplier = 1 if power > 0 else -1 @@ -80,6 +87,7 @@ def unit_power(to_modify: Unit, power: int): # are two functions. def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: + """Split unit_str into a stack of parsed units.""" unit_stack: list[Unit] = [] split_str = split_unit_str(unit_str) inverse_next_unit = False @@ -106,6 +114,7 @@ def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: return unit_stack def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: + """Parse unit_str into a unit.""" try: if not validate_unit_str(unit_str): raise ValueError('unit_str contains forbidden characters.') From 8722315e2f4ac8dde4002430992d6b39f8bb26b0 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 25 Sep 2024 08:18:42 +0100 Subject: [PATCH 088/675] Refactored parse named unit so it just takes one arg. --- sasdata/quantities/unit_parser.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 90f86633b..351036b3c 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -140,11 +140,13 @@ def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: # TODO: Just noticed that, if a parsed unit is already provided, then the unit_str is redundant. Could solve this # through function overloading but I don't know if I can do this based on the types of parameters alone. -def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: - if parsed_unit is None: - generic_unit = parse_unit(unit_str) +def parse_named_unit(unit: str | Unit) -> NamedUnit: + if isinstance(unit, str): + generic_unit = parse_unit(unit) + elif isinstance(unit, Unit): + generic_unit = unit else: - generic_unit = parsed_unit + raise ValueError('Unit must be a string, or Unit') for named_unit in all_units: if named_unit == generic_unit: return named_unit @@ -154,14 +156,14 @@ def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUn parsed_unit = parse_unit_from_group(unit_str, from_group) if parsed_unit is None: raise ValueError('That unit cannot be parsed from the specified group.') - return parse_named_unit('', parsed_unit) + return parse_named_unit(parsed_unit) if __name__ == "__main__": to_parse = input('Enter a unit to parse: ') try: generic_unit = parse_unit(to_parse) print(f'Generic Unit: {generic_unit}') - named_unit = parse_named_unit(to_parse, generic_unit) + named_unit = parse_named_unit(generic_unit) print(f'Named Unit: {named_unit}') except ValueError: print('There is no named unit available.') From f822a669a29c8b9a3bb978fd5f48eee9ad1b3555 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 25 Sep 2024 08:20:58 +0100 Subject: [PATCH 089/675] Removed old todo comment. --- sasdata/quantities/unit_parser.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 351036b3c..ffe0333ae 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -138,8 +138,6 @@ def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: else: return None -# TODO: Just noticed that, if a parsed unit is already provided, then the unit_str is redundant. Could solve this -# through function overloading but I don't know if I can do this based on the types of parameters alone. def parse_named_unit(unit: str | Unit) -> NamedUnit: if isinstance(unit, str): generic_unit = parse_unit(unit) From 43566e472cc6e04a150b7b862ae09cd3d83da020 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 25 Sep 2024 08:25:56 +0100 Subject: [PATCH 090/675] Added some docstrings. --- sasdata/quantities/unit_parser.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index ffe0333ae..a7c557a0d 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -139,6 +139,9 @@ def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: return None def parse_named_unit(unit: str | Unit) -> NamedUnit: + """Parses unit into a named unit. Parses unit into a Unit if it is not already, and then finds an equivaelent named + unit. Please note that this might not be the expected unit from the string itself. E.g. 'kgm/2' will become + newtons.""" if isinstance(unit, str): generic_unit = parse_unit(unit) elif isinstance(unit, Unit): @@ -151,6 +154,8 @@ def parse_named_unit(unit: str | Unit) -> NamedUnit: raise ValueError('A named unit does not exist for this unit.') def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUnit: + """Parses unit_str into a named unit. The named unit found must be part of from_group. If two units are found, the + unit that is present in from_group is returned. This is useful in cases of ambiguities.""" parsed_unit = parse_unit_from_group(unit_str, from_group) if parsed_unit is None: raise ValueError('That unit cannot be parsed from the specified group.') From deb18147002fa3e404216eac9b80fae00fc9bfdf Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 25 Sep 2024 08:31:18 +0100 Subject: [PATCH 091/675] Stop linter from moaning. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index a7c557a0d..97de492f6 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -69,7 +69,7 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longes if unit_str == '': return current_units parsed_unit, remaining_str = parse_single_unit(unit_str, longest_unit=longest_unit) - if not parsed_unit is None: + if parsed_unit is not None: current_units += [parsed_unit] return parse_unit_strs(remaining_str, current_units, longest_unit) else: From 3d99c368144ba6b766976a5745f6ee5f18a1825c Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 30 Sep 2024 13:49:34 +0100 Subject: [PATCH 092/675] Accept spaces in the unit str. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 97de492f6..e670ed4df 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -31,7 +31,7 @@ def split_unit_str(unit_str: str) -> list[str]: def validate_unit_str(unit_str: str) -> bool: """Validate whether unit_str is valid. This doesn't mean that the unit specified in unit_str exists but rather it only consists of letters, and numbers as a unit string should.""" - return not fullmatch(r'[A-Za-z1-9\-\+/]+', unit_str) is None + return not fullmatch(r'[A-Za-z1-9\-\+/\ ]+', unit_str) is None def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit From 00ac754cfe137fdaef15947637fd2dec2687262e Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 30 Sep 2024 14:40:59 +0100 Subject: [PATCH 093/675] Split by dots as well. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index e670ed4df..599abd5d2 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -31,7 +31,7 @@ def split_unit_str(unit_str: str) -> list[str]: def validate_unit_str(unit_str: str) -> bool: """Validate whether unit_str is valid. This doesn't mean that the unit specified in unit_str exists but rather it only consists of letters, and numbers as a unit string should.""" - return not fullmatch(r'[A-Za-z1-9\-\+/\ ]+', unit_str) is None + return not fullmatch(r'[A-Za-z1-9\-\+/\ \.]+', unit_str) is None def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit From 070a55aee8d18035ad8cfc3131bcccb517f2409c Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 23 Sep 2024 16:17:37 +0100 Subject: [PATCH 094/675] Work on adding uncertainties, adding non-integer powers --- sasdata/data.py | 2 +- sasdata/quantities/_accessor_base.py | 8 +- sasdata/quantities/_build_tables.py | 1 + sasdata/quantities/_units_base.py | 50 ++++-- sasdata/quantities/absolute_temperature.py | 6 +- sasdata/quantities/accessors.py | 8 +- sasdata/quantities/notes.rst | 16 +- sasdata/quantities/operations.py | 4 +- sasdata/quantities/quantities_tests.py | 54 +++---- sasdata/quantities/quantity.py | 180 +++++++++++++++------ sasdata/quantities/units.py | 4 + sasdata/transforms/operation.py | 4 +- 12 files changed, 224 insertions(+), 113 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 18fd296d5..b8daddce3 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from sasdata.quantities.quantity import BaseQuantity, NamedQuantity +from sasdata.quantities.quantity import Quantity, NamedQuantity from sasdata.metadata import Metadata import numpy as np diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 4ec7bf8cb..42d14fc10 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -1,6 +1,6 @@ from typing import TypeVar, Sequence -from sasdata.quantities.quantity import BaseQuantity +from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit @@ -33,7 +33,7 @@ def value(self) -> float | None: -class QuantityAccessor[DataType](Accessor[DataType, BaseQuantity[DataType]]): +class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): """ Base class for accessors that work with quantities that have units """ def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): super().__init__(target_object, value_target) @@ -54,8 +54,8 @@ def unit(self) -> Unit: return Unit.parse(self._unit_part()) @property - def value(self) -> BaseQuantity[DataType] | None: + def value(self) -> Quantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: - return BaseQuantity(self._numerical_part(), self.unit) + return Quantity(self._numerical_part(), self.unit) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 750509349..a65ea39c9 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -98,6 +98,7 @@ "au": ["a.u.", "amu"], "percent": ["%"], "deg": ["degr"], + "none": ["Counts", "counts", "cnts", "Cnts"] } diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index bab0cb546..6c62c35cf 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -1,10 +1,14 @@ from dataclasses import dataclass from typing import Sequence, Self, TypeVar +from fractions import Fraction import numpy as np from sasdata.quantities.unicode_superscript import int_as_unicode_superscript +class DimensionError(Exception): + pass + class Dimensions: """ @@ -65,19 +69,46 @@ def __truediv__(self: Self, other: Self): self.moles_hint - other.moles_hint, self.angle_hint - other.angle_hint) - def __pow__(self, power: int): + def __pow__(self, power: int | float): - if not isinstance(power, int): + if not isinstance(power, (int, float)): return NotImplemented + frac = Fraction(power) + denominator = frac.denominator + numerator = frac.numerator + + # Throw errors if dimension is not a multiple of the denominator + + if self.length % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with length dimensionality {self.length}") + + if self.time % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with time dimensionality {self.time}") + + if self.mass % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with mass dimensionality {self.mass}") + + if self.current % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with current dimensionality {self.current}") + + if self.temperature % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with temperature dimensionality {self.temperature}") + + if self.moles_hint % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with moles hint dimensionality of {self.moles_hint}") + + if self.angle_hint % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with angle hint dimensionality of {self.angle_hint}") + return Dimensions( - self.length * power, - self.time * power, - self.mass * power, - self.current * power, - self.temperature * power, - self.moles_hint * power, - self.angle_hint * power) + (self.length * numerator) // denominator, + (self.time * numerator) // denominator, + (self.mass * numerator) // denominator, + (self.current * numerator) // denominator, + (self.temperature * numerator) // denominator, + (self.moles_hint * numerator) // denominator, + (self.angle_hint * numerator) // denominator) def __eq__(self: Self, other: Self): if isinstance(other, Dimensions): @@ -177,6 +208,7 @@ def __pow__(self, power: int): return Unit(self.scale**power, self.dimensions**power) + def equivalent(self: Self, other: "Unit"): return self.dimensions == other.dimensions diff --git a/sasdata/quantities/absolute_temperature.py b/sasdata/quantities/absolute_temperature.py index 7bcccce4e..95c8982fb 100644 --- a/sasdata/quantities/absolute_temperature.py +++ b/sasdata/quantities/absolute_temperature.py @@ -1,6 +1,6 @@ from typing import TypeVar -from quantities.quantity import BaseQuantity +from quantities.quantity import Quantity from sasdata.quantities.accessors import TemperatureAccessor @@ -8,8 +8,8 @@ class AbsoluteTemperatureAccessor(TemperatureAccessor[DataType]): """ Parsing for absolute temperatures """ @property - def value(self) -> BaseQuantity[DataType] | None: + def value(self) -> Quantity[DataType] | None: if self._numerical_part() is None: return None else: - return BaseQuantity.parse(self._numerical_part(), self._unit_part(), absolute_temperature=True) + return Quantity.parse(self._numerical_part(), self._unit_part(), absolute_temperature=True) diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index caaeb0022..92ef82bd1 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -80,7 +80,7 @@ from typing import TypeVar, Sequence -from sasdata.quantities.quantity import BaseQuantity +from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit @@ -113,7 +113,7 @@ def value(self) -> float | None: -class QuantityAccessor[DataType](Accessor[DataType, BaseQuantity[DataType]]): +class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): """ Base class for accessors that work with quantities that have units """ def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): super().__init__(target_object, value_target) @@ -134,9 +134,9 @@ def unit(self) -> Unit: return Unit.parse(self._unit_part()) @property - def value(self) -> BaseQuantity[DataType] | None: + def value(self) -> Quantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: - return BaseQuantity(self._numerical_part(), self.unit) + return Quantity(self._numerical_part(), self.unit) diff --git a/sasdata/quantities/notes.rst b/sasdata/quantities/notes.rst index 9c4f47c43..cc34a41e3 100644 --- a/sasdata/quantities/notes.rst +++ b/sasdata/quantities/notes.rst @@ -1,17 +1,3 @@ -Mutability ----------- - -DataSets: Immutable -Quantities: Immutable -Units: Hard coded - -Quantity methods ----------------- - -in_* methods return numbers/arrays in a given unit system -to_* converts to different units - - Identifying of Quantities -------------------- @@ -20,4 +6,4 @@ Either we give them names, in which case we risk collisions, or we use hashes, w have issues with things not being identified correctly. The decision here is to use hashes of the data, not names, because it would be too easy to -give different things the same name. \ No newline at end of file +give different things the same name. diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py index dcbdcf242..28cf5561c 100644 --- a/sasdata/quantities/operations.py +++ b/sasdata/quantities/operations.py @@ -2,7 +2,7 @@ import json -# from sasdata.quantities.quantity import BaseQuantity +from sasdata.quantities.quantity import Quantity T = TypeVar("T") @@ -709,4 +709,4 @@ def __eq__(self, other): Neg, Inv, Add, Sub, Mul, Div, Pow] -_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} +_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} \ No newline at end of file diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index dd7271bee..5ed7f8fc0 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -1,72 +1,72 @@ import numpy as np -from sasdata.quantities.quantity import BaseQuantity, UnitError +from sasdata.quantities.quantity import Quantity, UnitError import sasdata.quantities.units as units import sasdata.quantities.si as si import pytest def test_in_units_of_calculation(): """ Just a couple of unit conversions """ - assert BaseQuantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 - assert BaseQuantity(10, units.minutes).in_units_of(units.seconds) == 600 - assert BaseQuantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000 / 9.81, abs=1) - assert BaseQuantity(0, units.meters).in_units_of(units.exameters) == 0 + assert Quantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 + assert Quantity(10, units.minutes).in_units_of(units.seconds) == 600 + assert Quantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000 / 9.81, abs=1) + assert Quantity(0, units.meters).in_units_of(units.exameters) == 0 def test_unit_compounding_pow(): """ Test units compound correctly when __pow__ is used""" - assert (BaseQuantity(1, units.millimeters) ** 2).in_units_of(units.square_meters) == 1e-6 - assert (BaseQuantity(1, units.minutes) ** 3).in_units_of(units.seconds ** 3) == 60 ** 3 + assert (Quantity(1, units.millimeters) ** 2).in_units_of(units.square_meters) == 1e-6 + assert (Quantity(1, units.minutes) ** 3).in_units_of(units.seconds ** 3) == 60 ** 3 def test_unit_compounding_mul(): """ Test units compound correctly when __mul__ is used""" - assert (BaseQuantity(4, units.minutes) * BaseQuantity(0.25, units.hertz)).in_units_of(units.none) == 60 - assert (BaseQuantity(250, units.volts) * BaseQuantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 + assert (Quantity(4, units.minutes) * Quantity(0.25, units.hertz)).in_units_of(units.none) == 60 + assert (Quantity(250, units.volts) * Quantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 def test_unit_compounding_div(): """ Test units compound correctly when __truediv__ is used""" - assert (BaseQuantity(10, units.kilometers) / BaseQuantity(2, units.minutes) + assert (Quantity(10, units.kilometers) / Quantity(2, units.minutes) ).in_units_of(units.meters_per_second) == pytest.approx(250/3, abs=1e-6) - assert (BaseQuantity(1, units.nanowebers) / (BaseQuantity(1, units.millimeters) ** 2)).in_units_of(units.millitesla) == 1 + assert (Quantity(1, units.nanowebers) / (Quantity(1, units.millimeters) ** 2)).in_units_of(units.millitesla) == 1 def test_value_mul(): """ Test value part of quantities multiply correctly""" - assert (BaseQuantity(1j, units.seconds) * BaseQuantity(1j, units.watts)).in_units_of(units.joules) == -1 + assert (Quantity(1j, units.seconds) * Quantity(1j, units.watts)).in_units_of(units.joules) == -1 def test_scalar_mul(): - assert (BaseQuantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 - assert (10 * BaseQuantity(1, units.seconds)).in_units_of(units.seconds) == 10 - assert (1000 * BaseQuantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 + assert (Quantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 + assert (10 * Quantity(1, units.seconds)).in_units_of(units.seconds) == 10 + assert (1000 * Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 def test_scalar_div(): - assert (BaseQuantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 - assert (10 / BaseQuantity(1, units.seconds)).in_units_of(units.hertz) == 10 - assert (0.001 / BaseQuantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 + assert (Quantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 + assert (10 / Quantity(1, units.seconds)).in_units_of(units.hertz) == 10 + assert (0.001 / Quantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 def test_good_add_sub(): """ Test that adding and subtracting units works """ - assert (BaseQuantity(1, units.seconds) + BaseQuantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 - assert (BaseQuantity(1, units.seconds) - BaseQuantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 + assert (Quantity(1, units.seconds) + Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 + assert (Quantity(1, units.seconds) - Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 - assert (BaseQuantity(1, units.inches) + BaseQuantity(1, units.feet)).in_units_of(units.inches) == 13 + assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == 13 @pytest.mark.parametrize("unit_1", si.all_si) @pytest.mark.parametrize("unit_2", si.all_si) def test_mixed_quantity_add_sub(unit_1, unit_2): if unit_1.equivalent(unit_2): - assert (BaseQuantity(0, unit_1) + BaseQuantity(0, unit_2)).in_units_of(unit_1) == 0 + assert (Quantity(0, unit_1) + Quantity(0, unit_2)).in_units_of(unit_1) == 0 else: with pytest.raises(UnitError): - BaseQuantity(1, unit_1) + BaseQuantity(1, unit_2) + Quantity(1, unit_1) + Quantity(1, unit_2) def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float, abs=1e-9): """ Helper function for testing units that are multiples of each other """ assert u1.equivalent(u2), "Units should be compatible for this test" - assert (BaseQuantity(1, u1) / BaseQuantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) + assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) def test_american_units(): @@ -76,7 +76,7 @@ def test_american_units(): assert_unit_ratio(units.pounds_force_per_square_inch, units.pounds_force / (units.inches**2), 1, abs=1e-5) def test_percent(): - assert BaseQuantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) + assert Quantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) @pytest.mark.parametrize("unit_1", si.all_si) @pytest.mark.parametrize("unit_2", si.all_si) @@ -84,9 +84,9 @@ def test_conversion_errors(unit_1, unit_2): """ Test conversion errors are thrown when units are not compatible """ if unit_1 == unit_2: - assert BaseQuantity(1, unit_1).in_units_of(unit_2) == 1 + assert Quantity(1, unit_1).in_units_of(unit_2) == 1 else: with pytest.raises(UnitError): - BaseQuantity(1, units.seconds).in_units_of(units.meters) + Quantity(1, units.seconds).in_units_of(units.meters) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index e7b91cce8..74de90a50 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,6 +1,7 @@ from typing import Collection, Sequence, TypeVar, Generic, Self from dataclasses import dataclass +import numpy as np from numpy._typing import ArrayLike from sasdata.quantities.operations import Operation, Variable @@ -12,52 +13,154 @@ class UnitError(Exception): """Errors caused by unit specification not being correct""" +def hash_numpy_data(*data: np.ndarray): + + md5_hash = hashlib.md5() + + for datum in data: + data_bytes = datum.tobytes() + md5_hash.update(data_bytes) + + # Hash function returns a hex string, we want an int + return int(md5_hash.hexdigest(), 16) + QuantityType = TypeVar("QuantityType") -class BaseQuantity[QuantityType]: - def __init__(self, value: QuantityType, units: Unit): + +class QuantityHistory: + def __init__(self, operation_tree: Operation, references: dict[int, "Quantity"]): + self.operation_tree = operation_tree + self.references = references + + def jacobian(self) -> list[Operation]: + """ Derivative of this quantity's operation history with respect to each of the references """ + + # Use the hash value to specify the variable of differentiation + return [self.operation_tree.derivative(hash_value) for hash_value in self.references] + + def standard_error_propagate(self, covariances: dict[tuple[int, int]: "Quantity"] = {}): + """ Do standard error propagation to calculate the uncertainties associated with this quantity + + @param: covariances, off diagonal entries for the covariance matrix + """ + + jacobian = self.jacobian() + + # Evaluate the jacobian + evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] # should we use quantities here? + + output = 0 + + for hash_value in self.references: + output += evaluated_jacobian * (self.references[hash_value].variance * evaluated_jacobian) + + for (cov1, cov2) in covariances: + pass + + + + @staticmethod + def variable(quantity: "Quantity"): + """ Create a history that starts with the provided data """ + return QuantityHistory(Variable(quantity.hash_value), {quantity.hash_value: quantity}) + + @staticmethod + def _apply_operation(operation: type[Operation], *histories: "QuantityHistory") -> "QuantityHistory": + """ Apply an operation to the history + + This is slightly unsafe as it is possible to attempt to apply an n-ary operation to a number of trees other + than n, but it is relatively concise. Because it is concise we'll go with this for now and see if it causes + any problems down the line. It is a private static method to discourage misuse. + + """ + + # Copy references over, even though it overrides on collision, + # this should behave because only data based variables should be represented. + # Should not be a problem any more than losing histories + references = {} + for history in histories: + references.update(history.references) + + return QuantityHistory( + operation(*[history.operation_tree for history in histories]), + references) + + + +class Quantity[QuantityType]: + + + def __init__(self, + value: QuantityType, + units: Unit, + variance: QuantityType | None = None): + self.value = value + """ Numerical value of this data, in the specified units""" + self.units = units + """ Units of this data """ + + self.hash_value = -1 + """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ + + self._variance = variance + """ Contains the variance if it is data driven, else it is """ + + if variance is None: + self.hash_value = hash_numpy_data(value) + else: + self.hash_value = hash_numpy_data(value, variance.value) + + self.history = QuantityHistory.variable(self) + + @property + def variance(self) -> "Quantity": + pass + + def standard_deviation(self) -> "Quantity": + return self.variance ** (1/2) def in_units_of(self, units: Unit) -> QuantityType: + """ Get this quantity in other units """ if self.units.equivalent(units): return (self.units.scale / units.scale) * self.value else: raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") def __mul__(self: Self, other: ArrayLike | Self ) -> Self: - if isinstance(other, BaseQuantity): - return BaseQuantity(self.value * other.value, self.units * other.units) + if isinstance(other, Quantity): + return Quantity(self.value * other.value, self.units * other.units) else: - return BaseQuantity(self.value * other, self.units) + return Quantity(self.value * other, self.units) def __rmul__(self: Self, other: ArrayLike | Self): - if isinstance(other, BaseQuantity): - return BaseQuantity(other.value * self.value, other.units * self.units) + if isinstance(other, Quantity): + return Quantity(other.value * self.value, other.units * self.units) else: - return BaseQuantity(other * self.value, self.units) + return Quantity(other * self.value, self.units) def __truediv__(self: Self, other: float | Self) -> Self: - if isinstance(other, BaseQuantity): - return BaseQuantity(self.value / other.value, self.units / other.units) + if isinstance(other, Quantity): + return Quantity(self.value / other.value, self.units / other.units) else: - return BaseQuantity(self.value / other, self.units) + return Quantity(self.value / other, self.units) def __rtruediv__(self: Self, other: float | Self) -> Self: - if isinstance(other, BaseQuantity): - return BaseQuantity(self.value / other.value, self.units / other.units) + if isinstance(other, Quantity): + return Quantity(self.value / other.value, self.units / other.units) else: - return BaseQuantity(self.value / other, self.units) + return Quantity(self.value / other, self.units) def __add__(self: Self, other: Self | ArrayLike) -> Self: - if isinstance(other, BaseQuantity): + if isinstance(other, Quantity): if self.units.equivalent(other.units): - return BaseQuantity(self.value + (other.value * other.units.scale) / self.units.scale, self.units) + return Quantity(self.value + (other.value * other.units.scale) / self.units.scale, self.units) else: raise UnitError(f"Units do not have the same dimensionality: {self.units} vs {other.units}") @@ -67,7 +170,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: # Don't need __radd__ because only quantity/quantity operations should be allowed def __neg__(self): - return BaseQuantity(-self.value, self.units) + return Quantity(-self.value, self.units) def __sub__(self: Self, other: Self | ArrayLike) -> Self: return self + (-other) @@ -76,42 +179,27 @@ def __rsub__(self: Self, other: Self | ArrayLike) -> Self: return (-self) + other def __pow__(self: Self, other: int): - return BaseQuantity(self.value ** other, self.units ** other) + return Quantity(self.value ** other, self.units ** other) @staticmethod def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass -class Quantity[QuantityType](BaseQuantity[QuantityType]): - def with_uncertainty(self, uncertainty: BaseQuantity[QuantityType]): - return UncertainQuantity(self.value, self.units, uncertainty=uncertainty) +class NamedQuantity[QuantityType](Quantity[QuantityType]): + def __init__(self, + value: QuantityType, + units: Unit, + name: str, + variance: QuantityType | None = None): - -class NamedQuantity[QuantityType](BaseQuantity[QuantityType]): - def __init__(self, value: QuantityType, units: Unit, name: str): - super().__init__(value, units) + super().__init__(value, units, variance=variance) self.name = name - def with_uncertainty(self, uncertainty: BaseQuantity[QuantityType]): - return UncertainNamedQuantity(self.value, self.units, uncertainty=uncertainty, name=self.name) - - -class UncertainBaseQuantity[QuantityType](BaseQuantity[QuantityType]): - pass - -class UncertainQuantity[QuantityType](BaseQuantity[QuantityType]): - def __init__(self, value: QuantityType, units: Unit, uncertainty: BaseQuantity[QuantityType]): - super().__init__(value, units) - self.uncertainty = uncertainty - - hash_value = hashlib.md5(value, uncertainty) - - -class UncertainNamedQuantity[QuantityType](BaseQuantity[QuantityType]): - def __init__(self, value: QuantityType, units: Unit, uncertainty: BaseQuantity[QuantityType], name: str): - super().__init__(value, units) - self.uncertainty = uncertainty - self.name = name +class DerivedQuantity[QuantityType](Quantity[QuantityType]): + def __init__(self, value, units, variance, history): - self.history = Variable(self.name) \ No newline at end of file + self._variance_cache = None + @property + def variance(self): + pass \ No newline at end of file diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 2da7b4537..da19d67d5 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -1952,6 +1952,10 @@ def __init__(self, name: str, units: list[NamedUnit]): "a.u.": atomic_mass_units, "amu": atomic_mass_units, "degr": degrees, + "Counts": none, + "counts": none, + "cnts": none, + "Cnts": none, } diff --git a/sasdata/transforms/operation.py b/sasdata/transforms/operation.py index b7c54ad2b..59121882d 100644 --- a/sasdata/transforms/operation.py +++ b/sasdata/transforms/operation.py @@ -1,5 +1,5 @@ import numpy as np -from sasdata.quantities.quantity import BaseQuantity +from sasdata.quantities.quantity import Quantity class Operation: """ Sketch of what model post-processing classes might look like """ @@ -11,7 +11,7 @@ class Operation: def name(self) -> str: raise NotImplementedError("No name for transform") - def evaluate(self) -> BaseQuantity[np.ndarray]: + def evaluate(self) -> Quantity[np.ndarray]: pass def __call__(self, *children, **named_children): From c0ba0f916f0d2034455451f7a6a31f39c3d44d4f Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 23 Sep 2024 16:47:39 +0100 Subject: [PATCH 095/675] Integer unit powers now work --- sasdata/quantities/_units_base.py | 6 +-- sasdata/quantities/operations.py | 2 - sasdata/quantities/quantities_tests.py | 26 +++++++++++++ sasdata/quantities/quantity.py | 8 ++-- sasdata/quantities/units.py | 54 ++++++++++++++++++++------ 5 files changed, 76 insertions(+), 20 deletions(-) diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 6c62c35cf..d843783b7 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -74,7 +74,7 @@ def __pow__(self, power: int | float): if not isinstance(power, (int, float)): return NotImplemented - frac = Fraction(power) + frac = Fraction(power).limit_denominator(500) # Probably way bigger than needed, 10 would probably be fine denominator = frac.denominator numerator = frac.numerator @@ -202,8 +202,8 @@ def __rtruediv__(self: Self, other: "Unit"): else: return NotImplemented - def __pow__(self, power: int): - if not isinstance(power, int): + def __pow__(self, power: int | float): + if not isinstance(power, int | float): return NotImplemented return Unit(self.scale**power, self.dimensions**power) diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py index 28cf5561c..e8724e055 100644 --- a/sasdata/quantities/operations.py +++ b/sasdata/quantities/operations.py @@ -2,8 +2,6 @@ import json -from sasdata.quantities.quantity import Quantity - T = TypeVar("T") def hash_and_name(hash_or_name: int | str): diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index 5ed7f8fc0..bfc6d4977 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -51,6 +51,32 @@ def test_good_add_sub(): assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == 13 +@pytest.mark.parametrize("unit_in, power, unit_out", [ + (units.meters**2, 1/2, units.meters), + (units.meters**3, 1/3, units.meters), + (units.meters**3, 2/3, units.meters**2), + (units.meters**3, -5/3, units.meters**-5), + (units.none, 1/10, units.none), + (units.none, 19/17, units.none), + (units.none, np.pi, units.none) +]) +def test_good_non_integer_unit_powers(unit_in, power, unit_out): + """ Check that we can do various square and cube root stuff if we need to, + If dimensionless, we should be able to do arbitrary powers + """ + assert unit_in**power == unit_out + +@pytest.mark.parametrize("unit, power", [ + (units.meters, 1/2), + (units.milliohms, 1/3), + (units.meters, 3/2), + (units.meters**2, 2/3) +]) +def test_bad_non_integer_unit_powers(unit, power): + """ Check that we get an error if we try and do something silly with powers""" + with pytest.raises(units.DimensionError): + x = unit**power + @pytest.mark.parametrize("unit_1", si.all_si) @pytest.mark.parametrize("unit_2", si.all_si) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 74de90a50..0476cc240 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -13,12 +13,12 @@ class UnitError(Exception): """Errors caused by unit specification not being correct""" -def hash_numpy_data(*data: np.ndarray): +def hash_data_via_numpy(*data: ArrayLike): md5_hash = hashlib.md5() for datum in data: - data_bytes = datum.tobytes() + data_bytes = np.array(datum).tobytes() md5_hash.update(data_bytes) # Hash function returns a hex string, we want an int @@ -109,9 +109,9 @@ def __init__(self, """ Contains the variance if it is data driven, else it is """ if variance is None: - self.hash_value = hash_numpy_data(value) + self.hash_value = hash_data_via_numpy(value) else: - self.hash_value = hash_numpy_data(value, variance.value) + self.hash_value = hash_data_via_numpy(value, variance.value) self.history = QuantityHistory.variable(self) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index da19d67d5..33edc9354 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -84,11 +84,15 @@ from dataclasses import dataclass from typing import Sequence, Self, TypeVar +from fractions import Fraction import numpy as np from sasdata.quantities.unicode_superscript import int_as_unicode_superscript +class DimensionError(Exception): + pass + class Dimensions: """ @@ -149,19 +153,46 @@ def __truediv__(self: Self, other: Self): self.moles_hint - other.moles_hint, self.angle_hint - other.angle_hint) - def __pow__(self, power: int): + def __pow__(self, power: int | float): - if not isinstance(power, int): + if not isinstance(power, (int, float)): return NotImplemented + frac = Fraction(power).limit_denominator(500) # Probably way bigger than needed, 10 would probably be fine + denominator = frac.denominator + numerator = frac.numerator + + # Throw errors if dimension is not a multiple of the denominator + + if self.length % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with length dimensionality {self.length}") + + if self.time % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with time dimensionality {self.time}") + + if self.mass % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with mass dimensionality {self.mass}") + + if self.current % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with current dimensionality {self.current}") + + if self.temperature % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with temperature dimensionality {self.temperature}") + + if self.moles_hint % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with moles hint dimensionality of {self.moles_hint}") + + if self.angle_hint % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with angle hint dimensionality of {self.angle_hint}") + return Dimensions( - self.length * power, - self.time * power, - self.mass * power, - self.current * power, - self.temperature * power, - self.moles_hint * power, - self.angle_hint * power) + (self.length * numerator) // denominator, + (self.time * numerator) // denominator, + (self.mass * numerator) // denominator, + (self.current * numerator) // denominator, + (self.temperature * numerator) // denominator, + (self.moles_hint * numerator) // denominator, + (self.angle_hint * numerator) // denominator) def __eq__(self: Self, other: Self): if isinstance(other, Dimensions): @@ -255,12 +286,13 @@ def __rtruediv__(self: Self, other: "Unit"): else: return NotImplemented - def __pow__(self, power: int): - if not isinstance(power, int): + def __pow__(self, power: int | float): + if not isinstance(power, int | float): return NotImplemented return Unit(self.scale**power, self.dimensions**power) + def equivalent(self: Self, other: "Unit"): return self.dimensions == other.dimensions From 6b9c688fdfe82d8088cc65870efbdf2c448cd980 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 25 Sep 2024 15:26:25 +0100 Subject: [PATCH 096/675] Quantities now have histories, and variance could work, needs testing though --- sasdata/quantities/notes.rst | 10 ++- sasdata/quantities/quantity.py | 127 ++++++++++++++++++++++++++------- 2 files changed, 111 insertions(+), 26 deletions(-) diff --git a/sasdata/quantities/notes.rst b/sasdata/quantities/notes.rst index cc34a41e3..a9a2b58ea 100644 --- a/sasdata/quantities/notes.rst +++ b/sasdata/quantities/notes.rst @@ -1,3 +1,11 @@ +Mutability +---------- + +DataSets: Immutable +Quantities: Immutable +Units: Hard coded + + Identifying of Quantities -------------------- @@ -6,4 +14,4 @@ Either we give them names, in which case we risk collisions, or we use hashes, w have issues with things not being identified correctly. The decision here is to use hashes of the data, not names, because it would be too easy to -give different things the same name. +give different things the same name. \ No newline at end of file diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 0476cc240..620561490 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -5,6 +5,7 @@ from numpy._typing import ArrayLike from sasdata.quantities.operations import Operation, Variable +from quantities import operations from sasdata.quantities.units import Unit import hashlib @@ -45,19 +46,25 @@ def standard_error_propagate(self, covariances: dict[tuple[int, int]: "Quantity" @param: covariances, off diagonal entries for the covariance matrix """ + if covariances: + raise NotImplementedError("User specified covariances not currently implemented") + jacobian = self.jacobian() # Evaluate the jacobian - evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] # should we use quantities here? - - output = 0 + # TODO: should we use quantities here, does that work automatically? + evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] - for hash_value in self.references: - output += evaluated_jacobian * (self.references[hash_value].variance * evaluated_jacobian) + hash_values = [key for key in self.references] + output = None - for (cov1, cov2) in covariances: - pass + for hash_value, jac_component in zip(hash_values, evaluated_jacobian): + if output is None: + output = jac_component * (self.references[hash_value].variance * jac_component) + else: + output += jac_component * (self.references[hash_value].variance * jac_component) + return output @staticmethod @@ -66,7 +73,7 @@ def variable(quantity: "Quantity"): return QuantityHistory(Variable(quantity.hash_value), {quantity.hash_value: quantity}) @staticmethod - def _apply_operation(operation: type[Operation], *histories: "QuantityHistory") -> "QuantityHistory": + def apply_operation(operation: type[Operation], *histories: "QuantityHistory") -> "QuantityHistory": """ Apply an operation to the history This is slightly unsafe as it is possible to attempt to apply an n-ary operation to a number of trees other @@ -117,10 +124,14 @@ def __init__(self, @property def variance(self) -> "Quantity": - pass + """ Get the variance of this object""" + if self._variance is None: + return Quantity(np.zeros_like(self.value), self.units**2) + else: + return Quantity(self._variance, self.units**2) def standard_deviation(self) -> "Quantity": - return self.variance ** (1/2) + return self.variance ** 0.5 def in_units_of(self, units: Unit) -> QuantityType: """ Get this quantity in other units """ @@ -131,36 +142,86 @@ def in_units_of(self, units: Unit) -> QuantityType: def __mul__(self: Self, other: ArrayLike | Self ) -> Self: if isinstance(other, Quantity): - return Quantity(self.value * other.value, self.units * other.units) + return DerivedQuantity( + self.value * other.value, + self.units * other.units, + history=QuantityHistory.apply_operation(operations.Mul, self.history, other.history)) else: - return Quantity(self.value * other, self.units) + return DerivedQuantity(self.value * other, self.units, + QuantityHistory( + operations.Mul( + self.history.operation_tree, + operations.Constant(other)), + self.history.references)) def __rmul__(self: Self, other: ArrayLike | Self): if isinstance(other, Quantity): - return Quantity(other.value * self.value, other.units * self.units) + return DerivedQuantity( + other.value * self.value, + other.units * self.units, + history=QuantityHistory.apply_operation( + operations.Mul, + other.history, + self.history)) else: - return Quantity(other * self.value, self.units) + return DerivedQuantity(other * self.value, self.units, + QuantityHistory( + operations.Mul( + operations.Constant(other), + self.history.operation_tree), + self.history.references)) def __truediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): - return Quantity(self.value / other.value, self.units / other.units) + return DerivedQuantity( + self.value / other.value, + self.units / other.units, + history=QuantityHistory.apply_operation( + operations.Div, + self.history, + other.history)) else: - return Quantity(self.value / other, self.units) + return DerivedQuantity(self.value / other, self.units, + QuantityHistory( + operations.Div( + operations.Constant(other), + self.history.operation_tree), + self.history.references)) def __rtruediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): - return Quantity(self.value / other.value, self.units / other.units) + return DerivedQuantity( + other.value / self.value, + other.units / self.units, + history=QuantityHistory.apply_operation( + operations.Div, + other.history, + self.history + )) else: - return Quantity(self.value / other, self.units) + return DerivedQuantity( + other / self.value, + self.units ** -1, + QuantityHistory( + operations.Div( + operations.Constant(other), + self.history.operation_tree), + self.history.references)) def __add__(self: Self, other: Self | ArrayLike) -> Self: if isinstance(other, Quantity): if self.units.equivalent(other.units): - return Quantity(self.value + (other.value * other.units.scale) / self.units.scale, self.units) + return DerivedQuantity( + self.value + (other.value * other.units.scale) / self.units.scale, + self.units, + QuantityHistory.apply_operation( + operations.Add, + self.history, + other.history)) else: raise UnitError(f"Units do not have the same dimensionality: {self.units} vs {other.units}") @@ -170,7 +231,11 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: # Don't need __radd__ because only quantity/quantity operations should be allowed def __neg__(self): - return Quantity(-self.value, self.units) + return DerivedQuantity(-self.value, self.units, + QuantityHistory.apply_operation( + operations.Neg, + self.history + )) def __sub__(self: Self, other: Self | ArrayLike) -> Self: return self + (-other) @@ -178,8 +243,14 @@ def __sub__(self: Self, other: Self | ArrayLike) -> Self: def __rsub__(self: Self, other: Self | ArrayLike) -> Self: return (-self) + other - def __pow__(self: Self, other: int): - return Quantity(self.value ** other, self.units ** other) + def __pow__(self: Self, other: int | float): + return DerivedQuantity(self.value ** other, + self.units ** other, + QuantityHistory( + operations.Pow( + self.history.operation_tree, + other), + self.history.references)) @staticmethod def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): @@ -197,9 +268,15 @@ def __init__(self, self.name = name class DerivedQuantity[QuantityType](Quantity[QuantityType]): - def __init__(self, value, units, variance, history): + def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): + super().__init__(value, units, variance=None) + self.history = history self._variance_cache = None + @property - def variance(self): - pass \ No newline at end of file + def variance(self) -> Quantity: + if self._variance_cache is None: + self._variance_cache = self.history.standard_error_propagate() + + return self._variance_cache \ No newline at end of file From bb7e49c948979be88a94b427decee427a9189e50 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 26 Sep 2024 10:21:33 +0100 Subject: [PATCH 097/675] Quantities ready for testing --- sasdata/quantities/quantity_examples.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sasdata/quantities/quantity_examples.py b/sasdata/quantities/quantity_examples.py index cc12640db..91b49c84a 100644 --- a/sasdata/quantities/quantity_examples.py +++ b/sasdata/quantities/quantity_examples.py @@ -1,8 +1,9 @@ +from sasdata.quantities.quantity import Quantity from sasdata.quantities import units -from sasdata.quantities.quantity import NamedQuantity -x = NamedQuantity("x", 1, units.meters, standard_error=1) -y = NamedQuantity("y", 1, units.decimeters, standard_error=1) +x = Quantity(1, units.meters, variance=1) +y = Quantity(1, units.meters, variance=1) -print(x+y) -print((x+y).to_units_of(units.centimeters)) +z = x+y + +print(z) From 20206aabd9711c9dfb3c9d99957e56cbfa84c1a5 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 27 Sep 2024 13:26:26 +0100 Subject: [PATCH 098/675] Quantity combining seems to work --- sasdata/quantities/_units_base.py | 37 +++++++++- sasdata/quantities/notes.rst | 6 ++ sasdata/quantities/operations.py | 20 ++--- sasdata/quantities/quantity.py | 98 +++++++++++++++++++++++-- sasdata/quantities/quantity_examples.py | 11 ++- sasdata/quantities/units.py | 37 +++++++++- sasdata/raw_form.py | 2 + 7 files changed, 187 insertions(+), 24 deletions(-) diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index d843783b7..e6bee7a44 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -171,6 +171,36 @@ def __repr__(self): return s + def si_repr(self): + s = "" + for name, size in [ + ("kg", self.mass), + ("m", self.length), + ("s", self.time), + ("A", self.current), + ("K", self.temperature), + ("mol", self.moles_hint)]: + + if size == 0: + pass + elif size == 1: + s += f"{name}" + else: + s += f"{name}{int_as_unicode_superscript(size)}" + + match self.angle_hint: + case 0: + pass + case 2: + s += "sr" + case -2: + s += "sr" + int_as_unicode_superscript(-1) + case _: + s += "rad" + int_as_unicode_superscript(self.angle_hint) + + return s + + class Unit: def __init__(self, si_scaling_factor: float, @@ -224,7 +254,12 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): pass def __repr__(self): - return f"Unit[{self.scale}, {self.dimensions}]" + if self.scale == 1: + # We're in SI + return self.dimensions.si_repr() + + else: + return f"Unit[{self.scale}, {self.dimensions}]" @staticmethod def parse(unit_string: str) -> "Unit": diff --git a/sasdata/quantities/notes.rst b/sasdata/quantities/notes.rst index a9a2b58ea..9c4f47c43 100644 --- a/sasdata/quantities/notes.rst +++ b/sasdata/quantities/notes.rst @@ -5,6 +5,12 @@ DataSets: Immutable Quantities: Immutable Units: Hard coded +Quantity methods +---------------- + +in_* methods return numbers/arrays in a given unit system +to_* converts to different units + Identifying of Quantities -------------------- diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py index e8724e055..da9bd539e 100644 --- a/sasdata/quantities/operations.py +++ b/sasdata/quantities/operations.py @@ -73,21 +73,21 @@ def derivative(self, variable: Union[str, int, "Variable"], simplify=True): derivative_string = derivative.serialise() - print("---------------") - print("Base") - print("---------------") - print(derivative.summary()) + # print("---------------") + # print("Base") + # print("---------------") + # print(derivative.summary()) # Inefficient way of doing repeated simplification, but it will work for i in range(100): # set max iterations derivative = derivative._clean() - - print("-------------------") - print("Iteration", i+1) - print("-------------------") - print(derivative.summary()) - print("-------------------") + # + # print("-------------------") + # print("Iteration", i+1) + # print("-------------------") + # print(derivative.summary()) + # print("-------------------") new_derivative_string = derivative.serialise() diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 620561490..d54116251 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -5,7 +5,7 @@ from numpy._typing import ArrayLike from sasdata.quantities.operations import Operation, Variable -from quantities import operations +from quantities import operations, units from sasdata.quantities.units import Unit import hashlib @@ -93,6 +93,12 @@ def apply_operation(operation: type[Operation], *histories: "QuantityHistory") - operation(*[history.operation_tree for history in histories]), references) + def has_variance(self): + for key in self.references: + if self.references[key].has_variance: + return True + + return False class Quantity[QuantityType]: @@ -101,7 +107,8 @@ class Quantity[QuantityType]: def __init__(self, value: QuantityType, units: Unit, - variance: QuantityType | None = None): + variance: QuantityType | None = None, + hash_seed = ""): self.value = value """ Numerical value of this data, in the specified units""" @@ -116,12 +123,16 @@ def __init__(self, """ Contains the variance if it is data driven, else it is """ if variance is None: - self.hash_value = hash_data_via_numpy(value) + self.hash_value = hash_data_via_numpy(hash_seed, value) else: - self.hash_value = hash_data_via_numpy(value, variance.value) + self.hash_value = hash_data_via_numpy(hash_seed, value, variance) self.history = QuantityHistory.variable(self) + @property + def has_variance(self): + return self._variance is not None + @property def variance(self) -> "Quantity": """ Get the variance of this object""" @@ -140,6 +151,35 @@ def in_units_of(self, units: Unit) -> QuantityType: else: raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") + def variance_in_units_of(self, units: Unit) -> QuantityType: + """ Get the variance of quantity in other units """ + variance = self.variance + if variance.units.equivalent(units): + return (variance.units.scale / units.scale) * variance + else: + raise UnitError(f"Target units ({units}) not compatible with existing units ({variance.units}).") + + def in_si(self): + si_units = self.units.si_equivalent() + return self.in_units_of(si_units) + + def in_units_of_with_standard_error(self, units): + variance = self.variance + units_squared = units**2 + + if variance.units.equivalent(units_squared): + scale_factor = self.units.scale / units.scale + + return scale_factor*self.value, scale_factor * np.sqrt(self.variance.in_units_of(units_squared)) + else: + raise UnitError(f"Target units ({units}) not compatible with existing units ({variance.units}).") + + def in_si_with_standard_error(self): + if self.has_variance: + return self.in_units_of_with_standard_error(self.units.si_equivalent()) + else: + return self.in_si(), None + def __mul__(self: Self, other: ArrayLike | Self ) -> Self: if isinstance(other, Quantity): return DerivedQuantity( @@ -252,6 +292,44 @@ def __pow__(self: Self, other: int | float): other), self.history.references)) + @staticmethod + def _array_repr_format(arr: np.ndarray): + """ Format the array """ + order = len(arr.shape) + reshaped = arr.reshape(-1) + if len(reshaped) > 4: + numbers = ",".join([f"{n}" for n in reshaped]) + else: + numbers = f"{reshaped[0]}, {reshaped[1]} ... {reshaped[-2]}, {reshaped[-1]}" + + return "["*order + numbers + "]"*order + + def __repr__(self): + + if isinstance(self.units, units.NamedUnit): + + value = self.value + error = np.sqrt(self.standard_deviation().value) + unit_string = self.units.symbol + + else: + value, error = self.in_si_with_standard_error() + unit_string = self.units.dimensions.si_repr() + + if isinstance(self.value, np.ndarray): + # Get the array in short form + numeric_string = self._array_repr_format(value) + + if self.has_variance: + numeric_string += " ± " + self._array_repr_format(error) + + else: + numeric_string = f"{value}" + if self.has_variance: + numeric_string += f" ± {error}" + + return numeric_string + " " + unit_string + @staticmethod def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass @@ -259,20 +337,28 @@ def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: Fa class NamedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, + name: str, value: QuantityType, units: Unit, - name: str, variance: QuantityType | None = None): - super().__init__(value, units, variance=variance) + super().__init__(value, units, variance=variance, hash_seed=name) self.name = name + def __repr__(self): + return f"[{self.name}] " + super().__repr__() + class DerivedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): super().__init__(value, units, variance=None) self.history = history self._variance_cache = None + self._has_variance = history.has_variance() + + @property + def has_variance(self): + return self._has_variance @property def variance(self) -> Quantity: diff --git a/sasdata/quantities/quantity_examples.py b/sasdata/quantities/quantity_examples.py index 91b49c84a..9ccb77bbd 100644 --- a/sasdata/quantities/quantity_examples.py +++ b/sasdata/quantities/quantity_examples.py @@ -1,9 +1,8 @@ -from sasdata.quantities.quantity import Quantity +from sasdata.quantities.quantity import Quantity, NamedQuantity from sasdata.quantities import units -x = Quantity(1, units.meters, variance=1) -y = Quantity(1, units.meters, variance=1) +x = NamedQuantity("x", 1, units.meters, variance=1) +y = NamedQuantity("y", 1, units.meters, variance=1) -z = x+y - -print(z) +print(x+y) +print(x+x) \ No newline at end of file diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 33edc9354..ebe738331 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -255,6 +255,36 @@ def __repr__(self): return s + def si_repr(self): + s = "" + for name, size in [ + ("kg", self.mass), + ("m", self.length), + ("s", self.time), + ("A", self.current), + ("K", self.temperature), + ("mol", self.moles_hint)]: + + if size == 0: + pass + elif size == 1: + s += f"{name}" + else: + s += f"{name}{int_as_unicode_superscript(size)}" + + match self.angle_hint: + case 0: + pass + case 2: + s += "sr" + case -2: + s += "sr" + int_as_unicode_superscript(-1) + case _: + s += "rad" + int_as_unicode_superscript(self.angle_hint) + + return s + + class Unit: def __init__(self, si_scaling_factor: float, @@ -308,7 +338,12 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): pass def __repr__(self): - return f"Unit[{self.scale}, {self.dimensions}]" + if self.scale == 1: + # We're in SI + return self.dimensions.si_repr() + + else: + return f"Unit[{self.scale}, {self.dimensions}]" @staticmethod def parse(unit_string: str) -> "Unit": diff --git a/sasdata/raw_form.py b/sasdata/raw_form.py index a58c09ce5..9519dead8 100644 --- a/sasdata/raw_form.py +++ b/sasdata/raw_form.py @@ -5,6 +5,8 @@ DataType = TypeVar("DataType") +""" Sasdata metadata tree """ + def shorten_string(string): lines = string.split("\n") if len(lines) <= 1: From 3355e967f6ab68060d3b58121bf25e7c30f1e3cb Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 27 Sep 2024 16:46:49 +0100 Subject: [PATCH 099/675] Fixed error in helper function --- sasdata/quantities/quantities_tests.py | 8 +++- sasdata/quantities/quantity.py | 44 ++++++++++++++-------- sasdata/quantities/quantity_error_tests.py | 20 ++++++++++ sasdata/quantities/quantity_examples.py | 7 ++-- 4 files changed, 59 insertions(+), 20 deletions(-) create mode 100644 sasdata/quantities/quantity_error_tests.py diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index bfc6d4977..453f88925 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -17,6 +17,12 @@ def test_unit_compounding_pow(): assert (Quantity(1, units.millimeters) ** 2).in_units_of(units.square_meters) == 1e-6 assert (Quantity(1, units.minutes) ** 3).in_units_of(units.seconds ** 3) == 60 ** 3 +def test_pow_scaling(): + q2 = Quantity(1000, units.millimeters)**2 + assert q2.units.scale == 1e-6 + assert q2.value == 1e6 + + def test_unit_compounding_mul(): """ Test units compound correctly when __mul__ is used""" assert (Quantity(4, units.minutes) * Quantity(0.25, units.hertz)).in_units_of(units.none) == 60 @@ -49,7 +55,7 @@ def test_good_add_sub(): assert (Quantity(1, units.seconds) + Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 assert (Quantity(1, units.seconds) - Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 - assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == 13 + assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == pytest.approx(13, abs=1e-8) @pytest.mark.parametrize("unit_in, power, unit_out", [ (units.meters**2, 1/2, units.meters), diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index d54116251..37cb65ff3 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -34,30 +34,42 @@ def __init__(self, operation_tree: Operation, references: dict[int, "Quantity"]) self.operation_tree = operation_tree self.references = references + self.reference_key_list = [key for key in self.references] + self.si_reference_values = {key: self.references[key].in_si() for key in self.references} + def jacobian(self) -> list[Operation]: """ Derivative of this quantity's operation history with respect to each of the references """ # Use the hash value to specify the variable of differentiation - return [self.operation_tree.derivative(hash_value) for hash_value in self.references] + return [self.operation_tree.derivative(key) for key in self.reference_key_list] - def standard_error_propagate(self, covariances: dict[tuple[int, int]: "Quantity"] = {}): + def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, int]: "Quantity"] = {}): """ Do standard error propagation to calculate the uncertainties associated with this quantity - @param: covariances, off diagonal entries for the covariance matrix + :param quantity_units: units in which the output should be calculated + :param covariances: off diagonal entries for the covariance matrix """ if covariances: raise NotImplementedError("User specified covariances not currently implemented") jacobian = self.jacobian() + # jacobian_units = [quantity_units / self.references[key].units for key in self.reference_key_list] + # + # # Evaluate the jacobian + # # TODO: should we use quantities here, does that work automatically? + # evaluated_jacobian = [Quantity( + # value=entry.evaluate(self.si_reference_values), + # units=unit.si_equivalent()) + # for entry, unit in zip(jacobian, jacobian_units)] - # Evaluate the jacobian - # TODO: should we use quantities here, does that work automatically? evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] hash_values = [key for key in self.references] output = None + print(evaluated_jacobian) + for hash_value, jac_component in zip(hash_values, evaluated_jacobian): if output is None: output = jac_component * (self.references[hash_value].variance * jac_component) @@ -107,7 +119,7 @@ class Quantity[QuantityType]: def __init__(self, value: QuantityType, units: Unit, - variance: QuantityType | None = None, + standard_error: QuantityType | None = None, hash_seed = ""): self.value = value @@ -119,13 +131,14 @@ def __init__(self, self.hash_value = -1 """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ - self._variance = variance """ Contains the variance if it is data driven, else it is """ - if variance is None: + if standard_error is None: + self._variance = None self.hash_value = hash_data_via_numpy(hash_seed, value) else: - self.hash_value = hash_data_via_numpy(hash_seed, value, variance) + self._variance = standard_error ** 2 + self.hash_value = hash_data_via_numpy(hash_seed, value, standard_error) self.history = QuantityHistory.variable(self) @@ -168,9 +181,8 @@ def in_units_of_with_standard_error(self, units): units_squared = units**2 if variance.units.equivalent(units_squared): - scale_factor = self.units.scale / units.scale - return scale_factor*self.value, scale_factor * np.sqrt(self.variance.in_units_of(units_squared)) + return self.in_units_of(units), np.sqrt(self.variance.in_units_of(units_squared)) else: raise UnitError(f"Target units ({units}) not compatible with existing units ({variance.units}).") @@ -309,7 +321,7 @@ def __repr__(self): if isinstance(self.units, units.NamedUnit): value = self.value - error = np.sqrt(self.standard_deviation().value) + error = self.standard_deviation().value unit_string = self.units.symbol else: @@ -340,9 +352,9 @@ def __init__(self, name: str, value: QuantityType, units: Unit, - variance: QuantityType | None = None): + standard_error: QuantityType | None = None): - super().__init__(value, units, variance=variance, hash_seed=name) + super().__init__(value, units, standard_error=standard_error, hash_seed=name) self.name = name def __repr__(self): @@ -350,7 +362,7 @@ def __repr__(self): class DerivedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): - super().__init__(value, units, variance=None) + super().__init__(value, units, standard_error=None) self.history = history self._variance_cache = None @@ -363,6 +375,6 @@ def has_variance(self): @property def variance(self) -> Quantity: if self._variance_cache is None: - self._variance_cache = self.history.standard_error_propagate() + self._variance_cache = self.history.variance_propagate(self.units) return self._variance_cache \ No newline at end of file diff --git a/sasdata/quantities/quantity_error_tests.py b/sasdata/quantities/quantity_error_tests.py new file mode 100644 index 000000000..7f202e387 --- /dev/null +++ b/sasdata/quantities/quantity_error_tests.py @@ -0,0 +1,20 @@ +from sasdata.quantities import units +from sasdata.quantities.quantity import NamedQuantity +import pytest +import numpy as np + +@pytest.mark.parametrize("x_err, y_err, x_units, y_units", + [(1, 1, units.meters, units.meters), + (1, 1, units.centimeters, units.centimeters), + (1, 2, units.meters, units.millimeters)]) +def test_addition_propagation(x_err, y_err, x_units, y_units): + """ Test that errors in addition of independent variables works with different units in the mix""" + + expected_err = np.sqrt((x_err*x_units.scale)**2 + (y_err*y_units.scale)**2) + + x = NamedQuantity("x", 0, x_units, standard_error=x_err) + y = NamedQuantity("y", 0, y_units, standard_error=y_err) + + _, err = (x + y).in_si_with_standard_error() + + assert err == pytest.approx(expected_err, abs=1e-8) \ No newline at end of file diff --git a/sasdata/quantities/quantity_examples.py b/sasdata/quantities/quantity_examples.py index 9ccb77bbd..89ebb6a5c 100644 --- a/sasdata/quantities/quantity_examples.py +++ b/sasdata/quantities/quantity_examples.py @@ -1,8 +1,9 @@ from sasdata.quantities.quantity import Quantity, NamedQuantity from sasdata.quantities import units -x = NamedQuantity("x", 1, units.meters, variance=1) -y = NamedQuantity("y", 1, units.meters, variance=1) +x = NamedQuantity("x", 1, units.meters, standard_error=1) +y = NamedQuantity("y", 1, units.decimeters, standard_error=1) print(x+y) -print(x+x) \ No newline at end of file +print(x+x) +print(y+y) \ No newline at end of file From 9bf5daa65cc8e2efbb50f49cfee93ba384624268 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 27 Sep 2024 17:09:04 +0100 Subject: [PATCH 100/675] Fixed error formatting bug --- sasdata/quantities/quantity.py | 35 +++++++++++++++++++++---- sasdata/quantities/quantity_examples.py | 3 +-- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 37cb65ff3..84c1a7924 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -6,7 +6,7 @@ from sasdata.quantities.operations import Operation, Variable from quantities import operations, units -from sasdata.quantities.units import Unit +from sasdata.quantities.units import Unit, NamedUnit import hashlib @@ -68,8 +68,6 @@ def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, hash_values = [key for key in self.references] output = None - print(evaluated_jacobian) - for hash_value, jac_component in zip(hash_values, evaluated_jacobian): if output is None: output = jac_component * (self.references[hash_value].variance * jac_component) @@ -128,6 +126,9 @@ def __init__(self, self.units = units """ Units of this data """ + self._hash_seed = hash_seed + """ Retain this for copying operations""" + self.hash_value = -1 """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ @@ -164,6 +165,13 @@ def in_units_of(self, units: Unit) -> QuantityType: else: raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") + def to_units_of(self, new_units: Unit) -> "Quantity[QuantityType]": + new_value, new_error = self.in_units_of_with_standard_error(new_units) + return Quantity(value=new_value, + units=new_units, + standard_error=new_error, + hash_seed=self._hash_seed) + def variance_in_units_of(self, units: Unit) -> QuantityType: """ Get the variance of quantity in other units """ variance = self.variance @@ -318,10 +326,10 @@ def _array_repr_format(arr: np.ndarray): def __repr__(self): - if isinstance(self.units, units.NamedUnit): + if isinstance(self.units, NamedUnit): value = self.value - error = self.standard_deviation().value + error = self.standard_deviation().in_units_of(self.units) unit_string = self.units.symbol else: @@ -360,6 +368,15 @@ def __init__(self, def __repr__(self): return f"[{self.name}] " + super().__repr__() + + def to_units_of(self, new_units: Unit) -> "NamedQuantity[QuantityType]": + new_value, new_error = self.in_units_of_with_standard_error(new_units) + return NamedQuantity(value=new_value, + units=new_units, + standard_error=new_error, + name=self.name) + + class DerivedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): super().__init__(value, units, standard_error=None) @@ -368,6 +385,14 @@ def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): self._variance_cache = None self._has_variance = history.has_variance() + + def to_units_of(self, new_units: Unit) -> "Quantity[QuantityType]": + # TODO: Lots of tests needed for this + return DerivedQuantity( + value=self.in_units_of(new_units), + units=new_units, + history=self.history) + @property def has_variance(self): return self._has_variance diff --git a/sasdata/quantities/quantity_examples.py b/sasdata/quantities/quantity_examples.py index 89ebb6a5c..8c505e59e 100644 --- a/sasdata/quantities/quantity_examples.py +++ b/sasdata/quantities/quantity_examples.py @@ -5,5 +5,4 @@ y = NamedQuantity("y", 1, units.decimeters, standard_error=1) print(x+y) -print(x+x) -print(y+y) \ No newline at end of file +print((x+y).to_units_of(units.centimeters)) \ No newline at end of file From f8a46665fb9ab7ab7ca2a0e0a56b4f47c6c11316 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 30 Sep 2024 11:50:15 +0100 Subject: [PATCH 101/675] Tests for error propagation --- sasdata/quantities/quantity_error_tests.py | 136 ++++++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/quantity_error_tests.py b/sasdata/quantities/quantity_error_tests.py index 7f202e387..e8e9378fb 100644 --- a/sasdata/quantities/quantity_error_tests.py +++ b/sasdata/quantities/quantity_error_tests.py @@ -17,4 +17,138 @@ def test_addition_propagation(x_err, y_err, x_units, y_units): _, err = (x + y).in_si_with_standard_error() - assert err == pytest.approx(expected_err, abs=1e-8) \ No newline at end of file + assert err == pytest.approx(expected_err, abs=1e-8) + +@pytest.mark.parametrize("x_val, y_val, x_units, y_units", + [(1, 1, units.meters, units.meters), + (1, 1, units.centimeters, units.centimeters), + (2, 2, units.meters, units.meters), + (1, 2, units.centimeters, units.centimeters), + (1, 2, units.meters, units.millimeters), + (3, 4, units.milliseconds, units.microseconds), + (0, 1, units.meters, units.meters)]) +def test_asymmetry_propagation(x_val, y_val, x_units, y_units): + + x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(x_val)) + y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(y_val)) + + x_si, x_err = x.in_si_with_standard_error() + y_si, y_err = y.in_si_with_standard_error() + + numerator = x-y + denominator = x+y + a = numerator/denominator + + # Check numerator and denominator + expected_error = np.sqrt(x_err ** 2 + y_err ** 2) + + value, error = numerator.in_si_with_standard_error() + assert error == pytest.approx(expected_error, rel=1e-6) + + value, error = denominator.in_si_with_standard_error() + assert error == pytest.approx(expected_error, rel=1e-6) + + # check whole thing + value, error = a.in_si_with_standard_error() + expected_error = (2 / (x_si + y_si)**2) * np.sqrt((x_err*y_si)**2 + (y_err*x_si)**2) + assert error == pytest.approx(expected_error, rel=1e-6) + +@pytest.mark.parametrize("x_val, y_val, x_units, y_units", + [(1, 1, units.meters, units.meters), + (1, 1, units.centimeters, units.centimeters), + (2, 2, units.meters, units.meters), + (1, 2, units.centimeters, units.centimeters), + (1, 2, units.meters, units.millimeters), + (3, 4, units.milliseconds, units.microseconds), + (0, 1, units.meters, units.meters)]) +def test_power_propagation(x_val, y_val, x_units, y_units): + + x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(x_val)) + y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(y_val)) + + x_si, x_err = x.in_si_with_standard_error() + y_si, y_err = y.in_si_with_standard_error() + + x_var = x_err ** 2 + y_var = y_err ** 2 + + z = (x*y)**3 + + # check whole thing + value, error = z.in_si_with_standard_error() + expected_variance = 9*((x_si*y_si)**4)*(x_var*y_si*y_si + x_si*x_si*y_var) + assert error == pytest.approx(np.sqrt(expected_variance), rel=1e-6) + +@pytest.mark.parametrize("k", [0.1, 0.5, 1, 2, 10]) +@pytest.mark.parametrize("x_val, y_val, x_units, y_units", + [(1, 1, units.meters, units.meters), + (1, 1, units.centimeters, units.centimeters), + (2, 2, units.meters, units.meters), + (1, 2, units.centimeters, units.centimeters), + (1, 2, units.meters, units.millimeters), + (3, 4, units.milliseconds, units.microseconds), + (0, 1, units.meters, units.meters), + (0, 0, units.meters, units.meters)]) +def test_complex_power_propagation(x_val, y_val, x_units, y_units, k): + + x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(k*x_val)) + y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(k*y_val)) + + x_si, x_err = x.in_si_with_standard_error() + y_si, y_err = y.in_si_with_standard_error() + + x_var = x_err ** 2 + y_var = y_err ** 2 + + z = (x+y)**3 + x**3 + y**3 + + value, error = z.in_si_with_standard_error() + expected_variance = \ + 9*x_var*(x_si**2 + (x_si+y_si)**2)**2 + \ + 9*y_var*(y_si**2 + (x_si+y_si)**2)**2 + + assert error == pytest.approx(np.sqrt(expected_variance), rel=1e-6) + +@pytest.mark.parametrize("k_x", [0.1, 0.5, 1, 2, 10]) +@pytest.mark.parametrize("k_y", [0.1, 0.5, 1, 2, 10]) +@pytest.mark.parametrize("x_val, y_val, x_units, y_units", + [(1, 1, units.meters, units.meters), + (1, 1, units.centimeters, units.centimeters), + (2, 2, units.meters, units.meters), + (1, 2, units.centimeters, units.centimeters), + (1, 2, units.meters, units.millimeters), + (3, 4, units.milliseconds, units.microseconds), + (0, 1, units.meters, units.meters), + (0, 0, units.meters, units.meters)]) +def test_complex_propagation(x_val, y_val, x_units, y_units, k_x, k_y): + + x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(k_x*x_val)) + y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(k_y*y_val)) + + cx = NamedQuantity("cx", 1.7, x_units) + cy = NamedQuantity("cy", 1.2, y_units) + c0 = 4*NamedQuantity("c0", value=7, units=units.none) + + cx_si = cx.in_si() + cy_si = cy.in_si() + + c0_si = c0.in_si() + + x_si, x_err = x.in_si_with_standard_error() + y_si, y_err = y.in_si_with_standard_error() + + x_var = x_err ** 2 + y_var = y_err ** 2 + + z = (((x-cx)**4 + (y-cy)**4)**(1/4)) + c0*(-x-y) + + value, error = z.in_si_with_standard_error() + + denom_factor = ((x_si - cx_si)**4 + (y_si - cy_si)**4)**(-3/4) + x_num = (cx_si - x_si)**3 + y_num = (cy_si - y_si)**3 + + expected_variance = x_var*(c0_si + x_num*denom_factor)**2 + y_var*(c0_si + y_num*denom_factor)**2 + + assert error == pytest.approx(np.sqrt(expected_variance), rel=1e-8) + From c3f4890492023ef9c554c2f8e3cdb09d2869e66c Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 30 Sep 2024 12:21:08 +0100 Subject: [PATCH 102/675] More aliases for units --- sasdata/quantities/_build_tables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index a65ea39c9..7c68fe2a4 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -97,7 +97,7 @@ "Ang": ["A", "Å"], "au": ["a.u.", "amu"], "percent": ["%"], - "deg": ["degr"], + "deg": ["degr", "Deg", "degrees", "Degrees"], "none": ["Counts", "counts", "cnts", "Cnts"] } From 03c3ec0336362090415ea8af4d0f2fbdc913cada Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 30 Sep 2024 13:29:25 +0100 Subject: [PATCH 103/675] Made file for target object for metadata --- sasdata/target_data_object.py | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 sasdata/target_data_object.py diff --git a/sasdata/target_data_object.py b/sasdata/target_data_object.py new file mode 100644 index 000000000..7b868ef76 --- /dev/null +++ b/sasdata/target_data_object.py @@ -0,0 +1,3 @@ +class TargetData: + def __init__(self): + self.reference_string \ No newline at end of file From 05f9c17d0e10c8fdc2bb092e8439342f7004b17f Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 30 Sep 2024 17:01:52 +0100 Subject: [PATCH 104/675] Main data reading for HDF5 prototype --- sasdata/quantities/quantity.py | 28 +++++++++--- sasdata/raw_form.py | 13 +++--- sasdata/target_data_object.py | 2 +- sasdata/temp_hdf5_reader.py | 81 +++++++++++++++++++++++++++------- 4 files changed, 96 insertions(+), 28 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 84c1a7924..b4d9a9362 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -317,10 +317,15 @@ def _array_repr_format(arr: np.ndarray): """ Format the array """ order = len(arr.shape) reshaped = arr.reshape(-1) - if len(reshaped) > 4: + if len(reshaped) <= 2: numbers = ",".join([f"{n}" for n in reshaped]) else: - numbers = f"{reshaped[0]}, {reshaped[1]} ... {reshaped[-2]}, {reshaped[-1]}" + numbers = f"{reshaped[0]} ... {reshaped[-1]}" + + # if len(reshaped) <= 4: + # numbers = ",".join([f"{n}" for n in reshaped]) + # else: + # numbers = f"{reshaped[0]}, {reshaped[1]} ... {reshaped[-2]}, {reshaped[-1]}" return "["*order + numbers + "]"*order @@ -368,13 +373,24 @@ def __init__(self, def __repr__(self): return f"[{self.name}] " + super().__repr__() - def to_units_of(self, new_units: Unit) -> "NamedQuantity[QuantityType]": new_value, new_error = self.in_units_of_with_standard_error(new_units) return NamedQuantity(value=new_value, - units=new_units, - standard_error=new_error, - name=self.name) + units=new_units, + standard_error=new_error, + name=self.name) + + def with_standard_error(self, standard_error: Quantity): + if standard_error.units.equivalent(self.units): + return NamedQuantity( + value=self.value, + units=self.units, + standard_error=standard_error.in_units_of(self.units), + name=self.name) + + else: + raise UnitError(f"Standard error units ({standard_error.units}) " + f"are not compatible with value units ({self.units})") class DerivedQuantity[QuantityType](Quantity[QuantityType]): diff --git a/sasdata/raw_form.py b/sasdata/raw_form.py index 9519dead8..e1883381d 100644 --- a/sasdata/raw_form.py +++ b/sasdata/raw_form.py @@ -18,7 +18,7 @@ def shorten_string(string): class Dataset[DataType]: name: str data: DataType - attributes: dict[str, Self] + attributes: dict[str, Self | str] def summary(self, indent_amount: int = 0, indent: str = " ") -> str: @@ -54,11 +54,14 @@ class RawData: data_contents: list[NamedQuantity] raw_metadata: dict[str, Dataset | Group] - def __repr__(self): - indent = " " - + def summary(self, indent = " "): s = f"{self.name}\n" + + for data in self.data_contents: + s += f"{indent}{data}\n" + + s += f"{indent}Metadata:\n" for key in self.raw_metadata: - s += self.raw_metadata[key].summary(1, indent) + s += self.raw_metadata[key].summary(2, indent) return s \ No newline at end of file diff --git a/sasdata/target_data_object.py b/sasdata/target_data_object.py index 7b868ef76..d88581752 100644 --- a/sasdata/target_data_object.py +++ b/sasdata/target_data_object.py @@ -1,3 +1,3 @@ class TargetData: def __init__(self): - self.reference_string \ No newline at end of file + self.reference_string = \ No newline at end of file diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 51bd86695..4b746855f 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -14,29 +14,35 @@ from sasdata.raw_form import RawData from sasdata.raw_form import Dataset as SASDataDataset, Group as SASDataGroup +from quantities.quantity import NamedQuantity +from quantities import units + test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" # test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" logger = logging.getLogger(__name__) -def hdf5_attr(entry): - return entry def recurse_hdf5(hdf5_entry): if isinstance(hdf5_entry, HDF5Dataset): + attributes = {name: hdf5_entry.attrs[name] for name in hdf5_entry.attrs} + if isinstance(hdf5_entry.dtype, np.dtypes.BytesDType): data = hdf5_entry[()][0].decode("utf-8") + return SASDataDataset[str]( + name=hdf5_entry.name, + data=data, + attributes=attributes) + else: data = np.array(hdf5_entry, dtype=hdf5_entry.dtype) - attributes = {name: hdf5_attr(hdf5_entry.attrs[name]) for name in hdf5_entry.attrs} - - return SASDataDataset( - name=hdf5_entry.name, - data=data, - attributes=attributes) + return SASDataDataset[np.ndarray]( + name=hdf5_entry.name, + data=data, + attributes=attributes) elif isinstance(hdf5_entry, HDF5Group): return SASDataGroup( @@ -46,6 +52,50 @@ def recurse_hdf5(hdf5_entry): else: raise TypeError(f"Unknown type found during HDF5 parsing: {type(hdf5_entry)} ({hdf5_entry})") +def parse_units_placeholder(string: str) -> units.Unit: + #TODO: Remove when not needed + return units.meters + +def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: + """ In the context of NeXus files, load a group of data entries that are organised together + match up the units and errors with their values""" + # Gather together data with its error terms + + uncertainty_map = {} + uncertainties = set() + entries = {} + + for name in node.children: + + child = node.children[name] + # TODO: Actual unit parser here + units = parse_units_placeholder(child.attributes["units"]) + + quantity = NamedQuantity(name=name_prefix+child.name, + value=child.data, + units=units) + + if "uncertainty" in child.attributes: + uncertainty_name = child.attributes["uncertainty"] + uncertainty_map[name] = uncertainty_name + uncertainties.add(uncertainty_name) + + entries[name] = quantity + + output = [] + + for name, entry in entries.items(): + if name not in uncertainties: + if name in uncertainty_map: + uncertainty = entries[uncertainty_map[name]] + new_entry = entry.with_standard_error(uncertainty) + output.append(new_entry) + else: + output.append(entry) + + return output + + def load_data(filename) -> list[RawData]: with h5py.File(filename, 'r') as f: @@ -65,14 +115,13 @@ def load_data(filename) -> list[RawData]: for key in entry_keys: component = entry[key] - # if key.lower() == "sasdata": - # datum = recurse_hdf5(component) - # data_contents.append(datum) - # - # else: - # raw_metadata[key] = recurse_hdf5(component) - raw_metadata[key] = recurse_hdf5(component) + if key.lower() == "sasdata": + datum = recurse_hdf5(component) + # TODO: Use named identifier + data_contents = connected_data(datum, "FILE_ID_HERE") + else: + raw_metadata[key] = recurse_hdf5(component) loaded_data.append( @@ -89,4 +138,4 @@ def load_data(filename) -> list[RawData]: data = load_data(test_file) for dataset in data: - print(dataset) + print(dataset.summary()) \ No newline at end of file From 148ba9b6d64f04773c770ea6b2a07c24c71a4bad Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 1 Oct 2024 15:15:12 +0100 Subject: [PATCH 105/675] integrating the units stuff --- sasdata/quantities/_build_tables.py | 21 +- sasdata/quantities/_units_base.py | 22 +- sasdata/quantities/unit_parser.py | 73 ++--- sasdata/quantities/units.py | 427 ++++++++++++++-------------- test/utest_unit_parser.py | 77 +++-- 5 files changed, 326 insertions(+), 294 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 7c68fe2a4..b16ed3122 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -90,7 +90,13 @@ # Add Hartree? Rydberg? Bohrs? # Add CGS -aliases = { +# Two stages of aliases, to make sure units don't get lost + +aliases_1 = { + "A": ["Amps", "amps"] +} + +aliases_2 = { "y": ["yr", "year"], "d": ["day"], "h": ["hr", "hour"], @@ -102,6 +108,7 @@ } + all_units = base_si_units + derived_si_units + non_si_units encoding = "utf-8" @@ -237,7 +244,7 @@ def format_name(name: str): f"ascii_symbol='{length_symbol}/{time_symbol}', " f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") - fid.write(f"{accel_name} = NamedUnit({length_scale / time_scale}, " + fid.write(f"{accel_name} = NamedUnit({length_scale / time_scale**2}, " f"Dimensions(length=1, time=-2), " f"name='{accel_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}^2', " @@ -286,10 +293,12 @@ def format_name(name: str): # Add aliases to symbol lookup table # - for base_name in aliases: - alias_list = aliases[base_name] - for alias in alias_list: - symbol_lookup[alias] = symbol_lookup[base_name] + # Apply the alias transforms sequentially + for aliases in [aliases_1, aliases_2]: + for base_name in aliases: + alias_list = aliases[base_name] + for alias in alias_list: + symbol_lookup[alias] = symbol_lookup[base_name] # # Write out the symbol lookup table diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index e6bee7a44..a3b68b892 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -152,7 +152,7 @@ def __hash__(self): 17**abs(self.moles_hint) * 19**abs(self.angle_hint) def __repr__(self): - s = "" + tokens = [] for name, size in [ ("length", self.length), ("time", self.time), @@ -165,14 +165,14 @@ def __repr__(self): if size == 0: pass elif size == 1: - s += f"{name}" + tokens.append(f"{name}") else: - s += f"{name}{int_as_unicode_superscript(size)}" + tokens.append(f"{name}{int_as_unicode_superscript(size)}") - return s + return ' '.join(tokens) def si_repr(self): - s = "" + tokens = [] for name, size in [ ("kg", self.mass), ("m", self.length), @@ -184,21 +184,21 @@ def si_repr(self): if size == 0: pass elif size == 1: - s += f"{name}" + tokens.append(f"{name}") else: - s += f"{name}{int_as_unicode_superscript(size)}" + tokens.append(f"{name}{int_as_unicode_superscript(size)}") match self.angle_hint: case 0: pass case 2: - s += "sr" + tokens.append("sr") case -2: - s += "sr" + int_as_unicode_superscript(-1) + tokens.append("sr" + int_as_unicode_superscript(-1)) case _: - s += "rad" + int_as_unicode_superscript(self.angle_hint) + tokens.append("rad" + int_as_unicode_superscript(self.angle_hint)) - return s + return ''.join(tokens) class Unit: diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 599abd5d2..cd3c10d71 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -4,26 +4,11 @@ # TODO: This shouldn't be in this file but I don't want to edit Lucas' code before he is finished. all_units_groups = [group.units for group in unit_groups.values()] +unit_groups_by_dimension_hash = {hash(group.units[0].dimensions): group for group in unit_groups.values()} all_units: list[NamedUnit] = [] for group in all_units_groups: all_units.extend(group) -def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> Dimensions: - """Multiply each dimension in dimensions_1 with the same dimension in dimensions_2""" - return Dimensions( - length=dimensions_1.length * dimensions_2.length, - time=dimensions_1.time * dimensions_2.time, - mass=dimensions_1.mass * dimensions_2.mass, - current=dimensions_1.current * dimensions_2.current, - temperature=dimensions_1.temperature * dimensions_2.temperature, - moles_hint=dimensions_1.moles_hint * dimensions_2.moles_hint, - angle_hint=dimensions_1.angle_hint * dimensions_2.angle_hint - ) - -def combine_units(unit_1: Unit, unit_2: Unit): - """Combine unit_1, and unit_2 into one unit.""" - return Unit(unit_1.scale * unit_2.scale, unit_1.dimensions * unit_2.dimensions) - def split_unit_str(unit_str: str) -> list[str]: """Separate the letters from the numbers in unit_str""" return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) @@ -54,13 +39,13 @@ def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longes if len(potential_symbols) == 0: break string_pos += 1 - current_unit= potential_unit_str + current_unit = potential_unit_str if not longest_unit and current_unit in lookup_dict.keys(): break if current_unit == '': - return (None, unit_str) + return None, unit_str remaining_str = unit_str[string_pos::] - return (lookup_dict[current_unit], remaining_str) + return lookup_dict[current_unit], remaining_str def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longest_unit: bool = True) -> list[Unit]: """Recursively parse units from unit_str until no more characters are present.""" @@ -75,14 +60,6 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longes else: raise ValueError(f'Could not interpret {remaining_str}') -def unit_power(to_modify: Unit, power: int): - """Raise to_modify to power""" - # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. - dimension_multiplier = Dimensions(power, power, power, power, power, power, power) - scale_multiplier = 1 if power > 0 else -1 - return Unit(to_modify.scale ** scale_multiplier, multiply_dimensions(to_modify.dimensions, dimension_multiplier)) - - # Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there # are two functions. @@ -98,14 +75,16 @@ def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: continue power = int(token) to_modify = unit_stack[-1] - modified = unit_power(to_modify, power) + modified = to_modify ** power + # modified = unit_power(to_modify, power) unit_stack[-1] = modified except ValueError: new_units = parse_unit_strs(token, None, longest_unit) if inverse_next_unit: # TODO: Assume the power is going to be -1. This might not be true. power = -1 - new_units[0] = unit_power(new_units[0], power) + new_units[0] = new_units[0] ** power + # new_units[0] = unit_power(new_units[0], power) unit_stack += new_units # This error will happen if it tries to read a modifier but there are no units on the stack. We will just have # to ignore it. Strings being parsed shouldn't really have it anyway (e.g. -1m). @@ -121,7 +100,8 @@ def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: parsed_unit = Unit(1, Dimensions()) unit_stack = parse_unit_stack(unit_str, longest_unit) for unit in unit_stack: - parsed_unit = combine_units(parsed_unit, unit) + # parsed_unit = combine_units(parsed_unit, unit) + parsed_unit *= unit return parsed_unit except KeyError: raise ValueError('Unit string contains an unrecognised pattern.') @@ -138,28 +118,37 @@ def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: else: return None -def parse_named_unit(unit: str | Unit) -> NamedUnit: +def parse_named_unit(unit_string: str, rtol: float=1e-14) -> NamedUnit: """Parses unit into a named unit. Parses unit into a Unit if it is not already, and then finds an equivaelent named unit. Please note that this might not be the expected unit from the string itself. E.g. 'kgm/2' will become - newtons.""" - if isinstance(unit, str): - generic_unit = parse_unit(unit) - elif isinstance(unit, Unit): - generic_unit = unit - else: - raise ValueError('Unit must be a string, or Unit') - for named_unit in all_units: - if named_unit == generic_unit: - return named_unit + newtons. + + :param unit_string: string describing the units, e.g. km/s + :param rtol: relative tolerance for matching scale factors + """ + unit = parse_unit(unit_string) + return find_named_unit(unit) + +def find_named_unit(unit: Unit, rtol: float=1e-14) -> NamedUnit: + """ Find a named unit matching the one provided """ + dimension_hash = hash(unit.dimensions) + if dimension_hash in unit_groups_by_dimension_hash: + unit_group = unit_groups_by_dimension_hash[hash(unit.dimensions)] + + for named_unit in unit_group.units: + if abs(named_unit.scale - unit.scale) < rtol*named_unit.scale: + return named_unit + raise ValueError('A named unit does not exist for this unit.') + def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUnit: """Parses unit_str into a named unit. The named unit found must be part of from_group. If two units are found, the unit that is present in from_group is returned. This is useful in cases of ambiguities.""" parsed_unit = parse_unit_from_group(unit_str, from_group) if parsed_unit is None: raise ValueError('That unit cannot be parsed from the specified group.') - return parse_named_unit(parsed_unit) + return find_named_unit(parsed_unit) if __name__ == "__main__": to_parse = input('Enter a unit to parse: ') diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index ebe738331..32faec793 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -236,7 +236,7 @@ def __hash__(self): 17**abs(self.moles_hint) * 19**abs(self.angle_hint) def __repr__(self): - s = "" + tokens = [] for name, size in [ ("length", self.length), ("time", self.time), @@ -249,14 +249,14 @@ def __repr__(self): if size == 0: pass elif size == 1: - s += f"{name}" + tokens.append(f"{name}") else: - s += f"{name}{int_as_unicode_superscript(size)}" + tokens.append(f"{name}{int_as_unicode_superscript(size)}") - return s + return ' '.join(tokens) def si_repr(self): - s = "" + tokens = [] for name, size in [ ("kg", self.mass), ("m", self.length), @@ -268,21 +268,21 @@ def si_repr(self): if size == 0: pass elif size == 1: - s += f"{name}" + tokens.append(f"{name}") else: - s += f"{name}{int_as_unicode_superscript(size)}" + tokens.append(f"{name}{int_as_unicode_superscript(size)}") match self.angle_hint: case 0: pass case 2: - s += "sr" + tokens.append("sr") case -2: - s += "sr" + int_as_unicode_superscript(-1) + tokens.append("sr" + int_as_unicode_superscript(-1)) case _: - s += "rad" + int_as_unicode_superscript(self.angle_hint) + tokens.append("rad" + int_as_unicode_superscript(self.angle_hint)) - return s + return ''.join(tokens) class Unit: @@ -804,443 +804,443 @@ def __init__(self, name: str, units: list[NamedUnit]): meters_per_second = NamedUnit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') meters_per_square_second = NamedUnit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') meters_per_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') -meters_per_square_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') +meters_per_square_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') meters_per_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') -meters_per_square_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') +meters_per_square_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') meters_per_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') -meters_per_square_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') +meters_per_square_nanosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') meters_per_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') -meters_per_square_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') +meters_per_square_picosecond = NamedUnit(1.0000000000000001e+24, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') meters_per_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') -meters_per_square_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') +meters_per_square_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') meters_per_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') -meters_per_square_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') +meters_per_square_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') meters_per_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') -meters_per_square_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') +meters_per_square_minute = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') meters_per_hour = NamedUnit(0.002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') -meters_per_square_hour = NamedUnit(0.002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') +meters_per_square_hour = NamedUnit(7.71604938271605e-06, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') meters_per_day = NamedUnit(0.00011574074074074075, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') -meters_per_square_day = NamedUnit(0.00011574074074074075, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') +meters_per_square_day = NamedUnit(1.3395919067215363e-08, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') meters_per_year = NamedUnit(3.168873850681143e-07, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') -meters_per_square_year = NamedUnit(3.168873850681143e-07, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') +meters_per_square_year = NamedUnit(1.0041761481530735e-13, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') exameters_per_second = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') exameters_per_square_second = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') exameters_per_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') -exameters_per_square_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') +exameters_per_square_millisecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') exameters_per_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') -exameters_per_square_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') +exameters_per_square_microsecond = NamedUnit(1e+30, Dimensions(length=1, time=-2), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') exameters_per_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') -exameters_per_square_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') +exameters_per_square_nanosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') exameters_per_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-1), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') -exameters_per_square_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-2), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') +exameters_per_square_picosecond = NamedUnit(1e+42, Dimensions(length=1, time=-2), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') exameters_per_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') -exameters_per_square_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') +exameters_per_square_femtosecond = NamedUnit(9.999999999999999e+47, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') exameters_per_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-1), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') -exameters_per_square_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') +exameters_per_square_attosecond = NamedUnit(9.999999999999999e+53, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') exameters_per_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') -exameters_per_square_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') +exameters_per_square_minute = NamedUnit(277777777777777.78, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') exameters_per_hour = NamedUnit(2777777777777778.0, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') -exameters_per_square_hour = NamedUnit(2777777777777778.0, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') +exameters_per_square_hour = NamedUnit(7716049382716.05, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') exameters_per_day = NamedUnit(115740740740740.73, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') -exameters_per_square_day = NamedUnit(115740740740740.73, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') +exameters_per_square_day = NamedUnit(13395919067.215364, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') exameters_per_year = NamedUnit(316887385068.1143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') -exameters_per_square_year = NamedUnit(316887385068.1143, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') +exameters_per_square_year = NamedUnit(100417.61481530734, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') petameters_per_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') petameters_per_square_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') petameters_per_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') -petameters_per_square_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') +petameters_per_square_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') petameters_per_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') -petameters_per_square_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') +petameters_per_square_microsecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') petameters_per_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') -petameters_per_square_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') +petameters_per_square_nanosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') petameters_per_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-1), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') -petameters_per_square_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') +petameters_per_square_picosecond = NamedUnit(1.0000000000000001e+39, Dimensions(length=1, time=-2), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') petameters_per_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') -petameters_per_square_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') +petameters_per_square_femtosecond = NamedUnit(1e+45, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') petameters_per_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') -petameters_per_square_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') +petameters_per_square_attosecond = NamedUnit(9.999999999999998e+50, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') petameters_per_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') -petameters_per_square_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') +petameters_per_square_minute = NamedUnit(277777777777.7778, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') petameters_per_hour = NamedUnit(2777777777777.778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') -petameters_per_square_hour = NamedUnit(2777777777777.778, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') +petameters_per_square_hour = NamedUnit(7716049382.716049, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') petameters_per_day = NamedUnit(115740740740.74074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') -petameters_per_square_day = NamedUnit(115740740740.74074, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') +petameters_per_square_day = NamedUnit(13395919.067215364, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') petameters_per_year = NamedUnit(316887385.0681143, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') -petameters_per_square_year = NamedUnit(316887385.0681143, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') +petameters_per_square_year = NamedUnit(100.41761481530735, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') terameters_per_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') terameters_per_square_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') terameters_per_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') -terameters_per_square_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') +terameters_per_square_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') terameters_per_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') -terameters_per_square_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') +terameters_per_square_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') terameters_per_nanosecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') -terameters_per_square_nanosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') +terameters_per_square_nanosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') terameters_per_picosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') -terameters_per_square_picosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') +terameters_per_square_picosecond = NamedUnit(1e+36, Dimensions(length=1, time=-2), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') terameters_per_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') -terameters_per_square_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') +terameters_per_square_femtosecond = NamedUnit(9.999999999999999e+41, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') terameters_per_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') -terameters_per_square_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') +terameters_per_square_attosecond = NamedUnit(9.999999999999999e+47, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') terameters_per_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') -terameters_per_square_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') +terameters_per_square_minute = NamedUnit(277777777.7777778, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') terameters_per_hour = NamedUnit(2777777777.7777777, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') -terameters_per_square_hour = NamedUnit(2777777777.7777777, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') +terameters_per_square_hour = NamedUnit(7716049.382716049, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') terameters_per_day = NamedUnit(115740740.74074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') -terameters_per_square_day = NamedUnit(115740740.74074075, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') +terameters_per_square_day = NamedUnit(13395.919067215364, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') terameters_per_year = NamedUnit(316887.38506811426, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') -terameters_per_square_year = NamedUnit(316887.38506811426, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') +terameters_per_square_year = NamedUnit(0.10041761481530735, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') gigameters_per_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') gigameters_per_square_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') gigameters_per_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') -gigameters_per_square_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') +gigameters_per_square_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') gigameters_per_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') -gigameters_per_square_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') +gigameters_per_square_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') gigameters_per_nanosecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') -gigameters_per_square_nanosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') +gigameters_per_square_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') gigameters_per_picosecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') -gigameters_per_square_picosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') +gigameters_per_square_picosecond = NamedUnit(1.0000000000000001e+33, Dimensions(length=1, time=-2), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') gigameters_per_femtosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') -gigameters_per_square_femtosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') +gigameters_per_square_femtosecond = NamedUnit(1e+39, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') gigameters_per_attosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') -gigameters_per_square_attosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') +gigameters_per_square_attosecond = NamedUnit(1e+45, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') gigameters_per_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') -gigameters_per_square_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') +gigameters_per_square_minute = NamedUnit(277777.77777777775, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') gigameters_per_hour = NamedUnit(2777777.777777778, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') -gigameters_per_square_hour = NamedUnit(2777777.777777778, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') +gigameters_per_square_hour = NamedUnit(7716.049382716049, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') gigameters_per_day = NamedUnit(115740.74074074074, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') -gigameters_per_square_day = NamedUnit(115740.74074074074, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') +gigameters_per_square_day = NamedUnit(13.395919067215363, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') gigameters_per_year = NamedUnit(316.88738506811427, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') -gigameters_per_square_year = NamedUnit(316.88738506811427, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') +gigameters_per_square_year = NamedUnit(0.00010041761481530735, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') megameters_per_second = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') megameters_per_square_second = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') megameters_per_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') -megameters_per_square_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') +megameters_per_square_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') megameters_per_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') -megameters_per_square_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') +megameters_per_square_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') megameters_per_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') -megameters_per_square_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') +megameters_per_square_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') megameters_per_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') -megameters_per_square_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') +megameters_per_square_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-2), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') megameters_per_femtosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') -megameters_per_square_femtosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') +megameters_per_square_femtosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') megameters_per_attosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') -megameters_per_square_attosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') +megameters_per_square_attosecond = NamedUnit(9.999999999999999e+41, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') megameters_per_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') -megameters_per_square_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') +megameters_per_square_minute = NamedUnit(277.77777777777777, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') megameters_per_hour = NamedUnit(2777.777777777778, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') -megameters_per_square_hour = NamedUnit(2777.777777777778, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') +megameters_per_square_hour = NamedUnit(7.716049382716049, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') megameters_per_day = NamedUnit(115.74074074074075, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') -megameters_per_square_day = NamedUnit(115.74074074074075, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') +megameters_per_square_day = NamedUnit(0.013395919067215363, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') megameters_per_year = NamedUnit(0.3168873850681143, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') -megameters_per_square_year = NamedUnit(0.3168873850681143, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') +megameters_per_square_year = NamedUnit(1.0041761481530735e-07, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') kilometers_per_second = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') kilometers_per_square_second = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') kilometers_per_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') -kilometers_per_square_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') +kilometers_per_square_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') kilometers_per_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') -kilometers_per_square_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') +kilometers_per_square_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') kilometers_per_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') -kilometers_per_square_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-2), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') +kilometers_per_square_nanosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') kilometers_per_picosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') -kilometers_per_square_picosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') +kilometers_per_square_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') kilometers_per_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') -kilometers_per_square_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') +kilometers_per_square_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') kilometers_per_attosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') -kilometers_per_square_attosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') +kilometers_per_square_attosecond = NamedUnit(1e+39, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') kilometers_per_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') -kilometers_per_square_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') +kilometers_per_square_minute = NamedUnit(0.2777777777777778, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') kilometers_per_hour = NamedUnit(2.7777777777777777, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') -kilometers_per_square_hour = NamedUnit(2.7777777777777777, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') +kilometers_per_square_hour = NamedUnit(0.007716049382716049, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') kilometers_per_day = NamedUnit(0.11574074074074074, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') -kilometers_per_square_day = NamedUnit(0.11574074074074074, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') +kilometers_per_square_day = NamedUnit(1.3395919067215363e-05, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') kilometers_per_year = NamedUnit(0.0003168873850681143, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') -kilometers_per_square_year = NamedUnit(0.0003168873850681143, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') +kilometers_per_square_year = NamedUnit(1.0041761481530735e-10, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') millimeters_per_second = NamedUnit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') millimeters_per_square_second = NamedUnit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') millimeters_per_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') -millimeters_per_square_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') +millimeters_per_square_millisecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') millimeters_per_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') -millimeters_per_square_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') +millimeters_per_square_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') millimeters_per_nanosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') -millimeters_per_square_nanosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') +millimeters_per_square_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') millimeters_per_picosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') -millimeters_per_square_picosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') +millimeters_per_square_picosecond = NamedUnit(1.0000000000000001e+21, Dimensions(length=1, time=-2), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') millimeters_per_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') -millimeters_per_square_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') +millimeters_per_square_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') millimeters_per_attosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') -millimeters_per_square_attosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') +millimeters_per_square_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') millimeters_per_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') -millimeters_per_square_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') +millimeters_per_square_minute = NamedUnit(2.7777777777777776e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') millimeters_per_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') -millimeters_per_square_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') +millimeters_per_square_hour = NamedUnit(7.71604938271605e-09, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') millimeters_per_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') -millimeters_per_square_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') +millimeters_per_square_day = NamedUnit(1.3395919067215364e-11, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') millimeters_per_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') -millimeters_per_square_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') +millimeters_per_square_year = NamedUnit(1.0041761481530735e-16, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') micrometers_per_second = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') micrometers_per_square_second = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') micrometers_per_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') -micrometers_per_square_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') +micrometers_per_square_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') micrometers_per_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') -micrometers_per_square_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') +micrometers_per_square_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') micrometers_per_nanosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-1), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') -micrometers_per_square_nanosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-2), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') +micrometers_per_square_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-2), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') micrometers_per_picosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') -micrometers_per_square_picosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') +micrometers_per_square_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') micrometers_per_femtosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') -micrometers_per_square_femtosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') +micrometers_per_square_femtosecond = NamedUnit(9.999999999999998e+23, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') micrometers_per_attosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') -micrometers_per_square_attosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') +micrometers_per_square_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') micrometers_per_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') -micrometers_per_square_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') +micrometers_per_square_minute = NamedUnit(2.7777777777777777e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') micrometers_per_hour = NamedUnit(2.7777777777777776e-09, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') -micrometers_per_square_hour = NamedUnit(2.7777777777777776e-09, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') +micrometers_per_square_hour = NamedUnit(7.716049382716049e-12, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') micrometers_per_day = NamedUnit(1.1574074074074074e-10, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') -micrometers_per_square_day = NamedUnit(1.1574074074074074e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') +micrometers_per_square_day = NamedUnit(1.3395919067215363e-14, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') micrometers_per_year = NamedUnit(3.168873850681143e-13, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') -micrometers_per_square_year = NamedUnit(3.168873850681143e-13, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') +micrometers_per_square_year = NamedUnit(1.0041761481530734e-19, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') nanometers_per_second = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') nanometers_per_square_second = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') nanometers_per_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') -nanometers_per_square_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') +nanometers_per_square_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') nanometers_per_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') -nanometers_per_square_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') +nanometers_per_square_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') nanometers_per_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') -nanometers_per_square_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') +nanometers_per_square_nanosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') nanometers_per_picosecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') -nanometers_per_square_picosecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') +nanometers_per_square_picosecond = NamedUnit(1000000000000000.1, Dimensions(length=1, time=-2), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') nanometers_per_femtosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') -nanometers_per_square_femtosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') +nanometers_per_square_femtosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') nanometers_per_attosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') -nanometers_per_square_attosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') +nanometers_per_square_attosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') nanometers_per_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') -nanometers_per_square_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') +nanometers_per_square_minute = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') nanometers_per_hour = NamedUnit(2.777777777777778e-12, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') -nanometers_per_square_hour = NamedUnit(2.777777777777778e-12, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') +nanometers_per_square_hour = NamedUnit(7.71604938271605e-15, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') nanometers_per_day = NamedUnit(1.1574074074074076e-13, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') -nanometers_per_square_day = NamedUnit(1.1574074074074076e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') +nanometers_per_square_day = NamedUnit(1.3395919067215365e-17, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') nanometers_per_year = NamedUnit(3.1688738506811433e-16, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') -nanometers_per_square_year = NamedUnit(3.1688738506811433e-16, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') +nanometers_per_square_year = NamedUnit(1.0041761481530736e-22, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') picometers_per_second = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') picometers_per_square_second = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') picometers_per_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') -picometers_per_square_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') +picometers_per_square_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') picometers_per_microsecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') -picometers_per_square_microsecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') +picometers_per_square_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') picometers_per_nanosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') -picometers_per_square_nanosecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') +picometers_per_square_nanosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-2), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') picometers_per_picosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') -picometers_per_square_picosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') +picometers_per_square_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') picometers_per_femtosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-1), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') -picometers_per_square_femtosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') +picometers_per_square_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') picometers_per_attosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-1), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') -picometers_per_square_attosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') +picometers_per_square_attosecond = NamedUnit(9.999999999999998e+23, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') picometers_per_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') -picometers_per_square_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') +picometers_per_square_minute = NamedUnit(2.7777777777777775e-16, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') picometers_per_hour = NamedUnit(2.7777777777777776e-15, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') -picometers_per_square_hour = NamedUnit(2.7777777777777776e-15, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') +picometers_per_square_hour = NamedUnit(7.716049382716049e-18, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') picometers_per_day = NamedUnit(1.1574074074074073e-16, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') -picometers_per_square_day = NamedUnit(1.1574074074074073e-16, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') +picometers_per_square_day = NamedUnit(1.3395919067215364e-20, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') picometers_per_year = NamedUnit(3.168873850681143e-19, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') -picometers_per_square_year = NamedUnit(3.168873850681143e-19, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') +picometers_per_square_year = NamedUnit(1.0041761481530734e-25, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') femtometers_per_second = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') femtometers_per_square_second = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') femtometers_per_millisecond = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') -femtometers_per_square_millisecond = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') +femtometers_per_square_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') femtometers_per_microsecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') -femtometers_per_square_microsecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') +femtometers_per_square_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') femtometers_per_nanosecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') -femtometers_per_square_nanosecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') +femtometers_per_square_nanosecond = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') femtometers_per_picosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') -femtometers_per_square_picosecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') +femtometers_per_square_picosecond = NamedUnit(1000000000.0000001, Dimensions(length=1, time=-2), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') femtometers_per_femtosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') -femtometers_per_square_femtosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') +femtometers_per_square_femtosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') femtometers_per_attosecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') -femtometers_per_square_attosecond = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') +femtometers_per_square_attosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') femtometers_per_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') -femtometers_per_square_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') +femtometers_per_square_minute = NamedUnit(2.777777777777778e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') femtometers_per_hour = NamedUnit(2.777777777777778e-18, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') -femtometers_per_square_hour = NamedUnit(2.777777777777778e-18, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') +femtometers_per_square_hour = NamedUnit(7.71604938271605e-21, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') femtometers_per_day = NamedUnit(1.1574074074074075e-19, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') -femtometers_per_square_day = NamedUnit(1.1574074074074075e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') +femtometers_per_square_day = NamedUnit(1.3395919067215363e-23, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') femtometers_per_year = NamedUnit(3.168873850681143e-22, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') -femtometers_per_square_year = NamedUnit(3.168873850681143e-22, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') +femtometers_per_square_year = NamedUnit(1.0041761481530735e-28, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') attometers_per_second = NamedUnit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') attometers_per_square_second = NamedUnit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') attometers_per_millisecond = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') -attometers_per_square_millisecond = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') +attometers_per_square_millisecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') attometers_per_microsecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-1), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') -attometers_per_square_microsecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') +attometers_per_square_microsecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-2), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') attometers_per_nanosecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') -attometers_per_square_nanosecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') +attometers_per_square_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') attometers_per_picosecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-1), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') -attometers_per_square_picosecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-2), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') +attometers_per_square_picosecond = NamedUnit(1000000.0000000001, Dimensions(length=1, time=-2), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') attometers_per_femtosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') -attometers_per_square_femtosecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') +attometers_per_square_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') attometers_per_attosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') -attometers_per_square_attosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') +attometers_per_square_attosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') attometers_per_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') -attometers_per_square_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') +attometers_per_square_minute = NamedUnit(2.777777777777778e-22, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') attometers_per_hour = NamedUnit(2.7777777777777778e-21, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') -attometers_per_square_hour = NamedUnit(2.7777777777777778e-21, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') +attometers_per_square_hour = NamedUnit(7.71604938271605e-24, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') attometers_per_day = NamedUnit(1.1574074074074074e-22, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') -attometers_per_square_day = NamedUnit(1.1574074074074074e-22, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') +attometers_per_square_day = NamedUnit(1.3395919067215363e-26, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') attometers_per_year = NamedUnit(3.1688738506811433e-25, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') -attometers_per_square_year = NamedUnit(3.1688738506811433e-25, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') +attometers_per_square_year = NamedUnit(1.0041761481530734e-31, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') decimeters_per_second = NamedUnit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dmNone⁻¹') decimeters_per_square_second = NamedUnit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dmNone⁻²') decimeters_per_millisecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='decimeters_per_millisecond', ascii_symbol='dm/ms', symbol='dmms⁻¹') -decimeters_per_square_millisecond = NamedUnit(100.0, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') +decimeters_per_square_millisecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') decimeters_per_microsecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-1), name='decimeters_per_microsecond', ascii_symbol='dm/us', symbol='dmµs⁻¹') -decimeters_per_square_microsecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_microsecond', ascii_symbol='dm/us^2', symbol='dmµs⁻²') +decimeters_per_square_microsecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_microsecond', ascii_symbol='dm/us^2', symbol='dmµs⁻²') decimeters_per_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='decimeters_per_nanosecond', ascii_symbol='dm/ns', symbol='dmns⁻¹') -decimeters_per_square_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_nanosecond', ascii_symbol='dm/ns^2', symbol='dmns⁻²') +decimeters_per_square_nanosecond = NamedUnit(1e+17, Dimensions(length=1, time=-2), name='decimeters_per_square_nanosecond', ascii_symbol='dm/ns^2', symbol='dmns⁻²') decimeters_per_picosecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_picosecond', ascii_symbol='dm/ps', symbol='dmps⁻¹') -decimeters_per_square_picosecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_picosecond', ascii_symbol='dm/ps^2', symbol='dmps⁻²') +decimeters_per_square_picosecond = NamedUnit(1.0000000000000001e+23, Dimensions(length=1, time=-2), name='decimeters_per_square_picosecond', ascii_symbol='dm/ps^2', symbol='dmps⁻²') decimeters_per_femtosecond = NamedUnit(100000000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_femtosecond', ascii_symbol='dm/fs', symbol='dmfs⁻¹') -decimeters_per_square_femtosecond = NamedUnit(100000000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') +decimeters_per_square_femtosecond = NamedUnit(1e+29, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') decimeters_per_attosecond = NamedUnit(1e+17, Dimensions(length=1, time=-1), name='decimeters_per_attosecond', ascii_symbol='dm/as', symbol='dmas⁻¹') -decimeters_per_square_attosecond = NamedUnit(1e+17, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') +decimeters_per_square_attosecond = NamedUnit(1e+35, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') decimeters_per_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmNone⁻¹') -decimeters_per_square_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmNone⁻²') +decimeters_per_square_minute = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmNone⁻²') decimeters_per_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmNone⁻¹') -decimeters_per_square_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmNone⁻²') +decimeters_per_square_hour = NamedUnit(7.71604938271605e-07, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmNone⁻²') decimeters_per_day = NamedUnit(1.1574074074074075e-05, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmNone⁻¹') -decimeters_per_square_day = NamedUnit(1.1574074074074075e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmNone⁻²') +decimeters_per_square_day = NamedUnit(1.3395919067215364e-09, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmNone⁻²') decimeters_per_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmNone⁻¹') -decimeters_per_square_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmNone⁻²') +decimeters_per_square_year = NamedUnit(1.0041761481530735e-14, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmNone⁻²') centimeters_per_second = NamedUnit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cmNone⁻¹') centimeters_per_square_second = NamedUnit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cmNone⁻²') centimeters_per_millisecond = NamedUnit(10.0, Dimensions(length=1, time=-1), name='centimeters_per_millisecond', ascii_symbol='cm/ms', symbol='cmms⁻¹') -centimeters_per_square_millisecond = NamedUnit(10.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') +centimeters_per_square_millisecond = NamedUnit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') centimeters_per_microsecond = NamedUnit(10000.0, Dimensions(length=1, time=-1), name='centimeters_per_microsecond', ascii_symbol='cm/us', symbol='cmµs⁻¹') -centimeters_per_square_microsecond = NamedUnit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_microsecond', ascii_symbol='cm/us^2', symbol='cmµs⁻²') +centimeters_per_square_microsecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_microsecond', ascii_symbol='cm/us^2', symbol='cmµs⁻²') centimeters_per_nanosecond = NamedUnit(10000000.0, Dimensions(length=1, time=-1), name='centimeters_per_nanosecond', ascii_symbol='cm/ns', symbol='cmns⁻¹') -centimeters_per_square_nanosecond = NamedUnit(10000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_nanosecond', ascii_symbol='cm/ns^2', symbol='cmns⁻²') +centimeters_per_square_nanosecond = NamedUnit(1e+16, Dimensions(length=1, time=-2), name='centimeters_per_square_nanosecond', ascii_symbol='cm/ns^2', symbol='cmns⁻²') centimeters_per_picosecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_picosecond', ascii_symbol='cm/ps', symbol='cmps⁻¹') -centimeters_per_square_picosecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_picosecond', ascii_symbol='cm/ps^2', symbol='cmps⁻²') +centimeters_per_square_picosecond = NamedUnit(1e+22, Dimensions(length=1, time=-2), name='centimeters_per_square_picosecond', ascii_symbol='cm/ps^2', symbol='cmps⁻²') centimeters_per_femtosecond = NamedUnit(10000000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_femtosecond', ascii_symbol='cm/fs', symbol='cmfs⁻¹') -centimeters_per_square_femtosecond = NamedUnit(10000000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') +centimeters_per_square_femtosecond = NamedUnit(1e+28, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') centimeters_per_attosecond = NamedUnit(1e+16, Dimensions(length=1, time=-1), name='centimeters_per_attosecond', ascii_symbol='cm/as', symbol='cmas⁻¹') -centimeters_per_square_attosecond = NamedUnit(1e+16, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') +centimeters_per_square_attosecond = NamedUnit(1e+34, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') centimeters_per_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmNone⁻¹') -centimeters_per_square_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmNone⁻²') +centimeters_per_square_minute = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmNone⁻²') centimeters_per_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmNone⁻¹') -centimeters_per_square_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmNone⁻²') +centimeters_per_square_hour = NamedUnit(7.71604938271605e-08, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmNone⁻²') centimeters_per_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmNone⁻¹') -centimeters_per_square_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmNone⁻²') +centimeters_per_square_day = NamedUnit(1.3395919067215363e-10, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmNone⁻²') centimeters_per_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmNone⁻¹') -centimeters_per_square_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmNone⁻²') +centimeters_per_square_year = NamedUnit(1.0041761481530735e-15, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmNone⁻²') angstroms_per_second = NamedUnit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') angstroms_per_square_second = NamedUnit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') angstroms_per_millisecond = NamedUnit(1e-07, Dimensions(length=1, time=-1), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') -angstroms_per_square_millisecond = NamedUnit(1e-07, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') +angstroms_per_square_millisecond = NamedUnit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') angstroms_per_microsecond = NamedUnit(0.0001, Dimensions(length=1, time=-1), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') -angstroms_per_square_microsecond = NamedUnit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') +angstroms_per_square_microsecond = NamedUnit(100.0, Dimensions(length=1, time=-2), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') angstroms_per_nanosecond = NamedUnit(0.09999999999999999, Dimensions(length=1, time=-1), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') -angstroms_per_square_nanosecond = NamedUnit(0.09999999999999999, Dimensions(length=1, time=-2), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') +angstroms_per_square_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') angstroms_per_picosecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') -angstroms_per_square_picosecond = NamedUnit(100.0, Dimensions(length=1, time=-2), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') +angstroms_per_square_picosecond = NamedUnit(100000000000000.02, Dimensions(length=1, time=-2), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') angstroms_per_femtosecond = NamedUnit(100000.0, Dimensions(length=1, time=-1), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') -angstroms_per_square_femtosecond = NamedUnit(100000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') +angstroms_per_square_femtosecond = NamedUnit(1e+20, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') angstroms_per_attosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') -angstroms_per_square_attosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') +angstroms_per_square_attosecond = NamedUnit(9.999999999999999e+25, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') angstroms_per_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') -angstroms_per_square_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') +angstroms_per_square_minute = NamedUnit(2.7777777777777778e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') angstroms_per_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') -angstroms_per_square_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') +angstroms_per_square_hour = NamedUnit(7.716049382716049e-16, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') angstroms_per_day = NamedUnit(1.1574074074074074e-14, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') -angstroms_per_square_day = NamedUnit(1.1574074074074074e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') +angstroms_per_square_day = NamedUnit(1.3395919067215363e-18, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') angstroms_per_year = NamedUnit(3.168873850681143e-17, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') -angstroms_per_square_year = NamedUnit(3.168873850681143e-17, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') +angstroms_per_square_year = NamedUnit(1.0041761481530734e-23, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') miles_per_second = NamedUnit(1609.344, Dimensions(length=1, time=-1), name='miles_per_second', ascii_symbol='miles/s', symbol='NoneNone⁻¹') miles_per_square_second = NamedUnit(1609.344, Dimensions(length=1, time=-2), name='miles_per_square_second', ascii_symbol='miles/s^2', symbol='NoneNone⁻²') miles_per_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-1), name='miles_per_millisecond', ascii_symbol='miles/ms', symbol='Nonems⁻¹') -miles_per_square_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-2), name='miles_per_square_millisecond', ascii_symbol='miles/ms^2', symbol='Nonems⁻²') +miles_per_square_millisecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-2), name='miles_per_square_millisecond', ascii_symbol='miles/ms^2', symbol='Nonems⁻²') miles_per_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-1), name='miles_per_microsecond', ascii_symbol='miles/us', symbol='Noneµs⁻¹') -miles_per_square_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-2), name='miles_per_square_microsecond', ascii_symbol='miles/us^2', symbol='Noneµs⁻²') +miles_per_square_microsecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-2), name='miles_per_square_microsecond', ascii_symbol='miles/us^2', symbol='Noneµs⁻²') miles_per_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-1), name='miles_per_nanosecond', ascii_symbol='miles/ns', symbol='Nonens⁻¹') -miles_per_square_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-2), name='miles_per_square_nanosecond', ascii_symbol='miles/ns^2', symbol='Nonens⁻²') +miles_per_square_nanosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-2), name='miles_per_square_nanosecond', ascii_symbol='miles/ns^2', symbol='Nonens⁻²') miles_per_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-1), name='miles_per_picosecond', ascii_symbol='miles/ps', symbol='Noneps⁻¹') -miles_per_square_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-2), name='miles_per_square_picosecond', ascii_symbol='miles/ps^2', symbol='Noneps⁻²') +miles_per_square_picosecond = NamedUnit(1.609344e+27, Dimensions(length=1, time=-2), name='miles_per_square_picosecond', ascii_symbol='miles/ps^2', symbol='Noneps⁻²') miles_per_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-1), name='miles_per_femtosecond', ascii_symbol='miles/fs', symbol='Nonefs⁻¹') -miles_per_square_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-2), name='miles_per_square_femtosecond', ascii_symbol='miles/fs^2', symbol='Nonefs⁻²') +miles_per_square_femtosecond = NamedUnit(1.609344e+33, Dimensions(length=1, time=-2), name='miles_per_square_femtosecond', ascii_symbol='miles/fs^2', symbol='Nonefs⁻²') miles_per_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-1), name='miles_per_attosecond', ascii_symbol='miles/as', symbol='Noneas⁻¹') -miles_per_square_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-2), name='miles_per_square_attosecond', ascii_symbol='miles/as^2', symbol='Noneas⁻²') +miles_per_square_attosecond = NamedUnit(1.609344e+39, Dimensions(length=1, time=-2), name='miles_per_square_attosecond', ascii_symbol='miles/as^2', symbol='Noneas⁻²') miles_per_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-1), name='miles_per_minute', ascii_symbol='miles/min', symbol='NoneNone⁻¹') -miles_per_square_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-2), name='miles_per_square_minute', ascii_symbol='miles/min^2', symbol='NoneNone⁻²') +miles_per_square_minute = NamedUnit(0.44704, Dimensions(length=1, time=-2), name='miles_per_square_minute', ascii_symbol='miles/min^2', symbol='NoneNone⁻²') miles_per_hour = NamedUnit(4.4704, Dimensions(length=1, time=-1), name='miles_per_hour', ascii_symbol='miles/h', symbol='NoneNone⁻¹') -miles_per_square_hour = NamedUnit(4.4704, Dimensions(length=1, time=-2), name='miles_per_square_hour', ascii_symbol='miles/h^2', symbol='NoneNone⁻²') +miles_per_square_hour = NamedUnit(0.012417777777777778, Dimensions(length=1, time=-2), name='miles_per_square_hour', ascii_symbol='miles/h^2', symbol='NoneNone⁻²') miles_per_day = NamedUnit(0.18626666666666666, Dimensions(length=1, time=-1), name='miles_per_day', ascii_symbol='miles/d', symbol='NoneNone⁻¹') -miles_per_square_day = NamedUnit(0.18626666666666666, Dimensions(length=1, time=-2), name='miles_per_square_day', ascii_symbol='miles/d^2', symbol='NoneNone⁻²') +miles_per_square_day = NamedUnit(2.1558641975308643e-05, Dimensions(length=1, time=-2), name='miles_per_square_day', ascii_symbol='miles/d^2', symbol='NoneNone⁻²') miles_per_year = NamedUnit(0.0005099808118350593, Dimensions(length=1, time=-1), name='miles_per_year', ascii_symbol='miles/y', symbol='NoneNone⁻¹') -miles_per_square_year = NamedUnit(0.0005099808118350593, Dimensions(length=1, time=-2), name='miles_per_square_year', ascii_symbol='miles/y^2', symbol='NoneNone⁻²') +miles_per_square_year = NamedUnit(1.61606485897326e-10, Dimensions(length=1, time=-2), name='miles_per_square_year', ascii_symbol='miles/y^2', symbol='NoneNone⁻²') yards_per_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-1), name='yards_per_second', ascii_symbol='yrd/s', symbol='NoneNone⁻¹') yards_per_square_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-2), name='yards_per_square_second', ascii_symbol='yrd/s^2', symbol='NoneNone⁻²') yards_per_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-1), name='yards_per_millisecond', ascii_symbol='yrd/ms', symbol='Nonems⁻¹') -yards_per_square_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-2), name='yards_per_square_millisecond', ascii_symbol='yrd/ms^2', symbol='Nonems⁻²') +yards_per_square_millisecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-2), name='yards_per_square_millisecond', ascii_symbol='yrd/ms^2', symbol='Nonems⁻²') yards_per_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-1), name='yards_per_microsecond', ascii_symbol='yrd/us', symbol='Noneµs⁻¹') -yards_per_square_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-2), name='yards_per_square_microsecond', ascii_symbol='yrd/us^2', symbol='Noneµs⁻²') +yards_per_square_microsecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-2), name='yards_per_square_microsecond', ascii_symbol='yrd/us^2', symbol='Noneµs⁻²') yards_per_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-1), name='yards_per_nanosecond', ascii_symbol='yrd/ns', symbol='Nonens⁻¹') -yards_per_square_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-2), name='yards_per_square_nanosecond', ascii_symbol='yrd/ns^2', symbol='Nonens⁻²') +yards_per_square_nanosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-2), name='yards_per_square_nanosecond', ascii_symbol='yrd/ns^2', symbol='Nonens⁻²') yards_per_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-1), name='yards_per_picosecond', ascii_symbol='yrd/ps', symbol='Noneps⁻¹') -yards_per_square_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-2), name='yards_per_square_picosecond', ascii_symbol='yrd/ps^2', symbol='Noneps⁻²') +yards_per_square_picosecond = NamedUnit(9.144000000000002e+23, Dimensions(length=1, time=-2), name='yards_per_square_picosecond', ascii_symbol='yrd/ps^2', symbol='Noneps⁻²') yards_per_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-1), name='yards_per_femtosecond', ascii_symbol='yrd/fs', symbol='Nonefs⁻¹') -yards_per_square_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-2), name='yards_per_square_femtosecond', ascii_symbol='yrd/fs^2', symbol='Nonefs⁻²') +yards_per_square_femtosecond = NamedUnit(9.144e+29, Dimensions(length=1, time=-2), name='yards_per_square_femtosecond', ascii_symbol='yrd/fs^2', symbol='Nonefs⁻²') yards_per_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-1), name='yards_per_attosecond', ascii_symbol='yrd/as', symbol='Noneas⁻¹') -yards_per_square_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-2), name='yards_per_square_attosecond', ascii_symbol='yrd/as^2', symbol='Noneas⁻²') +yards_per_square_attosecond = NamedUnit(9.144e+35, Dimensions(length=1, time=-2), name='yards_per_square_attosecond', ascii_symbol='yrd/as^2', symbol='Noneas⁻²') yards_per_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-1), name='yards_per_minute', ascii_symbol='yrd/min', symbol='NoneNone⁻¹') -yards_per_square_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-2), name='yards_per_square_minute', ascii_symbol='yrd/min^2', symbol='NoneNone⁻²') +yards_per_square_minute = NamedUnit(0.00025400000000000005, Dimensions(length=1, time=-2), name='yards_per_square_minute', ascii_symbol='yrd/min^2', symbol='NoneNone⁻²') yards_per_hour = NamedUnit(0.00254, Dimensions(length=1, time=-1), name='yards_per_hour', ascii_symbol='yrd/h', symbol='NoneNone⁻¹') -yards_per_square_hour = NamedUnit(0.00254, Dimensions(length=1, time=-2), name='yards_per_square_hour', ascii_symbol='yrd/h^2', symbol='NoneNone⁻²') +yards_per_square_hour = NamedUnit(7.055555555555557e-06, Dimensions(length=1, time=-2), name='yards_per_square_hour', ascii_symbol='yrd/h^2', symbol='NoneNone⁻²') yards_per_day = NamedUnit(0.00010583333333333335, Dimensions(length=1, time=-1), name='yards_per_day', ascii_symbol='yrd/d', symbol='NoneNone⁻¹') -yards_per_square_day = NamedUnit(0.00010583333333333335, Dimensions(length=1, time=-2), name='yards_per_square_day', ascii_symbol='yrd/d^2', symbol='NoneNone⁻²') +yards_per_square_day = NamedUnit(1.224922839506173e-08, Dimensions(length=1, time=-2), name='yards_per_square_day', ascii_symbol='yrd/d^2', symbol='NoneNone⁻²') yards_per_year = NamedUnit(2.897618249062837e-07, Dimensions(length=1, time=-1), name='yards_per_year', ascii_symbol='yrd/y', symbol='NoneNone⁻¹') -yards_per_square_year = NamedUnit(2.897618249062837e-07, Dimensions(length=1, time=-2), name='yards_per_square_year', ascii_symbol='yrd/y^2', symbol='NoneNone⁻²') +yards_per_square_year = NamedUnit(9.182186698711705e-14, Dimensions(length=1, time=-2), name='yards_per_square_year', ascii_symbol='yrd/y^2', symbol='NoneNone⁻²') feet_per_second = NamedUnit(0.3048, Dimensions(length=1, time=-1), name='feet_per_second', ascii_symbol='ft/s', symbol='NoneNone⁻¹') feet_per_square_second = NamedUnit(0.3048, Dimensions(length=1, time=-2), name='feet_per_square_second', ascii_symbol='ft/s^2', symbol='NoneNone⁻²') feet_per_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-1), name='feet_per_millisecond', ascii_symbol='ft/ms', symbol='Nonems⁻¹') -feet_per_square_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-2), name='feet_per_square_millisecond', ascii_symbol='ft/ms^2', symbol='Nonems⁻²') +feet_per_square_millisecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-2), name='feet_per_square_millisecond', ascii_symbol='ft/ms^2', symbol='Nonems⁻²') feet_per_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-1), name='feet_per_microsecond', ascii_symbol='ft/us', symbol='Noneµs⁻¹') -feet_per_square_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-2), name='feet_per_square_microsecond', ascii_symbol='ft/us^2', symbol='Noneµs⁻²') +feet_per_square_microsecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-2), name='feet_per_square_microsecond', ascii_symbol='ft/us^2', symbol='Noneµs⁻²') feet_per_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-1), name='feet_per_nanosecond', ascii_symbol='ft/ns', symbol='Nonens⁻¹') -feet_per_square_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-2), name='feet_per_square_nanosecond', ascii_symbol='ft/ns^2', symbol='Nonens⁻²') +feet_per_square_nanosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-2), name='feet_per_square_nanosecond', ascii_symbol='ft/ns^2', symbol='Nonens⁻²') feet_per_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-1), name='feet_per_picosecond', ascii_symbol='ft/ps', symbol='Noneps⁻¹') -feet_per_square_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-2), name='feet_per_square_picosecond', ascii_symbol='ft/ps^2', symbol='Noneps⁻²') +feet_per_square_picosecond = NamedUnit(3.048e+23, Dimensions(length=1, time=-2), name='feet_per_square_picosecond', ascii_symbol='ft/ps^2', symbol='Noneps⁻²') feet_per_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-1), name='feet_per_femtosecond', ascii_symbol='ft/fs', symbol='Nonefs⁻¹') -feet_per_square_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-2), name='feet_per_square_femtosecond', ascii_symbol='ft/fs^2', symbol='Nonefs⁻²') +feet_per_square_femtosecond = NamedUnit(3.048e+29, Dimensions(length=1, time=-2), name='feet_per_square_femtosecond', ascii_symbol='ft/fs^2', symbol='Nonefs⁻²') feet_per_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-1), name='feet_per_attosecond', ascii_symbol='ft/as', symbol='Noneas⁻¹') -feet_per_square_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-2), name='feet_per_square_attosecond', ascii_symbol='ft/as^2', symbol='Noneas⁻²') +feet_per_square_attosecond = NamedUnit(3.0479999999999997e+35, Dimensions(length=1, time=-2), name='feet_per_square_attosecond', ascii_symbol='ft/as^2', symbol='Noneas⁻²') feet_per_minute = NamedUnit(0.00508, Dimensions(length=1, time=-1), name='feet_per_minute', ascii_symbol='ft/min', symbol='NoneNone⁻¹') -feet_per_square_minute = NamedUnit(0.00508, Dimensions(length=1, time=-2), name='feet_per_square_minute', ascii_symbol='ft/min^2', symbol='NoneNone⁻²') +feet_per_square_minute = NamedUnit(8.466666666666667e-05, Dimensions(length=1, time=-2), name='feet_per_square_minute', ascii_symbol='ft/min^2', symbol='NoneNone⁻²') feet_per_hour = NamedUnit(0.0008466666666666667, Dimensions(length=1, time=-1), name='feet_per_hour', ascii_symbol='ft/h', symbol='NoneNone⁻¹') -feet_per_square_hour = NamedUnit(0.0008466666666666667, Dimensions(length=1, time=-2), name='feet_per_square_hour', ascii_symbol='ft/h^2', symbol='NoneNone⁻²') +feet_per_square_hour = NamedUnit(2.351851851851852e-06, Dimensions(length=1, time=-2), name='feet_per_square_hour', ascii_symbol='ft/h^2', symbol='NoneNone⁻²') feet_per_day = NamedUnit(3.527777777777778e-05, Dimensions(length=1, time=-1), name='feet_per_day', ascii_symbol='ft/d', symbol='NoneNone⁻¹') -feet_per_square_day = NamedUnit(3.527777777777778e-05, Dimensions(length=1, time=-2), name='feet_per_square_day', ascii_symbol='ft/d^2', symbol='NoneNone⁻²') +feet_per_square_day = NamedUnit(4.083076131687243e-09, Dimensions(length=1, time=-2), name='feet_per_square_day', ascii_symbol='ft/d^2', symbol='NoneNone⁻²') feet_per_year = NamedUnit(9.658727496876123e-08, Dimensions(length=1, time=-1), name='feet_per_year', ascii_symbol='ft/y', symbol='NoneNone⁻¹') -feet_per_square_year = NamedUnit(9.658727496876123e-08, Dimensions(length=1, time=-2), name='feet_per_square_year', ascii_symbol='ft/y^2', symbol='NoneNone⁻²') +feet_per_square_year = NamedUnit(3.060728899570568e-14, Dimensions(length=1, time=-2), name='feet_per_square_year', ascii_symbol='ft/y^2', symbol='NoneNone⁻²') inches_per_second = NamedUnit(0.0254, Dimensions(length=1, time=-1), name='inches_per_second', ascii_symbol='in/s', symbol='NoneNone⁻¹') inches_per_square_second = NamedUnit(0.0254, Dimensions(length=1, time=-2), name='inches_per_square_second', ascii_symbol='in/s^2', symbol='NoneNone⁻²') inches_per_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-1), name='inches_per_millisecond', ascii_symbol='in/ms', symbol='Nonems⁻¹') -inches_per_square_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-2), name='inches_per_square_millisecond', ascii_symbol='in/ms^2', symbol='Nonems⁻²') +inches_per_square_millisecond = NamedUnit(25400.0, Dimensions(length=1, time=-2), name='inches_per_square_millisecond', ascii_symbol='in/ms^2', symbol='Nonems⁻²') inches_per_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-1), name='inches_per_microsecond', ascii_symbol='in/us', symbol='Noneµs⁻¹') -inches_per_square_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-2), name='inches_per_square_microsecond', ascii_symbol='in/us^2', symbol='Noneµs⁻²') +inches_per_square_microsecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-2), name='inches_per_square_microsecond', ascii_symbol='in/us^2', symbol='Noneµs⁻²') inches_per_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-1), name='inches_per_nanosecond', ascii_symbol='in/ns', symbol='Nonens⁻¹') -inches_per_square_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-2), name='inches_per_square_nanosecond', ascii_symbol='in/ns^2', symbol='Nonens⁻²') +inches_per_square_nanosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-2), name='inches_per_square_nanosecond', ascii_symbol='in/ns^2', symbol='Nonens⁻²') inches_per_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-1), name='inches_per_picosecond', ascii_symbol='in/ps', symbol='Noneps⁻¹') -inches_per_square_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-2), name='inches_per_square_picosecond', ascii_symbol='in/ps^2', symbol='Noneps⁻²') +inches_per_square_picosecond = NamedUnit(2.54e+22, Dimensions(length=1, time=-2), name='inches_per_square_picosecond', ascii_symbol='in/ps^2', symbol='Noneps⁻²') inches_per_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-1), name='inches_per_femtosecond', ascii_symbol='in/fs', symbol='Nonefs⁻¹') -inches_per_square_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-2), name='inches_per_square_femtosecond', ascii_symbol='in/fs^2', symbol='Nonefs⁻²') +inches_per_square_femtosecond = NamedUnit(2.54e+28, Dimensions(length=1, time=-2), name='inches_per_square_femtosecond', ascii_symbol='in/fs^2', symbol='Nonefs⁻²') inches_per_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-1), name='inches_per_attosecond', ascii_symbol='in/as', symbol='Noneas⁻¹') -inches_per_square_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-2), name='inches_per_square_attosecond', ascii_symbol='in/as^2', symbol='Noneas⁻²') +inches_per_square_attosecond = NamedUnit(2.5399999999999998e+34, Dimensions(length=1, time=-2), name='inches_per_square_attosecond', ascii_symbol='in/as^2', symbol='Noneas⁻²') inches_per_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-1), name='inches_per_minute', ascii_symbol='in/min', symbol='NoneNone⁻¹') -inches_per_square_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-2), name='inches_per_square_minute', ascii_symbol='in/min^2', symbol='NoneNone⁻²') +inches_per_square_minute = NamedUnit(7.055555555555555e-06, Dimensions(length=1, time=-2), name='inches_per_square_minute', ascii_symbol='in/min^2', symbol='NoneNone⁻²') inches_per_hour = NamedUnit(7.055555555555556e-05, Dimensions(length=1, time=-1), name='inches_per_hour', ascii_symbol='in/h', symbol='NoneNone⁻¹') -inches_per_square_hour = NamedUnit(7.055555555555556e-05, Dimensions(length=1, time=-2), name='inches_per_square_hour', ascii_symbol='in/h^2', symbol='NoneNone⁻²') +inches_per_square_hour = NamedUnit(1.9598765432098765e-07, Dimensions(length=1, time=-2), name='inches_per_square_hour', ascii_symbol='in/h^2', symbol='NoneNone⁻²') inches_per_day = NamedUnit(2.9398148148148147e-06, Dimensions(length=1, time=-1), name='inches_per_day', ascii_symbol='in/d', symbol='NoneNone⁻¹') -inches_per_square_day = NamedUnit(2.9398148148148147e-06, Dimensions(length=1, time=-2), name='inches_per_square_day', ascii_symbol='in/d^2', symbol='NoneNone⁻²') +inches_per_square_day = NamedUnit(3.4025634430727023e-10, Dimensions(length=1, time=-2), name='inches_per_square_day', ascii_symbol='in/d^2', symbol='NoneNone⁻²') inches_per_year = NamedUnit(8.048939580730103e-09, Dimensions(length=1, time=-1), name='inches_per_year', ascii_symbol='in/y', symbol='NoneNone⁻¹') -inches_per_square_year = NamedUnit(8.048939580730103e-09, Dimensions(length=1, time=-2), name='inches_per_square_year', ascii_symbol='in/y^2', symbol='NoneNone⁻²') +inches_per_square_year = NamedUnit(2.5506074163088065e-15, Dimensions(length=1, time=-2), name='inches_per_square_year', ascii_symbol='in/y^2', symbol='NoneNone⁻²') grams_per_cubic_meter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') exagrams_per_cubic_meter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') petagrams_per_cubic_meter = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') @@ -2011,6 +2011,8 @@ def __init__(self, name: str, units: list[NamedUnit]): "psi": pounds_force_per_square_inch, "percent": percent, "%": percent, + "Amps": amperes, + "amps": amperes, "yr": years, "year": years, "day": days, @@ -2019,6 +2021,9 @@ def __init__(self, name: str, units: list[NamedUnit]): "a.u.": atomic_mass_units, "amu": atomic_mass_units, "degr": degrees, + "Deg": degrees, + "degrees": degrees, + "Degrees": degrees, "Counts": none, "counts": none, "cnts": none, diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index d5fc0d5d1..afce93f13 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -1,32 +1,61 @@ from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group, parse_unit -from sasdata.quantities.units import meters, speed, meters_per_second, per_angstrom, kilometers_per_square_hour, newtons -from pytest import raises - - -def test_parse(): - parsed_metres = parse_named_unit('m') - assert parsed_metres == meters - # Have to specify a group because this is ambigious with inverse of milliseconds. - parsed_metres_per_second = parse_named_unit_from_group('ms-1', speed) - assert parsed_metres_per_second == meters_per_second - parsed_inverse_angstroms = parse_named_unit('A-1') - assert parsed_inverse_angstroms == per_angstrom - parsed_inverse_angstroms_slant = parse_named_unit('1/A') - assert parsed_inverse_angstroms_slant == per_angstrom - parsed_kilometers_per_square_hour = parse_named_unit('kmh-2') - assert parsed_kilometers_per_square_hour == kilometers_per_square_hour - parsed_kilometers_per_square_hour_slant = parse_named_unit('km/h2') - assert parsed_kilometers_per_square_hour_slant == kilometers_per_square_hour - parsed_newton = parse_named_unit('kgm/s2') - assert parsed_newton == newtons +from sasdata.quantities import units +from sasdata.quantities.units import Unit + +import pytest + +named_units_for_testing = [ + ('m', units.meters), + ('A-1', units.per_angstrom), + ('1/A', units.per_angstrom), + ('kmh-2', units.kilometers_per_square_hour), + ('km/h2', units.kilometers_per_square_hour), + ('kgm/s2', units.newtons), + ('m m', units.square_meters), + ('mm', units.millimeters), + ('A^-1', units.per_angstrom), + ('V/Amps', units.ohms), + ('Ω', units.ohms), + ('Å', units.angstroms), + ('%', units.percent) +] + +unnamed_units_for_testing = [ + ('m13', units.meters**13), + ('kW/sr', units.kilowatts/units.stradians) +] + + +@pytest.mark.parametrize("string, expected_units", named_units_for_testing) +def test_name_parse(string: str, expected_units: Unit): + """ Test basic parsing""" + assert parse_named_unit(string) == expected_units + +@pytest.mark.parametrize("string, expected_units", named_units_for_testing + unnamed_units_for_testing) +def test_equivalent(string: str, expected_units: Unit): + """ Check dimensions of parsed units""" + assert parse_unit(string).equivalent(expected_units) + + +@pytest.mark.parametrize("string, expected_units", named_units_for_testing + unnamed_units_for_testing) +def test_scale_same(string: str, expected_units: Unit): + """ Test basic parsing""" + assert parse_unit(string).scale == pytest.approx(expected_units.scale, rel=1e-14) + + +def test_parse_from_group(): + """ Test group based disambiguation""" + parsed_metres_per_second = parse_named_unit_from_group('ms-1', units.speed) + assert parsed_metres_per_second == units.meters_per_second + def test_parse_errors(): # Fails because the unit is not in that specific group. - with raises(ValueError, match='That unit cannot be parsed from the specified group.'): - parse_named_unit_from_group('km', speed) + with pytest.raises(ValueError, match='That unit cannot be parsed from the specified group.'): + parse_named_unit_from_group('km', units.speed) # Fails because part of the unit matches but there is an unknown unit '@' - with raises(ValueError, match='unit_str contains forbidden characters.'): + with pytest.raises(ValueError, match='unit_str contains forbidden characters.'): parse_unit('km@-1') # Fails because 'da' is not a unit. - with raises(ValueError, match='Unit string contains an unrecognised pattern.'): + with pytest.raises(ValueError, match='Unit string contains an unrecognised pattern.'): parse_unit('mmda2') From 3c193aa55cbc2ea93700eccf00c3763e0cec7f00 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 1 Oct 2024 17:35:43 +0100 Subject: [PATCH 106/675] Parsing of units in HDF5 reader --- sasdata/quantities/unit_parser.py | 26 ++++++++++++++++++++++---- sasdata/temp_hdf5_reader.py | 21 +++++++++++---------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index cd3c10d71..0f7965a97 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -127,9 +127,13 @@ def parse_named_unit(unit_string: str, rtol: float=1e-14) -> NamedUnit: :param rtol: relative tolerance for matching scale factors """ unit = parse_unit(unit_string) - return find_named_unit(unit) + named_unit = find_named_unit(unit) + if named_unit is None: + raise ValueError(f"We don't have a for this unit: '{unit}'") + else: + return named_unit -def find_named_unit(unit: Unit, rtol: float=1e-14) -> NamedUnit: +def find_named_unit(unit: Unit, rtol: float=1e-14) -> NamedUnit | None: """ Find a named unit matching the one provided """ dimension_hash = hash(unit.dimensions) if dimension_hash in unit_groups_by_dimension_hash: @@ -139,7 +143,7 @@ def find_named_unit(unit: Unit, rtol: float=1e-14) -> NamedUnit: if abs(named_unit.scale - unit.scale) < rtol*named_unit.scale: return named_unit - raise ValueError('A named unit does not exist for this unit.') + return None def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUnit: @@ -150,12 +154,26 @@ def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUn raise ValueError('That unit cannot be parsed from the specified group.') return find_named_unit(parsed_unit) +def parse(string: str, + name_lookup: bool = True, + longest_unit: bool = True, + lookup_rtol: float = 1e-14): + + unit = parse_unit(string, longest_unit=longest_unit) + if name_lookup: + named = find_named_unit(unit, rtol=lookup_rtol) + if named is not None: + return named + + return unit + + if __name__ == "__main__": to_parse = input('Enter a unit to parse: ') try: generic_unit = parse_unit(to_parse) print(f'Generic Unit: {generic_unit}') - named_unit = parse_named_unit(generic_unit) + named_unit = find_named_unit(generic_unit) print(f'Named Unit: {named_unit}') except ValueError: print('There is no named unit available.') diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 4b746855f..32bb4f7ae 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -14,11 +14,12 @@ from sasdata.raw_form import RawData from sasdata.raw_form import Dataset as SASDataDataset, Group as SASDataGroup -from quantities.quantity import NamedQuantity -from quantities import units +from sasdata.quantities.quantity import NamedQuantity +from sasdata.quantities import units +from sasdata.quantities.unit_parser import parse -test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" -# test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" +# test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" +test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" logger = logging.getLogger(__name__) @@ -52,10 +53,7 @@ def recurse_hdf5(hdf5_entry): else: raise TypeError(f"Unknown type found during HDF5 parsing: {type(hdf5_entry)} ({hdf5_entry})") -def parse_units_placeholder(string: str) -> units.Unit: - #TODO: Remove when not needed - return units.meters - +GET_UNITS_FROM_ELSEWHERE = units.meters def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: """ In the context of NeXus files, load a group of data entries that are organised together match up the units and errors with their values""" @@ -68,8 +66,11 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: for name in node.children: child = node.children[name] - # TODO: Actual unit parser here - units = parse_units_placeholder(child.attributes["units"]) + + if "units" in child.attributes: + units = parse(child.attributes["units"]) + else: + units = GET_UNITS_FROM_ELSEWHERE quantity = NamedQuantity(name=name_prefix+child.name, value=child.data, From bb20728816e023b0bcff475e7199eaa1efb72e4d Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 2 Oct 2024 11:09:44 +0100 Subject: [PATCH 107/675] Fixed moles potentially --- sasdata/quantities/_build_tables.py | 2 +- sasdata/quantities/units.py | 460 ++++++++++++++-------------- 2 files changed, 231 insertions(+), 231 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index b16ed3122..9c2f66082 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -185,7 +185,7 @@ def format_name(name: str): # Units dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) fid.write(f"{combined_name_plural} = NamedUnit({combined_scale}, " - f"Dimensions({length}, {time}, {mass}, {current}, {temperature})," + f"Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," f"name='{combined_name_plural}'," f"ascii_symbol='{combined_symbol}'," f"symbol='{combined_special_symbol}')\n") diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 32faec793..2bec4bc7e 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -430,235 +430,235 @@ def __init__(self, name: str, units: list[NamedUnit]): # meters = NamedUnit(1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') -exameters = NamedUnit(1e+18, Dimensions(1, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') -petameters = NamedUnit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') -terameters = NamedUnit(1000000000000.0, Dimensions(1, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') -gigameters = NamedUnit(1000000000.0, Dimensions(1, 0, 0, 0, 0),name='gigameters',ascii_symbol='Gm',symbol='Gm') -megameters = NamedUnit(1000000.0, Dimensions(1, 0, 0, 0, 0),name='megameters',ascii_symbol='Mm',symbol='Mm') -kilometers = NamedUnit(1000.0, Dimensions(1, 0, 0, 0, 0),name='kilometers',ascii_symbol='km',symbol='km') -millimeters = NamedUnit(0.001, Dimensions(1, 0, 0, 0, 0),name='millimeters',ascii_symbol='mm',symbol='mm') -micrometers = NamedUnit(1e-06, Dimensions(1, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',symbol='µm') -nanometers = NamedUnit(1e-09, Dimensions(1, 0, 0, 0, 0),name='nanometers',ascii_symbol='nm',symbol='nm') -picometers = NamedUnit(1e-12, Dimensions(1, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') -femtometers = NamedUnit(1e-15, Dimensions(1, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') -attometers = NamedUnit(1e-18, Dimensions(1, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') -decimeters = NamedUnit(0.1, Dimensions(1, 0, 0, 0, 0),name='decimeters',ascii_symbol='dm',symbol='dm') -centimeters = NamedUnit(0.01, Dimensions(1, 0, 0, 0, 0),name='centimeters',ascii_symbol='cm',symbol='cm') +exameters = NamedUnit(1e+18, Dimensions(1, 0, 0, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') +petameters = NamedUnit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') +terameters = NamedUnit(1000000000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') +gigameters = NamedUnit(1000000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='gigameters',ascii_symbol='Gm',symbol='Gm') +megameters = NamedUnit(1000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='megameters',ascii_symbol='Mm',symbol='Mm') +kilometers = NamedUnit(1000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='kilometers',ascii_symbol='km',symbol='km') +millimeters = NamedUnit(0.001, Dimensions(1, 0, 0, 0, 0, 0, 0),name='millimeters',ascii_symbol='mm',symbol='mm') +micrometers = NamedUnit(1e-06, Dimensions(1, 0, 0, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',symbol='µm') +nanometers = NamedUnit(1e-09, Dimensions(1, 0, 0, 0, 0, 0, 0),name='nanometers',ascii_symbol='nm',symbol='nm') +picometers = NamedUnit(1e-12, Dimensions(1, 0, 0, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') +femtometers = NamedUnit(1e-15, Dimensions(1, 0, 0, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') +attometers = NamedUnit(1e-18, Dimensions(1, 0, 0, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') +decimeters = NamedUnit(0.1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='decimeters',ascii_symbol='dm',symbol='dm') +centimeters = NamedUnit(0.01, Dimensions(1, 0, 0, 0, 0, 0, 0),name='centimeters',ascii_symbol='cm',symbol='cm') seconds = NamedUnit(1, Dimensions(0, 1, 0, 0, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') -milliseconds = NamedUnit(0.001, Dimensions(0, 1, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') -microseconds = NamedUnit(1e-06, Dimensions(0, 1, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') -nanoseconds = NamedUnit(1e-09, Dimensions(0, 1, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') -picoseconds = NamedUnit(1e-12, Dimensions(0, 1, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') -femtoseconds = NamedUnit(1e-15, Dimensions(0, 1, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') -attoseconds = NamedUnit(1e-18, Dimensions(0, 1, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') +milliseconds = NamedUnit(0.001, Dimensions(0, 1, 0, 0, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') +microseconds = NamedUnit(1e-06, Dimensions(0, 1, 0, 0, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') +nanoseconds = NamedUnit(1e-09, Dimensions(0, 1, 0, 0, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') +picoseconds = NamedUnit(1e-12, Dimensions(0, 1, 0, 0, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') +femtoseconds = NamedUnit(1e-15, Dimensions(0, 1, 0, 0, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') +attoseconds = NamedUnit(1e-18, Dimensions(0, 1, 0, 0, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') grams = NamedUnit(0.001, Dimensions(0, 0, 1, 0, 0, 0, 0),name='grams',ascii_symbol='g',symbol='g') -exagrams = NamedUnit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') -petagrams = NamedUnit(1000000000000.0, Dimensions(0, 0, 1, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') -teragrams = NamedUnit(1000000000.0, Dimensions(0, 0, 1, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') -gigagrams = NamedUnit(1000000.0, Dimensions(0, 0, 1, 0, 0),name='gigagrams',ascii_symbol='Gg',symbol='Gg') -megagrams = NamedUnit(1000.0, Dimensions(0, 0, 1, 0, 0),name='megagrams',ascii_symbol='Mg',symbol='Mg') -kilograms = NamedUnit(1.0, Dimensions(0, 0, 1, 0, 0),name='kilograms',ascii_symbol='kg',symbol='kg') -milligrams = NamedUnit(1e-06, Dimensions(0, 0, 1, 0, 0),name='milligrams',ascii_symbol='mg',symbol='mg') -micrograms = NamedUnit(1e-09, Dimensions(0, 0, 1, 0, 0),name='micrograms',ascii_symbol='ug',symbol='µg') -nanograms = NamedUnit(1.0000000000000002e-12, Dimensions(0, 0, 1, 0, 0),name='nanograms',ascii_symbol='ng',symbol='ng') -picograms = NamedUnit(1e-15, Dimensions(0, 0, 1, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') -femtograms = NamedUnit(1e-18, Dimensions(0, 0, 1, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') -attograms = NamedUnit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') +exagrams = NamedUnit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') +petagrams = NamedUnit(1000000000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') +teragrams = NamedUnit(1000000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') +gigagrams = NamedUnit(1000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='gigagrams',ascii_symbol='Gg',symbol='Gg') +megagrams = NamedUnit(1000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='megagrams',ascii_symbol='Mg',symbol='Mg') +kilograms = NamedUnit(1.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='kilograms',ascii_symbol='kg',symbol='kg') +milligrams = NamedUnit(1e-06, Dimensions(0, 0, 1, 0, 0, 0, 0),name='milligrams',ascii_symbol='mg',symbol='mg') +micrograms = NamedUnit(1e-09, Dimensions(0, 0, 1, 0, 0, 0, 0),name='micrograms',ascii_symbol='ug',symbol='µg') +nanograms = NamedUnit(1.0000000000000002e-12, Dimensions(0, 0, 1, 0, 0, 0, 0),name='nanograms',ascii_symbol='ng',symbol='ng') +picograms = NamedUnit(1e-15, Dimensions(0, 0, 1, 0, 0, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') +femtograms = NamedUnit(1e-18, Dimensions(0, 0, 1, 0, 0, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') +attograms = NamedUnit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') amperes = NamedUnit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amperes',ascii_symbol='A',symbol='A') -exaamperes = NamedUnit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamperes',ascii_symbol='EA',symbol='EA') -petaamperes = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamperes',ascii_symbol='PA',symbol='PA') -teraamperes = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamperes',ascii_symbol='TA',symbol='TA') -gigaamperes = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 1, 0),name='gigaamperes',ascii_symbol='GA',symbol='GA') -megaamperes = NamedUnit(1000000.0, Dimensions(0, 0, 0, 1, 0),name='megaamperes',ascii_symbol='MA',symbol='MA') -kiloamperes = NamedUnit(1000.0, Dimensions(0, 0, 0, 1, 0),name='kiloamperes',ascii_symbol='kA',symbol='kA') -milliamperes = NamedUnit(0.001, Dimensions(0, 0, 0, 1, 0),name='milliamperes',ascii_symbol='mA',symbol='mA') -microamperes = NamedUnit(1e-06, Dimensions(0, 0, 0, 1, 0),name='microamperes',ascii_symbol='uA',symbol='µA') -nanoamperes = NamedUnit(1e-09, Dimensions(0, 0, 0, 1, 0),name='nanoamperes',ascii_symbol='nA',symbol='nA') -picoamperes = NamedUnit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamperes',ascii_symbol='pA',symbol='pA') -femtoamperes = NamedUnit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamperes',ascii_symbol='fA',symbol='fA') -attoamperes = NamedUnit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamperes',ascii_symbol='aA',symbol='aA') +exaamperes = NamedUnit(1e+18, Dimensions(0, 0, 0, 1, 0, 0, 0),name='exaamperes',ascii_symbol='EA',symbol='EA') +petaamperes = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='petaamperes',ascii_symbol='PA',symbol='PA') +teraamperes = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='teraamperes',ascii_symbol='TA',symbol='TA') +gigaamperes = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='gigaamperes',ascii_symbol='GA',symbol='GA') +megaamperes = NamedUnit(1000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='megaamperes',ascii_symbol='MA',symbol='MA') +kiloamperes = NamedUnit(1000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='kiloamperes',ascii_symbol='kA',symbol='kA') +milliamperes = NamedUnit(0.001, Dimensions(0, 0, 0, 1, 0, 0, 0),name='milliamperes',ascii_symbol='mA',symbol='mA') +microamperes = NamedUnit(1e-06, Dimensions(0, 0, 0, 1, 0, 0, 0),name='microamperes',ascii_symbol='uA',symbol='µA') +nanoamperes = NamedUnit(1e-09, Dimensions(0, 0, 0, 1, 0, 0, 0),name='nanoamperes',ascii_symbol='nA',symbol='nA') +picoamperes = NamedUnit(1e-12, Dimensions(0, 0, 0, 1, 0, 0, 0),name='picoamperes',ascii_symbol='pA',symbol='pA') +femtoamperes = NamedUnit(1e-15, Dimensions(0, 0, 0, 1, 0, 0, 0),name='femtoamperes',ascii_symbol='fA',symbol='fA') +attoamperes = NamedUnit(1e-18, Dimensions(0, 0, 0, 1, 0, 0, 0),name='attoamperes',ascii_symbol='aA',symbol='aA') kelvin = NamedUnit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kelvin',ascii_symbol='K',symbol='K') -exakelvin = NamedUnit(1e+18, Dimensions(0, 0, 0, 0, 1),name='exakelvin',ascii_symbol='EK',symbol='EK') -petakelvin = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1),name='petakelvin',ascii_symbol='PK',symbol='PK') -terakelvin = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 0, 1),name='terakelvin',ascii_symbol='TK',symbol='TK') -gigakelvin = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 0, 1),name='gigakelvin',ascii_symbol='GK',symbol='GK') -megakelvin = NamedUnit(1000000.0, Dimensions(0, 0, 0, 0, 1),name='megakelvin',ascii_symbol='MK',symbol='MK') -kilokelvin = NamedUnit(1000.0, Dimensions(0, 0, 0, 0, 1),name='kilokelvin',ascii_symbol='kK',symbol='kK') -millikelvin = NamedUnit(0.001, Dimensions(0, 0, 0, 0, 1),name='millikelvin',ascii_symbol='mK',symbol='mK') -microkelvin = NamedUnit(1e-06, Dimensions(0, 0, 0, 0, 1),name='microkelvin',ascii_symbol='uK',symbol='µK') -nanokelvin = NamedUnit(1e-09, Dimensions(0, 0, 0, 0, 1),name='nanokelvin',ascii_symbol='nK',symbol='nK') -picokelvin = NamedUnit(1e-12, Dimensions(0, 0, 0, 0, 1),name='picokelvin',ascii_symbol='pK',symbol='pK') -femtokelvin = NamedUnit(1e-15, Dimensions(0, 0, 0, 0, 1),name='femtokelvin',ascii_symbol='fK',symbol='fK') -attokelvin = NamedUnit(1e-18, Dimensions(0, 0, 0, 0, 1),name='attokelvin',ascii_symbol='aK',symbol='aK') +exakelvin = NamedUnit(1e+18, Dimensions(0, 0, 0, 0, 1, 0, 0),name='exakelvin',ascii_symbol='EK',symbol='EK') +petakelvin = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='petakelvin',ascii_symbol='PK',symbol='PK') +terakelvin = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='terakelvin',ascii_symbol='TK',symbol='TK') +gigakelvin = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='gigakelvin',ascii_symbol='GK',symbol='GK') +megakelvin = NamedUnit(1000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='megakelvin',ascii_symbol='MK',symbol='MK') +kilokelvin = NamedUnit(1000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kilokelvin',ascii_symbol='kK',symbol='kK') +millikelvin = NamedUnit(0.001, Dimensions(0, 0, 0, 0, 1, 0, 0),name='millikelvin',ascii_symbol='mK',symbol='mK') +microkelvin = NamedUnit(1e-06, Dimensions(0, 0, 0, 0, 1, 0, 0),name='microkelvin',ascii_symbol='uK',symbol='µK') +nanokelvin = NamedUnit(1e-09, Dimensions(0, 0, 0, 0, 1, 0, 0),name='nanokelvin',ascii_symbol='nK',symbol='nK') +picokelvin = NamedUnit(1e-12, Dimensions(0, 0, 0, 0, 1, 0, 0),name='picokelvin',ascii_symbol='pK',symbol='pK') +femtokelvin = NamedUnit(1e-15, Dimensions(0, 0, 0, 0, 1, 0, 0),name='femtokelvin',ascii_symbol='fK',symbol='fK') +attokelvin = NamedUnit(1e-18, Dimensions(0, 0, 0, 0, 1, 0, 0),name='attokelvin',ascii_symbol='aK',symbol='aK') hertz = NamedUnit(1, Dimensions(0, -1, 0, 0, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') -exahertz = NamedUnit(1e+18, Dimensions(0, -1, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') -petahertz = NamedUnit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') -terahertz = NamedUnit(1000000000000.0, Dimensions(0, -1, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') -gigahertz = NamedUnit(1000000000.0, Dimensions(0, -1, 0, 0, 0),name='gigahertz',ascii_symbol='GHz',symbol='GHz') -megahertz = NamedUnit(1000000.0, Dimensions(0, -1, 0, 0, 0),name='megahertz',ascii_symbol='MHz',symbol='MHz') -kilohertz = NamedUnit(1000.0, Dimensions(0, -1, 0, 0, 0),name='kilohertz',ascii_symbol='kHz',symbol='kHz') -millihertz = NamedUnit(0.001, Dimensions(0, -1, 0, 0, 0),name='millihertz',ascii_symbol='mHz',symbol='mHz') -microhertz = NamedUnit(1e-06, Dimensions(0, -1, 0, 0, 0),name='microhertz',ascii_symbol='uHz',symbol='µHz') -nanohertz = NamedUnit(1e-09, Dimensions(0, -1, 0, 0, 0),name='nanohertz',ascii_symbol='nHz',symbol='nHz') -picohertz = NamedUnit(1e-12, Dimensions(0, -1, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') -femtohertz = NamedUnit(1e-15, Dimensions(0, -1, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') -attohertz = NamedUnit(1e-18, Dimensions(0, -1, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') +exahertz = NamedUnit(1e+18, Dimensions(0, -1, 0, 0, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') +petahertz = NamedUnit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') +terahertz = NamedUnit(1000000000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') +gigahertz = NamedUnit(1000000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='gigahertz',ascii_symbol='GHz',symbol='GHz') +megahertz = NamedUnit(1000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='megahertz',ascii_symbol='MHz',symbol='MHz') +kilohertz = NamedUnit(1000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='kilohertz',ascii_symbol='kHz',symbol='kHz') +millihertz = NamedUnit(0.001, Dimensions(0, -1, 0, 0, 0, 0, 0),name='millihertz',ascii_symbol='mHz',symbol='mHz') +microhertz = NamedUnit(1e-06, Dimensions(0, -1, 0, 0, 0, 0, 0),name='microhertz',ascii_symbol='uHz',symbol='µHz') +nanohertz = NamedUnit(1e-09, Dimensions(0, -1, 0, 0, 0, 0, 0),name='nanohertz',ascii_symbol='nHz',symbol='nHz') +picohertz = NamedUnit(1e-12, Dimensions(0, -1, 0, 0, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') +femtohertz = NamedUnit(1e-15, Dimensions(0, -1, 0, 0, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') +attohertz = NamedUnit(1e-18, Dimensions(0, -1, 0, 0, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') newtons = NamedUnit(1, Dimensions(1, -2, 1, 0, 0, 0, 0),name='newtons',ascii_symbol='N',symbol='N') -exanewtons = NamedUnit(1e+18, Dimensions(1, -2, 1, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') -petanewtons = NamedUnit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') -teranewtons = NamedUnit(1000000000000.0, Dimensions(1, -2, 1, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') -giganewtons = NamedUnit(1000000000.0, Dimensions(1, -2, 1, 0, 0),name='giganewtons',ascii_symbol='GN',symbol='GN') -meganewtons = NamedUnit(1000000.0, Dimensions(1, -2, 1, 0, 0),name='meganewtons',ascii_symbol='MN',symbol='MN') -kilonewtons = NamedUnit(1000.0, Dimensions(1, -2, 1, 0, 0),name='kilonewtons',ascii_symbol='kN',symbol='kN') -millinewtons = NamedUnit(0.001, Dimensions(1, -2, 1, 0, 0),name='millinewtons',ascii_symbol='mN',symbol='mN') -micronewtons = NamedUnit(1e-06, Dimensions(1, -2, 1, 0, 0),name='micronewtons',ascii_symbol='uN',symbol='µN') -nanonewtons = NamedUnit(1e-09, Dimensions(1, -2, 1, 0, 0),name='nanonewtons',ascii_symbol='nN',symbol='nN') -piconewtons = NamedUnit(1e-12, Dimensions(1, -2, 1, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') -femtonewtons = NamedUnit(1e-15, Dimensions(1, -2, 1, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') -attonewtons = NamedUnit(1e-18, Dimensions(1, -2, 1, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') +exanewtons = NamedUnit(1e+18, Dimensions(1, -2, 1, 0, 0, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') +petanewtons = NamedUnit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') +teranewtons = NamedUnit(1000000000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') +giganewtons = NamedUnit(1000000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='giganewtons',ascii_symbol='GN',symbol='GN') +meganewtons = NamedUnit(1000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='meganewtons',ascii_symbol='MN',symbol='MN') +kilonewtons = NamedUnit(1000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kilonewtons',ascii_symbol='kN',symbol='kN') +millinewtons = NamedUnit(0.001, Dimensions(1, -2, 1, 0, 0, 0, 0),name='millinewtons',ascii_symbol='mN',symbol='mN') +micronewtons = NamedUnit(1e-06, Dimensions(1, -2, 1, 0, 0, 0, 0),name='micronewtons',ascii_symbol='uN',symbol='µN') +nanonewtons = NamedUnit(1e-09, Dimensions(1, -2, 1, 0, 0, 0, 0),name='nanonewtons',ascii_symbol='nN',symbol='nN') +piconewtons = NamedUnit(1e-12, Dimensions(1, -2, 1, 0, 0, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') +femtonewtons = NamedUnit(1e-15, Dimensions(1, -2, 1, 0, 0, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') +attonewtons = NamedUnit(1e-18, Dimensions(1, -2, 1, 0, 0, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') pascals = NamedUnit(1, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') -exapascals = NamedUnit(1e+18, Dimensions(-1, -2, 1, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') -petapascals = NamedUnit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') -terapascals = NamedUnit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') -gigapascals = NamedUnit(1000000000.0, Dimensions(-1, -2, 1, 0, 0),name='gigapascals',ascii_symbol='GPa',symbol='GPa') -megapascals = NamedUnit(1000000.0, Dimensions(-1, -2, 1, 0, 0),name='megapascals',ascii_symbol='MPa',symbol='MPa') -kilopascals = NamedUnit(1000.0, Dimensions(-1, -2, 1, 0, 0),name='kilopascals',ascii_symbol='kPa',symbol='kPa') -millipascals = NamedUnit(0.001, Dimensions(-1, -2, 1, 0, 0),name='millipascals',ascii_symbol='mPa',symbol='mPa') -micropascals = NamedUnit(1e-06, Dimensions(-1, -2, 1, 0, 0),name='micropascals',ascii_symbol='uPa',symbol='µPa') -nanopascals = NamedUnit(1e-09, Dimensions(-1, -2, 1, 0, 0),name='nanopascals',ascii_symbol='nPa',symbol='nPa') -picopascals = NamedUnit(1e-12, Dimensions(-1, -2, 1, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') -femtopascals = NamedUnit(1e-15, Dimensions(-1, -2, 1, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') -attopascals = NamedUnit(1e-18, Dimensions(-1, -2, 1, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') +exapascals = NamedUnit(1e+18, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') +petapascals = NamedUnit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') +terapascals = NamedUnit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') +gigapascals = NamedUnit(1000000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='gigapascals',ascii_symbol='GPa',symbol='GPa') +megapascals = NamedUnit(1000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='megapascals',ascii_symbol='MPa',symbol='MPa') +kilopascals = NamedUnit(1000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='kilopascals',ascii_symbol='kPa',symbol='kPa') +millipascals = NamedUnit(0.001, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='millipascals',ascii_symbol='mPa',symbol='mPa') +micropascals = NamedUnit(1e-06, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='micropascals',ascii_symbol='uPa',symbol='µPa') +nanopascals = NamedUnit(1e-09, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='nanopascals',ascii_symbol='nPa',symbol='nPa') +picopascals = NamedUnit(1e-12, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') +femtopascals = NamedUnit(1e-15, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') +attopascals = NamedUnit(1e-18, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') joules = NamedUnit(1, Dimensions(2, -2, 1, 0, 0, 0, 0),name='joules',ascii_symbol='J',symbol='J') -exajoules = NamedUnit(1e+18, Dimensions(2, -2, 1, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') -petajoules = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') -terajoules = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') -gigajoules = NamedUnit(1000000000.0, Dimensions(2, -2, 1, 0, 0),name='gigajoules',ascii_symbol='GJ',symbol='GJ') -megajoules = NamedUnit(1000000.0, Dimensions(2, -2, 1, 0, 0),name='megajoules',ascii_symbol='MJ',symbol='MJ') -kilojoules = NamedUnit(1000.0, Dimensions(2, -2, 1, 0, 0),name='kilojoules',ascii_symbol='kJ',symbol='kJ') -millijoules = NamedUnit(0.001, Dimensions(2, -2, 1, 0, 0),name='millijoules',ascii_symbol='mJ',symbol='mJ') -microjoules = NamedUnit(1e-06, Dimensions(2, -2, 1, 0, 0),name='microjoules',ascii_symbol='uJ',symbol='µJ') -nanojoules = NamedUnit(1e-09, Dimensions(2, -2, 1, 0, 0),name='nanojoules',ascii_symbol='nJ',symbol='nJ') -picojoules = NamedUnit(1e-12, Dimensions(2, -2, 1, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') -femtojoules = NamedUnit(1e-15, Dimensions(2, -2, 1, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') -attojoules = NamedUnit(1e-18, Dimensions(2, -2, 1, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') +exajoules = NamedUnit(1e+18, Dimensions(2, -2, 1, 0, 0, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') +petajoules = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') +terajoules = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') +gigajoules = NamedUnit(1000000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='gigajoules',ascii_symbol='GJ',symbol='GJ') +megajoules = NamedUnit(1000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='megajoules',ascii_symbol='MJ',symbol='MJ') +kilojoules = NamedUnit(1000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='kilojoules',ascii_symbol='kJ',symbol='kJ') +millijoules = NamedUnit(0.001, Dimensions(2, -2, 1, 0, 0, 0, 0),name='millijoules',ascii_symbol='mJ',symbol='mJ') +microjoules = NamedUnit(1e-06, Dimensions(2, -2, 1, 0, 0, 0, 0),name='microjoules',ascii_symbol='uJ',symbol='µJ') +nanojoules = NamedUnit(1e-09, Dimensions(2, -2, 1, 0, 0, 0, 0),name='nanojoules',ascii_symbol='nJ',symbol='nJ') +picojoules = NamedUnit(1e-12, Dimensions(2, -2, 1, 0, 0, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') +femtojoules = NamedUnit(1e-15, Dimensions(2, -2, 1, 0, 0, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') +attojoules = NamedUnit(1e-18, Dimensions(2, -2, 1, 0, 0, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') watts = NamedUnit(1, Dimensions(2, -3, 1, 0, 0, 0, 0),name='watts',ascii_symbol='W',symbol='W') -exawatts = NamedUnit(1e+18, Dimensions(2, -3, 1, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') -petawatts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') -terawatts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') -gigawatts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, 0, 0),name='gigawatts',ascii_symbol='GW',symbol='GW') -megawatts = NamedUnit(1000000.0, Dimensions(2, -3, 1, 0, 0),name='megawatts',ascii_symbol='MW',symbol='MW') -kilowatts = NamedUnit(1000.0, Dimensions(2, -3, 1, 0, 0),name='kilowatts',ascii_symbol='kW',symbol='kW') -milliwatts = NamedUnit(0.001, Dimensions(2, -3, 1, 0, 0),name='milliwatts',ascii_symbol='mW',symbol='mW') -microwatts = NamedUnit(1e-06, Dimensions(2, -3, 1, 0, 0),name='microwatts',ascii_symbol='uW',symbol='µW') -nanowatts = NamedUnit(1e-09, Dimensions(2, -3, 1, 0, 0),name='nanowatts',ascii_symbol='nW',symbol='nW') -picowatts = NamedUnit(1e-12, Dimensions(2, -3, 1, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') -femtowatts = NamedUnit(1e-15, Dimensions(2, -3, 1, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') -attowatts = NamedUnit(1e-18, Dimensions(2, -3, 1, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') +exawatts = NamedUnit(1e+18, Dimensions(2, -3, 1, 0, 0, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') +petawatts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') +terawatts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') +gigawatts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='gigawatts',ascii_symbol='GW',symbol='GW') +megawatts = NamedUnit(1000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='megawatts',ascii_symbol='MW',symbol='MW') +kilowatts = NamedUnit(1000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='kilowatts',ascii_symbol='kW',symbol='kW') +milliwatts = NamedUnit(0.001, Dimensions(2, -3, 1, 0, 0, 0, 0),name='milliwatts',ascii_symbol='mW',symbol='mW') +microwatts = NamedUnit(1e-06, Dimensions(2, -3, 1, 0, 0, 0, 0),name='microwatts',ascii_symbol='uW',symbol='µW') +nanowatts = NamedUnit(1e-09, Dimensions(2, -3, 1, 0, 0, 0, 0),name='nanowatts',ascii_symbol='nW',symbol='nW') +picowatts = NamedUnit(1e-12, Dimensions(2, -3, 1, 0, 0, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') +femtowatts = NamedUnit(1e-15, Dimensions(2, -3, 1, 0, 0, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') +attowatts = NamedUnit(1e-18, Dimensions(2, -3, 1, 0, 0, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') coulombs = NamedUnit(1, Dimensions(0, 1, 0, 1, 0, 0, 0),name='coulombs',ascii_symbol='C',symbol='C') -exacoulombs = NamedUnit(1e+18, Dimensions(0, 1, 0, 1, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') -petacoulombs = NamedUnit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') -teracoulombs = NamedUnit(1000000000000.0, Dimensions(0, 1, 0, 1, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') -gigacoulombs = NamedUnit(1000000000.0, Dimensions(0, 1, 0, 1, 0),name='gigacoulombs',ascii_symbol='GC',symbol='GC') -megacoulombs = NamedUnit(1000000.0, Dimensions(0, 1, 0, 1, 0),name='megacoulombs',ascii_symbol='MC',symbol='MC') -kilocoulombs = NamedUnit(1000.0, Dimensions(0, 1, 0, 1, 0),name='kilocoulombs',ascii_symbol='kC',symbol='kC') -millicoulombs = NamedUnit(0.001, Dimensions(0, 1, 0, 1, 0),name='millicoulombs',ascii_symbol='mC',symbol='mC') -microcoulombs = NamedUnit(1e-06, Dimensions(0, 1, 0, 1, 0),name='microcoulombs',ascii_symbol='uC',symbol='µC') -nanocoulombs = NamedUnit(1e-09, Dimensions(0, 1, 0, 1, 0),name='nanocoulombs',ascii_symbol='nC',symbol='nC') -picocoulombs = NamedUnit(1e-12, Dimensions(0, 1, 0, 1, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') -femtocoulombs = NamedUnit(1e-15, Dimensions(0, 1, 0, 1, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') -attocoulombs = NamedUnit(1e-18, Dimensions(0, 1, 0, 1, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') +exacoulombs = NamedUnit(1e+18, Dimensions(0, 1, 0, 1, 0, 0, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') +petacoulombs = NamedUnit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') +teracoulombs = NamedUnit(1000000000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') +gigacoulombs = NamedUnit(1000000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='gigacoulombs',ascii_symbol='GC',symbol='GC') +megacoulombs = NamedUnit(1000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='megacoulombs',ascii_symbol='MC',symbol='MC') +kilocoulombs = NamedUnit(1000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='kilocoulombs',ascii_symbol='kC',symbol='kC') +millicoulombs = NamedUnit(0.001, Dimensions(0, 1, 0, 1, 0, 0, 0),name='millicoulombs',ascii_symbol='mC',symbol='mC') +microcoulombs = NamedUnit(1e-06, Dimensions(0, 1, 0, 1, 0, 0, 0),name='microcoulombs',ascii_symbol='uC',symbol='µC') +nanocoulombs = NamedUnit(1e-09, Dimensions(0, 1, 0, 1, 0, 0, 0),name='nanocoulombs',ascii_symbol='nC',symbol='nC') +picocoulombs = NamedUnit(1e-12, Dimensions(0, 1, 0, 1, 0, 0, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') +femtocoulombs = NamedUnit(1e-15, Dimensions(0, 1, 0, 1, 0, 0, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') +attocoulombs = NamedUnit(1e-18, Dimensions(0, 1, 0, 1, 0, 0, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') volts = NamedUnit(1, Dimensions(2, -3, 1, -1, 0, 0, 0),name='volts',ascii_symbol='V',symbol='V') -exavolts = NamedUnit(1e+18, Dimensions(2, -3, 1, -1, 0),name='exavolts',ascii_symbol='EV',symbol='EV') -petavolts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0),name='petavolts',ascii_symbol='PV',symbol='PV') -teravolts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -1, 0),name='teravolts',ascii_symbol='TV',symbol='TV') -gigavolts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -1, 0),name='gigavolts',ascii_symbol='GV',symbol='GV') -megavolts = NamedUnit(1000000.0, Dimensions(2, -3, 1, -1, 0),name='megavolts',ascii_symbol='MV',symbol='MV') -kilovolts = NamedUnit(1000.0, Dimensions(2, -3, 1, -1, 0),name='kilovolts',ascii_symbol='kV',symbol='kV') -millivolts = NamedUnit(0.001, Dimensions(2, -3, 1, -1, 0),name='millivolts',ascii_symbol='mV',symbol='mV') -microvolts = NamedUnit(1e-06, Dimensions(2, -3, 1, -1, 0),name='microvolts',ascii_symbol='uV',symbol='µV') -nanovolts = NamedUnit(1e-09, Dimensions(2, -3, 1, -1, 0),name='nanovolts',ascii_symbol='nV',symbol='nV') -picovolts = NamedUnit(1e-12, Dimensions(2, -3, 1, -1, 0),name='picovolts',ascii_symbol='pV',symbol='pV') -femtovolts = NamedUnit(1e-15, Dimensions(2, -3, 1, -1, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') -attovolts = NamedUnit(1e-18, Dimensions(2, -3, 1, -1, 0),name='attovolts',ascii_symbol='aV',symbol='aV') +exavolts = NamedUnit(1e+18, Dimensions(2, -3, 1, -1, 0, 0, 0),name='exavolts',ascii_symbol='EV',symbol='EV') +petavolts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='petavolts',ascii_symbol='PV',symbol='PV') +teravolts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='teravolts',ascii_symbol='TV',symbol='TV') +gigavolts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='gigavolts',ascii_symbol='GV',symbol='GV') +megavolts = NamedUnit(1000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='megavolts',ascii_symbol='MV',symbol='MV') +kilovolts = NamedUnit(1000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='kilovolts',ascii_symbol='kV',symbol='kV') +millivolts = NamedUnit(0.001, Dimensions(2, -3, 1, -1, 0, 0, 0),name='millivolts',ascii_symbol='mV',symbol='mV') +microvolts = NamedUnit(1e-06, Dimensions(2, -3, 1, -1, 0, 0, 0),name='microvolts',ascii_symbol='uV',symbol='µV') +nanovolts = NamedUnit(1e-09, Dimensions(2, -3, 1, -1, 0, 0, 0),name='nanovolts',ascii_symbol='nV',symbol='nV') +picovolts = NamedUnit(1e-12, Dimensions(2, -3, 1, -1, 0, 0, 0),name='picovolts',ascii_symbol='pV',symbol='pV') +femtovolts = NamedUnit(1e-15, Dimensions(2, -3, 1, -1, 0, 0, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') +attovolts = NamedUnit(1e-18, Dimensions(2, -3, 1, -1, 0, 0, 0),name='attovolts',ascii_symbol='aV',symbol='aV') ohms = NamedUnit(1, Dimensions(2, -3, 1, -2, 0, 0, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') -exaohms = NamedUnit(1e+18, Dimensions(2, -3, 1, -2, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') -petaohms = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') -teraohms = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -2, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') -gigaohms = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -2, 0),name='gigaohms',ascii_symbol='GOhm',symbol='GΩ') -megaohms = NamedUnit(1000000.0, Dimensions(2, -3, 1, -2, 0),name='megaohms',ascii_symbol='MOhm',symbol='MΩ') -kiloohms = NamedUnit(1000.0, Dimensions(2, -3, 1, -2, 0),name='kiloohms',ascii_symbol='kOhm',symbol='kΩ') -milliohms = NamedUnit(0.001, Dimensions(2, -3, 1, -2, 0),name='milliohms',ascii_symbol='mOhm',symbol='mΩ') -microohms = NamedUnit(1e-06, Dimensions(2, -3, 1, -2, 0),name='microohms',ascii_symbol='uOhm',symbol='µΩ') -nanoohms = NamedUnit(1e-09, Dimensions(2, -3, 1, -2, 0),name='nanoohms',ascii_symbol='nOhm',symbol='nΩ') -picoohms = NamedUnit(1e-12, Dimensions(2, -3, 1, -2, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') -femtoohms = NamedUnit(1e-15, Dimensions(2, -3, 1, -2, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') -attoohms = NamedUnit(1e-18, Dimensions(2, -3, 1, -2, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') +exaohms = NamedUnit(1e+18, Dimensions(2, -3, 1, -2, 0, 0, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') +petaohms = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') +teraohms = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') +gigaohms = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='gigaohms',ascii_symbol='GOhm',symbol='GΩ') +megaohms = NamedUnit(1000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='megaohms',ascii_symbol='MOhm',symbol='MΩ') +kiloohms = NamedUnit(1000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='kiloohms',ascii_symbol='kOhm',symbol='kΩ') +milliohms = NamedUnit(0.001, Dimensions(2, -3, 1, -2, 0, 0, 0),name='milliohms',ascii_symbol='mOhm',symbol='mΩ') +microohms = NamedUnit(1e-06, Dimensions(2, -3, 1, -2, 0, 0, 0),name='microohms',ascii_symbol='uOhm',symbol='µΩ') +nanoohms = NamedUnit(1e-09, Dimensions(2, -3, 1, -2, 0, 0, 0),name='nanoohms',ascii_symbol='nOhm',symbol='nΩ') +picoohms = NamedUnit(1e-12, Dimensions(2, -3, 1, -2, 0, 0, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') +femtoohms = NamedUnit(1e-15, Dimensions(2, -3, 1, -2, 0, 0, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') +attoohms = NamedUnit(1e-18, Dimensions(2, -3, 1, -2, 0, 0, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') farads = NamedUnit(1, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='farads',ascii_symbol='F',symbol='F') -exafarads = NamedUnit(1e+18, Dimensions(-2, 4, -1, 2, 0),name='exafarads',ascii_symbol='EF',symbol='EF') -petafarads = NamedUnit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='petafarads',ascii_symbol='PF',symbol='PF') -terafarads = NamedUnit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='terafarads',ascii_symbol='TF',symbol='TF') -gigafarads = NamedUnit(1000000000.0, Dimensions(-2, 4, -1, 2, 0),name='gigafarads',ascii_symbol='GF',symbol='GF') -megafarads = NamedUnit(1000000.0, Dimensions(-2, 4, -1, 2, 0),name='megafarads',ascii_symbol='MF',symbol='MF') -kilofarads = NamedUnit(1000.0, Dimensions(-2, 4, -1, 2, 0),name='kilofarads',ascii_symbol='kF',symbol='kF') -millifarads = NamedUnit(0.001, Dimensions(-2, 4, -1, 2, 0),name='millifarads',ascii_symbol='mF',symbol='mF') -microfarads = NamedUnit(1e-06, Dimensions(-2, 4, -1, 2, 0),name='microfarads',ascii_symbol='uF',symbol='µF') -nanofarads = NamedUnit(1e-09, Dimensions(-2, 4, -1, 2, 0),name='nanofarads',ascii_symbol='nF',symbol='nF') -picofarads = NamedUnit(1e-12, Dimensions(-2, 4, -1, 2, 0),name='picofarads',ascii_symbol='pF',symbol='pF') -femtofarads = NamedUnit(1e-15, Dimensions(-2, 4, -1, 2, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') -attofarads = NamedUnit(1e-18, Dimensions(-2, 4, -1, 2, 0),name='attofarads',ascii_symbol='aF',symbol='aF') +exafarads = NamedUnit(1e+18, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='exafarads',ascii_symbol='EF',symbol='EF') +petafarads = NamedUnit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='petafarads',ascii_symbol='PF',symbol='PF') +terafarads = NamedUnit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='terafarads',ascii_symbol='TF',symbol='TF') +gigafarads = NamedUnit(1000000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='gigafarads',ascii_symbol='GF',symbol='GF') +megafarads = NamedUnit(1000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='megafarads',ascii_symbol='MF',symbol='MF') +kilofarads = NamedUnit(1000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='kilofarads',ascii_symbol='kF',symbol='kF') +millifarads = NamedUnit(0.001, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='millifarads',ascii_symbol='mF',symbol='mF') +microfarads = NamedUnit(1e-06, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='microfarads',ascii_symbol='uF',symbol='µF') +nanofarads = NamedUnit(1e-09, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='nanofarads',ascii_symbol='nF',symbol='nF') +picofarads = NamedUnit(1e-12, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='picofarads',ascii_symbol='pF',symbol='pF') +femtofarads = NamedUnit(1e-15, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') +attofarads = NamedUnit(1e-18, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='attofarads',ascii_symbol='aF',symbol='aF') siemens = NamedUnit(1, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='siemens',ascii_symbol='S',symbol='S') -exasiemens = NamedUnit(1e+18, Dimensions(-2, 3, -1, 2, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') -petasiemens = NamedUnit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') -terasiemens = NamedUnit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') -gigasiemens = NamedUnit(1000000000.0, Dimensions(-2, 3, -1, 2, 0),name='gigasiemens',ascii_symbol='GS',symbol='GS') -megasiemens = NamedUnit(1000000.0, Dimensions(-2, 3, -1, 2, 0),name='megasiemens',ascii_symbol='MS',symbol='MS') -kilosiemens = NamedUnit(1000.0, Dimensions(-2, 3, -1, 2, 0),name='kilosiemens',ascii_symbol='kS',symbol='kS') -millisiemens = NamedUnit(0.001, Dimensions(-2, 3, -1, 2, 0),name='millisiemens',ascii_symbol='mS',symbol='mS') -microsiemens = NamedUnit(1e-06, Dimensions(-2, 3, -1, 2, 0),name='microsiemens',ascii_symbol='uS',symbol='µS') -nanosiemens = NamedUnit(1e-09, Dimensions(-2, 3, -1, 2, 0),name='nanosiemens',ascii_symbol='nS',symbol='nS') -picosiemens = NamedUnit(1e-12, Dimensions(-2, 3, -1, 2, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') -femtosiemens = NamedUnit(1e-15, Dimensions(-2, 3, -1, 2, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') -attosiemens = NamedUnit(1e-18, Dimensions(-2, 3, -1, 2, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') +exasiemens = NamedUnit(1e+18, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') +petasiemens = NamedUnit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') +terasiemens = NamedUnit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') +gigasiemens = NamedUnit(1000000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='gigasiemens',ascii_symbol='GS',symbol='GS') +megasiemens = NamedUnit(1000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='megasiemens',ascii_symbol='MS',symbol='MS') +kilosiemens = NamedUnit(1000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='kilosiemens',ascii_symbol='kS',symbol='kS') +millisiemens = NamedUnit(0.001, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='millisiemens',ascii_symbol='mS',symbol='mS') +microsiemens = NamedUnit(1e-06, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='microsiemens',ascii_symbol='uS',symbol='µS') +nanosiemens = NamedUnit(1e-09, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='nanosiemens',ascii_symbol='nS',symbol='nS') +picosiemens = NamedUnit(1e-12, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') +femtosiemens = NamedUnit(1e-15, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') +attosiemens = NamedUnit(1e-18, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') webers = NamedUnit(1, Dimensions(2, -2, 1, -1, 0, 0, 0),name='webers',ascii_symbol='Wb',symbol='Wb') -exawebers = NamedUnit(1e+18, Dimensions(2, -2, 1, -1, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') -petawebers = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') -terawebers = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -1, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') -gigawebers = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -1, 0),name='gigawebers',ascii_symbol='GWb',symbol='GWb') -megawebers = NamedUnit(1000000.0, Dimensions(2, -2, 1, -1, 0),name='megawebers',ascii_symbol='MWb',symbol='MWb') -kilowebers = NamedUnit(1000.0, Dimensions(2, -2, 1, -1, 0),name='kilowebers',ascii_symbol='kWb',symbol='kWb') -milliwebers = NamedUnit(0.001, Dimensions(2, -2, 1, -1, 0),name='milliwebers',ascii_symbol='mWb',symbol='mWb') -microwebers = NamedUnit(1e-06, Dimensions(2, -2, 1, -1, 0),name='microwebers',ascii_symbol='uWb',symbol='µWb') -nanowebers = NamedUnit(1e-09, Dimensions(2, -2, 1, -1, 0),name='nanowebers',ascii_symbol='nWb',symbol='nWb') -picowebers = NamedUnit(1e-12, Dimensions(2, -2, 1, -1, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') -femtowebers = NamedUnit(1e-15, Dimensions(2, -2, 1, -1, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') -attowebers = NamedUnit(1e-18, Dimensions(2, -2, 1, -1, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') +exawebers = NamedUnit(1e+18, Dimensions(2, -2, 1, -1, 0, 0, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') +petawebers = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') +terawebers = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') +gigawebers = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='gigawebers',ascii_symbol='GWb',symbol='GWb') +megawebers = NamedUnit(1000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='megawebers',ascii_symbol='MWb',symbol='MWb') +kilowebers = NamedUnit(1000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='kilowebers',ascii_symbol='kWb',symbol='kWb') +milliwebers = NamedUnit(0.001, Dimensions(2, -2, 1, -1, 0, 0, 0),name='milliwebers',ascii_symbol='mWb',symbol='mWb') +microwebers = NamedUnit(1e-06, Dimensions(2, -2, 1, -1, 0, 0, 0),name='microwebers',ascii_symbol='uWb',symbol='µWb') +nanowebers = NamedUnit(1e-09, Dimensions(2, -2, 1, -1, 0, 0, 0),name='nanowebers',ascii_symbol='nWb',symbol='nWb') +picowebers = NamedUnit(1e-12, Dimensions(2, -2, 1, -1, 0, 0, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') +femtowebers = NamedUnit(1e-15, Dimensions(2, -2, 1, -1, 0, 0, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') +attowebers = NamedUnit(1e-18, Dimensions(2, -2, 1, -1, 0, 0, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') tesla = NamedUnit(1, Dimensions(0, -2, 1, -1, 0, 0, 0),name='tesla',ascii_symbol='T',symbol='T') -exatesla = NamedUnit(1e+18, Dimensions(0, -2, 1, -1, 0),name='exatesla',ascii_symbol='ET',symbol='ET') -petatesla = NamedUnit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0),name='petatesla',ascii_symbol='PT',symbol='PT') -teratesla = NamedUnit(1000000000000.0, Dimensions(0, -2, 1, -1, 0),name='teratesla',ascii_symbol='TT',symbol='TT') -gigatesla = NamedUnit(1000000000.0, Dimensions(0, -2, 1, -1, 0),name='gigatesla',ascii_symbol='GT',symbol='GT') -megatesla = NamedUnit(1000000.0, Dimensions(0, -2, 1, -1, 0),name='megatesla',ascii_symbol='MT',symbol='MT') -kilotesla = NamedUnit(1000.0, Dimensions(0, -2, 1, -1, 0),name='kilotesla',ascii_symbol='kT',symbol='kT') -millitesla = NamedUnit(0.001, Dimensions(0, -2, 1, -1, 0),name='millitesla',ascii_symbol='mT',symbol='mT') -microtesla = NamedUnit(1e-06, Dimensions(0, -2, 1, -1, 0),name='microtesla',ascii_symbol='uT',symbol='µT') -nanotesla = NamedUnit(1e-09, Dimensions(0, -2, 1, -1, 0),name='nanotesla',ascii_symbol='nT',symbol='nT') -picotesla = NamedUnit(1e-12, Dimensions(0, -2, 1, -1, 0),name='picotesla',ascii_symbol='pT',symbol='pT') -femtotesla = NamedUnit(1e-15, Dimensions(0, -2, 1, -1, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') -attotesla = NamedUnit(1e-18, Dimensions(0, -2, 1, -1, 0),name='attotesla',ascii_symbol='aT',symbol='aT') +exatesla = NamedUnit(1e+18, Dimensions(0, -2, 1, -1, 0, 0, 0),name='exatesla',ascii_symbol='ET',symbol='ET') +petatesla = NamedUnit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='petatesla',ascii_symbol='PT',symbol='PT') +teratesla = NamedUnit(1000000000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='teratesla',ascii_symbol='TT',symbol='TT') +gigatesla = NamedUnit(1000000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='gigatesla',ascii_symbol='GT',symbol='GT') +megatesla = NamedUnit(1000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='megatesla',ascii_symbol='MT',symbol='MT') +kilotesla = NamedUnit(1000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='kilotesla',ascii_symbol='kT',symbol='kT') +millitesla = NamedUnit(0.001, Dimensions(0, -2, 1, -1, 0, 0, 0),name='millitesla',ascii_symbol='mT',symbol='mT') +microtesla = NamedUnit(1e-06, Dimensions(0, -2, 1, -1, 0, 0, 0),name='microtesla',ascii_symbol='uT',symbol='µT') +nanotesla = NamedUnit(1e-09, Dimensions(0, -2, 1, -1, 0, 0, 0),name='nanotesla',ascii_symbol='nT',symbol='nT') +picotesla = NamedUnit(1e-12, Dimensions(0, -2, 1, -1, 0, 0, 0),name='picotesla',ascii_symbol='pT',symbol='pT') +femtotesla = NamedUnit(1e-15, Dimensions(0, -2, 1, -1, 0, 0, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') +attotesla = NamedUnit(1e-18, Dimensions(0, -2, 1, -1, 0, 0, 0),name='attotesla',ascii_symbol='aT',symbol='aT') henry = NamedUnit(1, Dimensions(2, -2, 1, -2, 0, 0, 0),name='henry',ascii_symbol='H',symbol='H') -exahenry = NamedUnit(1e+18, Dimensions(2, -2, 1, -2, 0),name='exahenry',ascii_symbol='EH',symbol='EH') -petahenry = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0),name='petahenry',ascii_symbol='PH',symbol='PH') -terahenry = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -2, 0),name='terahenry',ascii_symbol='TH',symbol='TH') -gigahenry = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -2, 0),name='gigahenry',ascii_symbol='GH',symbol='GH') -megahenry = NamedUnit(1000000.0, Dimensions(2, -2, 1, -2, 0),name='megahenry',ascii_symbol='MH',symbol='MH') -kilohenry = NamedUnit(1000.0, Dimensions(2, -2, 1, -2, 0),name='kilohenry',ascii_symbol='kH',symbol='kH') -millihenry = NamedUnit(0.001, Dimensions(2, -2, 1, -2, 0),name='millihenry',ascii_symbol='mH',symbol='mH') -microhenry = NamedUnit(1e-06, Dimensions(2, -2, 1, -2, 0),name='microhenry',ascii_symbol='uH',symbol='µH') -nanohenry = NamedUnit(1e-09, Dimensions(2, -2, 1, -2, 0),name='nanohenry',ascii_symbol='nH',symbol='nH') -picohenry = NamedUnit(1e-12, Dimensions(2, -2, 1, -2, 0),name='picohenry',ascii_symbol='pH',symbol='pH') -femtohenry = NamedUnit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') -attohenry = NamedUnit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') +exahenry = NamedUnit(1e+18, Dimensions(2, -2, 1, -2, 0, 0, 0),name='exahenry',ascii_symbol='EH',symbol='EH') +petahenry = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='petahenry',ascii_symbol='PH',symbol='PH') +terahenry = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='terahenry',ascii_symbol='TH',symbol='TH') +gigahenry = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='gigahenry',ascii_symbol='GH',symbol='GH') +megahenry = NamedUnit(1000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='megahenry',ascii_symbol='MH',symbol='MH') +kilohenry = NamedUnit(1000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='kilohenry',ascii_symbol='kH',symbol='kH') +millihenry = NamedUnit(0.001, Dimensions(2, -2, 1, -2, 0, 0, 0),name='millihenry',ascii_symbol='mH',symbol='mH') +microhenry = NamedUnit(1e-06, Dimensions(2, -2, 1, -2, 0, 0, 0),name='microhenry',ascii_symbol='uH',symbol='µH') +nanohenry = NamedUnit(1e-09, Dimensions(2, -2, 1, -2, 0, 0, 0),name='nanohenry',ascii_symbol='nH',symbol='nH') +picohenry = NamedUnit(1e-12, Dimensions(2, -2, 1, -2, 0, 0, 0),name='picohenry',ascii_symbol='pH',symbol='pH') +femtohenry = NamedUnit(1e-15, Dimensions(2, -2, 1, -2, 0, 0, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') +attohenry = NamedUnit(1e-18, Dimensions(2, -2, 1, -2, 0, 0, 0),name='attohenry',ascii_symbol='aH',symbol='aH') angstroms = NamedUnit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') minutes = NamedUnit(60, Dimensions(0, 1, 0, 0, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') hours = NamedUnit(360, Dimensions(0, 1, 0, 0, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') @@ -669,26 +669,26 @@ def __init__(self, name: str, units: list[NamedUnit]): stradians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') litres = NamedUnit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') electronvolts = NamedUnit(1.602176634e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') -exaelectronvolts = NamedUnit(0.1602176634, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') -petaelectronvolts = NamedUnit(0.0001602176634, Dimensions(2, -2, 1, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') -teraelectronvolts = NamedUnit(1.602176634e-07, Dimensions(2, -2, 1, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') -gigaelectronvolts = NamedUnit(1.6021766339999998e-10, Dimensions(2, -2, 1, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') -megaelectronvolts = NamedUnit(1.6021766339999998e-13, Dimensions(2, -2, 1, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') -kiloelectronvolts = NamedUnit(1.602176634e-16, Dimensions(2, -2, 1, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') -millielectronvolts = NamedUnit(1.6021766339999998e-22, Dimensions(2, -2, 1, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') -microelectronvolts = NamedUnit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') -nanoelectronvolts = NamedUnit(1.602176634e-28, Dimensions(2, -2, 1, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') -picoelectronvolts = NamedUnit(1.6021766339999998e-31, Dimensions(2, -2, 1, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') -femtoelectronvolts = NamedUnit(1.602176634e-34, Dimensions(2, -2, 1, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') -attoelectronvolts = NamedUnit(1.602176634e-37, Dimensions(2, -2, 1, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') +exaelectronvolts = NamedUnit(0.1602176634, Dimensions(2, -2, 1, 0, 0, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') +petaelectronvolts = NamedUnit(0.0001602176634, Dimensions(2, -2, 1, 0, 0, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') +teraelectronvolts = NamedUnit(1.602176634e-07, Dimensions(2, -2, 1, 0, 0, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') +gigaelectronvolts = NamedUnit(1.6021766339999998e-10, Dimensions(2, -2, 1, 0, 0, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') +megaelectronvolts = NamedUnit(1.6021766339999998e-13, Dimensions(2, -2, 1, 0, 0, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') +kiloelectronvolts = NamedUnit(1.602176634e-16, Dimensions(2, -2, 1, 0, 0, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') +millielectronvolts = NamedUnit(1.6021766339999998e-22, Dimensions(2, -2, 1, 0, 0, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') +microelectronvolts = NamedUnit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') +nanoelectronvolts = NamedUnit(1.602176634e-28, Dimensions(2, -2, 1, 0, 0, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') +picoelectronvolts = NamedUnit(1.6021766339999998e-31, Dimensions(2, -2, 1, 0, 0, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') +femtoelectronvolts = NamedUnit(1.602176634e-34, Dimensions(2, -2, 1, 0, 0, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') +attoelectronvolts = NamedUnit(1.602176634e-37, Dimensions(2, -2, 1, 0, 0, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') atomic_mass_units = NamedUnit(1.660538921e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') moles = NamedUnit(6.02214076e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') -millimoles = NamedUnit(6.02214076e+20, Dimensions(0, 0, 0, 0, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') -micromoles = NamedUnit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') -nanomoles = NamedUnit(602214076000000.0, Dimensions(0, 0, 0, 0, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') -picomoles = NamedUnit(602214076000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') -femtomoles = NamedUnit(602214076.0, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') -attomoles = NamedUnit(602214.076, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') +millimoles = NamedUnit(6.02214076e+20, Dimensions(0, 0, 0, 0, 0, 1, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') +micromoles = NamedUnit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0, 1, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') +nanomoles = NamedUnit(602214076000000.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') +picomoles = NamedUnit(602214076000.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') +femtomoles = NamedUnit(602214076.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') +attomoles = NamedUnit(602214.076, Dimensions(0, 0, 0, 0, 0, 1, 0),name='attomoles',ascii_symbol='amol',symbol='amol') kg_force = NamedUnit(9.80665, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kg_force',ascii_symbol='kgForce',symbol='kgForce') degrees_celsius = NamedUnit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') miles = NamedUnit(1609.344, Dimensions(1, 0, 0, 0, 0, 0, 0),name='miles',ascii_symbol='miles',symbol='miles') From 182d713f2248b8962ee42df86a61ca2fa6bc21e8 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 2 Oct 2024 15:09:03 +0100 Subject: [PATCH 108/675] Unit name fixes --- sasdata/quantities/_build_tables.py | 17 +- sasdata/quantities/units.py | 900 ++++++++++++++-------------- 2 files changed, 463 insertions(+), 454 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 9c2f66082..6f4f8df17 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -237,18 +237,21 @@ def format_name(name: str): speed_dimensions = Dimensions(length=1, time=-1) accel_dimensions = Dimensions(length=1, time=-2) + length_special = length_special_symbol if length_special_symbol is not None else length_symbol + time_special = time_special_symbol if time_special_symbol is not None else time_symbol + fid.write(f"{speed_name} " f"= NamedUnit({length_scale / time_scale}, " f"Dimensions(length=1, time=-1), " f"name='{speed_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}', " - f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") + f"symbol='{length_special}{time_special}⁻¹')\n") fid.write(f"{accel_name} = NamedUnit({length_scale / time_scale**2}, " f"Dimensions(length=1, time=-2), " f"name='{accel_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}^2', " - f"symbol='{length_special_symbol}{time_special_symbol}⁻²')\n") + f"symbol='{length_special}{time_special}⁻²')\n") unit_types[hash(speed_dimensions)].append(speed_name) unit_types[hash(accel_dimensions)].append(accel_name) @@ -261,12 +264,15 @@ def format_name(name: str): dimensions = Dimensions(length=-3, mass=1) + mass_special = mass_symbol if mass_special_symbol is None else mass_special_symbol + length_special = length_symbol if length_special_symbol is None else length_special_symbol + fid.write(f"{name} " f"= NamedUnit({mass_scale / length_scale**3}, " f"Dimensions(length=-3, mass=1), " f"name='{name}', " f"ascii_symbol='{mass_symbol} {length_symbol}^-3', " - f"symbol='{mass_special_symbol}{length_special_symbol}⁻³')\n") + f"symbol='{mass_special}{length_special}⁻³')\n") unit_types[hash(dimensions)].append(name) @@ -278,12 +284,15 @@ def format_name(name: str): dimensions = Dimensions(length=-3, moles_hint=1) + length_special = length_symbol if length_special_symbol is None else length_special_symbol + amount_special = amount_symbol if amount_special_symbol is None else amount_special_symbol + fid.write(f"{name} " f"= NamedUnit({amount_scale / length_scale**3}, " f"Dimensions(length=-3, moles_hint=1), " f"name='{name}', " f"ascii_symbol='{amount_symbol} {length_symbol}^-3', " - f"symbol='{amount_special_symbol}{length_special_symbol}⁻³')\n") + f"symbol='{amount_special}{length_special}⁻³')\n") unit_types[hash(dimensions)].append(name) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 2bec4bc7e..b02646aa3 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -801,30 +801,30 @@ def __init__(self, name: str, units: list[NamedUnit]): per_inch = NamedUnit(39.37007874015748, Dimensions(length=-1), name='per_inch', ascii_symbol='in^-1', symbol='in⁻¹') per_square_inch = NamedUnit(1550.0031000062002, Dimensions(length=-2), name='per_square_inch', ascii_symbol='in^-2', symbol='in⁻²') per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3), name='per_cubic_inch', ascii_symbol='in^-3', symbol='in⁻³') -meters_per_second = NamedUnit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') -meters_per_square_second = NamedUnit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') -meters_per_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') -meters_per_square_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') -meters_per_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') -meters_per_square_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') -meters_per_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') -meters_per_square_nanosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') -meters_per_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') -meters_per_square_picosecond = NamedUnit(1.0000000000000001e+24, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') -meters_per_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') -meters_per_square_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') -meters_per_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') -meters_per_square_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') -meters_per_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') -meters_per_square_minute = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') -meters_per_hour = NamedUnit(0.002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') -meters_per_square_hour = NamedUnit(7.71604938271605e-06, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') -meters_per_day = NamedUnit(0.00011574074074074075, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') -meters_per_square_day = NamedUnit(1.3395919067215363e-08, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') -meters_per_year = NamedUnit(3.168873850681143e-07, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') -meters_per_square_year = NamedUnit(1.0041761481530735e-13, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') -exameters_per_second = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') -exameters_per_square_second = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') +meters_per_second = NamedUnit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='ms⁻¹') +meters_per_square_second = NamedUnit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='ms⁻²') +meters_per_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='mms⁻¹') +meters_per_square_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='mms⁻²') +meters_per_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='mµs⁻¹') +meters_per_square_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='mµs⁻²') +meters_per_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='mns⁻¹') +meters_per_square_nanosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='mns⁻²') +meters_per_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='mps⁻¹') +meters_per_square_picosecond = NamedUnit(1.0000000000000001e+24, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='mps⁻²') +meters_per_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='mfs⁻¹') +meters_per_square_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='mfs⁻²') +meters_per_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='mas⁻¹') +meters_per_square_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='mas⁻²') +meters_per_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='mmin⁻¹') +meters_per_square_minute = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='mmin⁻²') +meters_per_hour = NamedUnit(0.002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='mh⁻¹') +meters_per_square_hour = NamedUnit(7.71604938271605e-06, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='mh⁻²') +meters_per_day = NamedUnit(0.00011574074074074075, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='md⁻¹') +meters_per_square_day = NamedUnit(1.3395919067215363e-08, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='md⁻²') +meters_per_year = NamedUnit(3.168873850681143e-07, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='my⁻¹') +meters_per_square_year = NamedUnit(1.0041761481530735e-13, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='my⁻²') +exameters_per_second = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='Ems⁻¹') +exameters_per_square_second = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='Ems⁻²') exameters_per_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') exameters_per_square_millisecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') exameters_per_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') @@ -837,16 +837,16 @@ def __init__(self, name: str, units: list[NamedUnit]): exameters_per_square_femtosecond = NamedUnit(9.999999999999999e+47, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') exameters_per_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-1), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') exameters_per_square_attosecond = NamedUnit(9.999999999999999e+53, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') -exameters_per_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') -exameters_per_square_minute = NamedUnit(277777777777777.78, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') -exameters_per_hour = NamedUnit(2777777777777778.0, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') -exameters_per_square_hour = NamedUnit(7716049382716.05, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') -exameters_per_day = NamedUnit(115740740740740.73, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') -exameters_per_square_day = NamedUnit(13395919067.215364, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') -exameters_per_year = NamedUnit(316887385068.1143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') -exameters_per_square_year = NamedUnit(100417.61481530734, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') -petameters_per_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') -petameters_per_square_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') +exameters_per_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='Emmin⁻¹') +exameters_per_square_minute = NamedUnit(277777777777777.78, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='Emmin⁻²') +exameters_per_hour = NamedUnit(2777777777777778.0, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='Emh⁻¹') +exameters_per_square_hour = NamedUnit(7716049382716.05, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='Emh⁻²') +exameters_per_day = NamedUnit(115740740740740.73, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='Emd⁻¹') +exameters_per_square_day = NamedUnit(13395919067.215364, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='Emd⁻²') +exameters_per_year = NamedUnit(316887385068.1143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='Emy⁻¹') +exameters_per_square_year = NamedUnit(100417.61481530734, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='Emy⁻²') +petameters_per_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='Pms⁻¹') +petameters_per_square_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='Pms⁻²') petameters_per_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') petameters_per_square_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') petameters_per_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') @@ -859,16 +859,16 @@ def __init__(self, name: str, units: list[NamedUnit]): petameters_per_square_femtosecond = NamedUnit(1e+45, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') petameters_per_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') petameters_per_square_attosecond = NamedUnit(9.999999999999998e+50, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') -petameters_per_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') -petameters_per_square_minute = NamedUnit(277777777777.7778, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') -petameters_per_hour = NamedUnit(2777777777777.778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') -petameters_per_square_hour = NamedUnit(7716049382.716049, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') -petameters_per_day = NamedUnit(115740740740.74074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') -petameters_per_square_day = NamedUnit(13395919.067215364, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') -petameters_per_year = NamedUnit(316887385.0681143, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') -petameters_per_square_year = NamedUnit(100.41761481530735, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') -terameters_per_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') -terameters_per_square_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') +petameters_per_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='Pmmin⁻¹') +petameters_per_square_minute = NamedUnit(277777777777.7778, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='Pmmin⁻²') +petameters_per_hour = NamedUnit(2777777777777.778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='Pmh⁻¹') +petameters_per_square_hour = NamedUnit(7716049382.716049, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='Pmh⁻²') +petameters_per_day = NamedUnit(115740740740.74074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='Pmd⁻¹') +petameters_per_square_day = NamedUnit(13395919.067215364, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='Pmd⁻²') +petameters_per_year = NamedUnit(316887385.0681143, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='Pmy⁻¹') +petameters_per_square_year = NamedUnit(100.41761481530735, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='Pmy⁻²') +terameters_per_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='Tms⁻¹') +terameters_per_square_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='Tms⁻²') terameters_per_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') terameters_per_square_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') terameters_per_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') @@ -881,16 +881,16 @@ def __init__(self, name: str, units: list[NamedUnit]): terameters_per_square_femtosecond = NamedUnit(9.999999999999999e+41, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') terameters_per_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') terameters_per_square_attosecond = NamedUnit(9.999999999999999e+47, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') -terameters_per_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') -terameters_per_square_minute = NamedUnit(277777777.7777778, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') -terameters_per_hour = NamedUnit(2777777777.7777777, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') -terameters_per_square_hour = NamedUnit(7716049.382716049, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') -terameters_per_day = NamedUnit(115740740.74074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') -terameters_per_square_day = NamedUnit(13395.919067215364, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') -terameters_per_year = NamedUnit(316887.38506811426, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') -terameters_per_square_year = NamedUnit(0.10041761481530735, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') -gigameters_per_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') -gigameters_per_square_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') +terameters_per_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='Tmmin⁻¹') +terameters_per_square_minute = NamedUnit(277777777.7777778, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='Tmmin⁻²') +terameters_per_hour = NamedUnit(2777777777.7777777, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='Tmh⁻¹') +terameters_per_square_hour = NamedUnit(7716049.382716049, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='Tmh⁻²') +terameters_per_day = NamedUnit(115740740.74074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='Tmd⁻¹') +terameters_per_square_day = NamedUnit(13395.919067215364, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='Tmd⁻²') +terameters_per_year = NamedUnit(316887.38506811426, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='Tmy⁻¹') +terameters_per_square_year = NamedUnit(0.10041761481530735, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='Tmy⁻²') +gigameters_per_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='Gms⁻¹') +gigameters_per_square_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='Gms⁻²') gigameters_per_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') gigameters_per_square_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') gigameters_per_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') @@ -903,16 +903,16 @@ def __init__(self, name: str, units: list[NamedUnit]): gigameters_per_square_femtosecond = NamedUnit(1e+39, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') gigameters_per_attosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') gigameters_per_square_attosecond = NamedUnit(1e+45, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') -gigameters_per_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') -gigameters_per_square_minute = NamedUnit(277777.77777777775, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') -gigameters_per_hour = NamedUnit(2777777.777777778, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') -gigameters_per_square_hour = NamedUnit(7716.049382716049, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') -gigameters_per_day = NamedUnit(115740.74074074074, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') -gigameters_per_square_day = NamedUnit(13.395919067215363, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') -gigameters_per_year = NamedUnit(316.88738506811427, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') -gigameters_per_square_year = NamedUnit(0.00010041761481530735, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') -megameters_per_second = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') -megameters_per_square_second = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') +gigameters_per_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='Gmmin⁻¹') +gigameters_per_square_minute = NamedUnit(277777.77777777775, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='Gmmin⁻²') +gigameters_per_hour = NamedUnit(2777777.777777778, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='Gmh⁻¹') +gigameters_per_square_hour = NamedUnit(7716.049382716049, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='Gmh⁻²') +gigameters_per_day = NamedUnit(115740.74074074074, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='Gmd⁻¹') +gigameters_per_square_day = NamedUnit(13.395919067215363, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='Gmd⁻²') +gigameters_per_year = NamedUnit(316.88738506811427, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='Gmy⁻¹') +gigameters_per_square_year = NamedUnit(0.00010041761481530735, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='Gmy⁻²') +megameters_per_second = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='Mms⁻¹') +megameters_per_square_second = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='Mms⁻²') megameters_per_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') megameters_per_square_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') megameters_per_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') @@ -925,16 +925,16 @@ def __init__(self, name: str, units: list[NamedUnit]): megameters_per_square_femtosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') megameters_per_attosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') megameters_per_square_attosecond = NamedUnit(9.999999999999999e+41, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') -megameters_per_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') -megameters_per_square_minute = NamedUnit(277.77777777777777, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') -megameters_per_hour = NamedUnit(2777.777777777778, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') -megameters_per_square_hour = NamedUnit(7.716049382716049, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') -megameters_per_day = NamedUnit(115.74074074074075, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') -megameters_per_square_day = NamedUnit(0.013395919067215363, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') -megameters_per_year = NamedUnit(0.3168873850681143, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') -megameters_per_square_year = NamedUnit(1.0041761481530735e-07, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') -kilometers_per_second = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') -kilometers_per_square_second = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') +megameters_per_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='Mmmin⁻¹') +megameters_per_square_minute = NamedUnit(277.77777777777777, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='Mmmin⁻²') +megameters_per_hour = NamedUnit(2777.777777777778, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='Mmh⁻¹') +megameters_per_square_hour = NamedUnit(7.716049382716049, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='Mmh⁻²') +megameters_per_day = NamedUnit(115.74074074074075, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='Mmd⁻¹') +megameters_per_square_day = NamedUnit(0.013395919067215363, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='Mmd⁻²') +megameters_per_year = NamedUnit(0.3168873850681143, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='Mmy⁻¹') +megameters_per_square_year = NamedUnit(1.0041761481530735e-07, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='Mmy⁻²') +kilometers_per_second = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kms⁻¹') +kilometers_per_square_second = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kms⁻²') kilometers_per_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') kilometers_per_square_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') kilometers_per_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') @@ -947,16 +947,16 @@ def __init__(self, name: str, units: list[NamedUnit]): kilometers_per_square_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') kilometers_per_attosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') kilometers_per_square_attosecond = NamedUnit(1e+39, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') -kilometers_per_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') -kilometers_per_square_minute = NamedUnit(0.2777777777777778, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') -kilometers_per_hour = NamedUnit(2.7777777777777777, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') -kilometers_per_square_hour = NamedUnit(0.007716049382716049, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') -kilometers_per_day = NamedUnit(0.11574074074074074, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') -kilometers_per_square_day = NamedUnit(1.3395919067215363e-05, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') -kilometers_per_year = NamedUnit(0.0003168873850681143, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') -kilometers_per_square_year = NamedUnit(1.0041761481530735e-10, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') -millimeters_per_second = NamedUnit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') -millimeters_per_square_second = NamedUnit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') +kilometers_per_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmmin⁻¹') +kilometers_per_square_minute = NamedUnit(0.2777777777777778, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmmin⁻²') +kilometers_per_hour = NamedUnit(2.7777777777777777, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmh⁻¹') +kilometers_per_square_hour = NamedUnit(0.007716049382716049, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmh⁻²') +kilometers_per_day = NamedUnit(0.11574074074074074, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmd⁻¹') +kilometers_per_square_day = NamedUnit(1.3395919067215363e-05, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmd⁻²') +kilometers_per_year = NamedUnit(0.0003168873850681143, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmy⁻¹') +kilometers_per_square_year = NamedUnit(1.0041761481530735e-10, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmy⁻²') +millimeters_per_second = NamedUnit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mms⁻¹') +millimeters_per_square_second = NamedUnit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mms⁻²') millimeters_per_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') millimeters_per_square_millisecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') millimeters_per_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') @@ -969,16 +969,16 @@ def __init__(self, name: str, units: list[NamedUnit]): millimeters_per_square_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') millimeters_per_attosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') millimeters_per_square_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') -millimeters_per_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') -millimeters_per_square_minute = NamedUnit(2.7777777777777776e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') -millimeters_per_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') -millimeters_per_square_hour = NamedUnit(7.71604938271605e-09, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') -millimeters_per_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') -millimeters_per_square_day = NamedUnit(1.3395919067215364e-11, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') -millimeters_per_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') -millimeters_per_square_year = NamedUnit(1.0041761481530735e-16, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') -micrometers_per_second = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') -micrometers_per_square_second = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') +millimeters_per_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmmin⁻¹') +millimeters_per_square_minute = NamedUnit(2.7777777777777776e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmmin⁻²') +millimeters_per_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmh⁻¹') +millimeters_per_square_hour = NamedUnit(7.71604938271605e-09, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmh⁻²') +millimeters_per_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmd⁻¹') +millimeters_per_square_day = NamedUnit(1.3395919067215364e-11, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmd⁻²') +millimeters_per_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmy⁻¹') +millimeters_per_square_year = NamedUnit(1.0041761481530735e-16, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmy⁻²') +micrometers_per_second = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µms⁻¹') +micrometers_per_square_second = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µms⁻²') micrometers_per_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') micrometers_per_square_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') micrometers_per_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') @@ -991,16 +991,16 @@ def __init__(self, name: str, units: list[NamedUnit]): micrometers_per_square_femtosecond = NamedUnit(9.999999999999998e+23, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') micrometers_per_attosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') micrometers_per_square_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') -micrometers_per_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') -micrometers_per_square_minute = NamedUnit(2.7777777777777777e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') -micrometers_per_hour = NamedUnit(2.7777777777777776e-09, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') -micrometers_per_square_hour = NamedUnit(7.716049382716049e-12, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') -micrometers_per_day = NamedUnit(1.1574074074074074e-10, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') -micrometers_per_square_day = NamedUnit(1.3395919067215363e-14, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') -micrometers_per_year = NamedUnit(3.168873850681143e-13, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') -micrometers_per_square_year = NamedUnit(1.0041761481530734e-19, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') -nanometers_per_second = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') -nanometers_per_square_second = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') +micrometers_per_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmmin⁻¹') +micrometers_per_square_minute = NamedUnit(2.7777777777777777e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmmin⁻²') +micrometers_per_hour = NamedUnit(2.7777777777777776e-09, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmh⁻¹') +micrometers_per_square_hour = NamedUnit(7.716049382716049e-12, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmh⁻²') +micrometers_per_day = NamedUnit(1.1574074074074074e-10, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmd⁻¹') +micrometers_per_square_day = NamedUnit(1.3395919067215363e-14, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmd⁻²') +micrometers_per_year = NamedUnit(3.168873850681143e-13, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmy⁻¹') +micrometers_per_square_year = NamedUnit(1.0041761481530734e-19, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmy⁻²') +nanometers_per_second = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nms⁻¹') +nanometers_per_square_second = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nms⁻²') nanometers_per_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') nanometers_per_square_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') nanometers_per_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') @@ -1013,16 +1013,16 @@ def __init__(self, name: str, units: list[NamedUnit]): nanometers_per_square_femtosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') nanometers_per_attosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') nanometers_per_square_attosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') -nanometers_per_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') -nanometers_per_square_minute = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') -nanometers_per_hour = NamedUnit(2.777777777777778e-12, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') -nanometers_per_square_hour = NamedUnit(7.71604938271605e-15, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') -nanometers_per_day = NamedUnit(1.1574074074074076e-13, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') -nanometers_per_square_day = NamedUnit(1.3395919067215365e-17, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') -nanometers_per_year = NamedUnit(3.1688738506811433e-16, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') -nanometers_per_square_year = NamedUnit(1.0041761481530736e-22, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') -picometers_per_second = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') -picometers_per_square_second = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') +nanometers_per_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmmin⁻¹') +nanometers_per_square_minute = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmmin⁻²') +nanometers_per_hour = NamedUnit(2.777777777777778e-12, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmh⁻¹') +nanometers_per_square_hour = NamedUnit(7.71604938271605e-15, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmh⁻²') +nanometers_per_day = NamedUnit(1.1574074074074076e-13, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmd⁻¹') +nanometers_per_square_day = NamedUnit(1.3395919067215365e-17, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmd⁻²') +nanometers_per_year = NamedUnit(3.1688738506811433e-16, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmy⁻¹') +nanometers_per_square_year = NamedUnit(1.0041761481530736e-22, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmy⁻²') +picometers_per_second = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pms⁻¹') +picometers_per_square_second = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pms⁻²') picometers_per_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') picometers_per_square_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') picometers_per_microsecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') @@ -1035,16 +1035,16 @@ def __init__(self, name: str, units: list[NamedUnit]): picometers_per_square_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') picometers_per_attosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-1), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') picometers_per_square_attosecond = NamedUnit(9.999999999999998e+23, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') -picometers_per_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') -picometers_per_square_minute = NamedUnit(2.7777777777777775e-16, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') -picometers_per_hour = NamedUnit(2.7777777777777776e-15, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') -picometers_per_square_hour = NamedUnit(7.716049382716049e-18, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') -picometers_per_day = NamedUnit(1.1574074074074073e-16, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') -picometers_per_square_day = NamedUnit(1.3395919067215364e-20, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') -picometers_per_year = NamedUnit(3.168873850681143e-19, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') -picometers_per_square_year = NamedUnit(1.0041761481530734e-25, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') -femtometers_per_second = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') -femtometers_per_square_second = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') +picometers_per_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmmin⁻¹') +picometers_per_square_minute = NamedUnit(2.7777777777777775e-16, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmmin⁻²') +picometers_per_hour = NamedUnit(2.7777777777777776e-15, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmh⁻¹') +picometers_per_square_hour = NamedUnit(7.716049382716049e-18, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmh⁻²') +picometers_per_day = NamedUnit(1.1574074074074073e-16, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmd⁻¹') +picometers_per_square_day = NamedUnit(1.3395919067215364e-20, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmd⁻²') +picometers_per_year = NamedUnit(3.168873850681143e-19, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmy⁻¹') +picometers_per_square_year = NamedUnit(1.0041761481530734e-25, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmy⁻²') +femtometers_per_second = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fms⁻¹') +femtometers_per_square_second = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fms⁻²') femtometers_per_millisecond = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') femtometers_per_square_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') femtometers_per_microsecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') @@ -1057,16 +1057,16 @@ def __init__(self, name: str, units: list[NamedUnit]): femtometers_per_square_femtosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') femtometers_per_attosecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') femtometers_per_square_attosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') -femtometers_per_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') -femtometers_per_square_minute = NamedUnit(2.777777777777778e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') -femtometers_per_hour = NamedUnit(2.777777777777778e-18, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') -femtometers_per_square_hour = NamedUnit(7.71604938271605e-21, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') -femtometers_per_day = NamedUnit(1.1574074074074075e-19, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') -femtometers_per_square_day = NamedUnit(1.3395919067215363e-23, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') -femtometers_per_year = NamedUnit(3.168873850681143e-22, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') -femtometers_per_square_year = NamedUnit(1.0041761481530735e-28, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') -attometers_per_second = NamedUnit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') -attometers_per_square_second = NamedUnit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') +femtometers_per_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmmin⁻¹') +femtometers_per_square_minute = NamedUnit(2.777777777777778e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmmin⁻²') +femtometers_per_hour = NamedUnit(2.777777777777778e-18, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmh⁻¹') +femtometers_per_square_hour = NamedUnit(7.71604938271605e-21, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmh⁻²') +femtometers_per_day = NamedUnit(1.1574074074074075e-19, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmd⁻¹') +femtometers_per_square_day = NamedUnit(1.3395919067215363e-23, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmd⁻²') +femtometers_per_year = NamedUnit(3.168873850681143e-22, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmy⁻¹') +femtometers_per_square_year = NamedUnit(1.0041761481530735e-28, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmy⁻²') +attometers_per_second = NamedUnit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='ams⁻¹') +attometers_per_square_second = NamedUnit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='ams⁻²') attometers_per_millisecond = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') attometers_per_square_millisecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') attometers_per_microsecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-1), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') @@ -1079,16 +1079,16 @@ def __init__(self, name: str, units: list[NamedUnit]): attometers_per_square_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') attometers_per_attosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') attometers_per_square_attosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') -attometers_per_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') -attometers_per_square_minute = NamedUnit(2.777777777777778e-22, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') -attometers_per_hour = NamedUnit(2.7777777777777778e-21, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') -attometers_per_square_hour = NamedUnit(7.71604938271605e-24, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') -attometers_per_day = NamedUnit(1.1574074074074074e-22, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') -attometers_per_square_day = NamedUnit(1.3395919067215363e-26, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') -attometers_per_year = NamedUnit(3.1688738506811433e-25, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') -attometers_per_square_year = NamedUnit(1.0041761481530734e-31, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') -decimeters_per_second = NamedUnit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dmNone⁻¹') -decimeters_per_square_second = NamedUnit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dmNone⁻²') +attometers_per_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='ammin⁻¹') +attometers_per_square_minute = NamedUnit(2.777777777777778e-22, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='ammin⁻²') +attometers_per_hour = NamedUnit(2.7777777777777778e-21, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amh⁻¹') +attometers_per_square_hour = NamedUnit(7.71604938271605e-24, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amh⁻²') +attometers_per_day = NamedUnit(1.1574074074074074e-22, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amd⁻¹') +attometers_per_square_day = NamedUnit(1.3395919067215363e-26, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amd⁻²') +attometers_per_year = NamedUnit(3.1688738506811433e-25, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amy⁻¹') +attometers_per_square_year = NamedUnit(1.0041761481530734e-31, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amy⁻²') +decimeters_per_second = NamedUnit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dms⁻¹') +decimeters_per_square_second = NamedUnit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dms⁻²') decimeters_per_millisecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='decimeters_per_millisecond', ascii_symbol='dm/ms', symbol='dmms⁻¹') decimeters_per_square_millisecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') decimeters_per_microsecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-1), name='decimeters_per_microsecond', ascii_symbol='dm/us', symbol='dmµs⁻¹') @@ -1101,16 +1101,16 @@ def __init__(self, name: str, units: list[NamedUnit]): decimeters_per_square_femtosecond = NamedUnit(1e+29, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') decimeters_per_attosecond = NamedUnit(1e+17, Dimensions(length=1, time=-1), name='decimeters_per_attosecond', ascii_symbol='dm/as', symbol='dmas⁻¹') decimeters_per_square_attosecond = NamedUnit(1e+35, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') -decimeters_per_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmNone⁻¹') -decimeters_per_square_minute = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmNone⁻²') -decimeters_per_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmNone⁻¹') -decimeters_per_square_hour = NamedUnit(7.71604938271605e-07, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmNone⁻²') -decimeters_per_day = NamedUnit(1.1574074074074075e-05, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmNone⁻¹') -decimeters_per_square_day = NamedUnit(1.3395919067215364e-09, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmNone⁻²') -decimeters_per_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmNone⁻¹') -decimeters_per_square_year = NamedUnit(1.0041761481530735e-14, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmNone⁻²') -centimeters_per_second = NamedUnit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cmNone⁻¹') -centimeters_per_square_second = NamedUnit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cmNone⁻²') +decimeters_per_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmmin⁻¹') +decimeters_per_square_minute = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmmin⁻²') +decimeters_per_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmh⁻¹') +decimeters_per_square_hour = NamedUnit(7.71604938271605e-07, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmh⁻²') +decimeters_per_day = NamedUnit(1.1574074074074075e-05, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmd⁻¹') +decimeters_per_square_day = NamedUnit(1.3395919067215364e-09, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmd⁻²') +decimeters_per_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmy⁻¹') +decimeters_per_square_year = NamedUnit(1.0041761481530735e-14, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmy⁻²') +centimeters_per_second = NamedUnit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cms⁻¹') +centimeters_per_square_second = NamedUnit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cms⁻²') centimeters_per_millisecond = NamedUnit(10.0, Dimensions(length=1, time=-1), name='centimeters_per_millisecond', ascii_symbol='cm/ms', symbol='cmms⁻¹') centimeters_per_square_millisecond = NamedUnit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') centimeters_per_microsecond = NamedUnit(10000.0, Dimensions(length=1, time=-1), name='centimeters_per_microsecond', ascii_symbol='cm/us', symbol='cmµs⁻¹') @@ -1123,16 +1123,16 @@ def __init__(self, name: str, units: list[NamedUnit]): centimeters_per_square_femtosecond = NamedUnit(1e+28, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') centimeters_per_attosecond = NamedUnit(1e+16, Dimensions(length=1, time=-1), name='centimeters_per_attosecond', ascii_symbol='cm/as', symbol='cmas⁻¹') centimeters_per_square_attosecond = NamedUnit(1e+34, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') -centimeters_per_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmNone⁻¹') -centimeters_per_square_minute = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmNone⁻²') -centimeters_per_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmNone⁻¹') -centimeters_per_square_hour = NamedUnit(7.71604938271605e-08, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmNone⁻²') -centimeters_per_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmNone⁻¹') -centimeters_per_square_day = NamedUnit(1.3395919067215363e-10, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmNone⁻²') -centimeters_per_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmNone⁻¹') -centimeters_per_square_year = NamedUnit(1.0041761481530735e-15, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmNone⁻²') -angstroms_per_second = NamedUnit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') -angstroms_per_square_second = NamedUnit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') +centimeters_per_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmmin⁻¹') +centimeters_per_square_minute = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmmin⁻²') +centimeters_per_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmh⁻¹') +centimeters_per_square_hour = NamedUnit(7.71604938271605e-08, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmh⁻²') +centimeters_per_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmd⁻¹') +centimeters_per_square_day = NamedUnit(1.3395919067215363e-10, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmd⁻²') +centimeters_per_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmy⁻¹') +centimeters_per_square_year = NamedUnit(1.0041761481530735e-15, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmy⁻²') +angstroms_per_second = NamedUnit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='Ås⁻¹') +angstroms_per_square_second = NamedUnit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='Ås⁻²') angstroms_per_millisecond = NamedUnit(1e-07, Dimensions(length=1, time=-1), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') angstroms_per_square_millisecond = NamedUnit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') angstroms_per_microsecond = NamedUnit(0.0001, Dimensions(length=1, time=-1), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') @@ -1145,119 +1145,119 @@ def __init__(self, name: str, units: list[NamedUnit]): angstroms_per_square_femtosecond = NamedUnit(1e+20, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') angstroms_per_attosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') angstroms_per_square_attosecond = NamedUnit(9.999999999999999e+25, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') -angstroms_per_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') -angstroms_per_square_minute = NamedUnit(2.7777777777777778e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') -angstroms_per_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') -angstroms_per_square_hour = NamedUnit(7.716049382716049e-16, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') -angstroms_per_day = NamedUnit(1.1574074074074074e-14, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') -angstroms_per_square_day = NamedUnit(1.3395919067215363e-18, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') -angstroms_per_year = NamedUnit(3.168873850681143e-17, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') -angstroms_per_square_year = NamedUnit(1.0041761481530734e-23, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') -miles_per_second = NamedUnit(1609.344, Dimensions(length=1, time=-1), name='miles_per_second', ascii_symbol='miles/s', symbol='NoneNone⁻¹') -miles_per_square_second = NamedUnit(1609.344, Dimensions(length=1, time=-2), name='miles_per_square_second', ascii_symbol='miles/s^2', symbol='NoneNone⁻²') -miles_per_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-1), name='miles_per_millisecond', ascii_symbol='miles/ms', symbol='Nonems⁻¹') -miles_per_square_millisecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-2), name='miles_per_square_millisecond', ascii_symbol='miles/ms^2', symbol='Nonems⁻²') -miles_per_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-1), name='miles_per_microsecond', ascii_symbol='miles/us', symbol='Noneµs⁻¹') -miles_per_square_microsecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-2), name='miles_per_square_microsecond', ascii_symbol='miles/us^2', symbol='Noneµs⁻²') -miles_per_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-1), name='miles_per_nanosecond', ascii_symbol='miles/ns', symbol='Nonens⁻¹') -miles_per_square_nanosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-2), name='miles_per_square_nanosecond', ascii_symbol='miles/ns^2', symbol='Nonens⁻²') -miles_per_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-1), name='miles_per_picosecond', ascii_symbol='miles/ps', symbol='Noneps⁻¹') -miles_per_square_picosecond = NamedUnit(1.609344e+27, Dimensions(length=1, time=-2), name='miles_per_square_picosecond', ascii_symbol='miles/ps^2', symbol='Noneps⁻²') -miles_per_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-1), name='miles_per_femtosecond', ascii_symbol='miles/fs', symbol='Nonefs⁻¹') -miles_per_square_femtosecond = NamedUnit(1.609344e+33, Dimensions(length=1, time=-2), name='miles_per_square_femtosecond', ascii_symbol='miles/fs^2', symbol='Nonefs⁻²') -miles_per_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-1), name='miles_per_attosecond', ascii_symbol='miles/as', symbol='Noneas⁻¹') -miles_per_square_attosecond = NamedUnit(1.609344e+39, Dimensions(length=1, time=-2), name='miles_per_square_attosecond', ascii_symbol='miles/as^2', symbol='Noneas⁻²') -miles_per_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-1), name='miles_per_minute', ascii_symbol='miles/min', symbol='NoneNone⁻¹') -miles_per_square_minute = NamedUnit(0.44704, Dimensions(length=1, time=-2), name='miles_per_square_minute', ascii_symbol='miles/min^2', symbol='NoneNone⁻²') -miles_per_hour = NamedUnit(4.4704, Dimensions(length=1, time=-1), name='miles_per_hour', ascii_symbol='miles/h', symbol='NoneNone⁻¹') -miles_per_square_hour = NamedUnit(0.012417777777777778, Dimensions(length=1, time=-2), name='miles_per_square_hour', ascii_symbol='miles/h^2', symbol='NoneNone⁻²') -miles_per_day = NamedUnit(0.18626666666666666, Dimensions(length=1, time=-1), name='miles_per_day', ascii_symbol='miles/d', symbol='NoneNone⁻¹') -miles_per_square_day = NamedUnit(2.1558641975308643e-05, Dimensions(length=1, time=-2), name='miles_per_square_day', ascii_symbol='miles/d^2', symbol='NoneNone⁻²') -miles_per_year = NamedUnit(0.0005099808118350593, Dimensions(length=1, time=-1), name='miles_per_year', ascii_symbol='miles/y', symbol='NoneNone⁻¹') -miles_per_square_year = NamedUnit(1.61606485897326e-10, Dimensions(length=1, time=-2), name='miles_per_square_year', ascii_symbol='miles/y^2', symbol='NoneNone⁻²') -yards_per_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-1), name='yards_per_second', ascii_symbol='yrd/s', symbol='NoneNone⁻¹') -yards_per_square_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-2), name='yards_per_square_second', ascii_symbol='yrd/s^2', symbol='NoneNone⁻²') -yards_per_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-1), name='yards_per_millisecond', ascii_symbol='yrd/ms', symbol='Nonems⁻¹') -yards_per_square_millisecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-2), name='yards_per_square_millisecond', ascii_symbol='yrd/ms^2', symbol='Nonems⁻²') -yards_per_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-1), name='yards_per_microsecond', ascii_symbol='yrd/us', symbol='Noneµs⁻¹') -yards_per_square_microsecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-2), name='yards_per_square_microsecond', ascii_symbol='yrd/us^2', symbol='Noneµs⁻²') -yards_per_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-1), name='yards_per_nanosecond', ascii_symbol='yrd/ns', symbol='Nonens⁻¹') -yards_per_square_nanosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-2), name='yards_per_square_nanosecond', ascii_symbol='yrd/ns^2', symbol='Nonens⁻²') -yards_per_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-1), name='yards_per_picosecond', ascii_symbol='yrd/ps', symbol='Noneps⁻¹') -yards_per_square_picosecond = NamedUnit(9.144000000000002e+23, Dimensions(length=1, time=-2), name='yards_per_square_picosecond', ascii_symbol='yrd/ps^2', symbol='Noneps⁻²') -yards_per_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-1), name='yards_per_femtosecond', ascii_symbol='yrd/fs', symbol='Nonefs⁻¹') -yards_per_square_femtosecond = NamedUnit(9.144e+29, Dimensions(length=1, time=-2), name='yards_per_square_femtosecond', ascii_symbol='yrd/fs^2', symbol='Nonefs⁻²') -yards_per_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-1), name='yards_per_attosecond', ascii_symbol='yrd/as', symbol='Noneas⁻¹') -yards_per_square_attosecond = NamedUnit(9.144e+35, Dimensions(length=1, time=-2), name='yards_per_square_attosecond', ascii_symbol='yrd/as^2', symbol='Noneas⁻²') -yards_per_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-1), name='yards_per_minute', ascii_symbol='yrd/min', symbol='NoneNone⁻¹') -yards_per_square_minute = NamedUnit(0.00025400000000000005, Dimensions(length=1, time=-2), name='yards_per_square_minute', ascii_symbol='yrd/min^2', symbol='NoneNone⁻²') -yards_per_hour = NamedUnit(0.00254, Dimensions(length=1, time=-1), name='yards_per_hour', ascii_symbol='yrd/h', symbol='NoneNone⁻¹') -yards_per_square_hour = NamedUnit(7.055555555555557e-06, Dimensions(length=1, time=-2), name='yards_per_square_hour', ascii_symbol='yrd/h^2', symbol='NoneNone⁻²') -yards_per_day = NamedUnit(0.00010583333333333335, Dimensions(length=1, time=-1), name='yards_per_day', ascii_symbol='yrd/d', symbol='NoneNone⁻¹') -yards_per_square_day = NamedUnit(1.224922839506173e-08, Dimensions(length=1, time=-2), name='yards_per_square_day', ascii_symbol='yrd/d^2', symbol='NoneNone⁻²') -yards_per_year = NamedUnit(2.897618249062837e-07, Dimensions(length=1, time=-1), name='yards_per_year', ascii_symbol='yrd/y', symbol='NoneNone⁻¹') -yards_per_square_year = NamedUnit(9.182186698711705e-14, Dimensions(length=1, time=-2), name='yards_per_square_year', ascii_symbol='yrd/y^2', symbol='NoneNone⁻²') -feet_per_second = NamedUnit(0.3048, Dimensions(length=1, time=-1), name='feet_per_second', ascii_symbol='ft/s', symbol='NoneNone⁻¹') -feet_per_square_second = NamedUnit(0.3048, Dimensions(length=1, time=-2), name='feet_per_square_second', ascii_symbol='ft/s^2', symbol='NoneNone⁻²') -feet_per_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-1), name='feet_per_millisecond', ascii_symbol='ft/ms', symbol='Nonems⁻¹') -feet_per_square_millisecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-2), name='feet_per_square_millisecond', ascii_symbol='ft/ms^2', symbol='Nonems⁻²') -feet_per_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-1), name='feet_per_microsecond', ascii_symbol='ft/us', symbol='Noneµs⁻¹') -feet_per_square_microsecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-2), name='feet_per_square_microsecond', ascii_symbol='ft/us^2', symbol='Noneµs⁻²') -feet_per_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-1), name='feet_per_nanosecond', ascii_symbol='ft/ns', symbol='Nonens⁻¹') -feet_per_square_nanosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-2), name='feet_per_square_nanosecond', ascii_symbol='ft/ns^2', symbol='Nonens⁻²') -feet_per_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-1), name='feet_per_picosecond', ascii_symbol='ft/ps', symbol='Noneps⁻¹') -feet_per_square_picosecond = NamedUnit(3.048e+23, Dimensions(length=1, time=-2), name='feet_per_square_picosecond', ascii_symbol='ft/ps^2', symbol='Noneps⁻²') -feet_per_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-1), name='feet_per_femtosecond', ascii_symbol='ft/fs', symbol='Nonefs⁻¹') -feet_per_square_femtosecond = NamedUnit(3.048e+29, Dimensions(length=1, time=-2), name='feet_per_square_femtosecond', ascii_symbol='ft/fs^2', symbol='Nonefs⁻²') -feet_per_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-1), name='feet_per_attosecond', ascii_symbol='ft/as', symbol='Noneas⁻¹') -feet_per_square_attosecond = NamedUnit(3.0479999999999997e+35, Dimensions(length=1, time=-2), name='feet_per_square_attosecond', ascii_symbol='ft/as^2', symbol='Noneas⁻²') -feet_per_minute = NamedUnit(0.00508, Dimensions(length=1, time=-1), name='feet_per_minute', ascii_symbol='ft/min', symbol='NoneNone⁻¹') -feet_per_square_minute = NamedUnit(8.466666666666667e-05, Dimensions(length=1, time=-2), name='feet_per_square_minute', ascii_symbol='ft/min^2', symbol='NoneNone⁻²') -feet_per_hour = NamedUnit(0.0008466666666666667, Dimensions(length=1, time=-1), name='feet_per_hour', ascii_symbol='ft/h', symbol='NoneNone⁻¹') -feet_per_square_hour = NamedUnit(2.351851851851852e-06, Dimensions(length=1, time=-2), name='feet_per_square_hour', ascii_symbol='ft/h^2', symbol='NoneNone⁻²') -feet_per_day = NamedUnit(3.527777777777778e-05, Dimensions(length=1, time=-1), name='feet_per_day', ascii_symbol='ft/d', symbol='NoneNone⁻¹') -feet_per_square_day = NamedUnit(4.083076131687243e-09, Dimensions(length=1, time=-2), name='feet_per_square_day', ascii_symbol='ft/d^2', symbol='NoneNone⁻²') -feet_per_year = NamedUnit(9.658727496876123e-08, Dimensions(length=1, time=-1), name='feet_per_year', ascii_symbol='ft/y', symbol='NoneNone⁻¹') -feet_per_square_year = NamedUnit(3.060728899570568e-14, Dimensions(length=1, time=-2), name='feet_per_square_year', ascii_symbol='ft/y^2', symbol='NoneNone⁻²') -inches_per_second = NamedUnit(0.0254, Dimensions(length=1, time=-1), name='inches_per_second', ascii_symbol='in/s', symbol='NoneNone⁻¹') -inches_per_square_second = NamedUnit(0.0254, Dimensions(length=1, time=-2), name='inches_per_square_second', ascii_symbol='in/s^2', symbol='NoneNone⁻²') -inches_per_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-1), name='inches_per_millisecond', ascii_symbol='in/ms', symbol='Nonems⁻¹') -inches_per_square_millisecond = NamedUnit(25400.0, Dimensions(length=1, time=-2), name='inches_per_square_millisecond', ascii_symbol='in/ms^2', symbol='Nonems⁻²') -inches_per_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-1), name='inches_per_microsecond', ascii_symbol='in/us', symbol='Noneµs⁻¹') -inches_per_square_microsecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-2), name='inches_per_square_microsecond', ascii_symbol='in/us^2', symbol='Noneµs⁻²') -inches_per_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-1), name='inches_per_nanosecond', ascii_symbol='in/ns', symbol='Nonens⁻¹') -inches_per_square_nanosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-2), name='inches_per_square_nanosecond', ascii_symbol='in/ns^2', symbol='Nonens⁻²') -inches_per_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-1), name='inches_per_picosecond', ascii_symbol='in/ps', symbol='Noneps⁻¹') -inches_per_square_picosecond = NamedUnit(2.54e+22, Dimensions(length=1, time=-2), name='inches_per_square_picosecond', ascii_symbol='in/ps^2', symbol='Noneps⁻²') -inches_per_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-1), name='inches_per_femtosecond', ascii_symbol='in/fs', symbol='Nonefs⁻¹') -inches_per_square_femtosecond = NamedUnit(2.54e+28, Dimensions(length=1, time=-2), name='inches_per_square_femtosecond', ascii_symbol='in/fs^2', symbol='Nonefs⁻²') -inches_per_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-1), name='inches_per_attosecond', ascii_symbol='in/as', symbol='Noneas⁻¹') -inches_per_square_attosecond = NamedUnit(2.5399999999999998e+34, Dimensions(length=1, time=-2), name='inches_per_square_attosecond', ascii_symbol='in/as^2', symbol='Noneas⁻²') -inches_per_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-1), name='inches_per_minute', ascii_symbol='in/min', symbol='NoneNone⁻¹') -inches_per_square_minute = NamedUnit(7.055555555555555e-06, Dimensions(length=1, time=-2), name='inches_per_square_minute', ascii_symbol='in/min^2', symbol='NoneNone⁻²') -inches_per_hour = NamedUnit(7.055555555555556e-05, Dimensions(length=1, time=-1), name='inches_per_hour', ascii_symbol='in/h', symbol='NoneNone⁻¹') -inches_per_square_hour = NamedUnit(1.9598765432098765e-07, Dimensions(length=1, time=-2), name='inches_per_square_hour', ascii_symbol='in/h^2', symbol='NoneNone⁻²') -inches_per_day = NamedUnit(2.9398148148148147e-06, Dimensions(length=1, time=-1), name='inches_per_day', ascii_symbol='in/d', symbol='NoneNone⁻¹') -inches_per_square_day = NamedUnit(3.4025634430727023e-10, Dimensions(length=1, time=-2), name='inches_per_square_day', ascii_symbol='in/d^2', symbol='NoneNone⁻²') -inches_per_year = NamedUnit(8.048939580730103e-09, Dimensions(length=1, time=-1), name='inches_per_year', ascii_symbol='in/y', symbol='NoneNone⁻¹') -inches_per_square_year = NamedUnit(2.5506074163088065e-15, Dimensions(length=1, time=-2), name='inches_per_square_year', ascii_symbol='in/y^2', symbol='NoneNone⁻²') -grams_per_cubic_meter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_meter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') -petagrams_per_cubic_meter = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') -teragrams_per_cubic_meter = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_meter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') -megagrams_per_cubic_meter = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') -kilograms_per_cubic_meter = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgNone⁻³') -milligrams_per_cubic_meter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgNone⁻³') -micrograms_per_cubic_meter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgNone⁻³') -nanograms_per_cubic_meter = NamedUnit(1.0000000000000002e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngNone⁻³') -picograms_per_cubic_meter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') -femtograms_per_cubic_meter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') -attograms_per_cubic_meter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_meter = NamedUnit(1.660538921e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='NoneNone⁻³') -pounds_per_cubic_meter = NamedUnit(0.45359237, Dimensions(length=-3, mass=1), name='pounds_per_cubic_meter', ascii_symbol='lb m^-3', symbol='NoneNone⁻³') -ounces_per_cubic_meter = NamedUnit(0.028349523125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_meter', ascii_symbol='oz m^-3', symbol='NoneNone⁻³') -grams_per_cubic_exameter = NamedUnit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') +angstroms_per_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='Åmin⁻¹') +angstroms_per_square_minute = NamedUnit(2.7777777777777778e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='Åmin⁻²') +angstroms_per_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='Åh⁻¹') +angstroms_per_square_hour = NamedUnit(7.716049382716049e-16, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='Åh⁻²') +angstroms_per_day = NamedUnit(1.1574074074074074e-14, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='Åd⁻¹') +angstroms_per_square_day = NamedUnit(1.3395919067215363e-18, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='Åd⁻²') +angstroms_per_year = NamedUnit(3.168873850681143e-17, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='Åy⁻¹') +angstroms_per_square_year = NamedUnit(1.0041761481530734e-23, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='Åy⁻²') +miles_per_second = NamedUnit(1609.344, Dimensions(length=1, time=-1), name='miles_per_second', ascii_symbol='miles/s', symbol='miless⁻¹') +miles_per_square_second = NamedUnit(1609.344, Dimensions(length=1, time=-2), name='miles_per_square_second', ascii_symbol='miles/s^2', symbol='miless⁻²') +miles_per_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-1), name='miles_per_millisecond', ascii_symbol='miles/ms', symbol='milesms⁻¹') +miles_per_square_millisecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-2), name='miles_per_square_millisecond', ascii_symbol='miles/ms^2', symbol='milesms⁻²') +miles_per_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-1), name='miles_per_microsecond', ascii_symbol='miles/us', symbol='milesµs⁻¹') +miles_per_square_microsecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-2), name='miles_per_square_microsecond', ascii_symbol='miles/us^2', symbol='milesµs⁻²') +miles_per_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-1), name='miles_per_nanosecond', ascii_symbol='miles/ns', symbol='milesns⁻¹') +miles_per_square_nanosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-2), name='miles_per_square_nanosecond', ascii_symbol='miles/ns^2', symbol='milesns⁻²') +miles_per_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-1), name='miles_per_picosecond', ascii_symbol='miles/ps', symbol='milesps⁻¹') +miles_per_square_picosecond = NamedUnit(1.609344e+27, Dimensions(length=1, time=-2), name='miles_per_square_picosecond', ascii_symbol='miles/ps^2', symbol='milesps⁻²') +miles_per_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-1), name='miles_per_femtosecond', ascii_symbol='miles/fs', symbol='milesfs⁻¹') +miles_per_square_femtosecond = NamedUnit(1.609344e+33, Dimensions(length=1, time=-2), name='miles_per_square_femtosecond', ascii_symbol='miles/fs^2', symbol='milesfs⁻²') +miles_per_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-1), name='miles_per_attosecond', ascii_symbol='miles/as', symbol='milesas⁻¹') +miles_per_square_attosecond = NamedUnit(1.609344e+39, Dimensions(length=1, time=-2), name='miles_per_square_attosecond', ascii_symbol='miles/as^2', symbol='milesas⁻²') +miles_per_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-1), name='miles_per_minute', ascii_symbol='miles/min', symbol='milesmin⁻¹') +miles_per_square_minute = NamedUnit(0.44704, Dimensions(length=1, time=-2), name='miles_per_square_minute', ascii_symbol='miles/min^2', symbol='milesmin⁻²') +miles_per_hour = NamedUnit(4.4704, Dimensions(length=1, time=-1), name='miles_per_hour', ascii_symbol='miles/h', symbol='milesh⁻¹') +miles_per_square_hour = NamedUnit(0.012417777777777778, Dimensions(length=1, time=-2), name='miles_per_square_hour', ascii_symbol='miles/h^2', symbol='milesh⁻²') +miles_per_day = NamedUnit(0.18626666666666666, Dimensions(length=1, time=-1), name='miles_per_day', ascii_symbol='miles/d', symbol='milesd⁻¹') +miles_per_square_day = NamedUnit(2.1558641975308643e-05, Dimensions(length=1, time=-2), name='miles_per_square_day', ascii_symbol='miles/d^2', symbol='milesd⁻²') +miles_per_year = NamedUnit(0.0005099808118350593, Dimensions(length=1, time=-1), name='miles_per_year', ascii_symbol='miles/y', symbol='milesy⁻¹') +miles_per_square_year = NamedUnit(1.61606485897326e-10, Dimensions(length=1, time=-2), name='miles_per_square_year', ascii_symbol='miles/y^2', symbol='milesy⁻²') +yards_per_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-1), name='yards_per_second', ascii_symbol='yrd/s', symbol='yrds⁻¹') +yards_per_square_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-2), name='yards_per_square_second', ascii_symbol='yrd/s^2', symbol='yrds⁻²') +yards_per_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-1), name='yards_per_millisecond', ascii_symbol='yrd/ms', symbol='yrdms⁻¹') +yards_per_square_millisecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-2), name='yards_per_square_millisecond', ascii_symbol='yrd/ms^2', symbol='yrdms⁻²') +yards_per_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-1), name='yards_per_microsecond', ascii_symbol='yrd/us', symbol='yrdµs⁻¹') +yards_per_square_microsecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-2), name='yards_per_square_microsecond', ascii_symbol='yrd/us^2', symbol='yrdµs⁻²') +yards_per_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-1), name='yards_per_nanosecond', ascii_symbol='yrd/ns', symbol='yrdns⁻¹') +yards_per_square_nanosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-2), name='yards_per_square_nanosecond', ascii_symbol='yrd/ns^2', symbol='yrdns⁻²') +yards_per_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-1), name='yards_per_picosecond', ascii_symbol='yrd/ps', symbol='yrdps⁻¹') +yards_per_square_picosecond = NamedUnit(9.144000000000002e+23, Dimensions(length=1, time=-2), name='yards_per_square_picosecond', ascii_symbol='yrd/ps^2', symbol='yrdps⁻²') +yards_per_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-1), name='yards_per_femtosecond', ascii_symbol='yrd/fs', symbol='yrdfs⁻¹') +yards_per_square_femtosecond = NamedUnit(9.144e+29, Dimensions(length=1, time=-2), name='yards_per_square_femtosecond', ascii_symbol='yrd/fs^2', symbol='yrdfs⁻²') +yards_per_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-1), name='yards_per_attosecond', ascii_symbol='yrd/as', symbol='yrdas⁻¹') +yards_per_square_attosecond = NamedUnit(9.144e+35, Dimensions(length=1, time=-2), name='yards_per_square_attosecond', ascii_symbol='yrd/as^2', symbol='yrdas⁻²') +yards_per_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-1), name='yards_per_minute', ascii_symbol='yrd/min', symbol='yrdmin⁻¹') +yards_per_square_minute = NamedUnit(0.00025400000000000005, Dimensions(length=1, time=-2), name='yards_per_square_minute', ascii_symbol='yrd/min^2', symbol='yrdmin⁻²') +yards_per_hour = NamedUnit(0.00254, Dimensions(length=1, time=-1), name='yards_per_hour', ascii_symbol='yrd/h', symbol='yrdh⁻¹') +yards_per_square_hour = NamedUnit(7.055555555555557e-06, Dimensions(length=1, time=-2), name='yards_per_square_hour', ascii_symbol='yrd/h^2', symbol='yrdh⁻²') +yards_per_day = NamedUnit(0.00010583333333333335, Dimensions(length=1, time=-1), name='yards_per_day', ascii_symbol='yrd/d', symbol='yrdd⁻¹') +yards_per_square_day = NamedUnit(1.224922839506173e-08, Dimensions(length=1, time=-2), name='yards_per_square_day', ascii_symbol='yrd/d^2', symbol='yrdd⁻²') +yards_per_year = NamedUnit(2.897618249062837e-07, Dimensions(length=1, time=-1), name='yards_per_year', ascii_symbol='yrd/y', symbol='yrdy⁻¹') +yards_per_square_year = NamedUnit(9.182186698711705e-14, Dimensions(length=1, time=-2), name='yards_per_square_year', ascii_symbol='yrd/y^2', symbol='yrdy⁻²') +feet_per_second = NamedUnit(0.3048, Dimensions(length=1, time=-1), name='feet_per_second', ascii_symbol='ft/s', symbol='fts⁻¹') +feet_per_square_second = NamedUnit(0.3048, Dimensions(length=1, time=-2), name='feet_per_square_second', ascii_symbol='ft/s^2', symbol='fts⁻²') +feet_per_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-1), name='feet_per_millisecond', ascii_symbol='ft/ms', symbol='ftms⁻¹') +feet_per_square_millisecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-2), name='feet_per_square_millisecond', ascii_symbol='ft/ms^2', symbol='ftms⁻²') +feet_per_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-1), name='feet_per_microsecond', ascii_symbol='ft/us', symbol='ftµs⁻¹') +feet_per_square_microsecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-2), name='feet_per_square_microsecond', ascii_symbol='ft/us^2', symbol='ftµs⁻²') +feet_per_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-1), name='feet_per_nanosecond', ascii_symbol='ft/ns', symbol='ftns⁻¹') +feet_per_square_nanosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-2), name='feet_per_square_nanosecond', ascii_symbol='ft/ns^2', symbol='ftns⁻²') +feet_per_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-1), name='feet_per_picosecond', ascii_symbol='ft/ps', symbol='ftps⁻¹') +feet_per_square_picosecond = NamedUnit(3.048e+23, Dimensions(length=1, time=-2), name='feet_per_square_picosecond', ascii_symbol='ft/ps^2', symbol='ftps⁻²') +feet_per_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-1), name='feet_per_femtosecond', ascii_symbol='ft/fs', symbol='ftfs⁻¹') +feet_per_square_femtosecond = NamedUnit(3.048e+29, Dimensions(length=1, time=-2), name='feet_per_square_femtosecond', ascii_symbol='ft/fs^2', symbol='ftfs⁻²') +feet_per_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-1), name='feet_per_attosecond', ascii_symbol='ft/as', symbol='ftas⁻¹') +feet_per_square_attosecond = NamedUnit(3.0479999999999997e+35, Dimensions(length=1, time=-2), name='feet_per_square_attosecond', ascii_symbol='ft/as^2', symbol='ftas⁻²') +feet_per_minute = NamedUnit(0.00508, Dimensions(length=1, time=-1), name='feet_per_minute', ascii_symbol='ft/min', symbol='ftmin⁻¹') +feet_per_square_minute = NamedUnit(8.466666666666667e-05, Dimensions(length=1, time=-2), name='feet_per_square_minute', ascii_symbol='ft/min^2', symbol='ftmin⁻²') +feet_per_hour = NamedUnit(0.0008466666666666667, Dimensions(length=1, time=-1), name='feet_per_hour', ascii_symbol='ft/h', symbol='fth⁻¹') +feet_per_square_hour = NamedUnit(2.351851851851852e-06, Dimensions(length=1, time=-2), name='feet_per_square_hour', ascii_symbol='ft/h^2', symbol='fth⁻²') +feet_per_day = NamedUnit(3.527777777777778e-05, Dimensions(length=1, time=-1), name='feet_per_day', ascii_symbol='ft/d', symbol='ftd⁻¹') +feet_per_square_day = NamedUnit(4.083076131687243e-09, Dimensions(length=1, time=-2), name='feet_per_square_day', ascii_symbol='ft/d^2', symbol='ftd⁻²') +feet_per_year = NamedUnit(9.658727496876123e-08, Dimensions(length=1, time=-1), name='feet_per_year', ascii_symbol='ft/y', symbol='fty⁻¹') +feet_per_square_year = NamedUnit(3.060728899570568e-14, Dimensions(length=1, time=-2), name='feet_per_square_year', ascii_symbol='ft/y^2', symbol='fty⁻²') +inches_per_second = NamedUnit(0.0254, Dimensions(length=1, time=-1), name='inches_per_second', ascii_symbol='in/s', symbol='ins⁻¹') +inches_per_square_second = NamedUnit(0.0254, Dimensions(length=1, time=-2), name='inches_per_square_second', ascii_symbol='in/s^2', symbol='ins⁻²') +inches_per_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-1), name='inches_per_millisecond', ascii_symbol='in/ms', symbol='inms⁻¹') +inches_per_square_millisecond = NamedUnit(25400.0, Dimensions(length=1, time=-2), name='inches_per_square_millisecond', ascii_symbol='in/ms^2', symbol='inms⁻²') +inches_per_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-1), name='inches_per_microsecond', ascii_symbol='in/us', symbol='inµs⁻¹') +inches_per_square_microsecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-2), name='inches_per_square_microsecond', ascii_symbol='in/us^2', symbol='inµs⁻²') +inches_per_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-1), name='inches_per_nanosecond', ascii_symbol='in/ns', symbol='inns⁻¹') +inches_per_square_nanosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-2), name='inches_per_square_nanosecond', ascii_symbol='in/ns^2', symbol='inns⁻²') +inches_per_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-1), name='inches_per_picosecond', ascii_symbol='in/ps', symbol='inps⁻¹') +inches_per_square_picosecond = NamedUnit(2.54e+22, Dimensions(length=1, time=-2), name='inches_per_square_picosecond', ascii_symbol='in/ps^2', symbol='inps⁻²') +inches_per_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-1), name='inches_per_femtosecond', ascii_symbol='in/fs', symbol='infs⁻¹') +inches_per_square_femtosecond = NamedUnit(2.54e+28, Dimensions(length=1, time=-2), name='inches_per_square_femtosecond', ascii_symbol='in/fs^2', symbol='infs⁻²') +inches_per_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-1), name='inches_per_attosecond', ascii_symbol='in/as', symbol='inas⁻¹') +inches_per_square_attosecond = NamedUnit(2.5399999999999998e+34, Dimensions(length=1, time=-2), name='inches_per_square_attosecond', ascii_symbol='in/as^2', symbol='inas⁻²') +inches_per_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-1), name='inches_per_minute', ascii_symbol='in/min', symbol='inmin⁻¹') +inches_per_square_minute = NamedUnit(7.055555555555555e-06, Dimensions(length=1, time=-2), name='inches_per_square_minute', ascii_symbol='in/min^2', symbol='inmin⁻²') +inches_per_hour = NamedUnit(7.055555555555556e-05, Dimensions(length=1, time=-1), name='inches_per_hour', ascii_symbol='in/h', symbol='inh⁻¹') +inches_per_square_hour = NamedUnit(1.9598765432098765e-07, Dimensions(length=1, time=-2), name='inches_per_square_hour', ascii_symbol='in/h^2', symbol='inh⁻²') +inches_per_day = NamedUnit(2.9398148148148147e-06, Dimensions(length=1, time=-1), name='inches_per_day', ascii_symbol='in/d', symbol='ind⁻¹') +inches_per_square_day = NamedUnit(3.4025634430727023e-10, Dimensions(length=1, time=-2), name='inches_per_square_day', ascii_symbol='in/d^2', symbol='ind⁻²') +inches_per_year = NamedUnit(8.048939580730103e-09, Dimensions(length=1, time=-1), name='inches_per_year', ascii_symbol='in/y', symbol='iny⁻¹') +inches_per_square_year = NamedUnit(2.5506074163088065e-15, Dimensions(length=1, time=-2), name='inches_per_square_year', ascii_symbol='in/y^2', symbol='iny⁻²') +grams_per_cubic_meter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='gm⁻³') +exagrams_per_cubic_meter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='Egm⁻³') +petagrams_per_cubic_meter = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='Pgm⁻³') +teragrams_per_cubic_meter = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='Tgm⁻³') +gigagrams_per_cubic_meter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='Ggm⁻³') +megagrams_per_cubic_meter = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='Mgm⁻³') +kilograms_per_cubic_meter = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgm⁻³') +milligrams_per_cubic_meter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgm⁻³') +micrograms_per_cubic_meter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgm⁻³') +nanograms_per_cubic_meter = NamedUnit(1.0000000000000002e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngm⁻³') +picograms_per_cubic_meter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgm⁻³') +femtograms_per_cubic_meter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgm⁻³') +attograms_per_cubic_meter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agm⁻³') +atomic_mass_units_per_cubic_meter = NamedUnit(1.660538921e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='aum⁻³') +pounds_per_cubic_meter = NamedUnit(0.45359237, Dimensions(length=-3, mass=1), name='pounds_per_cubic_meter', ascii_symbol='lb m^-3', symbol='lbm⁻³') +ounces_per_cubic_meter = NamedUnit(0.028349523125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_meter', ascii_symbol='oz m^-3', symbol='ozm⁻³') +grams_per_cubic_exameter = NamedUnit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='gEm⁻³') exagrams_per_cubic_exameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') petagrams_per_cubic_exameter = NamedUnit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') teragrams_per_cubic_exameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') @@ -1270,10 +1270,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_exameter = NamedUnit(1e-69, Dimensions(length=-3, mass=1), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') femtograms_per_cubic_exameter = NamedUnit(1e-72, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') attograms_per_cubic_exameter = NamedUnit(1e-75, Dimensions(length=-3, mass=1), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -atomic_mass_units_per_cubic_exameter = NamedUnit(1.6605389209999996e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='NoneEm⁻³') -pounds_per_cubic_exameter = NamedUnit(4.5359237e-55, Dimensions(length=-3, mass=1), name='pounds_per_cubic_exameter', ascii_symbol='lb Em^-3', symbol='NoneEm⁻³') -ounces_per_cubic_exameter = NamedUnit(2.8349523125e-56, Dimensions(length=-3, mass=1), name='ounces_per_cubic_exameter', ascii_symbol='oz Em^-3', symbol='NoneEm⁻³') -grams_per_cubic_petameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') +atomic_mass_units_per_cubic_exameter = NamedUnit(1.6605389209999996e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='auEm⁻³') +pounds_per_cubic_exameter = NamedUnit(4.5359237e-55, Dimensions(length=-3, mass=1), name='pounds_per_cubic_exameter', ascii_symbol='lb Em^-3', symbol='lbEm⁻³') +ounces_per_cubic_exameter = NamedUnit(2.8349523125e-56, Dimensions(length=-3, mass=1), name='ounces_per_cubic_exameter', ascii_symbol='oz Em^-3', symbol='ozEm⁻³') +grams_per_cubic_petameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='gPm⁻³') exagrams_per_cubic_petameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') petagrams_per_cubic_petameter = NamedUnit(1e-33, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') teragrams_per_cubic_petameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') @@ -1286,10 +1286,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-60, Dimensions(length=-3, mass=1), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') femtograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-63, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') attograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -atomic_mass_units_per_cubic_petameter = NamedUnit(1.660538921e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='NonePm⁻³') -pounds_per_cubic_petameter = NamedUnit(4.5359237000000005e-46, Dimensions(length=-3, mass=1), name='pounds_per_cubic_petameter', ascii_symbol='lb Pm^-3', symbol='NonePm⁻³') -ounces_per_cubic_petameter = NamedUnit(2.8349523125000003e-47, Dimensions(length=-3, mass=1), name='ounces_per_cubic_petameter', ascii_symbol='oz Pm^-3', symbol='NonePm⁻³') -grams_per_cubic_terameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') +atomic_mass_units_per_cubic_petameter = NamedUnit(1.660538921e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='auPm⁻³') +pounds_per_cubic_petameter = NamedUnit(4.5359237000000005e-46, Dimensions(length=-3, mass=1), name='pounds_per_cubic_petameter', ascii_symbol='lb Pm^-3', symbol='lbPm⁻³') +ounces_per_cubic_petameter = NamedUnit(2.8349523125000003e-47, Dimensions(length=-3, mass=1), name='ounces_per_cubic_petameter', ascii_symbol='oz Pm^-3', symbol='ozPm⁻³') +grams_per_cubic_terameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='gTm⁻³') exagrams_per_cubic_terameter = NamedUnit(1e-21, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') petagrams_per_cubic_terameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') teragrams_per_cubic_terameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') @@ -1302,10 +1302,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_terameter = NamedUnit(1e-51, Dimensions(length=-3, mass=1), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') femtograms_per_cubic_terameter = NamedUnit(1e-54, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') attograms_per_cubic_terameter = NamedUnit(1.0000000000000001e-57, Dimensions(length=-3, mass=1), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -atomic_mass_units_per_cubic_terameter = NamedUnit(1.6605389209999997e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='NoneTm⁻³') -pounds_per_cubic_terameter = NamedUnit(4.5359237e-37, Dimensions(length=-3, mass=1), name='pounds_per_cubic_terameter', ascii_symbol='lb Tm^-3', symbol='NoneTm⁻³') -ounces_per_cubic_terameter = NamedUnit(2.8349523125e-38, Dimensions(length=-3, mass=1), name='ounces_per_cubic_terameter', ascii_symbol='oz Tm^-3', symbol='NoneTm⁻³') -grams_per_cubic_gigameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') +atomic_mass_units_per_cubic_terameter = NamedUnit(1.6605389209999997e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='auTm⁻³') +pounds_per_cubic_terameter = NamedUnit(4.5359237e-37, Dimensions(length=-3, mass=1), name='pounds_per_cubic_terameter', ascii_symbol='lb Tm^-3', symbol='lbTm⁻³') +ounces_per_cubic_terameter = NamedUnit(2.8349523125e-38, Dimensions(length=-3, mass=1), name='ounces_per_cubic_terameter', ascii_symbol='oz Tm^-3', symbol='ozTm⁻³') +grams_per_cubic_gigameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='gGm⁻³') exagrams_per_cubic_gigameter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') petagrams_per_cubic_gigameter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') teragrams_per_cubic_gigameter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') @@ -1318,10 +1318,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_gigameter = NamedUnit(1e-42, Dimensions(length=-3, mass=1), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') femtograms_per_cubic_gigameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') attograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -atomic_mass_units_per_cubic_gigameter = NamedUnit(1.660538921e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='NoneGm⁻³') -pounds_per_cubic_gigameter = NamedUnit(4.5359237e-28, Dimensions(length=-3, mass=1), name='pounds_per_cubic_gigameter', ascii_symbol='lb Gm^-3', symbol='NoneGm⁻³') -ounces_per_cubic_gigameter = NamedUnit(2.8349523125e-29, Dimensions(length=-3, mass=1), name='ounces_per_cubic_gigameter', ascii_symbol='oz Gm^-3', symbol='NoneGm⁻³') -grams_per_cubic_megameter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') +atomic_mass_units_per_cubic_gigameter = NamedUnit(1.660538921e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='auGm⁻³') +pounds_per_cubic_gigameter = NamedUnit(4.5359237e-28, Dimensions(length=-3, mass=1), name='pounds_per_cubic_gigameter', ascii_symbol='lb Gm^-3', symbol='lbGm⁻³') +ounces_per_cubic_gigameter = NamedUnit(2.8349523125e-29, Dimensions(length=-3, mass=1), name='ounces_per_cubic_gigameter', ascii_symbol='oz Gm^-3', symbol='ozGm⁻³') +grams_per_cubic_megameter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='gMm⁻³') exagrams_per_cubic_megameter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') petagrams_per_cubic_megameter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') teragrams_per_cubic_megameter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') @@ -1334,10 +1334,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_megameter = NamedUnit(1e-33, Dimensions(length=-3, mass=1), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') femtograms_per_cubic_megameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') attograms_per_cubic_megameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -atomic_mass_units_per_cubic_megameter = NamedUnit(1.6605389209999997e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='NoneMm⁻³') -pounds_per_cubic_megameter = NamedUnit(4.535923700000001e-19, Dimensions(length=-3, mass=1), name='pounds_per_cubic_megameter', ascii_symbol='lb Mm^-3', symbol='NoneMm⁻³') -ounces_per_cubic_megameter = NamedUnit(2.8349523125000004e-20, Dimensions(length=-3, mass=1), name='ounces_per_cubic_megameter', ascii_symbol='oz Mm^-3', symbol='NoneMm⁻³') -grams_per_cubic_kilometer = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') +atomic_mass_units_per_cubic_megameter = NamedUnit(1.6605389209999997e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='auMm⁻³') +pounds_per_cubic_megameter = NamedUnit(4.535923700000001e-19, Dimensions(length=-3, mass=1), name='pounds_per_cubic_megameter', ascii_symbol='lb Mm^-3', symbol='lbMm⁻³') +ounces_per_cubic_megameter = NamedUnit(2.8349523125000004e-20, Dimensions(length=-3, mass=1), name='ounces_per_cubic_megameter', ascii_symbol='oz Mm^-3', symbol='ozMm⁻³') +grams_per_cubic_kilometer = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='gkm⁻³') exagrams_per_cubic_kilometer = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') petagrams_per_cubic_kilometer = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') teragrams_per_cubic_kilometer = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') @@ -1350,10 +1350,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_kilometer = NamedUnit(1.0000000000000001e-24, Dimensions(length=-3, mass=1), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') femtograms_per_cubic_kilometer = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') attograms_per_cubic_kilometer = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') -atomic_mass_units_per_cubic_kilometer = NamedUnit(1.6605389209999997e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='Nonekm⁻³') -pounds_per_cubic_kilometer = NamedUnit(4.5359237000000004e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_kilometer', ascii_symbol='lb km^-3', symbol='Nonekm⁻³') -ounces_per_cubic_kilometer = NamedUnit(2.8349523125000003e-11, Dimensions(length=-3, mass=1), name='ounces_per_cubic_kilometer', ascii_symbol='oz km^-3', symbol='Nonekm⁻³') -grams_per_cubic_millimeter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') +atomic_mass_units_per_cubic_kilometer = NamedUnit(1.6605389209999997e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='aukm⁻³') +pounds_per_cubic_kilometer = NamedUnit(4.5359237000000004e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_kilometer', ascii_symbol='lb km^-3', symbol='lbkm⁻³') +ounces_per_cubic_kilometer = NamedUnit(2.8349523125000003e-11, Dimensions(length=-3, mass=1), name='ounces_per_cubic_kilometer', ascii_symbol='oz km^-3', symbol='ozkm⁻³') +grams_per_cubic_millimeter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='gmm⁻³') exagrams_per_cubic_millimeter = NamedUnit(1e+24, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') petagrams_per_cubic_millimeter = NamedUnit(1e+21, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') teragrams_per_cubic_millimeter = NamedUnit(1e+18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') @@ -1366,10 +1366,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_millimeter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') femtograms_per_cubic_millimeter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') attograms_per_cubic_millimeter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -atomic_mass_units_per_cubic_millimeter = NamedUnit(1.6605389209999997e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='Nonemm⁻³') -pounds_per_cubic_millimeter = NamedUnit(453592370.0, Dimensions(length=-3, mass=1), name='pounds_per_cubic_millimeter', ascii_symbol='lb mm^-3', symbol='Nonemm⁻³') -ounces_per_cubic_millimeter = NamedUnit(28349523.125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_millimeter', ascii_symbol='oz mm^-3', symbol='Nonemm⁻³') -grams_per_cubic_micrometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') +atomic_mass_units_per_cubic_millimeter = NamedUnit(1.6605389209999997e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='aumm⁻³') +pounds_per_cubic_millimeter = NamedUnit(453592370.0, Dimensions(length=-3, mass=1), name='pounds_per_cubic_millimeter', ascii_symbol='lb mm^-3', symbol='lbmm⁻³') +ounces_per_cubic_millimeter = NamedUnit(28349523.125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_millimeter', ascii_symbol='oz mm^-3', symbol='ozmm⁻³') +grams_per_cubic_micrometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='gµm⁻³') exagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') petagrams_per_cubic_micrometer = NamedUnit(1.0000000000000002e+30, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') teragrams_per_cubic_micrometer = NamedUnit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') @@ -1382,10 +1382,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_micrometer = NamedUnit(1000.0000000000002, Dimensions(length=-3, mass=1), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') femtograms_per_cubic_micrometer = NamedUnit(1.0000000000000002, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') attograms_per_cubic_micrometer = NamedUnit(0.0010000000000000002, Dimensions(length=-3, mass=1), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') -atomic_mass_units_per_cubic_micrometer = NamedUnit(1.660538921e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='Noneµm⁻³') -pounds_per_cubic_micrometer = NamedUnit(4.5359237000000006e+17, Dimensions(length=-3, mass=1), name='pounds_per_cubic_micrometer', ascii_symbol='lb um^-3', symbol='Noneµm⁻³') -ounces_per_cubic_micrometer = NamedUnit(2.8349523125000004e+16, Dimensions(length=-3, mass=1), name='ounces_per_cubic_micrometer', ascii_symbol='oz um^-3', symbol='Noneµm⁻³') -grams_per_cubic_nanometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') +atomic_mass_units_per_cubic_micrometer = NamedUnit(1.660538921e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='auµm⁻³') +pounds_per_cubic_micrometer = NamedUnit(4.5359237000000006e+17, Dimensions(length=-3, mass=1), name='pounds_per_cubic_micrometer', ascii_symbol='lb um^-3', symbol='lbµm⁻³') +ounces_per_cubic_micrometer = NamedUnit(2.8349523125000004e+16, Dimensions(length=-3, mass=1), name='ounces_per_cubic_micrometer', ascii_symbol='oz um^-3', symbol='ozµm⁻³') +grams_per_cubic_nanometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='gnm⁻³') exagrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') petagrams_per_cubic_nanometer = NamedUnit(9.999999999999998e+38, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') teragrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') @@ -1398,10 +1398,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_nanometer = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') femtograms_per_cubic_nanometer = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') attograms_per_cubic_nanometer = NamedUnit(999999.9999999999, Dimensions(length=-3, mass=1), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -atomic_mass_units_per_cubic_nanometer = NamedUnit(1.6605389209999994, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='Nonenm⁻³') -pounds_per_cubic_nanometer = NamedUnit(4.535923699999999e+26, Dimensions(length=-3, mass=1), name='pounds_per_cubic_nanometer', ascii_symbol='lb nm^-3', symbol='Nonenm⁻³') -ounces_per_cubic_nanometer = NamedUnit(2.8349523124999993e+25, Dimensions(length=-3, mass=1), name='ounces_per_cubic_nanometer', ascii_symbol='oz nm^-3', symbol='Nonenm⁻³') -grams_per_cubic_picometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') +atomic_mass_units_per_cubic_nanometer = NamedUnit(1.6605389209999994, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='aunm⁻³') +pounds_per_cubic_nanometer = NamedUnit(4.535923699999999e+26, Dimensions(length=-3, mass=1), name='pounds_per_cubic_nanometer', ascii_symbol='lb nm^-3', symbol='lbnm⁻³') +ounces_per_cubic_nanometer = NamedUnit(2.8349523124999993e+25, Dimensions(length=-3, mass=1), name='ounces_per_cubic_nanometer', ascii_symbol='oz nm^-3', symbol='oznm⁻³') +grams_per_cubic_picometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='gpm⁻³') exagrams_per_cubic_picometer = NamedUnit(1e+51, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') petagrams_per_cubic_picometer = NamedUnit(1e+48, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') teragrams_per_cubic_picometer = NamedUnit(1.0000000000000001e+45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') @@ -1414,10 +1414,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_picometer = NamedUnit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') femtograms_per_cubic_picometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') attograms_per_cubic_picometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -atomic_mass_units_per_cubic_picometer = NamedUnit(1660538921.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='Nonepm⁻³') -pounds_per_cubic_picometer = NamedUnit(4.5359237000000005e+35, Dimensions(length=-3, mass=1), name='pounds_per_cubic_picometer', ascii_symbol='lb pm^-3', symbol='Nonepm⁻³') -ounces_per_cubic_picometer = NamedUnit(2.8349523125000003e+34, Dimensions(length=-3, mass=1), name='ounces_per_cubic_picometer', ascii_symbol='oz pm^-3', symbol='Nonepm⁻³') -grams_per_cubic_femtometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') +atomic_mass_units_per_cubic_picometer = NamedUnit(1660538921.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='aupm⁻³') +pounds_per_cubic_picometer = NamedUnit(4.5359237000000005e+35, Dimensions(length=-3, mass=1), name='pounds_per_cubic_picometer', ascii_symbol='lb pm^-3', symbol='lbpm⁻³') +ounces_per_cubic_picometer = NamedUnit(2.8349523125000003e+34, Dimensions(length=-3, mass=1), name='ounces_per_cubic_picometer', ascii_symbol='oz pm^-3', symbol='ozpm⁻³') +grams_per_cubic_femtometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='gfm⁻³') exagrams_per_cubic_femtometer = NamedUnit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') petagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+56, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') teragrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+53, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') @@ -1430,10 +1430,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') femtograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') attograms_per_cubic_femtometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -atomic_mass_units_per_cubic_femtometer = NamedUnit(1.6605389209999992e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='Nonefm⁻³') -pounds_per_cubic_femtometer = NamedUnit(4.5359236999999985e+44, Dimensions(length=-3, mass=1), name='pounds_per_cubic_femtometer', ascii_symbol='lb fm^-3', symbol='Nonefm⁻³') -ounces_per_cubic_femtometer = NamedUnit(2.834952312499999e+43, Dimensions(length=-3, mass=1), name='ounces_per_cubic_femtometer', ascii_symbol='oz fm^-3', symbol='Nonefm⁻³') -grams_per_cubic_attometer = NamedUnit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') +atomic_mass_units_per_cubic_femtometer = NamedUnit(1.6605389209999992e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='aufm⁻³') +pounds_per_cubic_femtometer = NamedUnit(4.5359236999999985e+44, Dimensions(length=-3, mass=1), name='pounds_per_cubic_femtometer', ascii_symbol='lb fm^-3', symbol='lbfm⁻³') +ounces_per_cubic_femtometer = NamedUnit(2.834952312499999e+43, Dimensions(length=-3, mass=1), name='ounces_per_cubic_femtometer', ascii_symbol='oz fm^-3', symbol='ozfm⁻³') +grams_per_cubic_attometer = NamedUnit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='gam⁻³') exagrams_per_cubic_attometer = NamedUnit(9.999999999999999e+68, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') petagrams_per_cubic_attometer = NamedUnit(9.999999999999998e+65, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') teragrams_per_cubic_attometer = NamedUnit(9.999999999999999e+62, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') @@ -1446,10 +1446,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_attometer = NamedUnit(1e+39, Dimensions(length=-3, mass=1), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') femtograms_per_cubic_attometer = NamedUnit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') attograms_per_cubic_attometer = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') -atomic_mass_units_per_cubic_attometer = NamedUnit(1.6605389209999997e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='Noneam⁻³') -pounds_per_cubic_attometer = NamedUnit(4.5359237e+53, Dimensions(length=-3, mass=1), name='pounds_per_cubic_attometer', ascii_symbol='lb am^-3', symbol='Noneam⁻³') -ounces_per_cubic_attometer = NamedUnit(2.8349523125e+52, Dimensions(length=-3, mass=1), name='ounces_per_cubic_attometer', ascii_symbol='oz am^-3', symbol='Noneam⁻³') -grams_per_cubic_decimeter = NamedUnit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='Nonedm⁻³') +atomic_mass_units_per_cubic_attometer = NamedUnit(1.6605389209999997e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='auam⁻³') +pounds_per_cubic_attometer = NamedUnit(4.5359237e+53, Dimensions(length=-3, mass=1), name='pounds_per_cubic_attometer', ascii_symbol='lb am^-3', symbol='lbam⁻³') +ounces_per_cubic_attometer = NamedUnit(2.8349523125e+52, Dimensions(length=-3, mass=1), name='ounces_per_cubic_attometer', ascii_symbol='oz am^-3', symbol='ozam⁻³') +grams_per_cubic_decimeter = NamedUnit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='gdm⁻³') exagrams_per_cubic_decimeter = NamedUnit(9.999999999999997e+17, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_decimeter', ascii_symbol='Eg dm^-3', symbol='Egdm⁻³') petagrams_per_cubic_decimeter = NamedUnit(999999999999999.8, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_decimeter', ascii_symbol='Pg dm^-3', symbol='Pgdm⁻³') teragrams_per_cubic_decimeter = NamedUnit(999999999999.9998, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_decimeter', ascii_symbol='Tg dm^-3', symbol='Tgdm⁻³') @@ -1462,10 +1462,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_decimeter = NamedUnit(9.999999999999998e-13, Dimensions(length=-3, mass=1), name='picograms_per_cubic_decimeter', ascii_symbol='pg dm^-3', symbol='pgdm⁻³') femtograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_decimeter', ascii_symbol='fg dm^-3', symbol='fgdm⁻³') attograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-19, Dimensions(length=-3, mass=1), name='attograms_per_cubic_decimeter', ascii_symbol='ag dm^-3', symbol='agdm⁻³') -atomic_mass_units_per_cubic_decimeter = NamedUnit(1.6605389209999993e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='Nonedm⁻³') -pounds_per_cubic_decimeter = NamedUnit(453.5923699999999, Dimensions(length=-3, mass=1), name='pounds_per_cubic_decimeter', ascii_symbol='lb dm^-3', symbol='Nonedm⁻³') -ounces_per_cubic_decimeter = NamedUnit(28.349523124999994, Dimensions(length=-3, mass=1), name='ounces_per_cubic_decimeter', ascii_symbol='oz dm^-3', symbol='Nonedm⁻³') -grams_per_cubic_centimeter = NamedUnit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='Nonecm⁻³') +atomic_mass_units_per_cubic_decimeter = NamedUnit(1.6605389209999993e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='audm⁻³') +pounds_per_cubic_decimeter = NamedUnit(453.5923699999999, Dimensions(length=-3, mass=1), name='pounds_per_cubic_decimeter', ascii_symbol='lb dm^-3', symbol='lbdm⁻³') +ounces_per_cubic_decimeter = NamedUnit(28.349523124999994, Dimensions(length=-3, mass=1), name='ounces_per_cubic_decimeter', ascii_symbol='oz dm^-3', symbol='ozdm⁻³') +grams_per_cubic_centimeter = NamedUnit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='gcm⁻³') exagrams_per_cubic_centimeter = NamedUnit(9.999999999999999e+20, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_centimeter', ascii_symbol='Eg cm^-3', symbol='Egcm⁻³') petagrams_per_cubic_centimeter = NamedUnit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_centimeter', ascii_symbol='Pg cm^-3', symbol='Pgcm⁻³') teragrams_per_cubic_centimeter = NamedUnit(999999999999999.9, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_centimeter', ascii_symbol='Tg cm^-3', symbol='Tgcm⁻³') @@ -1478,10 +1478,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_centimeter = NamedUnit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='picograms_per_cubic_centimeter', ascii_symbol='pg cm^-3', symbol='pgcm⁻³') femtograms_per_cubic_centimeter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_centimeter', ascii_symbol='fg cm^-3', symbol='fgcm⁻³') attograms_per_cubic_centimeter = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='attograms_per_cubic_centimeter', ascii_symbol='ag cm^-3', symbol='agcm⁻³') -atomic_mass_units_per_cubic_centimeter = NamedUnit(1.6605389209999996e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='Nonecm⁻³') -pounds_per_cubic_centimeter = NamedUnit(453592.36999999994, Dimensions(length=-3, mass=1), name='pounds_per_cubic_centimeter', ascii_symbol='lb cm^-3', symbol='Nonecm⁻³') -ounces_per_cubic_centimeter = NamedUnit(28349.523124999996, Dimensions(length=-3, mass=1), name='ounces_per_cubic_centimeter', ascii_symbol='oz cm^-3', symbol='Nonecm⁻³') -grams_per_cubic_angstrom = NamedUnit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') +atomic_mass_units_per_cubic_centimeter = NamedUnit(1.6605389209999996e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='aucm⁻³') +pounds_per_cubic_centimeter = NamedUnit(453592.36999999994, Dimensions(length=-3, mass=1), name='pounds_per_cubic_centimeter', ascii_symbol='lb cm^-3', symbol='lbcm⁻³') +ounces_per_cubic_centimeter = NamedUnit(28349.523124999996, Dimensions(length=-3, mass=1), name='ounces_per_cubic_centimeter', ascii_symbol='oz cm^-3', symbol='ozcm⁻³') +grams_per_cubic_angstrom = NamedUnit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='gÅ⁻³') exagrams_per_cubic_angstrom = NamedUnit(1e+45, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') petagrams_per_cubic_angstrom = NamedUnit(9.999999999999999e+41, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') teragrams_per_cubic_angstrom = NamedUnit(1e+39, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') @@ -1494,213 +1494,213 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_angstrom = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') femtograms_per_cubic_angstrom = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') attograms_per_cubic_angstrom = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') -atomic_mass_units_per_cubic_angstrom = NamedUnit(1660.5389209999996, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='NoneÅ⁻³') -pounds_per_cubic_angstrom = NamedUnit(4.5359237e+29, Dimensions(length=-3, mass=1), name='pounds_per_cubic_angstrom', ascii_symbol='lb Ang^-3', symbol='NoneÅ⁻³') -ounces_per_cubic_angstrom = NamedUnit(2.8349523125e+28, Dimensions(length=-3, mass=1), name='ounces_per_cubic_angstrom', ascii_symbol='oz Ang^-3', symbol='NoneÅ⁻³') -grams_per_cubic_mile = NamedUnit(2.399127585789277e-13, Dimensions(length=-3, mass=1), name='grams_per_cubic_mile', ascii_symbol='g miles^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_mile = NamedUnit(239912.7585789277, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_mile', ascii_symbol='Eg miles^-3', symbol='EgNone⁻³') -petagrams_per_cubic_mile = NamedUnit(239.9127585789277, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_mile', ascii_symbol='Pg miles^-3', symbol='PgNone⁻³') -teragrams_per_cubic_mile = NamedUnit(0.2399127585789277, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_mile', ascii_symbol='Tg miles^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_mile = NamedUnit(0.0002399127585789277, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_mile', ascii_symbol='Gg miles^-3', symbol='GgNone⁻³') -megagrams_per_cubic_mile = NamedUnit(2.399127585789277e-07, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_mile', ascii_symbol='Mg miles^-3', symbol='MgNone⁻³') -kilograms_per_cubic_mile = NamedUnit(2.399127585789277e-10, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_mile', ascii_symbol='kg miles^-3', symbol='kgNone⁻³') -milligrams_per_cubic_mile = NamedUnit(2.399127585789277e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_mile', ascii_symbol='mg miles^-3', symbol='mgNone⁻³') -micrograms_per_cubic_mile = NamedUnit(2.3991275857892774e-19, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_mile', ascii_symbol='ug miles^-3', symbol='µgNone⁻³') -nanograms_per_cubic_mile = NamedUnit(2.3991275857892774e-22, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_mile', ascii_symbol='ng miles^-3', symbol='ngNone⁻³') -picograms_per_cubic_mile = NamedUnit(2.399127585789277e-25, Dimensions(length=-3, mass=1), name='picograms_per_cubic_mile', ascii_symbol='pg miles^-3', symbol='pgNone⁻³') -femtograms_per_cubic_mile = NamedUnit(2.3991275857892772e-28, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_mile', ascii_symbol='fg miles^-3', symbol='fgNone⁻³') -attograms_per_cubic_mile = NamedUnit(2.399127585789277e-31, Dimensions(length=-3, mass=1), name='attograms_per_cubic_mile', ascii_symbol='ag miles^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_mile = NamedUnit(3.98384473264786e-37, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_mile', ascii_symbol='au miles^-3', symbol='NoneNone⁻³') -pounds_per_cubic_mile = NamedUnit(1.0882259675705365e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_mile', ascii_symbol='lb miles^-3', symbol='NoneNone⁻³') -ounces_per_cubic_mile = NamedUnit(6.801412297315853e-12, Dimensions(length=-3, mass=1), name='ounces_per_cubic_mile', ascii_symbol='oz miles^-3', symbol='NoneNone⁻³') -grams_per_cubic_yard = NamedUnit(0.0013079506193143919, Dimensions(length=-3, mass=1), name='grams_per_cubic_yard', ascii_symbol='g yrd^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_yard = NamedUnit(1307950619314391.8, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_yard', ascii_symbol='Eg yrd^-3', symbol='EgNone⁻³') -petagrams_per_cubic_yard = NamedUnit(1307950619314.3918, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_yard', ascii_symbol='Pg yrd^-3', symbol='PgNone⁻³') -teragrams_per_cubic_yard = NamedUnit(1307950619.3143919, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_yard', ascii_symbol='Tg yrd^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_yard = NamedUnit(1307950.6193143919, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_yard', ascii_symbol='Gg yrd^-3', symbol='GgNone⁻³') -megagrams_per_cubic_yard = NamedUnit(1307.9506193143918, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_yard', ascii_symbol='Mg yrd^-3', symbol='MgNone⁻³') -kilograms_per_cubic_yard = NamedUnit(1.3079506193143917, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_yard', ascii_symbol='kg yrd^-3', symbol='kgNone⁻³') -milligrams_per_cubic_yard = NamedUnit(1.3079506193143917e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_yard', ascii_symbol='mg yrd^-3', symbol='mgNone⁻³') -micrograms_per_cubic_yard = NamedUnit(1.3079506193143919e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_yard', ascii_symbol='ug yrd^-3', symbol='µgNone⁻³') -nanograms_per_cubic_yard = NamedUnit(1.307950619314392e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_yard', ascii_symbol='ng yrd^-3', symbol='ngNone⁻³') -picograms_per_cubic_yard = NamedUnit(1.3079506193143919e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_yard', ascii_symbol='pg yrd^-3', symbol='pgNone⁻³') -femtograms_per_cubic_yard = NamedUnit(1.3079506193143918e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_yard', ascii_symbol='fg yrd^-3', symbol='fgNone⁻³') -attograms_per_cubic_yard = NamedUnit(1.307950619314392e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_yard', ascii_symbol='ag yrd^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_yard = NamedUnit(2.1719029101176016e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_yard', ascii_symbol='au yrd^-3', symbol='NoneNone⁻³') -pounds_per_cubic_yard = NamedUnit(0.5932764212577828, Dimensions(length=-3, mass=1), name='pounds_per_cubic_yard', ascii_symbol='lb yrd^-3', symbol='NoneNone⁻³') -ounces_per_cubic_yard = NamedUnit(0.037079776328611425, Dimensions(length=-3, mass=1), name='ounces_per_cubic_yard', ascii_symbol='oz yrd^-3', symbol='NoneNone⁻³') -grams_per_cubic_foot = NamedUnit(0.035314666721488586, Dimensions(length=-3, mass=1), name='grams_per_cubic_foot', ascii_symbol='g ft^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_foot = NamedUnit(3.5314666721488584e+16, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_foot', ascii_symbol='Eg ft^-3', symbol='EgNone⁻³') -petagrams_per_cubic_foot = NamedUnit(35314666721488.586, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_foot', ascii_symbol='Pg ft^-3', symbol='PgNone⁻³') -teragrams_per_cubic_foot = NamedUnit(35314666721.48859, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_foot', ascii_symbol='Tg ft^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_foot = NamedUnit(35314666.72148859, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_foot', ascii_symbol='Gg ft^-3', symbol='GgNone⁻³') -megagrams_per_cubic_foot = NamedUnit(35314.66672148858, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_foot', ascii_symbol='Mg ft^-3', symbol='MgNone⁻³') -kilograms_per_cubic_foot = NamedUnit(35.314666721488585, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_foot', ascii_symbol='kg ft^-3', symbol='kgNone⁻³') -milligrams_per_cubic_foot = NamedUnit(3.5314666721488586e-05, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_foot', ascii_symbol='mg ft^-3', symbol='mgNone⁻³') -micrograms_per_cubic_foot = NamedUnit(3.5314666721488584e-08, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_foot', ascii_symbol='ug ft^-3', symbol='µgNone⁻³') -nanograms_per_cubic_foot = NamedUnit(3.531466672148859e-11, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_foot', ascii_symbol='ng ft^-3', symbol='ngNone⁻³') -picograms_per_cubic_foot = NamedUnit(3.531466672148859e-14, Dimensions(length=-3, mass=1), name='picograms_per_cubic_foot', ascii_symbol='pg ft^-3', symbol='pgNone⁻³') -femtograms_per_cubic_foot = NamedUnit(3.5314666721488585e-17, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_foot', ascii_symbol='fg ft^-3', symbol='fgNone⁻³') -attograms_per_cubic_foot = NamedUnit(3.531466672148859e-20, Dimensions(length=-3, mass=1), name='attograms_per_cubic_foot', ascii_symbol='ag ft^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_foot = NamedUnit(5.864137857317526e-26, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_foot', ascii_symbol='au ft^-3', symbol='NoneNone⁻³') -pounds_per_cubic_foot = NamedUnit(16.018463373960138, Dimensions(length=-3, mass=1), name='pounds_per_cubic_foot', ascii_symbol='lb ft^-3', symbol='NoneNone⁻³') -ounces_per_cubic_foot = NamedUnit(1.0011539608725086, Dimensions(length=-3, mass=1), name='ounces_per_cubic_foot', ascii_symbol='oz ft^-3', symbol='NoneNone⁻³') -grams_per_cubic_inch = NamedUnit(61.02374409473229, Dimensions(length=-3, mass=1), name='grams_per_cubic_inch', ascii_symbol='g in^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_inch = NamedUnit(6.102374409473229e+19, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_inch', ascii_symbol='Eg in^-3', symbol='EgNone⁻³') -petagrams_per_cubic_inch = NamedUnit(6.102374409473229e+16, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_inch', ascii_symbol='Pg in^-3', symbol='PgNone⁻³') -teragrams_per_cubic_inch = NamedUnit(61023744094732.29, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_inch', ascii_symbol='Tg in^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_inch = NamedUnit(61023744094.732285, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_inch', ascii_symbol='Gg in^-3', symbol='GgNone⁻³') -megagrams_per_cubic_inch = NamedUnit(61023744.094732285, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_inch', ascii_symbol='Mg in^-3', symbol='MgNone⁻³') -kilograms_per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_inch', ascii_symbol='kg in^-3', symbol='kgNone⁻³') -milligrams_per_cubic_inch = NamedUnit(0.06102374409473228, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_inch', ascii_symbol='mg in^-3', symbol='mgNone⁻³') -micrograms_per_cubic_inch = NamedUnit(6.102374409473229e-05, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_inch', ascii_symbol='ug in^-3', symbol='µgNone⁻³') -nanograms_per_cubic_inch = NamedUnit(6.10237440947323e-08, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_inch', ascii_symbol='ng in^-3', symbol='ngNone⁻³') -picograms_per_cubic_inch = NamedUnit(6.102374409473229e-11, Dimensions(length=-3, mass=1), name='picograms_per_cubic_inch', ascii_symbol='pg in^-3', symbol='pgNone⁻³') -femtograms_per_cubic_inch = NamedUnit(6.10237440947323e-14, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_inch', ascii_symbol='fg in^-3', symbol='fgNone⁻³') -attograms_per_cubic_inch = NamedUnit(6.10237440947323e-17, Dimensions(length=-3, mass=1), name='attograms_per_cubic_inch', ascii_symbol='ag in^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_inch = NamedUnit(1.0133230217444687e-22, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_inch', ascii_symbol='au in^-3', symbol='NoneNone⁻³') -pounds_per_cubic_inch = NamedUnit(27679.904710203125, Dimensions(length=-3, mass=1), name='pounds_per_cubic_inch', ascii_symbol='lb in^-3', symbol='NoneNone⁻³') -ounces_per_cubic_inch = NamedUnit(1729.9940443876953, Dimensions(length=-3, mass=1), name='ounces_per_cubic_inch', ascii_symbol='oz in^-3', symbol='NoneNone⁻³') -moles_per_cubic_meter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_meter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_meter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_meter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_meter = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_meter = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_meter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolNone⁻³') -moles_per_cubic_exameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='NoneEm⁻³') +atomic_mass_units_per_cubic_angstrom = NamedUnit(1660.5389209999996, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='auÅ⁻³') +pounds_per_cubic_angstrom = NamedUnit(4.5359237e+29, Dimensions(length=-3, mass=1), name='pounds_per_cubic_angstrom', ascii_symbol='lb Ang^-3', symbol='lbÅ⁻³') +ounces_per_cubic_angstrom = NamedUnit(2.8349523125e+28, Dimensions(length=-3, mass=1), name='ounces_per_cubic_angstrom', ascii_symbol='oz Ang^-3', symbol='ozÅ⁻³') +grams_per_cubic_mile = NamedUnit(2.399127585789277e-13, Dimensions(length=-3, mass=1), name='grams_per_cubic_mile', ascii_symbol='g miles^-3', symbol='gmiles⁻³') +exagrams_per_cubic_mile = NamedUnit(239912.7585789277, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_mile', ascii_symbol='Eg miles^-3', symbol='Egmiles⁻³') +petagrams_per_cubic_mile = NamedUnit(239.9127585789277, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_mile', ascii_symbol='Pg miles^-3', symbol='Pgmiles⁻³') +teragrams_per_cubic_mile = NamedUnit(0.2399127585789277, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_mile', ascii_symbol='Tg miles^-3', symbol='Tgmiles⁻³') +gigagrams_per_cubic_mile = NamedUnit(0.0002399127585789277, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_mile', ascii_symbol='Gg miles^-3', symbol='Ggmiles⁻³') +megagrams_per_cubic_mile = NamedUnit(2.399127585789277e-07, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_mile', ascii_symbol='Mg miles^-3', symbol='Mgmiles⁻³') +kilograms_per_cubic_mile = NamedUnit(2.399127585789277e-10, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_mile', ascii_symbol='kg miles^-3', symbol='kgmiles⁻³') +milligrams_per_cubic_mile = NamedUnit(2.399127585789277e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_mile', ascii_symbol='mg miles^-3', symbol='mgmiles⁻³') +micrograms_per_cubic_mile = NamedUnit(2.3991275857892774e-19, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_mile', ascii_symbol='ug miles^-3', symbol='µgmiles⁻³') +nanograms_per_cubic_mile = NamedUnit(2.3991275857892774e-22, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_mile', ascii_symbol='ng miles^-3', symbol='ngmiles⁻³') +picograms_per_cubic_mile = NamedUnit(2.399127585789277e-25, Dimensions(length=-3, mass=1), name='picograms_per_cubic_mile', ascii_symbol='pg miles^-3', symbol='pgmiles⁻³') +femtograms_per_cubic_mile = NamedUnit(2.3991275857892772e-28, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_mile', ascii_symbol='fg miles^-3', symbol='fgmiles⁻³') +attograms_per_cubic_mile = NamedUnit(2.399127585789277e-31, Dimensions(length=-3, mass=1), name='attograms_per_cubic_mile', ascii_symbol='ag miles^-3', symbol='agmiles⁻³') +atomic_mass_units_per_cubic_mile = NamedUnit(3.98384473264786e-37, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_mile', ascii_symbol='au miles^-3', symbol='aumiles⁻³') +pounds_per_cubic_mile = NamedUnit(1.0882259675705365e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_mile', ascii_symbol='lb miles^-3', symbol='lbmiles⁻³') +ounces_per_cubic_mile = NamedUnit(6.801412297315853e-12, Dimensions(length=-3, mass=1), name='ounces_per_cubic_mile', ascii_symbol='oz miles^-3', symbol='ozmiles⁻³') +grams_per_cubic_yard = NamedUnit(0.0013079506193143919, Dimensions(length=-3, mass=1), name='grams_per_cubic_yard', ascii_symbol='g yrd^-3', symbol='gyrd⁻³') +exagrams_per_cubic_yard = NamedUnit(1307950619314391.8, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_yard', ascii_symbol='Eg yrd^-3', symbol='Egyrd⁻³') +petagrams_per_cubic_yard = NamedUnit(1307950619314.3918, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_yard', ascii_symbol='Pg yrd^-3', symbol='Pgyrd⁻³') +teragrams_per_cubic_yard = NamedUnit(1307950619.3143919, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_yard', ascii_symbol='Tg yrd^-3', symbol='Tgyrd⁻³') +gigagrams_per_cubic_yard = NamedUnit(1307950.6193143919, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_yard', ascii_symbol='Gg yrd^-3', symbol='Ggyrd⁻³') +megagrams_per_cubic_yard = NamedUnit(1307.9506193143918, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_yard', ascii_symbol='Mg yrd^-3', symbol='Mgyrd⁻³') +kilograms_per_cubic_yard = NamedUnit(1.3079506193143917, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_yard', ascii_symbol='kg yrd^-3', symbol='kgyrd⁻³') +milligrams_per_cubic_yard = NamedUnit(1.3079506193143917e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_yard', ascii_symbol='mg yrd^-3', symbol='mgyrd⁻³') +micrograms_per_cubic_yard = NamedUnit(1.3079506193143919e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_yard', ascii_symbol='ug yrd^-3', symbol='µgyrd⁻³') +nanograms_per_cubic_yard = NamedUnit(1.307950619314392e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_yard', ascii_symbol='ng yrd^-3', symbol='ngyrd⁻³') +picograms_per_cubic_yard = NamedUnit(1.3079506193143919e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_yard', ascii_symbol='pg yrd^-3', symbol='pgyrd⁻³') +femtograms_per_cubic_yard = NamedUnit(1.3079506193143918e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_yard', ascii_symbol='fg yrd^-3', symbol='fgyrd⁻³') +attograms_per_cubic_yard = NamedUnit(1.307950619314392e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_yard', ascii_symbol='ag yrd^-3', symbol='agyrd⁻³') +atomic_mass_units_per_cubic_yard = NamedUnit(2.1719029101176016e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_yard', ascii_symbol='au yrd^-3', symbol='auyrd⁻³') +pounds_per_cubic_yard = NamedUnit(0.5932764212577828, Dimensions(length=-3, mass=1), name='pounds_per_cubic_yard', ascii_symbol='lb yrd^-3', symbol='lbyrd⁻³') +ounces_per_cubic_yard = NamedUnit(0.037079776328611425, Dimensions(length=-3, mass=1), name='ounces_per_cubic_yard', ascii_symbol='oz yrd^-3', symbol='ozyrd⁻³') +grams_per_cubic_foot = NamedUnit(0.035314666721488586, Dimensions(length=-3, mass=1), name='grams_per_cubic_foot', ascii_symbol='g ft^-3', symbol='gft⁻³') +exagrams_per_cubic_foot = NamedUnit(3.5314666721488584e+16, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_foot', ascii_symbol='Eg ft^-3', symbol='Egft⁻³') +petagrams_per_cubic_foot = NamedUnit(35314666721488.586, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_foot', ascii_symbol='Pg ft^-3', symbol='Pgft⁻³') +teragrams_per_cubic_foot = NamedUnit(35314666721.48859, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_foot', ascii_symbol='Tg ft^-3', symbol='Tgft⁻³') +gigagrams_per_cubic_foot = NamedUnit(35314666.72148859, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_foot', ascii_symbol='Gg ft^-3', symbol='Ggft⁻³') +megagrams_per_cubic_foot = NamedUnit(35314.66672148858, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_foot', ascii_symbol='Mg ft^-3', symbol='Mgft⁻³') +kilograms_per_cubic_foot = NamedUnit(35.314666721488585, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_foot', ascii_symbol='kg ft^-3', symbol='kgft⁻³') +milligrams_per_cubic_foot = NamedUnit(3.5314666721488586e-05, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_foot', ascii_symbol='mg ft^-3', symbol='mgft⁻³') +micrograms_per_cubic_foot = NamedUnit(3.5314666721488584e-08, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_foot', ascii_symbol='ug ft^-3', symbol='µgft⁻³') +nanograms_per_cubic_foot = NamedUnit(3.531466672148859e-11, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_foot', ascii_symbol='ng ft^-3', symbol='ngft⁻³') +picograms_per_cubic_foot = NamedUnit(3.531466672148859e-14, Dimensions(length=-3, mass=1), name='picograms_per_cubic_foot', ascii_symbol='pg ft^-3', symbol='pgft⁻³') +femtograms_per_cubic_foot = NamedUnit(3.5314666721488585e-17, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_foot', ascii_symbol='fg ft^-3', symbol='fgft⁻³') +attograms_per_cubic_foot = NamedUnit(3.531466672148859e-20, Dimensions(length=-3, mass=1), name='attograms_per_cubic_foot', ascii_symbol='ag ft^-3', symbol='agft⁻³') +atomic_mass_units_per_cubic_foot = NamedUnit(5.864137857317526e-26, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_foot', ascii_symbol='au ft^-3', symbol='auft⁻³') +pounds_per_cubic_foot = NamedUnit(16.018463373960138, Dimensions(length=-3, mass=1), name='pounds_per_cubic_foot', ascii_symbol='lb ft^-3', symbol='lbft⁻³') +ounces_per_cubic_foot = NamedUnit(1.0011539608725086, Dimensions(length=-3, mass=1), name='ounces_per_cubic_foot', ascii_symbol='oz ft^-3', symbol='ozft⁻³') +grams_per_cubic_inch = NamedUnit(61.02374409473229, Dimensions(length=-3, mass=1), name='grams_per_cubic_inch', ascii_symbol='g in^-3', symbol='gin⁻³') +exagrams_per_cubic_inch = NamedUnit(6.102374409473229e+19, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_inch', ascii_symbol='Eg in^-3', symbol='Egin⁻³') +petagrams_per_cubic_inch = NamedUnit(6.102374409473229e+16, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_inch', ascii_symbol='Pg in^-3', symbol='Pgin⁻³') +teragrams_per_cubic_inch = NamedUnit(61023744094732.29, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_inch', ascii_symbol='Tg in^-3', symbol='Tgin⁻³') +gigagrams_per_cubic_inch = NamedUnit(61023744094.732285, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_inch', ascii_symbol='Gg in^-3', symbol='Ggin⁻³') +megagrams_per_cubic_inch = NamedUnit(61023744.094732285, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_inch', ascii_symbol='Mg in^-3', symbol='Mgin⁻³') +kilograms_per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_inch', ascii_symbol='kg in^-3', symbol='kgin⁻³') +milligrams_per_cubic_inch = NamedUnit(0.06102374409473228, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_inch', ascii_symbol='mg in^-3', symbol='mgin⁻³') +micrograms_per_cubic_inch = NamedUnit(6.102374409473229e-05, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_inch', ascii_symbol='ug in^-3', symbol='µgin⁻³') +nanograms_per_cubic_inch = NamedUnit(6.10237440947323e-08, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_inch', ascii_symbol='ng in^-3', symbol='ngin⁻³') +picograms_per_cubic_inch = NamedUnit(6.102374409473229e-11, Dimensions(length=-3, mass=1), name='picograms_per_cubic_inch', ascii_symbol='pg in^-3', symbol='pgin⁻³') +femtograms_per_cubic_inch = NamedUnit(6.10237440947323e-14, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_inch', ascii_symbol='fg in^-3', symbol='fgin⁻³') +attograms_per_cubic_inch = NamedUnit(6.10237440947323e-17, Dimensions(length=-3, mass=1), name='attograms_per_cubic_inch', ascii_symbol='ag in^-3', symbol='agin⁻³') +atomic_mass_units_per_cubic_inch = NamedUnit(1.0133230217444687e-22, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_inch', ascii_symbol='au in^-3', symbol='auin⁻³') +pounds_per_cubic_inch = NamedUnit(27679.904710203125, Dimensions(length=-3, mass=1), name='pounds_per_cubic_inch', ascii_symbol='lb in^-3', symbol='lbin⁻³') +ounces_per_cubic_inch = NamedUnit(1729.9940443876953, Dimensions(length=-3, mass=1), name='ounces_per_cubic_inch', ascii_symbol='oz in^-3', symbol='ozin⁻³') +moles_per_cubic_meter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='molm⁻³') +millimoles_per_cubic_meter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolm⁻³') +micromoles_per_cubic_meter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolm⁻³') +nanomoles_per_cubic_meter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolm⁻³') +picomoles_per_cubic_meter = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolm⁻³') +femtomoles_per_cubic_meter = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolm⁻³') +attomoles_per_cubic_meter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolm⁻³') +moles_per_cubic_exameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='molEm⁻³') millimoles_per_cubic_exameter = NamedUnit(6.02214076e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') micromoles_per_cubic_exameter = NamedUnit(6.02214076e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') nanomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') picomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') femtomoles_per_cubic_exameter = NamedUnit(6.02214076e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') attomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') -moles_per_cubic_petameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='NonePm⁻³') +moles_per_cubic_petameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='molPm⁻³') millimoles_per_cubic_petameter = NamedUnit(6.0221407600000005e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') micromoles_per_cubic_petameter = NamedUnit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') nanomoles_per_cubic_petameter = NamedUnit(6.02214076e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') picomoles_per_cubic_petameter = NamedUnit(6.0221407600000005e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') femtomoles_per_cubic_petameter = NamedUnit(6.022140760000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') attomoles_per_cubic_petameter = NamedUnit(6.022140760000001e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') -moles_per_cubic_terameter = NamedUnit(6.022140759999999e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='NoneTm⁻³') +moles_per_cubic_terameter = NamedUnit(6.022140759999999e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='molTm⁻³') millimoles_per_cubic_terameter = NamedUnit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') micromoles_per_cubic_terameter = NamedUnit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') nanomoles_per_cubic_terameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') picomoles_per_cubic_terameter = NamedUnit(6.02214076e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') femtomoles_per_cubic_terameter = NamedUnit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') attomoles_per_cubic_terameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') -moles_per_cubic_gigameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='NoneGm⁻³') +moles_per_cubic_gigameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='molGm⁻³') millimoles_per_cubic_gigameter = NamedUnit(6.022140760000001e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') micromoles_per_cubic_gigameter = NamedUnit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') nanomoles_per_cubic_gigameter = NamedUnit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') picomoles_per_cubic_gigameter = NamedUnit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') femtomoles_per_cubic_gigameter = NamedUnit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') attomoles_per_cubic_gigameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') -moles_per_cubic_megameter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='NoneMm⁻³') +moles_per_cubic_megameter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='molMm⁻³') millimoles_per_cubic_megameter = NamedUnit(602.214076, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') micromoles_per_cubic_megameter = NamedUnit(0.602214076, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') nanomoles_per_cubic_megameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') picomoles_per_cubic_megameter = NamedUnit(6.02214076e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') femtomoles_per_cubic_megameter = NamedUnit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') attomoles_per_cubic_megameter = NamedUnit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') -moles_per_cubic_kilometer = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='Nonekm⁻³') +moles_per_cubic_kilometer = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='molkm⁻³') millimoles_per_cubic_kilometer = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') micromoles_per_cubic_kilometer = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') nanomoles_per_cubic_kilometer = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') picomoles_per_cubic_kilometer = NamedUnit(602.214076, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') femtomoles_per_cubic_kilometer = NamedUnit(0.602214076, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') attomoles_per_cubic_kilometer = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') -moles_per_cubic_millimeter = NamedUnit(6.0221407599999995e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='Nonemm⁻³') +moles_per_cubic_millimeter = NamedUnit(6.0221407599999995e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='molmm⁻³') millimoles_per_cubic_millimeter = NamedUnit(6.02214076e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') micromoles_per_cubic_millimeter = NamedUnit(6.0221407599999996e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') nanomoles_per_cubic_millimeter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') picomoles_per_cubic_millimeter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') femtomoles_per_cubic_millimeter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') attomoles_per_cubic_millimeter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') -moles_per_cubic_micrometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='Noneµm⁻³') +moles_per_cubic_micrometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='molµm⁻³') millimoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') micromoles_per_cubic_micrometer = NamedUnit(6.0221407600000004e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') nanomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') picomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') femtomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') attomoles_per_cubic_micrometer = NamedUnit(6.0221407600000005e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') -moles_per_cubic_nanometer = NamedUnit(6.022140759999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='Nonenm⁻³') +moles_per_cubic_nanometer = NamedUnit(6.022140759999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='molnm⁻³') millimoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') micromoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') nanomoles_per_cubic_nanometer = NamedUnit(6.022140759999998e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') picomoles_per_cubic_nanometer = NamedUnit(6.0221407599999985e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') femtomoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') attomoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') -moles_per_cubic_picometer = NamedUnit(6.0221407600000005e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='Nonepm⁻³') +moles_per_cubic_picometer = NamedUnit(6.0221407600000005e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='molpm⁻³') millimoles_per_cubic_picometer = NamedUnit(6.0221407600000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') micromoles_per_cubic_picometer = NamedUnit(6.022140760000001e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') nanomoles_per_cubic_picometer = NamedUnit(6.0221407600000005e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') picomoles_per_cubic_picometer = NamedUnit(6.02214076e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') femtomoles_per_cubic_picometer = NamedUnit(6.022140760000001e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') attomoles_per_cubic_picometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') -moles_per_cubic_femtometer = NamedUnit(6.022140759999998e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='Nonefm⁻³') +moles_per_cubic_femtometer = NamedUnit(6.022140759999998e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='molfm⁻³') millimoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') micromoles_per_cubic_femtometer = NamedUnit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') nanomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') picomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') femtomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') attomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') -moles_per_cubic_attometer = NamedUnit(6.022140759999998e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='Noneam⁻³') +moles_per_cubic_attometer = NamedUnit(6.022140759999998e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='molam⁻³') millimoles_per_cubic_attometer = NamedUnit(6.022140759999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') micromoles_per_cubic_attometer = NamedUnit(6.022140759999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') nanomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') picomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') femtomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') attomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') -moles_per_cubic_decimeter = NamedUnit(6.022140759999998e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='Nonedm⁻³') +moles_per_cubic_decimeter = NamedUnit(6.022140759999998e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='moldm⁻³') millimoles_per_cubic_decimeter = NamedUnit(6.0221407599999985e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') micromoles_per_cubic_decimeter = NamedUnit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') nanomoles_per_cubic_decimeter = NamedUnit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') picomoles_per_cubic_decimeter = NamedUnit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') femtomoles_per_cubic_decimeter = NamedUnit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') attomoles_per_cubic_decimeter = NamedUnit(602214075.9999999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') -moles_per_cubic_centimeter = NamedUnit(6.022140759999999e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='Nonecm⁻³') +moles_per_cubic_centimeter = NamedUnit(6.022140759999999e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='molcm⁻³') millimoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') micromoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') nanomoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') picomoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') femtomoles_per_cubic_centimeter = NamedUnit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') attomoles_per_cubic_centimeter = NamedUnit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') -moles_per_cubic_angstrom = NamedUnit(6.022140759999999e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='NoneÅ⁻³') +moles_per_cubic_angstrom = NamedUnit(6.022140759999999e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='molÅ⁻³') millimoles_per_cubic_angstrom = NamedUnit(6.02214076e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') micromoles_per_cubic_angstrom = NamedUnit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') nanomoles_per_cubic_angstrom = NamedUnit(6.02214076e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') picomoles_per_cubic_angstrom = NamedUnit(6.02214076e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') femtomoles_per_cubic_angstrom = NamedUnit(6.022140759999999e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') attomoles_per_cubic_angstrom = NamedUnit(6.02214076e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') -moles_per_cubic_mile = NamedUnit(144478840228220.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_mile', ascii_symbol='mol miles^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_mile = NamedUnit(144478840228.22003, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_mile', ascii_symbol='mmol miles^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_mile = NamedUnit(144478840.22822002, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_mile', ascii_symbol='umol miles^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_mile = NamedUnit(144478.84022822, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_mile', ascii_symbol='nmol miles^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_mile = NamedUnit(144.47884022822, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_mile', ascii_symbol='pmol miles^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_mile = NamedUnit(0.14447884022822002, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_mile', ascii_symbol='fmol miles^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_mile = NamedUnit(0.00014447884022822003, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_mile', ascii_symbol='amol miles^-3', symbol='amolNone⁻³') -moles_per_cubic_yard = NamedUnit(7.876662736640442e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_yard', ascii_symbol='mol yrd^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_yard = NamedUnit(7.876662736640442e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_yard', ascii_symbol='mmol yrd^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_yard = NamedUnit(7.876662736640442e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_yard', ascii_symbol='umol yrd^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_yard = NamedUnit(787666273664044.2, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_yard', ascii_symbol='nmol yrd^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_yard = NamedUnit(787666273664.0442, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_yard', ascii_symbol='pmol yrd^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_yard = NamedUnit(787666273.6640443, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_yard', ascii_symbol='fmol yrd^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_yard = NamedUnit(787666.2736640442, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_yard', ascii_symbol='amol yrd^-3', symbol='amolNone⁻³') -moles_per_cubic_foot = NamedUnit(2.1266989388929195e+25, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_foot', ascii_symbol='mol ft^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_foot = NamedUnit(2.1266989388929197e+22, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_foot', ascii_symbol='mmol ft^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_foot = NamedUnit(2.1266989388929196e+19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_foot', ascii_symbol='umol ft^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_foot = NamedUnit(2.1266989388929196e+16, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_foot', ascii_symbol='nmol ft^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_foot = NamedUnit(21266989388929.2, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_foot', ascii_symbol='pmol ft^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_foot = NamedUnit(21266989388.9292, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_foot', ascii_symbol='fmol ft^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_foot = NamedUnit(21266989.388929196, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_foot', ascii_symbol='amol ft^-3', symbol='amolNone⁻³') -moles_per_cubic_inch = NamedUnit(3.6749357664069658e+28, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_inch', ascii_symbol='mol in^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_inch = NamedUnit(3.674935766406966e+25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_inch', ascii_symbol='mmol in^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_inch = NamedUnit(3.674935766406966e+22, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_inch', ascii_symbol='umol in^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_inch = NamedUnit(3.674935766406966e+19, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_inch', ascii_symbol='nmol in^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_inch = NamedUnit(3.674935766406966e+16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_inch', ascii_symbol='pmol in^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_inch = NamedUnit(36749357664069.664, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_inch', ascii_symbol='fmol in^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_inch = NamedUnit(36749357664.069664, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_inch', ascii_symbol='amol in^-3', symbol='amolNone⁻³') +moles_per_cubic_mile = NamedUnit(144478840228220.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_mile', ascii_symbol='mol miles^-3', symbol='molmiles⁻³') +millimoles_per_cubic_mile = NamedUnit(144478840228.22003, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_mile', ascii_symbol='mmol miles^-3', symbol='mmolmiles⁻³') +micromoles_per_cubic_mile = NamedUnit(144478840.22822002, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_mile', ascii_symbol='umol miles^-3', symbol='µmolmiles⁻³') +nanomoles_per_cubic_mile = NamedUnit(144478.84022822, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_mile', ascii_symbol='nmol miles^-3', symbol='nmolmiles⁻³') +picomoles_per_cubic_mile = NamedUnit(144.47884022822, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_mile', ascii_symbol='pmol miles^-3', symbol='pmolmiles⁻³') +femtomoles_per_cubic_mile = NamedUnit(0.14447884022822002, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_mile', ascii_symbol='fmol miles^-3', symbol='fmolmiles⁻³') +attomoles_per_cubic_mile = NamedUnit(0.00014447884022822003, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_mile', ascii_symbol='amol miles^-3', symbol='amolmiles⁻³') +moles_per_cubic_yard = NamedUnit(7.876662736640442e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_yard', ascii_symbol='mol yrd^-3', symbol='molyrd⁻³') +millimoles_per_cubic_yard = NamedUnit(7.876662736640442e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_yard', ascii_symbol='mmol yrd^-3', symbol='mmolyrd⁻³') +micromoles_per_cubic_yard = NamedUnit(7.876662736640442e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_yard', ascii_symbol='umol yrd^-3', symbol='µmolyrd⁻³') +nanomoles_per_cubic_yard = NamedUnit(787666273664044.2, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_yard', ascii_symbol='nmol yrd^-3', symbol='nmolyrd⁻³') +picomoles_per_cubic_yard = NamedUnit(787666273664.0442, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_yard', ascii_symbol='pmol yrd^-3', symbol='pmolyrd⁻³') +femtomoles_per_cubic_yard = NamedUnit(787666273.6640443, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_yard', ascii_symbol='fmol yrd^-3', symbol='fmolyrd⁻³') +attomoles_per_cubic_yard = NamedUnit(787666.2736640442, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_yard', ascii_symbol='amol yrd^-3', symbol='amolyrd⁻³') +moles_per_cubic_foot = NamedUnit(2.1266989388929195e+25, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_foot', ascii_symbol='mol ft^-3', symbol='molft⁻³') +millimoles_per_cubic_foot = NamedUnit(2.1266989388929197e+22, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_foot', ascii_symbol='mmol ft^-3', symbol='mmolft⁻³') +micromoles_per_cubic_foot = NamedUnit(2.1266989388929196e+19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_foot', ascii_symbol='umol ft^-3', symbol='µmolft⁻³') +nanomoles_per_cubic_foot = NamedUnit(2.1266989388929196e+16, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_foot', ascii_symbol='nmol ft^-3', symbol='nmolft⁻³') +picomoles_per_cubic_foot = NamedUnit(21266989388929.2, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_foot', ascii_symbol='pmol ft^-3', symbol='pmolft⁻³') +femtomoles_per_cubic_foot = NamedUnit(21266989388.9292, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_foot', ascii_symbol='fmol ft^-3', symbol='fmolft⁻³') +attomoles_per_cubic_foot = NamedUnit(21266989.388929196, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_foot', ascii_symbol='amol ft^-3', symbol='amolft⁻³') +moles_per_cubic_inch = NamedUnit(3.6749357664069658e+28, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_inch', ascii_symbol='mol in^-3', symbol='molin⁻³') +millimoles_per_cubic_inch = NamedUnit(3.674935766406966e+25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_inch', ascii_symbol='mmol in^-3', symbol='mmolin⁻³') +micromoles_per_cubic_inch = NamedUnit(3.674935766406966e+22, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_inch', ascii_symbol='umol in^-3', symbol='µmolin⁻³') +nanomoles_per_cubic_inch = NamedUnit(3.674935766406966e+19, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_inch', ascii_symbol='nmol in^-3', symbol='nmolin⁻³') +picomoles_per_cubic_inch = NamedUnit(3.674935766406966e+16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_inch', ascii_symbol='pmol in^-3', symbol='pmolin⁻³') +femtomoles_per_cubic_inch = NamedUnit(36749357664069.664, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_inch', ascii_symbol='fmol in^-3', symbol='fmolin⁻³') +attomoles_per_cubic_inch = NamedUnit(36749357664.069664, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_inch', ascii_symbol='amol in^-3', symbol='amolin⁻³') # # Lookup table from symbols to units From 64a9e045c19a5b9e922276a176089d2af5105942 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 3 Oct 2024 12:11:57 +0100 Subject: [PATCH 109/675] Filling in some of the working for the accessors --- sasdata/quantities/_accessor_base.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 42d14fc10..f8f0927bb 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -4,13 +4,36 @@ import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit +from sasdata.raw_form import Group, Dataset DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") +class AccessorTarget: + def __init__(self, data: Group): + self._data = data + + def get_value(self, path: str): + + tokens = path.split(".") + + # Navigate the tree from the entry we need + + current_tree_position: Group | Dataset = self._data + + for token in tokens: + if isinstance(current_tree_position, Group): + current_tree_position = current_tree_position.children[token] + elif isinstance(current_tree_position, Dataset): + current_tree_position = current_tree_position.attributes[token] + + + + + class Accessor[DataType, OutputType]: """ Base class """ - def __init__(self, target_object, value_target: str): + def __init__(self, target_object: AccessorTarget, value_target: str): self.target_object = target_object self.value_target = value_target @@ -35,7 +58,7 @@ def value(self) -> float | None: class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): """ Base class for accessors that work with quantities that have units """ - def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): + def __init__(self, target_object: AccessorTarget, value_target: str, unit_target: str, default_unit=units.none): super().__init__(target_object, value_target) self._unit_target = unit_target self.default_unit = default_unit From 5e7da06d4e87364bb87b832164d35a76e71afeb5 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 2 Oct 2024 09:46:04 +0100 Subject: [PATCH 110/675] Fixed bug where ohms, and angstroms were forbidden For now, I'm just specifying all of them in the regex. This should work for now because there aren't many symbols to specify, and I'm not sure what else would work without allowing symbols that shouldn't really be allowed. --- sasdata/quantities/unit_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 0f7965a97..a87c50467 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -11,12 +11,12 @@ def split_unit_str(unit_str: str) -> list[str]: """Separate the letters from the numbers in unit_str""" - return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) + return findall(r'[A-Za-zΩ%Å]+|[-\d]+|/', unit_str) def validate_unit_str(unit_str: str) -> bool: """Validate whether unit_str is valid. This doesn't mean that the unit specified in unit_str exists but rather it only consists of letters, and numbers as a unit string should.""" - return not fullmatch(r'[A-Za-z1-9\-\+/\ \.]+', unit_str) is None + return not fullmatch(r'[A-Za-zΩ%Å1-9\-\+/\ \.]+', unit_str) is None def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit From c2ec6ee2b106ba78be86a1c41f2c627cf014750d Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 2 Oct 2024 09:50:15 +0100 Subject: [PATCH 111/675] Accept the ^ char but don't do anything with it. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index a87c50467..082e6e340 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -16,7 +16,7 @@ def split_unit_str(unit_str: str) -> list[str]: def validate_unit_str(unit_str: str) -> bool: """Validate whether unit_str is valid. This doesn't mean that the unit specified in unit_str exists but rather it only consists of letters, and numbers as a unit string should.""" - return not fullmatch(r'[A-Za-zΩ%Å1-9\-\+/\ \.]+', unit_str) is None + return not fullmatch(r'[A-Za-zΩ%Å^1-9\-\+/\ \.]+', unit_str) is None def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit From 05fff2712289bfbc9afb1d5fd356a96041d8594d Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 10:25:07 +0100 Subject: [PATCH 112/675] Connecting metadata --- sasdata/metadata.py | 28 +++++++++++++++++++--------- sasdata/quantities/_accessor_base.py | 4 ++-- sasdata/quantities/accessors.py | 27 +++++++++++++++++++++++++-- sasdata/temp_hdf5_reader.py | 13 +++++++------ 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 316e99409..47f4bfedb 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -4,7 +4,7 @@ import sasdata.quantities.units as units from quantities.absolute_temperature import AbsoluteTemperatureAccessor from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor, \ - DimensionlessAccessor, FloatAccessor, TemperatureAccessor + DimensionlessAccessor, FloatAccessor, TemperatureAccessor, AccessorTarget class Detector: @@ -12,7 +12,7 @@ class Detector: Detector information """ - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): # Name of the instrument [string] self.name = StringAccessor(target_object, "detector.name") @@ -65,7 +65,7 @@ def summary(self): class Aperture: - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): # Name self.name = StringAccessor(target_object, "aperture.name") @@ -100,7 +100,7 @@ class Collimation: Class to hold collimation information """ - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): # Name self.name = StringAccessor(target_object, "collimation.name") @@ -128,7 +128,7 @@ class Source: Class to hold source information """ - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): # Name self.name = StringAccessor(target_object, "source.name") @@ -210,7 +210,7 @@ class Sample: """ Class to hold the sample description """ - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): # Short name for sample self.name = StringAccessor(target_object, "sample.name") @@ -273,7 +273,7 @@ class Process: Class that holds information about the processes performed on the data. """ - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): self.name = StringAccessor(target_object, "process.name") self.date = StringAccessor(target_object, "process.date") self.description = StringAccessor(target_object, "process.description") @@ -298,7 +298,7 @@ def __str__(self): f" Notes: {self.notes.value}" ) -class TransmissionSpectrum: +class TransmissionSpectrum(AccessorTarget): """ Class that holds information about transmission spectrum for white beams and spallation sources. @@ -333,5 +333,15 @@ def summary(self) -> str: f" Wavelengths: {self.wavelength.value}\n" f" Transmission: {self.transmission.value}\n") + class Metadata: - pass \ No newline at end of file + def __init__(self, target: AccessorTarget): + self._target = target + + self.aperture = Aperture(target) + self.collimation = Collimation(target) + self.detector = Detector(target) + self.process = Process(target) + self.sample = Sample(target) + self.source = Source(target) + self.transmission_spectrum = TransmissionSpectrum(target) \ No newline at end of file diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index f8f0927bb..78fc4d740 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -20,14 +20,14 @@ def get_value(self, path: str): # Navigate the tree from the entry we need current_tree_position: Group | Dataset = self._data - + for token in tokens: if isinstance(current_tree_position, Group): current_tree_position = current_tree_position.children[token] elif isinstance(current_tree_position, Dataset): current_tree_position = current_tree_position.attributes[token] - + return current_tree_position diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 92ef82bd1..3d9943b04 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -84,13 +84,36 @@ import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit +from sasdata.raw_form import Group, Dataset DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") +class AccessorTarget: + def __init__(self, data: Group): + self._data = data + + def get_value(self, path: str): + + tokens = path.split(".") + + # Navigate the tree from the entry we need + + current_tree_position: Group | Dataset = self._data + + for token in tokens: + if isinstance(current_tree_position, Group): + current_tree_position = current_tree_position.children[token] + elif isinstance(current_tree_position, Dataset): + current_tree_position = current_tree_position.attributes[token] + + return current_tree_position + + + class Accessor[DataType, OutputType]: """ Base class """ - def __init__(self, target_object, value_target: str): + def __init__(self, target_object: AccessorTarget, value_target: str): self.target_object = target_object self.value_target = value_target @@ -115,7 +138,7 @@ def value(self) -> float | None: class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): """ Base class for accessors that work with quantities that have units """ - def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): + def __init__(self, target_object: AccessorTarget, value_target: str, unit_target: str, default_unit=units.none): super().__init__(target_object, value_target) self._unit_target = unit_target self.default_unit = default_unit diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 32bb4f7ae..383e0b744 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -10,8 +10,9 @@ from h5py._hl.dataset import Dataset as HDF5Dataset from h5py._hl.group import Group as HDF5Group - -from sasdata.raw_form import RawData +from sasdata.metadata import Metadata +from sasdata.quantities.accessors import AccessorTarget +from sasdata.raw_form import RawData, Dataset from sasdata.raw_form import Dataset as SASDataDataset, Group as SASDataGroup from sasdata.quantities.quantity import NamedQuantity @@ -125,11 +126,11 @@ def load_data(filename) -> list[RawData]: raw_metadata[key] = recurse_hdf5(component) + target = AccessorTarget(SASDataGroup("root", raw_metadata)) + metadata = Metadata(target) + loaded_data.append( - RawData( - name=root_key, - data_contents=data_contents, - raw_metadata=raw_metadata)) + SasData) return loaded_data From 713c8be1edfe3760f8cf1c3030f927de8915e196 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 3 Oct 2024 14:07:18 +0100 Subject: [PATCH 113/675] Remove target data object attempt --- sasdata/target_data_object.py | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 sasdata/target_data_object.py diff --git a/sasdata/target_data_object.py b/sasdata/target_data_object.py deleted file mode 100644 index d88581752..000000000 --- a/sasdata/target_data_object.py +++ /dev/null @@ -1,3 +0,0 @@ -class TargetData: - def __init__(self): - self.reference_string = \ No newline at end of file From 672303e2387924f0257cdb5b785466c38024d869 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 10:32:02 +0100 Subject: [PATCH 114/675] Accessor changes --- sasdata/data.py | 137 +++++++++++++++++++++++++-- sasdata/postprocess.py | 41 -------- sasdata/quantities/_accessor_base.py | 2 +- sasdata/quantities/_build_tables.py | 8 +- sasdata/quantities/accessors.py | 3 +- sasdata/quantities/units.py | 8 +- sasdata/raw_form.py | 67 ------------- sasdata/temp_hdf5_reader.py | 13 ++- 8 files changed, 148 insertions(+), 131 deletions(-) delete mode 100644 sasdata/raw_form.py diff --git a/sasdata/data.py b/sasdata/data.py index b8daddce3..df839dc60 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,19 +1,136 @@ +from enum import Enum +from typing import TypeVar, Any, Self from dataclasses import dataclass -from sasdata.quantities.quantity import Quantity, NamedQuantity -from sasdata.metadata import Metadata -import numpy as np +from quantities.quantity import NamedQuantity -from sasdata.model_requirements import ModellingRequirements +DataType = TypeVar("DataType") +""" Sasdata metadata tree """ +def shorten_string(string): + lines = string.split("\n") + if len(lines) <= 1: + return string + else: + return lines[0][:30] + " ... " + lines[-1][-30:] +@dataclass +class Dataset[DataType]: + name: str + data: DataType + attributes: dict[str, Self | str] + + def summary(self, indent_amount: int = 0, indent: str = " ") -> str: + + s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" + s += f"{indent*(indent_amount+1)}{shorten_string(str(self.data))}\n" + for key in self.attributes: + value = self.attributes[key] + if isinstance(value, (Group, Dataset)): + value_string = value.summary(indent_amount+1, indent) + else: + value_string = f"{indent * (indent_amount+1)}{key}: {shorten_string(repr(value))}\n" + + s += value_string + + return s @dataclass -class DataSet: - abscissae: list[NamedQuantity[np.ndarray]] - ordinate: NamedQuantity[np.ndarray] - other: list[NamedQuantity[np.ndarray]] +class Group: + name: str + children: dict[str, Self | Dataset] + + def summary(self, indent_amount: int=0, indent=" "): + s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" + for key in self.children: + s += self.children[key].summary(indent_amount+1, indent) + + return s + +class Function: + """ Representation of a (data driven) function, such as I vs Q """ + + def __init__(self, abscissae: list[NamedQuantity], ordinate: NamedQuantity): + self.abscissae = abscissae + self.ordinate = ordinate + + +class FunctionType(Enum): + """ What kind of function is this, should not be relied upon to be perfectly descriptive + + The functions might be parametrised by more variables than the specification + """ + UNKNOWN = 0 + SCATTERING_INTENSITY_VS_Q = 1 + SCATTERING_INTENSITY_VS_Q_2D = 2 + SCATTERING_INTENSITY_VS_Q_3D = 3 + SCATTERING_INTENSITY_VS_ANGLE = 4 + UNKNOWN_METADATA = 20 + TRANSMISSION = 21 + POLARISATION_EFFICIENCY = 22 + UNKNOWN_REALSPACE = 30 + SESANS = 31 + CORRELATION_FUNCTION_1D = 32 + CORRELATION_FUNCTION_2D = 33 + CORRELATION_FUNCTION_3D = 34 + INTERFACE_DISTRIBUTION_FUNCTION = 35 + PROBABILITY_DISTRIBUTION = 40 + PROBABILITY_DENSITY = 41 + +def function_type_identification_key(names): + """ Create a key from the names of data objects that can be used to assign a function type""" + return ":".join([s.lower() for s in sorted(names)]) + +function_fields_to_type = [ + (["Q"], "I", FunctionType.SCATTERING_INTENSITY_VS_Q), + (["Qx", "Qy"], "I", FunctionType.SCATTERING_INTENSITY_VS_Q_2D), + (["Qx", "Qy", "Qz"], "I", FunctionType.SCATTERING_INTENSITY_VS_Q_3D), + (["Z"], "G", FunctionType.SESANS), + (["lambda"], "T", FunctionType.TRANSMISSION) +] + +function_fields_lookup = { + function_type_identification_key(inputs + [output]): function_type for (inputs, output), function_type in function_fields_to_type +} + +def build_main_data(data: list[NamedQuantity]) -> Function: + names = [datum.name for datum in data] + identifier = function_type_identification_key(names) + + if identifier in function_fields_lookup: + function_type = function_fields_lookup[identifier] + else: + function_type = FunctionType.UNKNOWN + + match function_type: + case FunctionType.UNKNOWN: + + case _: + raise NotImplementedError("Unknown ") +class SasData: + def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: Group): + self.name = name + self._data_contents = data_contents + self._raw_metadata = raw_metadata + + # TO IMPLEMENT + + # abscissae: list[NamedQuantity[np.ndarray]] + # ordinate: NamedQuantity[np.ndarray] + # other: list[NamedQuantity[np.ndarray]] + # + # metadata: Metadata + # model_requirements: ModellingRequirements + + def summary(self, indent = " "): + s = f"{self.name}\n" + + for data in self._data_contents: + s += f"{indent}{data}\n" + + s += f"{indent}Metadata:\n" + for key in self._raw_metadata.children: + s += self._raw_metadata.children[key].summary(2, indent) - metadata: Metadata - model_requirements: ModellingRequirements + return s \ No newline at end of file diff --git a/sasdata/postprocess.py b/sasdata/postprocess.py index 1d7866098..82ce61420 100644 --- a/sasdata/postprocess.py +++ b/sasdata/postprocess.py @@ -4,11 +4,6 @@ """ -import numpy as np - -from sasdata.data import SasData - - def fix_mantid_units_error(data: SasData) -> SasData: pass @@ -19,39 +14,3 @@ def apply_fixes(data: SasData, mantid_unit_error=True): data = fix_mantid_units_error(data) return data - - -def deduce_qz(data: SasData): - """Calculates and appends Qz to SasData if Qx, Qy, and wavelength are all present""" - # if Qz is not already in the dataset, but Qx and Qy are - if 'Qz' not in data._data_contents and 'Qx' in data._data_contents and 'Qy' in data._data_contents: - # we start by making the approximation that qz=0 - data._data_contents['Qz'] = 0*data._data_contents['Qx'] - - # now check if metadata has wavelength information - wavelength = getattr( - getattr( - getattr( - getattr(data, "metadata", None), - "instrument", - None - ), - "source", - None - ), - "wavelength", - None - ) - - if wavelength is not None: - # we can deduce the value of qz from qx and qy - # if we have the wavelength - qx = data._data_contents['Qx'] - qy = data._data_contents['Qy'] - - # this is how you convert qx, qy, and wavelength to qz - k0 = 2*np.pi/wavelength - qz = k0-(k0**2-qx**2-qy**2)**(0.5) - - data._data_contents['Qz'] = qz - diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 78fc4d740..84a305466 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -4,7 +4,7 @@ import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit -from sasdata.raw_form import Group, Dataset +from sasdata.data import Group, Dataset DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 6f4f8df17..cf7f3cc57 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -93,7 +93,8 @@ # Two stages of aliases, to make sure units don't get lost aliases_1 = { - "A": ["Amps", "amps"] + "A": ["Amps", "amps"], + "C": ["Coulombs", "coulombs"] } aliases_2 = { @@ -101,10 +102,11 @@ "d": ["day"], "h": ["hr", "hour"], "Ang": ["A", "Å"], - "au": ["a.u.", "amu"], + "au": ["amu"], "percent": ["%"], "deg": ["degr", "Deg", "degrees", "Degrees"], - "none": ["Counts", "counts", "cnts", "Cnts"] + "none": ["Counts", "counts", "cnts", "Cnts", "a.u.", "fraction", "Fraction"], + "K": ["C"] # Ugh, cansas } diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 3d9943b04..8018a452d 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -85,6 +85,7 @@ from sasdata.quantities.units import Dimensions, Unit from sasdata.raw_form import Group, Dataset +from sasdata.data import Group, Dataset DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") @@ -107,8 +108,6 @@ def get_value(self, path: str): elif isinstance(current_tree_position, Dataset): current_tree_position = current_tree_position.attributes[token] - return current_tree_position - class Accessor[DataType, OutputType]: diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index b02646aa3..834bb2436 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -1843,7 +1843,7 @@ def __init__(self, name: str, units: list[NamedUnit]): "pW": picowatts, "fW": femtowatts, "aW": attowatts, - "C": degrees_celsius, + "C": kelvin, "EC": exacoulombs, "PC": petacoulombs, "TC": teracoulombs, @@ -2013,12 +2013,13 @@ def __init__(self, name: str, units: list[NamedUnit]): "%": percent, "Amps": amperes, "amps": amperes, + "Coulombs": degrees_celsius, + "coulombs": degrees_celsius, "yr": years, "year": years, "day": days, "hr": hours, "hour": hours, - "a.u.": atomic_mass_units, "amu": atomic_mass_units, "degr": degrees, "Deg": degrees, @@ -2028,6 +2029,9 @@ def __init__(self, name: str, units: list[NamedUnit]): "counts": none, "cnts": none, "Cnts": none, + "a.u.": none, + "fraction": none, + "Fraction": none, } diff --git a/sasdata/raw_form.py b/sasdata/raw_form.py deleted file mode 100644 index e1883381d..000000000 --- a/sasdata/raw_form.py +++ /dev/null @@ -1,67 +0,0 @@ -from typing import TypeVar, Any, Self -from dataclasses import dataclass - -from quantities.quantity import NamedQuantity - -DataType = TypeVar("DataType") - -""" Sasdata metadata tree """ - -def shorten_string(string): - lines = string.split("\n") - if len(lines) <= 1: - return string - else: - return lines[0][:30] + " ... " + lines[-1][-30:] - -@dataclass -class Dataset[DataType]: - name: str - data: DataType - attributes: dict[str, Self | str] - - def summary(self, indent_amount: int = 0, indent: str = " ") -> str: - - s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" - s += f"{indent*(indent_amount+1)}{shorten_string(str(self.data))}\n" - for key in self.attributes: - value = self.attributes[key] - if isinstance(value, (Group, Dataset)): - value_string = value.summary(indent_amount+1, indent) - else: - value_string = f"{indent * (indent_amount+1)}{key}: {shorten_string(repr(value))}\n" - - s += value_string - - return s - -@dataclass -class Group: - name: str - children: dict[str, Self | Dataset] - - - def summary(self, indent_amount: int=0, indent=" "): - s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" - for key in self.children: - s += self.children[key].summary(indent_amount+1, indent) - - return s - -@dataclass -class RawData: - name: str - data_contents: list[NamedQuantity] - raw_metadata: dict[str, Dataset | Group] - - def summary(self, indent = " "): - s = f"{self.name}\n" - - for data in self.data_contents: - s += f"{indent}{data}\n" - - s += f"{indent}Metadata:\n" - for key in self.raw_metadata: - s += self.raw_metadata[key].summary(2, indent) - - return s \ No newline at end of file diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 383e0b744..03479b7ff 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -12,8 +12,8 @@ from sasdata.metadata import Metadata from sasdata.quantities.accessors import AccessorTarget -from sasdata.raw_form import RawData, Dataset -from sasdata.raw_form import Dataset as SASDataDataset, Group as SASDataGroup +from sasdata.data import SasData +from sasdata.data import Dataset as SASDataDataset, Group as SASDataGroup from sasdata.quantities.quantity import NamedQuantity from sasdata.quantities import units @@ -98,10 +98,10 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: return output -def load_data(filename) -> list[RawData]: +def load_data(filename) -> list[SasData]: with h5py.File(filename, 'r') as f: - loaded_data: list[RawData] = [] + loaded_data: list[SasData] = [] for root_key in f.keys(): @@ -130,7 +130,10 @@ def load_data(filename) -> list[RawData]: metadata = Metadata(target) loaded_data.append( - SasData) + SasData( + name=root_key, + data_contents=data_contents, + raw_metadata=SASDataGroup("root", raw_metadata))) return loaded_data From afd7a563df0486687f5c139a71a665a843df0308 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 10:47:21 +0100 Subject: [PATCH 115/675] Merge tidying --- sasdata/data.py | 109 ++------------------------- sasdata/data_backing.py | 18 +---- sasdata/quantities/_accessor_base.py | 2 +- sasdata/quantities/accessors.py | 4 +- sasdata/temp_hdf5_reader.py | 8 +- 5 files changed, 12 insertions(+), 129 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index df839dc60..f4accd924 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -3,117 +3,18 @@ from dataclasses import dataclass from quantities.quantity import NamedQuantity +from sasdata.metadata import Metadata +from sasdata.quantities.accessors import AccessorTarget +from sasdata.data_backing import Group -DataType = TypeVar("DataType") - -""" Sasdata metadata tree """ - -def shorten_string(string): - lines = string.split("\n") - if len(lines) <= 1: - return string - else: - return lines[0][:30] + " ... " + lines[-1][-30:] - -@dataclass -class Dataset[DataType]: - name: str - data: DataType - attributes: dict[str, Self | str] - - def summary(self, indent_amount: int = 0, indent: str = " ") -> str: - - s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" - s += f"{indent*(indent_amount+1)}{shorten_string(str(self.data))}\n" - for key in self.attributes: - value = self.attributes[key] - if isinstance(value, (Group, Dataset)): - value_string = value.summary(indent_amount+1, indent) - else: - value_string = f"{indent * (indent_amount+1)}{key}: {shorten_string(repr(value))}\n" - - s += value_string - - return s - -@dataclass -class Group: - name: str - children: dict[str, Self | Dataset] - - def summary(self, indent_amount: int=0, indent=" "): - s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" - for key in self.children: - s += self.children[key].summary(indent_amount+1, indent) - - return s - -class Function: - """ Representation of a (data driven) function, such as I vs Q """ - - def __init__(self, abscissae: list[NamedQuantity], ordinate: NamedQuantity): - self.abscissae = abscissae - self.ordinate = ordinate - - -class FunctionType(Enum): - """ What kind of function is this, should not be relied upon to be perfectly descriptive - - The functions might be parametrised by more variables than the specification - """ - UNKNOWN = 0 - SCATTERING_INTENSITY_VS_Q = 1 - SCATTERING_INTENSITY_VS_Q_2D = 2 - SCATTERING_INTENSITY_VS_Q_3D = 3 - SCATTERING_INTENSITY_VS_ANGLE = 4 - UNKNOWN_METADATA = 20 - TRANSMISSION = 21 - POLARISATION_EFFICIENCY = 22 - UNKNOWN_REALSPACE = 30 - SESANS = 31 - CORRELATION_FUNCTION_1D = 32 - CORRELATION_FUNCTION_2D = 33 - CORRELATION_FUNCTION_3D = 34 - INTERFACE_DISTRIBUTION_FUNCTION = 35 - PROBABILITY_DISTRIBUTION = 40 - PROBABILITY_DENSITY = 41 - -def function_type_identification_key(names): - """ Create a key from the names of data objects that can be used to assign a function type""" - return ":".join([s.lower() for s in sorted(names)]) - -function_fields_to_type = [ - (["Q"], "I", FunctionType.SCATTERING_INTENSITY_VS_Q), - (["Qx", "Qy"], "I", FunctionType.SCATTERING_INTENSITY_VS_Q_2D), - (["Qx", "Qy", "Qz"], "I", FunctionType.SCATTERING_INTENSITY_VS_Q_3D), - (["Z"], "G", FunctionType.SESANS), - (["lambda"], "T", FunctionType.TRANSMISSION) -] - -function_fields_lookup = { - function_type_identification_key(inputs + [output]): function_type for (inputs, output), function_type in function_fields_to_type -} - -def build_main_data(data: list[NamedQuantity]) -> Function: - names = [datum.name for datum in data] - identifier = function_type_identification_key(names) - - if identifier in function_fields_lookup: - function_type = function_fields_lookup[identifier] - else: - function_type = FunctionType.UNKNOWN - - match function_type: - case FunctionType.UNKNOWN: - - case _: - raise NotImplementedError("Unknown ") class SasData: def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: Group): self.name = name self._data_contents = data_contents self._raw_metadata = raw_metadata + self.metadata = Metadata(AccessorTarget(raw_metadata)) + # TO IMPLEMENT # abscissae: list[NamedQuantity[np.ndarray]] diff --git a/sasdata/data_backing.py b/sasdata/data_backing.py index 210ac33ca..dadfc194f 100644 --- a/sasdata/data_backing.py +++ b/sasdata/data_backing.py @@ -1,6 +1,6 @@ +from typing import TypeVar, Self from dataclasses import dataclass from enum import Enum -from typing import Self, TypeVar from sasdata.quantities.quantity import NamedQuantity @@ -108,19 +108,3 @@ def build_main_data(data: list[NamedQuantity]) -> Function: pass case _: raise NotImplementedError("Unknown ") - -def key_tree(data: Group | Dataset, indent_amount=0, indent: str = " ") -> str: - """ Show a metadata tree, showing the names of they keys used to access them""" - s = "" - if isinstance(data, Group): - for key in data.children: - s += indent*indent_amount + key + "\n" - s += key_tree(data.children[key], indent_amount=indent_amount+1, indent=indent) - - if isinstance(data, Dataset): - s += indent*indent_amount + "[data]\n" - for key in data.attributes: - s += indent*indent_amount + key + "\n" - s += key_tree(data.attributes[key], indent_amount=indent_amount+1, indent=indent) - - return s diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 84a305466..2f84e962d 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -4,7 +4,7 @@ import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit -from sasdata.data import Group, Dataset +from sasdata.data_backing import Group, Dataset DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 8018a452d..4b546d1d6 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -85,7 +85,7 @@ from sasdata.quantities.units import Dimensions, Unit from sasdata.raw_form import Group, Dataset -from sasdata.data import Group, Dataset +from sasdata.data_backing import Group, Dataset DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") @@ -108,6 +108,8 @@ def get_value(self, path: str): elif isinstance(current_tree_position, Dataset): current_tree_position = current_tree_position.attributes[token] + return current_tree_position + class Accessor[DataType, OutputType]: diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 03479b7ff..b5bc1f707 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -10,10 +10,9 @@ from h5py._hl.dataset import Dataset as HDF5Dataset from h5py._hl.group import Group as HDF5Group -from sasdata.metadata import Metadata -from sasdata.quantities.accessors import AccessorTarget + from sasdata.data import SasData -from sasdata.data import Dataset as SASDataDataset, Group as SASDataGroup +from sasdata.data_backing import Dataset as SASDataDataset, Group as SASDataGroup from sasdata.quantities.quantity import NamedQuantity from sasdata.quantities import units @@ -126,9 +125,6 @@ def load_data(filename) -> list[SasData]: raw_metadata[key] = recurse_hdf5(component) - target = AccessorTarget(SASDataGroup("root", raw_metadata)) - metadata = Metadata(target) - loaded_data.append( SasData( name=root_key, From 95ed102231851ad2df3a5394d9a1ad684e760c6f Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 11:31:19 +0100 Subject: [PATCH 116/675] Metadata linked up, just not pointing in the right place right now --- sasdata/data.py | 3 +-- sasdata/metadata.py | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index f4accd924..fea32fd18 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -31,7 +31,6 @@ def summary(self, indent = " "): s += f"{indent}{data}\n" s += f"{indent}Metadata:\n" - for key in self._raw_metadata.children: - s += self._raw_metadata.children[key].summary(2, indent) + s += self.metadata.summary() return s \ No newline at end of file diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 47f4bfedb..7646846fa 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -93,7 +93,7 @@ def summary(self): return (f"Aperture:\n" f" Name: {self.name.value}\n" f" Aperture size: {self.size.value}\n" - f" Aperture distance: {self.distance.value}") + f" Aperture distance: {self.distance.value}\n") class Collimation: """ @@ -112,7 +112,7 @@ def __init__(self, target_object: AccessorTarget): # Todo - how do we handle this - self.collimator = Collimation(target_object) + # self.collimator = Collimation(target_object) def summary(self): @@ -193,7 +193,7 @@ def summary(self) -> str: f" Min. Wavelength: {self.wavelength_min.value}\n" f" Max. Wavelength: {self.wavelength_max.value}\n" f" Wavelength Spread: {self.wavelength_spread.value}\n" - f" Beam Size: {self.beam_size}\n") + f" Beam Size: {self.beam_size.value}\n") @@ -289,13 +289,13 @@ def single_line_desc(self): """ return f"{self.name.value} {self.date.value} {self.description.value}" - def __str__(self): + def summary(self): return (f"Process:\n" f" Name: {self.name.value}\n" f" Date: {self.date.value}\n" f" Description: {self.description.value}\n" f" Term: {self.term.value}\n" - f" Notes: {self.notes.value}" + f" Notes: {self.notes.value}\n" ) class TransmissionSpectrum(AccessorTarget): @@ -323,7 +323,7 @@ def __init__(self, target_object): self.transmission_deviation = DimensionlessAccessor[ArrayLike](target_object, "transmission.transmission_deviation", "transmission.transmission_deviation.units", - default_units=units.none) + default_unit=units.none) def summary(self) -> str: @@ -344,4 +344,14 @@ def __init__(self, target: AccessorTarget): self.process = Process(target) self.sample = Sample(target) self.source = Source(target) - self.transmission_spectrum = TransmissionSpectrum(target) \ No newline at end of file + self.transmission_spectrum = TransmissionSpectrum(target) + def summary(self): + return ( + self.aperture.summary() + + self.collimation.summary() + + self.detector.summary() + + self.process.summary() + + self.sample.summary() + + self.source.summary() + + self.transmission_spectrum.summary() + ) \ No newline at end of file From a948c2818079d9347978930a19950354d2543c75 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 11:56:54 +0100 Subject: [PATCH 117/675] Added some debugging info to the summary --- sasdata/data.py | 5 ++++- sasdata/data_backing.py | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/sasdata/data.py b/sasdata/data.py index fea32fd18..f50fb61a9 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -5,7 +5,8 @@ from quantities.quantity import NamedQuantity from sasdata.metadata import Metadata from sasdata.quantities.accessors import AccessorTarget -from sasdata.data_backing import Group +from sasdata.data_backing import Group, key_tree + class SasData: def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: Group): @@ -33,4 +34,6 @@ def summary(self, indent = " "): s += f"{indent}Metadata:\n" s += self.metadata.summary() + s += key_tree(self._raw_metadata) + return s \ No newline at end of file diff --git a/sasdata/data_backing.py b/sasdata/data_backing.py index dadfc194f..564f466a6 100644 --- a/sasdata/data_backing.py +++ b/sasdata/data_backing.py @@ -108,3 +108,19 @@ def build_main_data(data: list[NamedQuantity]) -> Function: pass case _: raise NotImplementedError("Unknown ") + +def key_tree(data: Group | Dataset, indent_amount=0, indent: str = " ") -> str: + """ Show a metadata tree, showing the names of they keys used to access them""" + s = "" + if isinstance(data, Group): + for key in data.children: + s += indent*indent_amount + key + "\n" + s += key_tree(data.children[key], indent_amount=indent_amount+1, indent=indent) + + if isinstance(data, Dataset): + s += indent*indent_amount + "[data]\n" + for key in data.attributes: + s += indent*indent_amount + key + "\n" + s += key_tree(data.attributes[key], indent_amount=indent_amount+1, indent=indent) + + return s \ No newline at end of file From ea1082cdc633ebf5c332f4da6816369477e5e99b Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 15:25:00 +0100 Subject: [PATCH 118/675] Debugging metadata --- sasdata/data.py | 10 +++-- sasdata/metadata.py | 34 ++++++++--------- sasdata/quantities/_accessor_base.py | 56 +++++++++++++++++++++++----- sasdata/quantities/accessors.py | 56 +++++++++++++++++++++++----- sasdata/temp_hdf5_reader.py | 5 ++- 5 files changed, 120 insertions(+), 41 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index f50fb61a9..b30864a91 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -9,12 +9,13 @@ class SasData: - def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: Group): + def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: Group, verbose: bool=False): self.name = name self._data_contents = data_contents self._raw_metadata = raw_metadata + self._verbose = verbose - self.metadata = Metadata(AccessorTarget(raw_metadata)) + self.metadata = Metadata(AccessorTarget(raw_metadata, verbose=verbose)) # TO IMPLEMENT @@ -25,7 +26,7 @@ def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: # metadata: Metadata # model_requirements: ModellingRequirements - def summary(self, indent = " "): + def summary(self, indent = " ", include_raw=False): s = f"{self.name}\n" for data in self._data_contents: @@ -34,6 +35,7 @@ def summary(self, indent = " "): s += f"{indent}Metadata:\n" s += self.metadata.summary() - s += key_tree(self._raw_metadata) + if include_raw: + s += key_tree(self._raw_metadata) return s \ No newline at end of file diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 7646846fa..6ed0ba470 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -130,34 +130,34 @@ class Source: def __init__(self, target_object: AccessorTarget): # Name - self.name = StringAccessor(target_object, "source.name") + self.name = StringAccessor(target_object, "sassource.name") # Generic radiation type (Type and probe give more specific info) [string] - self.radiation = StringAccessor(target_object, "source.radiation") + self.radiation = StringAccessor(target_object, "sassource.radiation") # Type and probe are only written to by the NXcanSAS reader # Specific radiation type (Synchotron X-ray, Reactor neutron, etc) [string] - self.type = StringAccessor(target_object, "source.type") + self.type = StringAccessor(target_object, "sassource.type") # Radiation probe (generic probe such as neutron, x-ray, muon, etc) [string] - self.probe_particle = StringAccessor(target_object, "source.probe") + self.probe_particle = StringAccessor(target_object, "sassource.probe") # Beam size name - self.beam_size_name = StringAccessor(target_object, "source.beam_size_name") + self.beam_size_name = StringAccessor(target_object, "sassource.beam_size_name") # Beam size [Vector] [mm] self.beam_size = LengthAccessor[ArrayLike](target_object, - "source.beam_size", - "source.beam_size.units", + "sassource.beam_size", + "sassource.beam_size.units", default_unit=units.millimeters) # Beam shape [string] - self.beam_shape = StringAccessor(target_object, "source.beam_shape") + self.beam_shape = StringAccessor(target_object, "sassource.beam_shape") # Wavelength [float] [Angstrom] self.wavelength = LengthAccessor[float](target_object, - "source.wavelength", - "source.wavelength.units", + "sassource.wavelength", + "sassource.wavelength.units", default_unit=units.angstroms) # Minimum wavelength [float] [Angstrom] @@ -274,14 +274,14 @@ class Process: performed on the data. """ def __init__(self, target_object: AccessorTarget): - self.name = StringAccessor(target_object, "process.name") - self.date = StringAccessor(target_object, "process.date") - self.description = StringAccessor(target_object, "process.description") + self.name = StringAccessor(target_object, "sasprocess.name") + self.date = StringAccessor(target_object, "sasprocess.date") + self.description = StringAccessor(target_object, "sasprocess.description") #TODO: It seems like these might be lists of strings, this should be checked - self.term = StringAccessor(target_object, "process.term") - self.notes = StringAccessor(target_object, "process.notes") + self.term = StringAccessor(target_object, "sasprocess.term") + self.notes = StringAccessor(target_object, "sasprocess.notes") def single_line_desc(self): """ @@ -298,12 +298,12 @@ def summary(self): f" Notes: {self.notes.value}\n" ) -class TransmissionSpectrum(AccessorTarget): +class TransmissionSpectrum: """ Class that holds information about transmission spectrum for white beams and spallation sources. """ - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): # TODO: Needs to be multiple instances self.name = StringAccessor(target_object, "transmission.") self.timestamp = StringAccessor(target_object, "transmission.timestamp") diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 2f84e962d..192628fda 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -3,18 +3,31 @@ from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit +from sasdata.quantities.unit_parser import parse_unit, parse_unit_from_group from sasdata.data_backing import Group, Dataset +import logging +# logger = logging.getLogger("Accessors") +class LoggerDummy: + def info(self, data): + print(data) +logger = LoggerDummy() + DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") + class AccessorTarget: - def __init__(self, data: Group): + def __init__(self, data: Group, verbose=False): self._data = data + self.verbose = verbose def get_value(self, path: str): + if self.verbose: + logger.info(f"Finding: {path}") + tokens = path.split(".") # Navigate the tree from the entry we need @@ -23,11 +36,27 @@ def get_value(self, path: str): for token in tokens: if isinstance(current_tree_position, Group): - current_tree_position = current_tree_position.children[token] + if token in current_tree_position.children: + current_tree_position = current_tree_position.children[token] + else: + if self.verbose: + logger.info(f"Failed at token {token} on group {current_tree_position.name}. Options: " + + ",".join([key for key in current_tree_position.children])) + return None + elif isinstance(current_tree_position, Dataset): - current_tree_position = current_tree_position.attributes[token] + if token in current_tree_position.attributes: + current_tree_position = current_tree_position.attributes[token] + else: + if self.verbose: + logger.info(f"Failed at token {token} on attribute {current_tree_position.name}. Options: " + + ",".join([key for key in current_tree_position.attributes])) + return None - return current_tree_position + if self.verbose: + logger.info(f"Found value: {current_tree_position}") + + return current_tree_position.data @@ -39,19 +68,19 @@ def __init__(self, target_object: AccessorTarget, value_target: str): @property def value(self) -> OutputType | None: - pass + self.target_object.get_value(self.value_target) class StringAccessor(Accessor[str, str]): """ String based fields """ @property def value(self) -> str | None: - pass + self.target_object.get_value(self.value_target) class FloatAccessor(Accessor[float, float]): """ Float based fields """ @property def value(self) -> float | None: - pass + self.target_object.get_value(self.value_target) @@ -65,20 +94,29 @@ def __init__(self, target_object: AccessorTarget, value_target: str, unit_target def _numerical_part(self) -> DataType | None: """ Numerical part of the data """ + return self.target_object.get_value(self.value_target) def _unit_part(self) -> str | None: """ String form of units for the data """ + return self.target_object.get_value(self._unit_target) @property def unit(self) -> Unit: - if self._unit_part() is None: + u = self._unit_part() + if u is None: return self.default_unit else: - return Unit.parse(self._unit_part()) + return parse_unit(u) @property def value(self) -> Quantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: return Quantity(self._numerical_part(), self.unit) + return None + @property + def quantity(self): + if self._unit_part() is not None and self._numerical_part() is not None: + return Quantity(self._numerical_part(), self.unit) + return None diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 4b546d1d6..273a497c6 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -83,19 +83,32 @@ from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit +from sasdata.quantities.unit_parser import parse_unit, parse_unit_from_group from sasdata.raw_form import Group, Dataset from sasdata.data_backing import Group, Dataset +import logging +# logger = logging.getLogger("Accessors") +class LoggerDummy: + def info(self, data): + print(data) +logger = LoggerDummy() + DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") + class AccessorTarget: - def __init__(self, data: Group): + def __init__(self, data: Group, verbose=False): self._data = data + self.verbose = verbose def get_value(self, path: str): + if self.verbose: + logger.info(f"Finding: {path}") + tokens = path.split(".") # Navigate the tree from the entry we need @@ -104,11 +117,27 @@ def get_value(self, path: str): for token in tokens: if isinstance(current_tree_position, Group): - current_tree_position = current_tree_position.children[token] + if token in current_tree_position.children: + current_tree_position = current_tree_position.children[token] + else: + if self.verbose: + logger.info(f"Failed at token {token} on group {current_tree_position.name}. Options: " + + ",".join([key for key in current_tree_position.children])) + return None + elif isinstance(current_tree_position, Dataset): - current_tree_position = current_tree_position.attributes[token] + if token in current_tree_position.attributes: + current_tree_position = current_tree_position.attributes[token] + else: + if self.verbose: + logger.info(f"Failed at token {token} on attribute {current_tree_position.name}. Options: " + + ",".join([key for key in current_tree_position.attributes])) + return None - return current_tree_position + if self.verbose: + logger.info(f"Found value: {current_tree_position}") + + return current_tree_position.data @@ -120,19 +149,19 @@ def __init__(self, target_object: AccessorTarget, value_target: str): @property def value(self) -> OutputType | None: - pass + self.target_object.get_value(self.value_target) class StringAccessor(Accessor[str, str]): """ String based fields """ @property def value(self) -> str | None: - pass + self.target_object.get_value(self.value_target) class FloatAccessor(Accessor[float, float]): """ Float based fields """ @property def value(self) -> float | None: - pass + self.target_object.get_value(self.value_target) @@ -146,22 +175,31 @@ def __init__(self, target_object: AccessorTarget, value_target: str, unit_target def _numerical_part(self) -> DataType | None: """ Numerical part of the data """ + return self.target_object.get_value(self.value_target) def _unit_part(self) -> str | None: """ String form of units for the data """ + return self.target_object.get_value(self._unit_target) @property def unit(self) -> Unit: - if self._unit_part() is None: + u = self._unit_part() + if u is None: return self.default_unit else: - return Unit.parse(self._unit_part()) + return parse_unit(u) @property def value(self) -> Quantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: return Quantity(self._numerical_part(), self.unit) + return None + @property + def quantity(self): + if self._unit_part() is not None and self._numerical_part() is not None: + return Quantity(self._numerical_part(), self.unit) + return None class LengthAccessor[T](QuantityAccessor[T]): diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index b5bc1f707..8b38431ba 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -129,7 +129,8 @@ def load_data(filename) -> list[SasData]: SasData( name=root_key, data_contents=data_contents, - raw_metadata=SASDataGroup("root", raw_metadata))) + raw_metadata=SASDataGroup("root", raw_metadata), + verbose=True)) return loaded_data @@ -139,4 +140,4 @@ def load_data(filename) -> list[SasData]: data = load_data(test_file) for dataset in data: - print(dataset.summary()) \ No newline at end of file + print(dataset.summary(include_raw=True)) \ No newline at end of file From 0b5edb0db5319f31c8d70478586b6c8f50346e67 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 16:15:44 +0100 Subject: [PATCH 119/675] Better reference methods --- sasdata/data.py | 3 +- sasdata/metadata.py | 188 +++++++++++++++------------ sasdata/quantities/_accessor_base.py | 24 +++- sasdata/quantities/accessors.py | 24 +++- sasdata/temp_hdf5_reader.py | 2 +- 5 files changed, 144 insertions(+), 97 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index b30864a91..7f0cbfb2f 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -32,7 +32,8 @@ def summary(self, indent = " ", include_raw=False): for data in self._data_contents: s += f"{indent}{data}\n" - s += f"{indent}Metadata:\n" + s += f"Metadata:\n" + s += "\n" s += self.metadata.summary() if include_raw: diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 6ed0ba470..738444959 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -1,3 +1,5 @@ +from tokenize import String + import numpy as np from numpy.typing import ArrayLike @@ -15,41 +17,41 @@ class Detector: def __init__(self, target_object: AccessorTarget): # Name of the instrument [string] - self.name = StringAccessor(target_object, "detector.name") + self.name = StringAccessor(target_object, "name") # Sample to detector distance [float] [mm] self.distance = LengthAccessor[float](target_object, - "detector.distance", - "detector.distance.units", + "distance", + "distance.units", default_unit=units.millimeters) # Offset of this detector position in X, Y, # (and Z if necessary) [Vector] [mm] self.offset = LengthAccessor[ArrayLike](target_object, - "detector.offset", - "detector.offset.units", + "offset", + "offset.units", default_unit=units.millimeters) self.orientation = AngleAccessor[ArrayLike](target_object, - "detector.orientation", - "detector.orientation.units", + "orientation", + "orientation.units", default_unit=units.degrees) self.beam_center = LengthAccessor[ArrayLike](target_object, - "detector.beam_center", - "detector.beam_center.units", + "beam_center", + "beam_center.units", default_unit=units.millimeters) # Pixel size in X, Y, (and Z if necessary) [Vector] [mm] self.pixel_size = LengthAccessor[ArrayLike](target_object, - "detector.pixel_size", - "detector.pixel_size.units", + "pixel_size", + "pixel_size.units", default_unit=units.millimeters) # Slit length of the instrument for this detector.[float] [mm] self.slit_length = LengthAccessor[float](target_object, - "detector.slit_length", - "detector.slit_length.units", + "slit_length", + "slit_length.units", default_unit=units.millimeters) def summary(self): @@ -68,24 +70,24 @@ class Aperture: def __init__(self, target_object: AccessorTarget): # Name - self.name = StringAccessor(target_object, "aperture.name") + self.name = StringAccessor(target_object, "name") # Type - self.type = StringAccessor(target_object, "aperture.type") + self.type = StringAccessor(target_object, "type") # Size name - TODO: What is the name of a size - self.size_name = StringAccessor(target_object, "aperture.size_name") + self.size_name = StringAccessor(target_object, "size_name") # Aperture size [Vector] # TODO: Wat!?! self.size = QuantityAccessor[ArrayLike](target_object, - "aperture.size", - "aperture.size.units", + "size", + "size.units", default_unit=units.millimeters) # Aperture distance [float] self.distance = LengthAccessor[float](target_object, - "apature.distance", - "apature.distance.units", + "distance", + "distance.units", default_unit=units.millimeters) @@ -103,11 +105,11 @@ class Collimation: def __init__(self, target_object: AccessorTarget): # Name - self.name = StringAccessor(target_object, "collimation.name") + self.name = StringAccessor(target_object, "name") # Length [float] [mm] self.length = LengthAccessor[float](target_object, - "collimation.length", - "collimation.length.units", + "length", + "length.units", default_unit=units.millimeters) @@ -130,53 +132,53 @@ class Source: def __init__(self, target_object: AccessorTarget): # Name - self.name = StringAccessor(target_object, "sassource.name") + self.name = StringAccessor(target_object, "name") # Generic radiation type (Type and probe give more specific info) [string] - self.radiation = StringAccessor(target_object, "sassource.radiation") + self.radiation = StringAccessor(target_object, "radiation") # Type and probe are only written to by the NXcanSAS reader # Specific radiation type (Synchotron X-ray, Reactor neutron, etc) [string] - self.type = StringAccessor(target_object, "sassource.type") + self.type = StringAccessor(target_object, "type") # Radiation probe (generic probe such as neutron, x-ray, muon, etc) [string] - self.probe_particle = StringAccessor(target_object, "sassource.probe") + self.probe_particle = StringAccessor(target_object, "probe") # Beam size name - self.beam_size_name = StringAccessor(target_object, "sassource.beam_size_name") + self.beam_size_name = StringAccessor(target_object, "beam_size_name") # Beam size [Vector] [mm] self.beam_size = LengthAccessor[ArrayLike](target_object, - "sassource.beam_size", - "sassource.beam_size.units", + "beam_size", + "beam_size.units", default_unit=units.millimeters) # Beam shape [string] - self.beam_shape = StringAccessor(target_object, "sassource.beam_shape") + self.beam_shape = StringAccessor(target_object, "beam_shape") # Wavelength [float] [Angstrom] self.wavelength = LengthAccessor[float](target_object, - "sassource.wavelength", - "sassource.wavelength.units", + "wavelength", + "wavelength.units", default_unit=units.angstroms) # Minimum wavelength [float] [Angstrom] self.wavelength_min = LengthAccessor[float](target_object, - "source.wavelength_min", - "source.wavelength_min.units", + "wavelength_min", + "wavelength_min.units", default_unit=units.angstroms) # Maximum wavelength [float] [Angstrom] self.wavelength_max = LengthAccessor[float](target_object, - "source.wavelength_min", - "source.wavelength_max.units", + "wavelength_min", + "wavelength_max.units", default_unit=units.angstroms) # Wavelength spread [float] [Angstrom] # Quantity because it might have other units, such as percent self.wavelength_spread = QuantityAccessor[float](target_object, - "source.wavelength_spread", - "source.wavelength_spread.units", + "wavelength_spread", + "wavelength_spread.units", default_unit=units.angstroms) def summary(self) -> str: @@ -187,13 +189,13 @@ def summary(self) -> str: radiation = f"{self.radiation.value}" return (f"Source:\n" - f" Radiation: {radiation}\n" - f" Shape: {self.beam_shape.value}\n" - f" Wavelength: {self.wavelength.value}\n" - f" Min. Wavelength: {self.wavelength_min.value}\n" - f" Max. Wavelength: {self.wavelength_max.value}\n" + f" Radiation: {radiation}\n" + f" Shape: {self.beam_shape.value}\n" + f" Wavelength: {self.wavelength.value}\n" + f" Min. Wavelength: {self.wavelength_min.value}\n" + f" Max. Wavelength: {self.wavelength_max.value}\n" f" Wavelength Spread: {self.wavelength_spread.value}\n" - f" Beam Size: {self.beam_size.value}\n") + f" Beam Size: {self.beam_size.value}\n") @@ -213,39 +215,39 @@ class Sample: def __init__(self, target_object: AccessorTarget): # Short name for sample - self.name = StringAccessor(target_object, "sample.name") + self.name = StringAccessor(target_object, "name") # ID - self.sample_id = StringAccessor(target_object, "sample.id") + self.sample_id = StringAccessor(target_object, "id") # Thickness [float] [mm] self.thickness = LengthAccessor(target_object, - "sample.thickness", - "sample.thickness.units", + "thickness", + "thickness.units", default_unit=units.millimeters) # Transmission [float] [fraction] - self.transmission = FloatAccessor(target_object,"sample.transmission") + self.transmission = FloatAccessor(target_object,"transmission") # Temperature [float] [No Default] self.temperature = AbsoluteTemperatureAccessor(target_object, - "sample.temperature", - "sample.temperature.unit", + "temperature", + "temperature.unit", default_unit=units.kelvin) # Position [Vector] [mm] self.position = LengthAccessor[ArrayLike](target_object, - "sample.position", - "sample.position.unit", + "position", + "position.unit", default_unit=units.millimeters) # Orientation [Vector] [degrees] self.orientation = AngleAccessor[ArrayLike](target_object, - "sample.orientation", - "sample.orientation.unit", + "orientation", + "orientation.unit", default_unit=units.degrees) # Details - self.details = StringAccessor(target_object, "sample.details") + self.details = StringAccessor(target_object, "details") # SESANS zacceptance @@ -274,14 +276,14 @@ class Process: performed on the data. """ def __init__(self, target_object: AccessorTarget): - self.name = StringAccessor(target_object, "sasprocess.name") - self.date = StringAccessor(target_object, "sasprocess.date") - self.description = StringAccessor(target_object, "sasprocess.description") + self.name = StringAccessor(target_object, "name") + self.date = StringAccessor(target_object, "date") + self.description = StringAccessor(target_object, "description") #TODO: It seems like these might be lists of strings, this should be checked - self.term = StringAccessor(target_object, "sasprocess.term") - self.notes = StringAccessor(target_object, "sasprocess.notes") + self.term = StringAccessor(target_object, "term") + self.notes = StringAccessor(target_object, "notes") def single_line_desc(self): """ @@ -305,24 +307,24 @@ class TransmissionSpectrum: """ def __init__(self, target_object: AccessorTarget): # TODO: Needs to be multiple instances - self.name = StringAccessor(target_object, "transmission.") - self.timestamp = StringAccessor(target_object, "transmission.timestamp") + self.name = StringAccessor(target_object, "name") + self.timestamp = StringAccessor(target_object, "timestamp") # Wavelength (float) [A] self.wavelength = LengthAccessor[ArrayLike](target_object, - "transmission.wavelength", - "transmission.wavelength.units") + "wavelength", + "wavelength.units") # Transmission (float) [unit less] self.transmission = DimensionlessAccessor[ArrayLike](target_object, - "transmission.transmission", - "transmission.units", + "transmission", + "units", default_unit=units.none) # Transmission Deviation (float) [unit less] self.transmission_deviation = DimensionlessAccessor[ArrayLike](target_object, - "transmission.transmission_deviation", - "transmission.transmission_deviation.units", + "transmission_deviation", + "transmission_deviation.units", default_unit=units.none) @@ -334,24 +336,44 @@ def summary(self) -> str: f" Transmission: {self.transmission.value}\n") -class Metadata: +class Instrument: def __init__(self, target: AccessorTarget): - self._target = target - - self.aperture = Aperture(target) - self.collimation = Collimation(target) - self.detector = Detector(target) - self.process = Process(target) - self.sample = Sample(target) - self.source = Source(target) - self.transmission_spectrum = TransmissionSpectrum(target) + self.aperture = Aperture(target.with_path_prefix("sasaperture")) + self.collimation = Collimation(target.with_path_prefix("sascollimation")) + self.detector = Detector(target.with_path_prefix("sasdetector")) + self.source = Source(target.with_path_prefix("sassource")) def summary(self): return ( self.aperture.summary() + self.collimation.summary() + self.detector.summary() + + self.source.summary()) + + +class Metadata: + def __init__(self, target: AccessorTarget): + self._target = target + + self.instrument = Instrument(target.with_path_prefix("sasinstrument")) + self.process = Process(target.with_path_prefix("sasprocess")) + self.sample = Sample(target.with_path_prefix("sassample")) + self.transmission_spectrum = TransmissionSpectrum(target.with_path_prefix("sastransmission_spectrum")) + + self._title = StringAccessor(target, "title") + self._run = StringAccessor(target, "run") + self._definitiion = StringAccessor(target, "definition") + + self.title: str = self._title.value + self.run: str = self._run.value + self.definitiion: str = self._definitiion.value + + def summary(self): + return ( + f" {self.title}, Run: {self.run}\n" + " " + "="*len(self.title) + + "=======" + + "="*len(self.run) + "\n\n" + + f"Definition: {self.title}\n" + self.process.summary() + self.sample.summary() + - self.source.summary() + - self.transmission_spectrum.summary() - ) \ No newline at end of file + self.instrument.summary() + + self.transmission_spectrum.summary()) \ No newline at end of file diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 192628fda..f750623bc 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -19,16 +19,28 @@ def info(self, data): class AccessorTarget: - def __init__(self, data: Group, verbose=False): + def __init__(self, data: Group, verbose=False, prefix_tokens: tuple=()): self._data = data self.verbose = verbose + self.prefix_tokens = list(prefix_tokens) + + def with_path_prefix(self, path_prexix: str): + """ Get an accessor that looks at a subtree of the metadata with the supplied prefix + + For example, accessors aiming at a.b, when the target it c.d will look at c.d.a.b + """ + return AccessorTarget(self._data, + verbose=self.verbose, + prefix_tokens=tuple(self.prefix_tokens + [path_prexix])) + def get_value(self, path: str): + tokens = self.prefix_tokens + path.split(".") + if self.verbose: logger.info(f"Finding: {path}") - - tokens = path.split(".") + logger.info(f"Full path: {tokens}") # Navigate the tree from the entry we need @@ -68,19 +80,19 @@ def __init__(self, target_object: AccessorTarget, value_target: str): @property def value(self) -> OutputType | None: - self.target_object.get_value(self.value_target) + return self.target_object.get_value(self.value_target) class StringAccessor(Accessor[str, str]): """ String based fields """ @property def value(self) -> str | None: - self.target_object.get_value(self.value_target) + return self.target_object.get_value(self.value_target) class FloatAccessor(Accessor[float, float]): """ Float based fields """ @property def value(self) -> float | None: - self.target_object.get_value(self.value_target) + return self.target_object.get_value(self.value_target) diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 273a497c6..df903406d 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -100,16 +100,28 @@ def info(self, data): class AccessorTarget: - def __init__(self, data: Group, verbose=False): + def __init__(self, data: Group, verbose=False, prefix_tokens: tuple=()): self._data = data self.verbose = verbose + self.prefix_tokens = list(prefix_tokens) + + def with_path_prefix(self, path_prexix: str): + """ Get an accessor that looks at a subtree of the metadata with the supplied prefix + + For example, accessors aiming at a.b, when the target it c.d will look at c.d.a.b + """ + return AccessorTarget(self._data, + verbose=self.verbose, + prefix_tokens=tuple(self.prefix_tokens + [path_prexix])) + def get_value(self, path: str): + tokens = self.prefix_tokens + path.split(".") + if self.verbose: logger.info(f"Finding: {path}") - - tokens = path.split(".") + logger.info(f"Full path: {tokens}") # Navigate the tree from the entry we need @@ -149,19 +161,19 @@ def __init__(self, target_object: AccessorTarget, value_target: str): @property def value(self) -> OutputType | None: - self.target_object.get_value(self.value_target) + return self.target_object.get_value(self.value_target) class StringAccessor(Accessor[str, str]): """ String based fields """ @property def value(self) -> str | None: - self.target_object.get_value(self.value_target) + return self.target_object.get_value(self.value_target) class FloatAccessor(Accessor[float, float]): """ Float based fields """ @property def value(self) -> float | None: - self.target_object.get_value(self.value_target) + return self.target_object.get_value(self.value_target) diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 8b38431ba..b0bf6e423 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -130,7 +130,7 @@ def load_data(filename) -> list[SasData]: name=root_key, data_contents=data_contents, raw_metadata=SASDataGroup("root", raw_metadata), - verbose=True)) + verbose=False)) return loaded_data From a313945c3d5b6388d8fb6e16d36bc8b631510fd3 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 21 Oct 2024 10:46:48 +0100 Subject: [PATCH 120/675] Fixed import errors. I can't get the ASCII dialog to run when using relative imports in these files so I've converted them to absolute imports. --- sasdata/data.py | 4 ++-- sasdata/metadata.py | 2 +- sasdata/quantities/absolute_temperature.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 7f0cbfb2f..042d473ee 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -2,7 +2,7 @@ from typing import TypeVar, Any, Self from dataclasses import dataclass -from quantities.quantity import NamedQuantity +from sasdata.quantities.quantity import NamedQuantity from sasdata.metadata import Metadata from sasdata.quantities.accessors import AccessorTarget from sasdata.data_backing import Group, key_tree @@ -39,4 +39,4 @@ def summary(self, indent = " ", include_raw=False): if include_raw: s += key_tree(self._raw_metadata) - return s \ No newline at end of file + return s diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 738444959..9d74fbfeb 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -4,7 +4,7 @@ from numpy.typing import ArrayLike import sasdata.quantities.units as units -from quantities.absolute_temperature import AbsoluteTemperatureAccessor +from sasdata.quantities.absolute_temperature import AbsoluteTemperatureAccessor from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor, \ DimensionlessAccessor, FloatAccessor, TemperatureAccessor, AccessorTarget diff --git a/sasdata/quantities/absolute_temperature.py b/sasdata/quantities/absolute_temperature.py index 95c8982fb..ecfd0e6d9 100644 --- a/sasdata/quantities/absolute_temperature.py +++ b/sasdata/quantities/absolute_temperature.py @@ -1,6 +1,6 @@ from typing import TypeVar -from quantities.quantity import Quantity +from sasdata.quantities.quantity import Quantity from sasdata.quantities.accessors import TemperatureAccessor From 6832f9b3f826a3beb79f13468ab9f50fd3fb0596 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 9 Oct 2024 17:44:40 +0100 Subject: [PATCH 121/675] Is anyone capable of putting sensible things in HDF5 files? Corrections for ineptitudes. --- sasdata/metadata.py | 50 ++++++++++++++++++++-------- sasdata/quantities/_accessor_base.py | 35 ++++++++++++++----- sasdata/quantities/accessors.py | 35 ++++++++++++++----- sasdata/temp_hdf5_reader.py | 22 ++++++++---- 4 files changed, 106 insertions(+), 36 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 9d74fbfeb..ab580b3e5 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -338,10 +338,10 @@ def summary(self) -> str: class Instrument: def __init__(self, target: AccessorTarget): - self.aperture = Aperture(target.with_path_prefix("sasaperture")) - self.collimation = Collimation(target.with_path_prefix("sascollimation")) - self.detector = Detector(target.with_path_prefix("sasdetector")) - self.source = Source(target.with_path_prefix("sassource")) + self.aperture = Aperture(target.with_path_prefix("sasaperture|aperture")) + self.collimation = Collimation(target.with_path_prefix("sascollimation|collimation")) + self.detector = Detector(target.with_path_prefix("sasdetector|detector")) + self.source = Source(target.with_path_prefix("sassource|source")) def summary(self): return ( self.aperture.summary() + @@ -349,27 +349,51 @@ def summary(self): self.detector.summary() + self.source.summary()) +def decode_string(data): + """ This is some crazy stuff""" + + if isinstance(data, str): + return data + + elif isinstance(data, np.ndarray): + + if data.dtype == object: + + data = data.reshape(-1) + data = data[0] + + if isinstance(data, bytes): + return data.decode("utf-8") + + return str(data) + + else: + return data.tobytes().decode("utf-8") + + else: + return str(data) class Metadata: def __init__(self, target: AccessorTarget): self._target = target - self.instrument = Instrument(target.with_path_prefix("sasinstrument")) - self.process = Process(target.with_path_prefix("sasprocess")) - self.sample = Sample(target.with_path_prefix("sassample")) - self.transmission_spectrum = TransmissionSpectrum(target.with_path_prefix("sastransmission_spectrum")) + self.instrument = Instrument(target.with_path_prefix("sasinstrument|instrument")) + self.process = Process(target.with_path_prefix("sasprocess|process")) + self.sample = Sample(target.with_path_prefix("sassample|sample")) + self.transmission_spectrum = TransmissionSpectrum(target.with_path_prefix("sastransmission_spectrum|transmission_spectrum")) self._title = StringAccessor(target, "title") self._run = StringAccessor(target, "run") - self._definitiion = StringAccessor(target, "definition") + self._definition = StringAccessor(target, "definition") - self.title: str = self._title.value - self.run: str = self._run.value - self.definitiion: str = self._definitiion.value + self.title: str = decode_string(self._title.value) + self.run: str = decode_string(self._run.value) + self.definition: str = decode_string(self._definition.value) def summary(self): return ( - f" {self.title}, Run: {self.run}\n" + " " + "="*len(self.title) + + f" {self.title}, Run: {self.run}\n" + + " " + "="*len(self.title) + "=======" + "="*len(self.run) + "\n\n" + f"Definition: {self.title}\n" + diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index f750623bc..b56ecce68 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -47,21 +47,40 @@ def get_value(self, path: str): current_tree_position: Group | Dataset = self._data for token in tokens: + + options = token.split("|") + if isinstance(current_tree_position, Group): - if token in current_tree_position.children: - current_tree_position = current_tree_position.children[token] - else: + + found = False + for option in options: + if option in current_tree_position.children: + current_tree_position = current_tree_position.children[option] + found = True + + if self.verbose: + logger.info(f"Found option: {option}") + + if not found: if self.verbose: - logger.info(f"Failed at token {token} on group {current_tree_position.name}. Options: " + + logger.info(f"Failed to find any of {options} on group {current_tree_position.name}. Options: " + ",".join([key for key in current_tree_position.children])) return None elif isinstance(current_tree_position, Dataset): - if token in current_tree_position.attributes: - current_tree_position = current_tree_position.attributes[token] - else: + + found = False + for option in options: + if option in current_tree_position.attributes: + current_tree_position = current_tree_position.attributes[option] + found = True + + if self.verbose: + logger.info(f"Found option: {option}") + + if not found: if self.verbose: - logger.info(f"Failed at token {token} on attribute {current_tree_position.name}. Options: " + + logger.info(f"Failed to find any of {options} on attribute {current_tree_position.name}. Options: " + ",".join([key for key in current_tree_position.attributes])) return None diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index df903406d..be7b7bddd 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -128,21 +128,40 @@ def get_value(self, path: str): current_tree_position: Group | Dataset = self._data for token in tokens: + + options = token.split("|") + if isinstance(current_tree_position, Group): - if token in current_tree_position.children: - current_tree_position = current_tree_position.children[token] - else: + + found = False + for option in options: + if option in current_tree_position.children: + current_tree_position = current_tree_position.children[option] + found = True + + if self.verbose: + logger.info(f"Found option: {option}") + + if not found: if self.verbose: - logger.info(f"Failed at token {token} on group {current_tree_position.name}. Options: " + + logger.info(f"Failed to find any of {options} on group {current_tree_position.name}. Options: " + ",".join([key for key in current_tree_position.children])) return None elif isinstance(current_tree_position, Dataset): - if token in current_tree_position.attributes: - current_tree_position = current_tree_position.attributes[token] - else: + + found = False + for option in options: + if option in current_tree_position.attributes: + current_tree_position = current_tree_position.attributes[option] + found = True + + if self.verbose: + logger.info(f"Found option: {option}") + + if not found: if self.verbose: - logger.info(f"Failed at token {token} on attribute {current_tree_position.name}. Options: " + + logger.info(f"Failed to find any of {options} on attribute {current_tree_position.name}. Options: " + ",".join([key for key in current_tree_position.attributes])) return None diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index b0bf6e423..f96b2a4cb 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -19,7 +19,10 @@ from sasdata.quantities.unit_parser import parse # test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" -test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" +# test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" +# test_file = "./example_data/2d_data/BAM_2D.h5" +test_file = "./example_data/2d_data/14250_2D_NoDetInfo_NXcanSAS_v3.h5" +# test_file = "./example_data/2d_data/33837rear_2D_1.75_16.5_NXcanSAS_v3.h5" logger = logging.getLogger(__name__) @@ -76,8 +79,12 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: value=child.data, units=units) - if "uncertainty" in child.attributes: - uncertainty_name = child.attributes["uncertainty"] + # Turns out people can't be trusted to use the same keys here + if "uncertainty" in child.attributes or "uncertainties" in child.attributes: + try: + uncertainty_name = child.attributes["uncertainty"] + except: + uncertainty_name = child.attributes["uncertainties"] uncertainty_map[name] = uncertainty_name uncertainties.add(uncertainty_name) @@ -111,12 +118,13 @@ def load_data(filename) -> list[SasData]: entry_keys = [key for key in entry.keys()] - if "sasdata" not in entry_keys: - logger.warning("No sasdata key") + if "sasdata" not in entry_keys and "data" not in entry_keys: + logger.warning("No sasdata or data key") for key in entry_keys: component = entry[key] - if key.lower() == "sasdata": + lower_key = key.lower() + if lower_key == "sasdata" or lower_key == "data": datum = recurse_hdf5(component) # TODO: Use named identifier data_contents = connected_data(datum, "FILE_ID_HERE") @@ -140,4 +148,4 @@ def load_data(filename) -> list[SasData]: data = load_data(test_file) for dataset in data: - print(dataset.summary(include_raw=True)) \ No newline at end of file + print(dataset.summary(include_raw=False)) \ No newline at end of file From 7a83d4ef96da1bc7a731ea88bc5704cde3584265 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Dec 2024 11:43:30 +0000 Subject: [PATCH 122/675] Implemented equality based on the hash. --- sasdata/quantities/quantity.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index b4d9a9362..095fe7e78 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -312,6 +312,9 @@ def __pow__(self: Self, other: int | float): other), self.history.references)) + def __eq__(self, other: object) -> bool: + return isinstance(other, Quantity) and self.hash_value == other.hash_value + @staticmethod def _array_repr_format(arr: np.ndarray): """ Format the array """ From cc74ff75c526b2db82cb02e1bd61f8c809e9033d Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 23 Oct 2024 09:54:20 +0100 Subject: [PATCH 123/675] Line endings :) --- sasdata/quantities/_autogen_warning.py | 2 +- sasdata/quantities/operations_examples.py | 2 +- sasdata/quantities/operations_test.py | 136 ++++++------ sasdata/quantities/quantities_tests.py | 248 +++++++++++----------- sasdata/quantities/units_tests.py | 2 +- sasdata/transforms/operation.py | 36 ++-- sasdata/util.py | 2 +- 7 files changed, 214 insertions(+), 214 deletions(-) diff --git a/sasdata/quantities/_autogen_warning.py b/sasdata/quantities/_autogen_warning.py index fc8be67dc..5adb4b569 100644 --- a/sasdata/quantities/_autogen_warning.py +++ b/sasdata/quantities/_autogen_warning.py @@ -76,4 +76,4 @@ -""" +""" \ No newline at end of file diff --git a/sasdata/quantities/operations_examples.py b/sasdata/quantities/operations_examples.py index 6c484eb36..4509a86ac 100644 --- a/sasdata/quantities/operations_examples.py +++ b/sasdata/quantities/operations_examples.py @@ -8,4 +8,4 @@ dfdx = f.derivative(x).derivative(y).derivative(z) -print(dfdx.summary()) \ No newline at end of file +print(dfdx.summary()) diff --git a/sasdata/quantities/operations_test.py b/sasdata/quantities/operations_test.py index 6fffb3680..0899eee7f 100644 --- a/sasdata/quantities/operations_test.py +++ b/sasdata/quantities/operations_test.py @@ -1,68 +1,68 @@ -import pytest - -from sasdata.quantities.operations import Operation, \ - Neg, Inv, \ - Add, Sub, Mul, Div, Pow, \ - Variable, Constant, AdditiveIdentity, MultiplicativeIdentity - -operation_with_everything = \ - Div( - Pow( - Mul( - Sub( - Add( - Neg(Inv(MultiplicativeIdentity())), - Variable("x")), - Constant(7)), - AdditiveIdentity()), - 2), - Variable("y")) - -def test_serialise_deserialise(): - print(operation_with_everything._serialise_json()) - - serialised = operation_with_everything.serialise() - deserialised = Operation.deserialise(serialised) - reserialised = deserialised.serialise() - - assert serialised == reserialised - - -@pytest.mark.parametrize("op, a, b, result", [ - (Add, 1, 1, 2), - (Add, 7, 8, 15), - (Sub, 1, 1, 0), - (Sub, 7, 8, -1), - (Mul, 1, 1, 1), - (Mul, 7, 8, 56), - (Div, 1, 1, 1), - (Div, 7, 8, 7/8), - (Pow, 1, 1, 1), - (Pow, 7, 2, 49)]) -def test_binary_evaluation(op, a, b, result): - f = op(Constant(a), b if op == Pow else Constant(b)) - assert f.evaluate({}) == result - -x = Variable("x") -y = Variable("y") -z = Variable("z") -@pytest.mark.parametrize("x_over_x", [ - Div(x,x), - Mul(Inv(x), x), - Mul(x, Inv(x)), -]) -def test_dx_over_x_by_dx_should_be_zero(x_over_x): - - - dfdx = x_over_x.derivative(x) - - print(dfdx.summary()) - - assert dfdx == AdditiveIdentity() - - -def test_d_xyz_by_components_should_be_1(): - f = Mul(Mul(x, y), z) - assert f.derivative(x).derivative(y).derivative(z) == MultiplicativeIdentity() - - +import pytest + +from sasdata.quantities.operations import Operation, \ + Neg, Inv, \ + Add, Sub, Mul, Div, Pow, \ + Variable, Constant, AdditiveIdentity, MultiplicativeIdentity + +operation_with_everything = \ + Div( + Pow( + Mul( + Sub( + Add( + Neg(Inv(MultiplicativeIdentity())), + Variable("x")), + Constant(7)), + AdditiveIdentity()), + 2), + Variable("y")) + +def test_serialise_deserialise(): + print(operation_with_everything._serialise_json()) + + serialised = operation_with_everything.serialise() + deserialised = Operation.deserialise(serialised) + reserialised = deserialised.serialise() + + assert serialised == reserialised + + +@pytest.mark.parametrize("op, a, b, result", [ + (Add, 1, 1, 2), + (Add, 7, 8, 15), + (Sub, 1, 1, 0), + (Sub, 7, 8, -1), + (Mul, 1, 1, 1), + (Mul, 7, 8, 56), + (Div, 1, 1, 1), + (Div, 7, 8, 7/8), + (Pow, 1, 1, 1), + (Pow, 7, 2, 49)]) +def test_binary_evaluation(op, a, b, result): + f = op(Constant(a), b if op == Pow else Constant(b)) + assert f.evaluate({}) == result + +x = Variable("x") +y = Variable("y") +z = Variable("z") +@pytest.mark.parametrize("x_over_x", [ + Div(x,x), + Mul(Inv(x), x), + Mul(x, Inv(x)), +]) +def test_dx_over_x_by_dx_should_be_zero(x_over_x): + + + dfdx = x_over_x.derivative(x) + + print(dfdx.summary()) + + assert dfdx == AdditiveIdentity() + + +def test_d_xyz_by_components_should_be_1(): + f = Mul(Mul(x, y), z) + assert f.derivative(x).derivative(y).derivative(z) == MultiplicativeIdentity() + + diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index 453f88925..8ab0a41ce 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -1,124 +1,124 @@ -import numpy as np - -from sasdata.quantities.quantity import Quantity, UnitError -import sasdata.quantities.units as units -import sasdata.quantities.si as si -import pytest -def test_in_units_of_calculation(): - """ Just a couple of unit conversions """ - assert Quantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 - assert Quantity(10, units.minutes).in_units_of(units.seconds) == 600 - assert Quantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000 / 9.81, abs=1) - assert Quantity(0, units.meters).in_units_of(units.exameters) == 0 - - -def test_unit_compounding_pow(): - """ Test units compound correctly when __pow__ is used""" - assert (Quantity(1, units.millimeters) ** 2).in_units_of(units.square_meters) == 1e-6 - assert (Quantity(1, units.minutes) ** 3).in_units_of(units.seconds ** 3) == 60 ** 3 - -def test_pow_scaling(): - q2 = Quantity(1000, units.millimeters)**2 - assert q2.units.scale == 1e-6 - assert q2.value == 1e6 - - -def test_unit_compounding_mul(): - """ Test units compound correctly when __mul__ is used""" - assert (Quantity(4, units.minutes) * Quantity(0.25, units.hertz)).in_units_of(units.none) == 60 - assert (Quantity(250, units.volts) * Quantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 - -def test_unit_compounding_div(): - """ Test units compound correctly when __truediv__ is used""" - assert (Quantity(10, units.kilometers) / Quantity(2, units.minutes) - ).in_units_of(units.meters_per_second) == pytest.approx(250/3, abs=1e-6) - - assert (Quantity(1, units.nanowebers) / (Quantity(1, units.millimeters) ** 2)).in_units_of(units.millitesla) == 1 - -def test_value_mul(): - """ Test value part of quantities multiply correctly""" - assert (Quantity(1j, units.seconds) * Quantity(1j, units.watts)).in_units_of(units.joules) == -1 - -def test_scalar_mul(): - assert (Quantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 - assert (10 * Quantity(1, units.seconds)).in_units_of(units.seconds) == 10 - assert (1000 * Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 - -def test_scalar_div(): - - assert (Quantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 - assert (10 / Quantity(1, units.seconds)).in_units_of(units.hertz) == 10 - assert (0.001 / Quantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 - -def test_good_add_sub(): - """ Test that adding and subtracting units works """ - assert (Quantity(1, units.seconds) + Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 - assert (Quantity(1, units.seconds) - Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 - - assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == pytest.approx(13, abs=1e-8) - -@pytest.mark.parametrize("unit_in, power, unit_out", [ - (units.meters**2, 1/2, units.meters), - (units.meters**3, 1/3, units.meters), - (units.meters**3, 2/3, units.meters**2), - (units.meters**3, -5/3, units.meters**-5), - (units.none, 1/10, units.none), - (units.none, 19/17, units.none), - (units.none, np.pi, units.none) -]) -def test_good_non_integer_unit_powers(unit_in, power, unit_out): - """ Check that we can do various square and cube root stuff if we need to, - If dimensionless, we should be able to do arbitrary powers - """ - assert unit_in**power == unit_out - -@pytest.mark.parametrize("unit, power", [ - (units.meters, 1/2), - (units.milliohms, 1/3), - (units.meters, 3/2), - (units.meters**2, 2/3) -]) -def test_bad_non_integer_unit_powers(unit, power): - """ Check that we get an error if we try and do something silly with powers""" - with pytest.raises(units.DimensionError): - x = unit**power - - -@pytest.mark.parametrize("unit_1", si.all_si) -@pytest.mark.parametrize("unit_2", si.all_si) -def test_mixed_quantity_add_sub(unit_1, unit_2): - if unit_1.equivalent(unit_2): - assert (Quantity(0, unit_1) + Quantity(0, unit_2)).in_units_of(unit_1) == 0 - - else: - with pytest.raises(UnitError): - Quantity(1, unit_1) + Quantity(1, unit_2) - -def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float, abs=1e-9): - """ Helper function for testing units that are multiples of each other """ - - assert u1.equivalent(u2), "Units should be compatible for this test" - assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) - - -def test_american_units(): - assert_unit_ratio(units.feet, units.inches, 12) - assert_unit_ratio(units.yards, units.inches, 36) - assert_unit_ratio(units.miles, units.inches, 63360) - assert_unit_ratio(units.pounds_force_per_square_inch, units.pounds_force / (units.inches**2), 1, abs=1e-5) - -def test_percent(): - assert Quantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) - -@pytest.mark.parametrize("unit_1", si.all_si) -@pytest.mark.parametrize("unit_2", si.all_si) -def test_conversion_errors(unit_1, unit_2): - """ Test conversion errors are thrown when units are not compatible """ - - if unit_1 == unit_2: - assert Quantity(1, unit_1).in_units_of(unit_2) == 1 - - else: - with pytest.raises(UnitError): - Quantity(1, units.seconds).in_units_of(units.meters) - +import numpy as np + +from sasdata.quantities.quantity import Quantity, UnitError +import sasdata.quantities.units as units +import sasdata.quantities.si as si +import pytest +def test_in_units_of_calculation(): + """ Just a couple of unit conversions """ + assert Quantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 + assert Quantity(10, units.minutes).in_units_of(units.seconds) == 600 + assert Quantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000 / 9.81, abs=1) + assert Quantity(0, units.meters).in_units_of(units.exameters) == 0 + + +def test_unit_compounding_pow(): + """ Test units compound correctly when __pow__ is used""" + assert (Quantity(1, units.millimeters) ** 2).in_units_of(units.square_meters) == 1e-6 + assert (Quantity(1, units.minutes) ** 3).in_units_of(units.seconds ** 3) == 60 ** 3 + +def test_pow_scaling(): + q2 = Quantity(1000, units.millimeters)**2 + assert q2.units.scale == 1e-6 + assert q2.value == 1e6 + + +def test_unit_compounding_mul(): + """ Test units compound correctly when __mul__ is used""" + assert (Quantity(4, units.minutes) * Quantity(0.25, units.hertz)).in_units_of(units.none) == 60 + assert (Quantity(250, units.volts) * Quantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 + +def test_unit_compounding_div(): + """ Test units compound correctly when __truediv__ is used""" + assert (Quantity(10, units.kilometers) / Quantity(2, units.minutes) + ).in_units_of(units.meters_per_second) == pytest.approx(250/3, abs=1e-6) + + assert (Quantity(1, units.nanowebers) / (Quantity(1, units.millimeters) ** 2)).in_units_of(units.millitesla) == 1 + +def test_value_mul(): + """ Test value part of quantities multiply correctly""" + assert (Quantity(1j, units.seconds) * Quantity(1j, units.watts)).in_units_of(units.joules) == -1 + +def test_scalar_mul(): + assert (Quantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 + assert (10 * Quantity(1, units.seconds)).in_units_of(units.seconds) == 10 + assert (1000 * Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 + +def test_scalar_div(): + + assert (Quantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 + assert (10 / Quantity(1, units.seconds)).in_units_of(units.hertz) == 10 + assert (0.001 / Quantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 + +def test_good_add_sub(): + """ Test that adding and subtracting units works """ + assert (Quantity(1, units.seconds) + Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 + assert (Quantity(1, units.seconds) - Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 + + assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == pytest.approx(13, abs=1e-8) + +@pytest.mark.parametrize("unit_in, power, unit_out", [ + (units.meters**2, 1/2, units.meters), + (units.meters**3, 1/3, units.meters), + (units.meters**3, 2/3, units.meters**2), + (units.meters**3, -5/3, units.meters**-5), + (units.none, 1/10, units.none), + (units.none, 19/17, units.none), + (units.none, np.pi, units.none) +]) +def test_good_non_integer_unit_powers(unit_in, power, unit_out): + """ Check that we can do various square and cube root stuff if we need to, + If dimensionless, we should be able to do arbitrary powers + """ + assert unit_in**power == unit_out + +@pytest.mark.parametrize("unit, power", [ + (units.meters, 1/2), + (units.milliohms, 1/3), + (units.meters, 3/2), + (units.meters**2, 2/3) +]) +def test_bad_non_integer_unit_powers(unit, power): + """ Check that we get an error if we try and do something silly with powers""" + with pytest.raises(units.DimensionError): + x = unit**power + + +@pytest.mark.parametrize("unit_1", si.all_si) +@pytest.mark.parametrize("unit_2", si.all_si) +def test_mixed_quantity_add_sub(unit_1, unit_2): + if unit_1.equivalent(unit_2): + assert (Quantity(0, unit_1) + Quantity(0, unit_2)).in_units_of(unit_1) == 0 + + else: + with pytest.raises(UnitError): + Quantity(1, unit_1) + Quantity(1, unit_2) + +def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float, abs=1e-9): + """ Helper function for testing units that are multiples of each other """ + + assert u1.equivalent(u2), "Units should be compatible for this test" + assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) + + +def test_american_units(): + assert_unit_ratio(units.feet, units.inches, 12) + assert_unit_ratio(units.yards, units.inches, 36) + assert_unit_ratio(units.miles, units.inches, 63360) + assert_unit_ratio(units.pounds_force_per_square_inch, units.pounds_force / (units.inches**2), 1, abs=1e-5) + +def test_percent(): + assert Quantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) + +@pytest.mark.parametrize("unit_1", si.all_si) +@pytest.mark.parametrize("unit_2", si.all_si) +def test_conversion_errors(unit_1, unit_2): + """ Test conversion errors are thrown when units are not compatible """ + + if unit_1 == unit_2: + assert Quantity(1, unit_1).in_units_of(unit_2) == 1 + + else: + with pytest.raises(UnitError): + Quantity(1, units.seconds).in_units_of(units.meters) + diff --git a/sasdata/quantities/units_tests.py b/sasdata/quantities/units_tests.py index d0d090934..9fea2a6b8 100644 --- a/sasdata/quantities/units_tests.py +++ b/sasdata/quantities/units_tests.py @@ -43,4 +43,4 @@ def run_test(self): for test in tests: print(test.test_name) - test.run_test() \ No newline at end of file + test.run_test() diff --git a/sasdata/transforms/operation.py b/sasdata/transforms/operation.py index 59121882d..c06bb379a 100644 --- a/sasdata/transforms/operation.py +++ b/sasdata/transforms/operation.py @@ -1,19 +1,19 @@ -import numpy as np -from sasdata.quantities.quantity import Quantity - -class Operation: - """ Sketch of what model post-processing classes might look like """ - - children: list["Operation"] - named_children: dict[str, "Operation"] - - @property - def name(self) -> str: - raise NotImplementedError("No name for transform") - - def evaluate(self) -> Quantity[np.ndarray]: - pass - - def __call__(self, *children, **named_children): - self.children = children +import numpy as np +from sasdata.quantities.quantity import Quantity + +class Operation: + """ Sketch of what model post-processing classes might look like """ + + children: list["Operation"] + named_children: dict[str, "Operation"] + + @property + def name(self) -> str: + raise NotImplementedError("No name for transform") + + def evaluate(self) -> Quantity[np.ndarray]: + pass + + def __call__(self, *children, **named_children): + self.children = children self.named_children = named_children \ No newline at end of file diff --git a/sasdata/util.py b/sasdata/util.py index d378a9908..2cd6e3f48 100644 --- a/sasdata/util.py +++ b/sasdata/util.py @@ -14,4 +14,4 @@ def wrapper() -> T: return cache_state[1] - return wrapper + return wrapper \ No newline at end of file From 28f00dc8e0a50717f3e74750a770d054c9b467e0 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 10 Oct 2024 13:04:08 +0100 Subject: [PATCH 124/675] Added matrix operations, needs tests --- sasdata/quantities/operations.py | 112 ++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py index da9bd539e..c168aa925 100644 --- a/sasdata/quantities/operations.py +++ b/sasdata/quantities/operations.py @@ -702,9 +702,119 @@ def __eq__(self, other): if isinstance(other, Pow): return self.a == other.a and self.power == other.power + + +# +# Matrix operations +# + +class Transpose(UnaryOperation): + """ Transpose operation - as per numpy""" + + serialisation_name = "transpose" + + def evaluate(self, variables: dict[int, T]) -> T: + return np.transpose(self.a.evaluate(variables)) + + def _derivative(self, hash_value: int) -> Operation: + return Transpose(self.a.derivative(hash_value)) # TODO: Check! + + def _clean(self): + clean_a = self.a._clean() + return Transpose(clean_a) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Transpose(Operation.deserialise_json(parameters["a"])) + + def _summary_open(self): + return "Transpose" + + def __eq__(self, other): + if isinstance(other, Transpose): + return other.a == self.a + + +class Dot(BinaryOperation): + """ Dot product - backed by numpy's dot method""" + + serialisation_name = "dot" + + def _self_cls(self) -> type: + return Dot + + def evaluate(self, variables: dict[int, T]) -> T: + return np.dot(self.a.evaluate(variables) + self.b.evaluate(variables)) + + def _derivative(self, hash_value: int) -> Operation: + return Add( + Dot(self.a, + self.b._derivative(hash_value)), + Dot(self.a._derivative(hash_value), + self.b)) + + def _clean_ab(self, a, b): + return Dot(a, b) # Do nothing for now + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Dot(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Dot" + + +# TODO: Add to base operation class, and to quantities +class MatMul(BinaryOperation): + """ Matrix multiplication, using __matmul__ dunder""" + + serialisation_name = "matmul" + + def _self_cls(self) -> type: + return MatMul + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) @ self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add( + MatMul(self.a, + self.b._derivative(hash_value)), + MatMul(self.a._derivative(hash_value), + self.b)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): + # Convert 0*b or a*0 to 0 + return AdditiveIdentity() + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"@"b" to "a@b" + return Constant(a.evaluate({}) @ b.evaluate({}))._clean() + + elif isinstance(a, Neg): + return Neg(Mul(a.a, b)) + + elif isinstance(b, Neg): + return Neg(Mul(a, b.a)) + + return MatMul(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return MatMul(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "MatMul" + + + _serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, Variable, Neg, Inv, - Add, Sub, Mul, Div, Pow] + Add, Sub, Mul, Div, Pow, + Transpose, Dot, MatMul] _serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} \ No newline at end of file From dd573827463c1aa16fba9f6b3b9a61cb43476b35 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 10 Oct 2024 13:05:22 +0100 Subject: [PATCH 125/675] Numpy import --- sasdata/quantities/operations.py | 1 + sasdata/quantities/quantity.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py index c168aa925..4c143b40f 100644 --- a/sasdata/quantities/operations.py +++ b/sasdata/quantities/operations.py @@ -1,4 +1,5 @@ from typing import Any, TypeVar, Union +import numpy as np import json diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 095fe7e78..9b5fa2061 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -233,6 +233,9 @@ def __rmul__(self: Self, other: ArrayLike | Self): self.history.operation_tree), self.history.references)) + + + def __truediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): return DerivedQuantity( @@ -421,4 +424,4 @@ def variance(self) -> Quantity: if self._variance_cache is None: self._variance_cache = self.history.variance_propagate(self.units) - return self._variance_cache \ No newline at end of file + return self._variance_cache From 6158eaa7abd878bd99e6d4eff98dfb9aab8885df Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 10 Oct 2024 13:12:27 +0100 Subject: [PATCH 126/675] Added __matmul__ and __rmatmul__ to quantities --- sasdata/quantities/quantity.py | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 9b5fa2061..24d3263a1 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -234,6 +234,42 @@ def __rmul__(self: Self, other: ArrayLike | Self): self.history.references)) + def __matmul__(self, other: ArrayLike | Self): + if isinstance(other, Quantity): + return DerivedQuantity( + self.value @ other.value, + self.units * other.units, + history=QuantityHistory.apply_operation( + operations.MatMul, + self.history, + other.history)) + else: + return DerivedQuantity( + self.value @ other, + self.units, + QuantityHistory( + operations.MatMul( + self.history.operation_tree, + operations.Constant(other)), + self.history.references)) + + def __rmatmul__(self, other: ArrayLike | Self): + if isinstance(other, Quantity): + return DerivedQuantity( + other.value @ self.value, + other.units * self.units, + history=QuantityHistory.apply_operation( + operations.MatMul, + other.history, + self.history)) + + else: + return DerivedQuantity(other @ self.value, self.units, + QuantityHistory( + operations.MatMul( + operations.Constant(other), + self.history.operation_tree), + self.history.references)) def __truediv__(self: Self, other: float | Self) -> Self: From 9a486583156ee6cbbe3c86a908b7c4aee7c633e3 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 10 Oct 2024 14:36:27 +0100 Subject: [PATCH 127/675] Entrypoint for rebinning --- sasdata/model_requirements.py | 2 +- sasdata/transforms/operation.py | 19 --- sasdata/transforms/rebinning.py | 256 +------------------------------- 3 files changed, 3 insertions(+), 274 deletions(-) delete mode 100644 sasdata/transforms/operation.py diff --git a/sasdata/model_requirements.py b/sasdata/model_requirements.py index 5d68ad1b4..40b6ac728 100644 --- a/sasdata/model_requirements.py +++ b/sasdata/model_requirements.py @@ -3,7 +3,7 @@ import numpy as np from sasdata.metadata import Metadata -from transforms.operation import Operation +from sasdata.quantities.operations import Operation @dataclass diff --git a/sasdata/transforms/operation.py b/sasdata/transforms/operation.py deleted file mode 100644 index c06bb379a..000000000 --- a/sasdata/transforms/operation.py +++ /dev/null @@ -1,19 +0,0 @@ -import numpy as np -from sasdata.quantities.quantity import Quantity - -class Operation: - """ Sketch of what model post-processing classes might look like """ - - children: list["Operation"] - named_children: dict[str, "Operation"] - - @property - def name(self) -> str: - raise NotImplementedError("No name for transform") - - def evaluate(self) -> Quantity[np.ndarray]: - pass - - def __call__(self, *children, **named_children): - self.children = children - self.named_children = named_children \ No newline at end of file diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index dcb9a2ca6..7c9ba710b 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -1,261 +1,9 @@ """ Algorithms for interpolation and rebinning """ +from typing import TypeVar -from enum import Enum - -import numpy as np from numpy._typing import ArrayLike -from scipy.sparse import coo_matrix from sasdata.quantities.quantity import Quantity - -class InterpolationOptions(Enum): - NEAREST_NEIGHBOUR = 0 - LINEAR = 1 - CUBIC = 3 - -class InterpolationError(Exception): - """ We probably want to raise exceptions because interpolation is not appropriate/well-defined, - not the same as numerical issues that will raise ValueErrors""" - - -def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], - output_axis: Quantity[ArrayLike], - mask: ArrayLike | None = None, - order: InterpolationOptions = InterpolationOptions.LINEAR, - is_density=False): - - """ Calculate the matrix that converts values recorded at points specified by input_axis to - values recorded at points specified by output_axis""" - - # We want the input values in terms of the output units, will implicitly check compatability - # TODO: incorporate mask - - working_units = output_axis.units - - input_x = input_axis.in_units_of(working_units) - output_x = output_axis.in_units_of(working_units) - - # Get the array indices that will map the array to a sorted one - input_sort = np.argsort(input_x) - output_sort = np.argsort(output_x) - - input_unsort = np.arange(len(input_x), dtype=int)[input_sort] - output_unsort = np.arange(len(output_x), dtype=int)[output_sort] - - sorted_in = input_x[input_sort] - sorted_out = output_x[output_sort] - - n_in = len(sorted_in) - n_out = len(sorted_out) - - conversion_matrix = None # output - - match order: - case InterpolationOptions.NEAREST_NEIGHBOUR: - - # COO Sparse matrix definition data - i_entries = [] - j_entries = [] - - crossing_points = 0.5*(sorted_out[1:] + sorted_out[:-1]) - - # Find the output values nearest to each of the input values - i=0 - for k, crossing_point in enumerate(crossing_points): - while i < n_in and sorted_in[i] < crossing_point: - i_entries.append(i) - j_entries.append(k) - i += 1 - - # All the rest in the last bin - while i < n_in: - i_entries.append(i) - j_entries.append(n_out-1) - i += 1 - - i_entries = input_unsort[np.array(i_entries, dtype=int)] - j_entries = output_unsort[np.array(j_entries, dtype=int)] - values = np.ones_like(i_entries, dtype=float) - - conversion_matrix = coo_matrix((values, (i_entries, j_entries)), shape=(n_in, n_out)) - - case InterpolationOptions.LINEAR: - - # Leverage existing linear interpolation methods to get the mapping - # do a linear interpolation on indices - # the floor should give the left bin - # the ceil should give the right bin - # the fractional part should give the relative weightings - - input_indices = np.arange(n_in, dtype=int) - output_indices = np.arange(n_out, dtype=int) - - fractional = np.interp(x=sorted_out, xp=sorted_in, fp=input_indices, left=0, right=n_in-1) - - left_bins = np.floor(fractional).astype(int) - right_bins = np.ceil(fractional).astype(int) - - right_weight = fractional % 1 - left_weight = 1 - right_weight - - # There *should* be no repeated entries for both i and j in the main part, but maybe at the ends - # If left bin is the same as right bin, then we only want one entry, and the weight should be 1 - - same = left_bins == right_bins - not_same = ~same - - same_bins = left_bins[same] # could equally be right bins, they're the same - - same_indices = output_indices[same] - not_same_indices = output_indices[not_same] - - j_entries_sorted = np.concatenate((same_indices, not_same_indices, not_same_indices)) - i_entries_sorted = np.concatenate((same_bins, left_bins[not_same], right_bins[not_same])) - - i_entries = input_unsort[i_entries_sorted] - j_entries = output_unsort[j_entries_sorted] - - # weights don't need to be unsorted # TODO: check this is right, it should become obvious if we use unsorted data - weights = np.concatenate((np.ones_like(same_bins, dtype=float), left_weight[not_same], right_weight[not_same])) - - conversion_matrix = coo_matrix((weights, (i_entries, j_entries)), shape=(n_in, n_out)) - - case InterpolationOptions.CUBIC: - # Cubic interpolation, much harder to implement because we can't just cheat and use numpy - - input_indices = np.arange(n_in, dtype=int) - output_indices = np.arange(n_out, dtype=int) - - # Find the location of the largest value in sorted_in that - # is less than every value of sorted_out - lower_bound = ( - np.sum(np.where(np.less.outer(sorted_in, sorted_out), 1, 0), axis=0) - 1 - ) - - # We're using the Finite Difference Cubic Hermite spline - # https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Interpolation_on_an_arbitrary_interval - # https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Finite_difference - - x1 = sorted_in[lower_bound] # xₖ on the wiki - x2 = sorted_in[lower_bound + 1] # xₖ₊₁ on the wiki - - x0 = sorted_in[lower_bound[lower_bound - 1 >= 0] - 1] # xpₖ₋₁ on the wiki - x0 = np.hstack([np.zeros(x1.size - x0.size), x0]) - - x3 = sorted_in[ - lower_bound[lower_bound + 2 < sorted_in.size] + 2 - ] # xₖ₊₂ on the wiki - x3 = np.hstack([x3, np.zeros(x2.size - x3.size)]) - - t = (sorted_out - x1) / (x2 - x1) # t on the wiki - - y0 = ( - -t * (x1 - x2) * (t**2 - 2 * t + 1) / (2 * x0 - 2 * x1) - ) # The coefficient to pₖ₋₁ on the wiki - y1 = ( - -t * (t**2 - 2 * t + 1) * (x0 - 2 * x1 + x2) - + (x0 - x1) * (3 * t**3 - 5 * t**2 + 2) - ) / (2 * (x0 - x1)) # The coefficient to pₖ - y2 = ( - t - * ( - -t * (t - 1) * (x1 - 2 * x2 + x3) - + (x2 - x3) * (-3 * t**2 + 4 * t + 1) - ) - / (2 * (x2 - x3)) - ) # The coefficient to pₗ₊₁ - y3 = t**2 * (t - 1) * (x1 - x2) / (2 * (x2 - x3)) # The coefficient to pₖ₊₂ - - conversion_matrix = np.zeros((n_in, n_out)) - - (row, column) = np.indices(conversion_matrix.shape) - - mask1 = row == lower_bound[column] - - conversion_matrix[np.roll(mask1, -1, axis=0)] = y0 - conversion_matrix[mask1] = y1 - conversion_matrix[np.roll(mask1, 1, axis=0)] = y2 - - # Special boundary condition for y3 - pick = np.roll(mask1, 2, axis=0) - pick[0:1, :] = 0 - if pick.any(): - conversion_matrix[pick] = y3 - - case _: - raise InterpolationError(f"Unsupported interpolation order: {order}") - - if mask is None: - return conversion_matrix, None - - else: - # Create a new mask - - # Convert to numerical values - # Conservative masking: anything touched by the previous mask is now masked - new_mask = (np.array(mask, dtype=float) @ conversion_matrix) != 0.0 - - return conversion_matrix, new_mask - - -def calculate_interpolation_matrix_2d_axis_axis(input_1: Quantity[ArrayLike], - input_2: Quantity[ArrayLike], - output_1: Quantity[ArrayLike], - output_2: Quantity[ArrayLike], - mask, - order: InterpolationOptions = InterpolationOptions.LINEAR, - is_density: bool = False): - - # This is just the same 1D matrices things - - match order: - case InterpolationOptions.NEAREST_NEIGHBOUR: - pass - - case InterpolationOptions.LINEAR: - pass - - case InterpolationOptions.CUBIC: - pass - - case _: - pass - - -def calculate_interpolation_matrix(input_axes: list[Quantity[ArrayLike]], - output_axes: list[Quantity[ArrayLike]], - data: ArrayLike | None = None, - mask: ArrayLike | None = None): - - # TODO: We probably should delete this, but lets keep it for now - - if len(input_axes) not in (1, 2): - raise InterpolationError("Interpolation is only supported for 1D and 2D data") - - if len(input_axes) == 1 and len(output_axes) == 1: - # Check for dimensionality - input_axis = input_axes[0] - output_axis = output_axes[0] - - if len(input_axis.value.shape) == 1: - if len(output_axis.value.shape) == 1: - calculate_interpolation_matrix_1d() - - if len(output_axes) != len(input_axes): - # Input or output axes might be 2D matrices - pass - - - -def rebin(data: Quantity[ArrayLike], - axes: list[Quantity[ArrayLike]], - new_axes: list[Quantity[ArrayLike]], - mask: ArrayLike | None = None, - interpolation_order: int = 1): - - """ This algorithm is only for operations that preserve dimensionality, - i.e. non-projective rebinning. - """ - +def rebin(data: Quantity[ArrayLike], axes: list[Quantity[ArrayLike]], new_axes: list[Quantity[ArrayLike]], interpolation_order=1): pass From 3864f37edcd3ce7f1f9c3de0e37caded7c0ab973 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 10 Oct 2024 14:42:42 +0100 Subject: [PATCH 128/675] Some better commenting on Quantity --- sasdata/quantities/quantity.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 24d3263a1..ad003094b 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -132,10 +132,10 @@ def __init__(self, self.hash_value = -1 """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ - """ Contains the variance if it is data driven, else it is """ + self._variance = None + """ Contains the variance if it is data driven """ if standard_error is None: - self._variance = None self.hash_value = hash_data_via_numpy(hash_seed, value) else: self._variance = standard_error ** 2 From 6041e094ad6a7481d5b0fa25ba21001c05e1d34a Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 10 Oct 2024 18:10:36 +0100 Subject: [PATCH 129/675] Work towards rebinning methods --- sasdata/data.py | 23 +++++++----- sasdata/transforms/rebinning.py | 66 ++++++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 11 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 042d473ee..378cb72ff 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -2,6 +2,8 @@ from typing import TypeVar, Any, Self from dataclasses import dataclass +import numpy as np + from sasdata.quantities.quantity import NamedQuantity from sasdata.metadata import Metadata from sasdata.quantities.accessors import AccessorTarget @@ -9,7 +11,11 @@ class SasData: - def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: Group, verbose: bool=False): + def __init__(self, name: str, + data_contents: list[NamedQuantity], + raw_metadata: Group, + verbose: bool=False): + self.name = name self._data_contents = data_contents self._raw_metadata = raw_metadata @@ -17,14 +23,11 @@ def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: self.metadata = Metadata(AccessorTarget(raw_metadata, verbose=verbose)) - # TO IMPLEMENT - - # abscissae: list[NamedQuantity[np.ndarray]] - # ordinate: NamedQuantity[np.ndarray] - # other: list[NamedQuantity[np.ndarray]] - # - # metadata: Metadata - # model_requirements: ModellingRequirements + # Components that need to be organised after creation + self.ordinate: NamedQuantity[np.ndarray] = None # TODO: fill out + self.abscissae: list[NamedQuantity[np.ndarray]] = None # TODO: fill out + self.mask = None # TODO: fill out + self.model_requirements = None # TODO: fill out def summary(self, indent = " ", include_raw=False): s = f"{self.name}\n" @@ -39,4 +42,4 @@ def summary(self, indent = " ", include_raw=False): if include_raw: s += key_tree(self._raw_metadata) - return s + return s \ No newline at end of file diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index 7c9ba710b..3a8c3e773 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -1,9 +1,73 @@ """ Algorithms for interpolation and rebinning """ from typing import TypeVar +import numpy as np from numpy._typing import ArrayLike from sasdata.quantities.quantity import Quantity +from scipy.sparse import coo_matrix + +from enum import Enum + +class InterpolationOptions(Enum): + NEAREST_NEIGHBOUR = 0 + LINEAR = 1 + + + +def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], + output_axis: Quantity[ArrayLike], + mask: ArrayLike | None = None, + order: InterpolationOptions = InterpolationOptions.NEAREST_NEIGHBOUR, + is_density=False): + + # We want the input values in terms of the output units, will implicitly check compatability + + working_units = output_axis.units + + input_x = input_axis.in_units_of(working_units) + output_x = output_axis.in_units_of(working_units) + + # Get the array indices that will map the array to a sorted one + input_sort = np.argsort(input_x) + output_sort = np.argsort(output_x) + + output_unsort = np.arange(len(input_x), dtype=int)[output_sort] + sorted_in = input_x[input_sort] + sorted_out = output_x[output_sort] + + match order: + case InterpolationOptions.NEAREST_NEIGHBOUR: + + # COO Sparse matrix definition data + values = [] + j_entries = [] + i_entries = [] + + # Find the output values nearest to each of the input values + for x_in in sorted_in: + + + case _: + raise ValueError(f"Unsupported interpolation order: {order}") + +def calculate_interpolation_matrix(input_axes: list[Quantity[ArrayLike]], + output_axes: list[Quantity[ArrayLike]], + data: ArrayLike | None = None, + mask: ArrayLike | None = None): + + pass + + + +def rebin(data: Quantity[ArrayLike], + axes: list[Quantity[ArrayLike]], + new_axes: list[Quantity[ArrayLike]], + mask: ArrayLike | None = None, + interpolation_order: int = 1): + + """ This algorithm is only for operations that preserve dimensionality, + i.e. non-projective rebinning. + """ -def rebin(data: Quantity[ArrayLike], axes: list[Quantity[ArrayLike]], new_axes: list[Quantity[ArrayLike]], interpolation_order=1): pass From 428103c3e4c0053c5adfdf5aa5848a7895ee5c34 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 11 Oct 2024 11:27:53 +0100 Subject: [PATCH 130/675] Zeroth order rebinning sketch --- sasdata/transforms/rebinning.py | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index 3a8c3e773..c490f8275 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -32,7 +32,9 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], input_sort = np.argsort(input_x) output_sort = np.argsort(output_x) + input_unsort = np.arange(len(output_x), dtype=int)[input_sort] output_unsort = np.arange(len(input_x), dtype=int)[output_sort] + sorted_in = input_x[input_sort] sorted_out = output_x[output_sort] @@ -40,13 +42,33 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], case InterpolationOptions.NEAREST_NEIGHBOUR: # COO Sparse matrix definition data - values = [] - j_entries = [] i_entries = [] + j_entries = [] + + crossing_points = 0.5*(sorted_out[1:] + sorted_out[:-1]) # Find the output values nearest to each of the input values - for x_in in sorted_in: - + n_i = len(sorted_in) + n_j = len(sorted_out) + i=0 + for k, crossing_point in enumerate(crossing_points): + while i < n_i and sorted_in[i] < crossing_point: + i_entries.append(i) + j_entries.append(k) + i += 1 + + # All the rest in the last bin + while i < n_i: + i_entries.append(i) + j_entries.append(n_j-1) + i += 1 + + i_entries = input_unsort[np.array(i_entries, dtype=int)] + j_entries = output_unsort[np.array(j_entries, dtype=int)] + values = np.ones_like(i_entries, dtype=float) + + return coo_matrix((values, (i_entries, j_entries)), shape=(n_i, n_j)) + case _: raise ValueError(f"Unsupported interpolation order: {order}") @@ -70,4 +92,4 @@ def rebin(data: Quantity[ArrayLike], i.e. non-projective rebinning. """ - pass + pass \ No newline at end of file From 0cbfc9e8da405457c6fd0f03feeeeaf363e40427 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 11 Oct 2024 12:08:04 +0100 Subject: [PATCH 131/675] First order rebinning --- sasdata/transforms/rebinning.py | 59 +++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index c490f8275..cd05cfc92 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -3,6 +3,7 @@ import numpy as np from numpy._typing import ArrayLike +from scipy.interpolate import interp1d from sasdata.quantities.quantity import Quantity from scipy.sparse import coo_matrix @@ -38,6 +39,11 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], sorted_in = input_x[input_sort] sorted_out = output_x[output_sort] + n_in = len(sorted_in) + n_out = len(sorted_out) + + conversion_matrix = None # output + match order: case InterpolationOptions.NEAREST_NEIGHBOUR: @@ -48,31 +54,72 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], crossing_points = 0.5*(sorted_out[1:] + sorted_out[:-1]) # Find the output values nearest to each of the input values - n_i = len(sorted_in) - n_j = len(sorted_out) i=0 for k, crossing_point in enumerate(crossing_points): - while i < n_i and sorted_in[i] < crossing_point: + while i < n_in and sorted_in[i] < crossing_point: i_entries.append(i) j_entries.append(k) i += 1 # All the rest in the last bin - while i < n_i: + while i < n_in: i_entries.append(i) - j_entries.append(n_j-1) + j_entries.append(n_out-1) i += 1 i_entries = input_unsort[np.array(i_entries, dtype=int)] j_entries = output_unsort[np.array(j_entries, dtype=int)] values = np.ones_like(i_entries, dtype=float) - return coo_matrix((values, (i_entries, j_entries)), shape=(n_i, n_j)) + conversion_matrix = coo_matrix((values, (i_entries, j_entries)), shape=(n_in, n_out)) + + case InterpolationOptions.LINEAR: + + # Leverage existing linear interpolation methods to get the mapping + # do a linear interpolation on indices + # the floor should give the left bin + # the ceil should give the right bin + # the fractional part should give the relative weightings + + input_indices = np.arange(n_in, dtype=int) + output_indices = np.arange(n_out, dtype=int) + + fractional = np.interp(x=sorted_out, xp=sorted_in, fp=input_indices, left=0, right=n_in-1) + + left_bins = np.floor(fractional, dtype=int) + right_bins = np.ceil(fractional, dtype=int) + + right_weight = fractional % 1 + left_weight = 1 - right_weight + + # There *should* be no repeated entries for both i and j in the main part, but maybe at the ends + # If left bin is the same as right bin, then we only want one entry, and the weight should be 1 + same = left_bins == right_bins + not_same = ~same + + same_bins = left_bins[same] # could equally be right bins, they're the same + + same_indices = output_indices[same] + not_same_indices = output_indices[not_same] + + j_entries_sorted = np.concatenate((same_indices, not_same_indices, not_same_indices)) + i_entries_sorted = np.concatenate((same_bins, left_bins[not_same], right_bins[not_same])) + + i_entries = input_unsort[i_entries_sorted] + j_entries = output_unsort[j_entries_sorted] + + # weights don't need to be unsorted # TODO: check this is right, it should become obvious if we use unsorted data + weights = np.concatenate((np.ones_like(same_bins, dtype=float), left_weight[not_same], right_weight[not_same])) + + conversion_matrix = coo_matrix((weights, (i_entries, j_entries)), shape=(n_in, n_out)) case _: raise ValueError(f"Unsupported interpolation order: {order}") + + return conversion_matrix + def calculate_interpolation_matrix(input_axes: list[Quantity[ArrayLike]], output_axes: list[Quantity[ArrayLike]], data: ArrayLike | None = None, From d706610a03cf3cbc2bf914f8e1e1b7b321f6333a Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 15 Oct 2024 16:26:44 +0100 Subject: [PATCH 132/675] Rebinning tests and extensions --- sasdata/manual_tests/interpolation.py | 11 +-- sasdata/quantities/math.py | 5 ++ sasdata/quantities/plotting.py | 2 +- sasdata/quantities/quantity.py | 18 +++++ sasdata/transforms/rebinning.py | 60 ++++++++++++++-- sasdata/transforms/test_interpolation.py | 91 ++++++++++++++++++++++++ 6 files changed, 174 insertions(+), 13 deletions(-) create mode 100644 sasdata/quantities/math.py create mode 100644 sasdata/transforms/test_interpolation.py diff --git a/sasdata/manual_tests/interpolation.py b/sasdata/manual_tests/interpolation.py index 59d53815c..9edee8a2e 100644 --- a/sasdata/manual_tests/interpolation.py +++ b/sasdata/manual_tests/interpolation.py @@ -1,11 +1,12 @@ -import matplotlib.pyplot as plt import numpy as np +import matplotlib.pyplot as plt -from sasdata.quantities import units -from sasdata.quantities.plotting import quantity_plot from sasdata.quantities.quantity import NamedQuantity -from sasdata.transforms.rebinning import InterpolationOptions, calculate_interpolation_matrix_1d +from sasdata.quantities.plotting import quantity_plot +from sasdata.quantities import units +from sasdata.transforms.rebinning import calculate_interpolation_matrix_1d +from sasdata.transforms.rebinning import InterpolationOptions def linear_interpolation_check(): @@ -33,7 +34,7 @@ def linear_interpolation_check(): quantity_plot(new_x, new_y) - print(new_y.history.summary()) + # print(new_y.history.summary()) plt.show() diff --git a/sasdata/quantities/math.py b/sasdata/quantities/math.py new file mode 100644 index 000000000..d252ccc0d --- /dev/null +++ b/sasdata/quantities/math.py @@ -0,0 +1,5 @@ +""" Math module extended to allow operations on quantities """ + +# TODO Implementations for trig and exp +# TODO Implementations for linear algebra stuff + diff --git a/sasdata/quantities/plotting.py b/sasdata/quantities/plotting.py index d4a99295c..854e23f5e 100644 --- a/sasdata/quantities/plotting.py +++ b/sasdata/quantities/plotting.py @@ -1,7 +1,7 @@ import matplotlib.pyplot as plt from numpy.typing import ArrayLike -from sasdata.quantities.quantity import NamedQuantity, Quantity +from sasdata.quantities.quantity import Quantity, NamedQuantity def quantity_plot(x: Quantity[ArrayLike], y: Quantity[ArrayLike], *args, **kwargs): diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index ad003094b..ffb6fe474 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -110,6 +110,16 @@ def has_variance(self): return False + def summary(self): + + variable_strings = [self.references[key].string_repr for key in self.references] + + s = "Variables: "+",".join(variable_strings) + s += "\n" + s += self.operation_tree.summary() + + return s + class Quantity[QuantityType]: @@ -401,6 +411,10 @@ def __repr__(self): def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass + @property + def string_repr(self): + return str(self.hash_value) + class NamedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, @@ -435,6 +449,10 @@ def with_standard_error(self, standard_error: Quantity): f"are not compatible with value units ({self.units})") + @property + def string_repr(self): + return self.name + class DerivedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): super().__init__(value, units, standard_error=None) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index cd05cfc92..3335216ac 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -13,13 +13,17 @@ class InterpolationOptions(Enum): NEAREST_NEIGHBOUR = 0 LINEAR = 1 + CUBIC = 3 +class InterpolationError(Exception): + """ We probably want to raise exceptions because interpolation is not appropriate/well-defined, + not the same as numerical issues that will raise ValueErrors""" def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], output_axis: Quantity[ArrayLike], mask: ArrayLike | None = None, - order: InterpolationOptions = InterpolationOptions.NEAREST_NEIGHBOUR, + order: InterpolationOptions = InterpolationOptions.LINEAR, is_density=False): # We want the input values in terms of the output units, will implicitly check compatability @@ -33,8 +37,8 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], input_sort = np.argsort(input_x) output_sort = np.argsort(output_x) - input_unsort = np.arange(len(output_x), dtype=int)[input_sort] - output_unsort = np.arange(len(input_x), dtype=int)[output_sort] + input_unsort = np.arange(len(input_x), dtype=int)[input_sort] + output_unsort = np.arange(len(output_x), dtype=int)[output_sort] sorted_in = input_x[input_sort] sorted_out = output_x[output_sort] @@ -86,8 +90,8 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], fractional = np.interp(x=sorted_out, xp=sorted_in, fp=input_indices, left=0, right=n_in-1) - left_bins = np.floor(fractional, dtype=int) - right_bins = np.ceil(fractional, dtype=int) + left_bins = np.floor(fractional).astype(int) + right_bins = np.ceil(fractional).astype(int) right_weight = fractional % 1 left_weight = 1 - right_weight @@ -114,18 +118,60 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], conversion_matrix = coo_matrix((weights, (i_entries, j_entries)), shape=(n_in, n_out)) + case InterpolationOptions.CUBIC: + # Cubic interpolation, much harder to implement because we can't just cheat and use numpy + raise NotImplementedError("Cubic interpolation not implemented yet") + case _: - raise ValueError(f"Unsupported interpolation order: {order}") + raise InterpolationError(f"Unsupported interpolation order: {order}") return conversion_matrix +def calculate_interpolation_matrix_2d_axis_axis(input_1: Quantity[ArrayLike], + input_2: Quantity[ArrayLike], + output_1: Quantity[ArrayLike], + output_2: Quantity[ArrayLike], + mask, + order: InterpolationOptions = InterpolationOptions.LINEAR, + is_density: bool = False): + + match order: + case InterpolationOptions.NEAREST_NEIGHBOUR: + pass + + case InterpolationOptions.LINEAR: + pass + + case InterpolationOptions.CUBIC: + pass + + case _: + pass + + def calculate_interpolation_matrix(input_axes: list[Quantity[ArrayLike]], output_axes: list[Quantity[ArrayLike]], data: ArrayLike | None = None, mask: ArrayLike | None = None): - pass + # TODO: We probably should delete this, but lets keep it for now + + if len(input_axes) not in (1, 2): + raise InterpolationError("Interpolation is only supported for 1D and 2D data") + + if len(input_axes) == 1 and len(output_axes) == 1: + # Check for dimensionality + input_axis = input_axes[0] + output_axis = output_axes[0] + + if len(input_axis.value.shape) == 1: + if len(output_axis.value.shape) == 1: + calculate_interpolation_matrix_1d() + + if len(output_axes) != len(input_axes): + # Input or output axes might be 2D matrices + diff --git a/sasdata/transforms/test_interpolation.py b/sasdata/transforms/test_interpolation.py new file mode 100644 index 000000000..688da65fd --- /dev/null +++ b/sasdata/transforms/test_interpolation.py @@ -0,0 +1,91 @@ +import pytest +import numpy as np +from matplotlib import pyplot as plt +from numpy.typing import ArrayLike +from typing import Callable + +from sasdata.quantities.plotting import quantity_plot +from sasdata.quantities.quantity import NamedQuantity, Quantity +from sasdata.quantities import units + +from sasdata.transforms.rebinning import calculate_interpolation_matrix_1d, InterpolationOptions + +test_functions = [ + lambda x: x**2, + lambda x: 2*x, + lambda x: x**3 +] + + +@pytest.mark.parametrize("fun", test_functions) +def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]]): + original_points = NamedQuantity("x_base", np.linspace(-10,10, 31), units.meters) + test_points = NamedQuantity("x_test", np.linspace(-5, 5, 11), units.meters) + + + mapping = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) + + y_original = fun(original_points) + y_test = y_original @ mapping + y_expected = fun(test_points) + + test_units = y_expected.units + + y_values_test = y_test.in_units_of(test_units) + y_values_expected = y_expected.in_units_of(test_units) + + # print(y_values_test) + # print(y_values_expected) + # + # quantity_plot(original_points, y_original) + # quantity_plot(test_points, y_test) + # quantity_plot(test_points, y_expected) + # plt.show() + + assert len(y_values_test) == len(y_values_expected) + + for t, e in zip(y_values_test, y_values_expected): + assert t == pytest.approx(e, abs=2) + + +@pytest.mark.parametrize("fun", test_functions) +def test_linear_interpolate_different_units(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]]): + original_points = NamedQuantity("x_base", np.linspace(-10,10, 107), units.meters) + test_points = NamedQuantity("x_test", np.linspace(-5000, 5000, 11), units.millimeters) + + mapping = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) + + y_original = fun(original_points) + y_test = y_original @ mapping + y_expected = fun(test_points) + + test_units = y_expected.units + + y_values_test = y_test.in_units_of(test_units) + y_values_expected = y_expected.in_units_of(test_units) + # + # print(y_values_test) + # print(y_test.in_si()) + # print(y_values_expected) + # + # plt.plot(original_points.in_si(), y_original.in_si()) + # plt.plot(test_points.in_si(), y_test.in_si(), "x") + # plt.plot(test_points.in_si(), y_expected.in_si(), "o") + # plt.show() + + assert len(y_values_test) == len(y_values_expected) + + for t, e in zip(y_values_test, y_values_expected): + assert t == pytest.approx(e, rel=5e-2) + +def test_linearity_linear(): + """ Test linear interpolation between two points""" + x_and_y = NamedQuantity("x_base", np.linspace(-10, 10, 2), units.meters) + new_x = NamedQuantity("x_test", np.linspace(-5000, 5000, 101), units.millimeters) + + mapping = calculate_interpolation_matrix_1d(x_and_y, new_x, order=InterpolationOptions.LINEAR) + + linear_points = x_and_y @ mapping + + for t, e in zip(new_x.in_si(), linear_points.in_si()): + assert t == pytest.approx(e, rel=1e-3) \ No newline at end of file From a74d36290053ceda5c6a98478ebb70c9fd3590a4 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 20 Sep 2023 13:16:45 +0100 Subject: [PATCH 133/675] Work towards fraction binning --- sasdata/data_util/geometry.py | 0 sasdata/data_util/meshmerge.py | 89 ++++++++++++++++++++++++++++ sasdata/data_util/sample_polygons.py | 31 ++++++++++ sasdata/data_util/transforms.py | 58 ++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 sasdata/data_util/geometry.py create mode 100644 sasdata/data_util/meshmerge.py create mode 100644 sasdata/data_util/sample_polygons.py create mode 100644 sasdata/data_util/transforms.py diff --git a/sasdata/data_util/geometry.py b/sasdata/data_util/geometry.py new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/data_util/meshmerge.py b/sasdata/data_util/meshmerge.py new file mode 100644 index 000000000..eef12bf99 --- /dev/null +++ b/sasdata/data_util/meshmerge.py @@ -0,0 +1,89 @@ +from typing import Sequence +from scipy.spatial import Delaunay + +import numpy as np + +from dataclasses import dataclass + +@dataclass +class Mesh: + points: np.ndarray + edges: Sequence[Sequence[int]] # List of pairs of points forming edges + cells: Sequence[Sequence[int]] # List of edges constituting a cell + + +def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: + """ Take two lists of polygons and find their intersections + + Polygons in each of the input variables should not overlap i.e. a point in space should be assignable to + at most one polygon in mesh_a and at most one polygon in mesh_b + + Mesh topology should be sensible, otherwise bad things might happen + + :returns: + 1) A triangulated mesh based on both sets of polygons together + 2) The indices of the mesh_a polygon that corresponds to each triangle, -1 for nothing + 3) The indices of the mesh_b polygon that corresponds to each triangle, -1 for nothing + + """ + + # Find intersections of all edges in mesh one with edges in mesh two + + new_points = [] + for edge_a in mesh_a.edges: + for edge_b in mesh_b.edges: + # + # Parametric description of intersection in terms of position along lines + # + # Simultaneous eqns (to reflect current wiki notation) + # s(x2 - x1) - t(x4 - x3) = x3 - x1 + # s(y2 - y1) - t(y4 - y3) = y3 - y1 + # + # in matrix form: + # m.(s,t) = v + # + + p1 = mesh_a.points[edge_a[0]] + p2 = mesh_a.points[edge_a[1]] + p3 = mesh_b.points[edge_b[0]] + p4 = mesh_b.points[edge_b[1]] + + m = np.array([ + [p2[0] - p1[0], p3[0] - p4[0]], + [p2[1] - p1[1], p3[1] - p4[1]]]) + + v = np.array([p3[0] - p1[0], p3[1] - p1[1]]) + + if np.linalg.det(m) == 0: + # Lines don't intersect + break + + st = np.linalg.solve(m, v) + + # As the purpose of this is finding new points for the merged mesh, we don't + # want new points if they are right at the end of the lines, hence non strict + # inequalities here + if np.any(st <= 0) or np.any(st >= 1): + # Exclude intection points, that are not on the *segments* + break + + x = p1[0] + (p2[0] - p1[1])*st[0] + y = p1[1] + (p2[1] - p1[1])*st[1] + + new_points.append((x, y)) + + # Build list of all input points, in a way that we can check for coincident points + + + + # Remove coincident points + + + # Triangulate based on these intersections + + # Find centroids of all output triangles, and find which source cells they belong to + + ## Assign -1 to all cells + ## Find centroids + ## Check whether within bounding box + ## If in bounding box, check cell properly using winding number, if inside, assign diff --git a/sasdata/data_util/sample_polygons.py b/sasdata/data_util/sample_polygons.py new file mode 100644 index 000000000..e12fb1e80 --- /dev/null +++ b/sasdata/data_util/sample_polygons.py @@ -0,0 +1,31 @@ +import numpy as np + +def wedge(q0, q1, theta0, theta1, clockwise=False, n_points_per_degree=2): + + # Traverse a rectangle in curvilinear coordinates (q0, theta0), (q0, theta1), (q1, theta1), (q1, theta0) + if clockwise: + if theta1 > theta0: + theta0 += 2*np.pi + + else: + if theta0 > theta1: + theta1 += 2*np.pi + + subtended_angle = np.abs(theta1 - theta0) + n_points = int(subtended_angle*180*n_points_per_degree/np.pi)+1 + + angles = np.linspace(theta0, theta1, n_points) + + xs = np.concatenate((q0*np.cos(angles), q1*np.cos(angles[::-1]))) + ys = np.concatenate((q0*np.sin(angles), q1*np.sin(angles[::-1]))) + + return np.array((xs, ys)).T + + +if __name__ == "__main__": + import matplotlib.pyplot as plt + xy = wedge(0.3, 0.6, 2, 3) + + plt.plot(xy[:,0], xy[:,1]) + plt.show() + diff --git a/sasdata/data_util/transforms.py b/sasdata/data_util/transforms.py new file mode 100644 index 000000000..d04742d3c --- /dev/null +++ b/sasdata/data_util/transforms.py @@ -0,0 +1,58 @@ +import numpy as np +from scipy.spatial import Voronoi, Delaunay +import matplotlib.pyplot as plt +from matplotlib import cm + + +# Some test data + +qx_base_values = np.linspace(-10, 10, 21) +qy_base_values = np.linspace(-10, 10, 21) + +qx, qy = np.meshgrid(qx_base_values, qy_base_values) + +include = np.logical_not((np.abs(qx) < 2) & (np.abs(qy) < 2)) + +qx = qx[include] +qy = qy[include] + +r = np.sqrt(qx**2 + qy**2) + +data = np.log((1+np.cos(3*r))*np.exp(-r*r)) + +colormap = cm.get_cmap('winter', 256) + +def get_data_mesh(x, y, data): + + input_data = np.array((x, y)).T + voronoi = Voronoi(input_data) + + # plt.scatter(voronoi.vertices[:,0], voronoi.vertices[:,1]) + # plt.scatter(voronoi.points[:,0], voronoi.points[:,1]) + + cmin = np.min(data) + cmax = np.max(data) + + color_index_map = np.array(255 * (data - cmin) / (cmax - cmin), dtype=int) + + for point_index, points in enumerate(voronoi.points): + + region_index = voronoi.point_region[point_index] + region = voronoi.regions[region_index] + + if len(region) > 0: + + if -1 in region: + + pass + + else: + + color = colormap(color_index_map[point_index]) + + circly = region + [region[0]] + plt.fill(voronoi.vertices[circly, 0], voronoi.vertices[circly, 1], color=color, edgecolor="white") + + plt.show() + +get_data_mesh(qx.reshape(-1), qy.reshape(-1), data) \ No newline at end of file From 8e343baaa8d021c19e1c108e8ec379fa53350f18 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 22 Sep 2023 13:50:25 +0100 Subject: [PATCH 134/675] Mesh merging and some refactoring --- sasdata/data_util/meshmerge.py | 89 --------- .../{geometry.py => slicing/__init__.py} | 0 sasdata/data_util/slicing/geometry.py | 0 sasdata/data_util/slicing/mesh.py | 28 +++ sasdata/data_util/slicing/meshmerge.py | 170 ++++++++++++++++++ .../{ => slicing}/sample_polygons.py | 0 sasdata/data_util/{ => slicing}/transforms.py | 0 sasdata/data_util/slicing/voronoi_mesh.py | 37 ++++ 8 files changed, 235 insertions(+), 89 deletions(-) delete mode 100644 sasdata/data_util/meshmerge.py rename sasdata/data_util/{geometry.py => slicing/__init__.py} (100%) create mode 100644 sasdata/data_util/slicing/geometry.py create mode 100644 sasdata/data_util/slicing/mesh.py create mode 100644 sasdata/data_util/slicing/meshmerge.py rename sasdata/data_util/{ => slicing}/sample_polygons.py (100%) rename sasdata/data_util/{ => slicing}/transforms.py (100%) create mode 100644 sasdata/data_util/slicing/voronoi_mesh.py diff --git a/sasdata/data_util/meshmerge.py b/sasdata/data_util/meshmerge.py deleted file mode 100644 index eef12bf99..000000000 --- a/sasdata/data_util/meshmerge.py +++ /dev/null @@ -1,89 +0,0 @@ -from typing import Sequence -from scipy.spatial import Delaunay - -import numpy as np - -from dataclasses import dataclass - -@dataclass -class Mesh: - points: np.ndarray - edges: Sequence[Sequence[int]] # List of pairs of points forming edges - cells: Sequence[Sequence[int]] # List of edges constituting a cell - - -def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: - """ Take two lists of polygons and find their intersections - - Polygons in each of the input variables should not overlap i.e. a point in space should be assignable to - at most one polygon in mesh_a and at most one polygon in mesh_b - - Mesh topology should be sensible, otherwise bad things might happen - - :returns: - 1) A triangulated mesh based on both sets of polygons together - 2) The indices of the mesh_a polygon that corresponds to each triangle, -1 for nothing - 3) The indices of the mesh_b polygon that corresponds to each triangle, -1 for nothing - - """ - - # Find intersections of all edges in mesh one with edges in mesh two - - new_points = [] - for edge_a in mesh_a.edges: - for edge_b in mesh_b.edges: - # - # Parametric description of intersection in terms of position along lines - # - # Simultaneous eqns (to reflect current wiki notation) - # s(x2 - x1) - t(x4 - x3) = x3 - x1 - # s(y2 - y1) - t(y4 - y3) = y3 - y1 - # - # in matrix form: - # m.(s,t) = v - # - - p1 = mesh_a.points[edge_a[0]] - p2 = mesh_a.points[edge_a[1]] - p3 = mesh_b.points[edge_b[0]] - p4 = mesh_b.points[edge_b[1]] - - m = np.array([ - [p2[0] - p1[0], p3[0] - p4[0]], - [p2[1] - p1[1], p3[1] - p4[1]]]) - - v = np.array([p3[0] - p1[0], p3[1] - p1[1]]) - - if np.linalg.det(m) == 0: - # Lines don't intersect - break - - st = np.linalg.solve(m, v) - - # As the purpose of this is finding new points for the merged mesh, we don't - # want new points if they are right at the end of the lines, hence non strict - # inequalities here - if np.any(st <= 0) or np.any(st >= 1): - # Exclude intection points, that are not on the *segments* - break - - x = p1[0] + (p2[0] - p1[1])*st[0] - y = p1[1] + (p2[1] - p1[1])*st[1] - - new_points.append((x, y)) - - # Build list of all input points, in a way that we can check for coincident points - - - - # Remove coincident points - - - # Triangulate based on these intersections - - # Find centroids of all output triangles, and find which source cells they belong to - - ## Assign -1 to all cells - ## Find centroids - ## Check whether within bounding box - ## If in bounding box, check cell properly using winding number, if inside, assign diff --git a/sasdata/data_util/geometry.py b/sasdata/data_util/slicing/__init__.py similarity index 100% rename from sasdata/data_util/geometry.py rename to sasdata/data_util/slicing/__init__.py diff --git a/sasdata/data_util/slicing/geometry.py b/sasdata/data_util/slicing/geometry.py new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/data_util/slicing/mesh.py b/sasdata/data_util/slicing/mesh.py new file mode 100644 index 000000000..c27be6030 --- /dev/null +++ b/sasdata/data_util/slicing/mesh.py @@ -0,0 +1,28 @@ +from typing import Sequence + +import numpy as np + +import matplotlib.pyplot as plt +from matplotlib.collections import LineCollection + +class Mesh: + def __init__(self, points: np.ndarray, edges: Sequence[Sequence[int]], cells: Sequence[Sequence[int]]): + self.points = points + self.edges = edges + self.cells = cells + + self._cells_to_points = None + + + def show(self, actually_show=True, **kwargs): + + ax = plt.gca() + segments = [[self.points[edge[0]], self.points[edge[1]]] for edge in self.edges] + line_collection = LineCollection(segments=segments, **kwargs) + ax.add_collection(line_collection) + + if actually_show: + plt.show() + + def show_data(self, data: np.ndarray): + raise NotImplementedError("Show data not implemented") \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshmerge.py b/sasdata/data_util/slicing/meshmerge.py new file mode 100644 index 000000000..32cd8e1f5 --- /dev/null +++ b/sasdata/data_util/slicing/meshmerge.py @@ -0,0 +1,170 @@ +from typing import Sequence +from scipy.spatial import Delaunay + +import numpy as np + +from dataclasses import dataclass + +from sasdata.data_util.slicing.mesh import Mesh + +import matplotlib.pyplot as plt + +def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: + """ Take two lists of polygons and find their intersections + + Polygons in each of the input variables should not overlap i.e. a point in space should be assignable to + at most one polygon in mesh_a and at most one polygon in mesh_b + + Mesh topology should be sensible, otherwise bad things might happen + + :returns: + 1) A triangulated mesh based on both sets of polygons together + 2) The indices of the mesh_a polygon that corresponds to each triangle, -1 for nothing + 3) The indices of the mesh_b polygon that corresponds to each triangle, -1 for nothing + + """ + + # Find intersections of all edges in mesh one with edges in mesh two + + new_x = [] + new_y = [] + for edge_a in mesh_a.edges: + for edge_b in mesh_b.edges: + + p1 = mesh_a.points[edge_a[0]] + p2 = mesh_a.points[edge_a[1]] + p3 = mesh_b.points[edge_b[0]] + p4 = mesh_b.points[edge_b[1]] + + # Bounding box check + + # First edge entirely to left of other + if max((p1[0], p2[0])) < min((p3[0], p4[0])): + continue + + # First edge entirely below other + if max((p1[1], p2[1])) < min((p3[1], p4[1])): + continue + + # First edge entirely to right of other + if min((p1[0], p2[0])) > max((p3[0], p4[0])): + continue + + # First edge entirely above other + if min((p1[1], p2[1])) > max((p3[1], p4[1])): + continue + + # + # Parametric description of intersection in terms of position along lines + # + # Simultaneous eqns (to reflect current wiki notation) + # s(x2 - x1) - t(x4 - x3) = x3 - x1 + # s(y2 - y1) - t(y4 - y3) = y3 - y1 + # + # in matrix form: + # m.(s,t) = v + # + + + m = np.array([ + [p2[0] - p1[0], p3[0] - p4[0]], + [p2[1] - p1[1], p3[1] - p4[1]]]) + + v = np.array([p3[0] - p1[0], p3[1] - p1[1]]) + + if np.linalg.det(m) == 0: + # Lines don't intersect, or are colinear in a way that doesn't matter + continue + + st = np.linalg.solve(m, v) + + # As the purpose of this is finding new points for the merged mesh, we don't + # want new points if they are right at the end of the lines, hence non-strict + # inequalities here + if np.any(st <= 0) or np.any(st >= 1): + # Exclude intection points, that are not on the *segments* + continue + + x = p1[0] + (p2[0] - p1[0])*st[0] + y = p1[1] + (p2[1] - p1[1])*st[0] + + new_x.append(x) + new_y.append(y) + + + + # Build list of all input points, in a way that we can check for coincident points + + # plt.scatter(mesh_a.points[:,0], mesh_a.points[:,1]) + # plt.scatter(mesh_b.points[:,0], mesh_b.points[:,1]) + # plt.scatter(new_x, new_y) + # + # mesh_a.show(False) + # mesh_b.show(False, color=(.8, .5, 0)) + # + # plt.xlim([0,1]) + # plt.ylim([0,1]) + # + # plt.show() + + points = np.concatenate(( + mesh_a.points, + mesh_b.points, + np.array((new_x, new_y)).T + )) + + # plt.scatter(points[:,0], points[:,1]) + # plt.show() + + # Remove coincident points + + points = np.unique(points, axis=0) + + # Triangulate based on these intersections + + # Find centroids of all output triangles, and find which source cells they belong to + + ## Assign -1 to all cells + ## Find centroids - they're just the closed voronoi cells? + ## Check whether within bounding box + ## If in bounding box, check cell properly using winding number, if inside, assign + + +def simple_intersection(): + mesh_a = Mesh( + np.array([[0, 0.5],[1,0.5]], dtype=float), + [[0, 1]], []) + + mesh_b = Mesh( + np.array([[0.5, 0], [0.5, 1]], dtype=float), + [[0, 1]], []) + + meshmerge(mesh_a, mesh_b) + + + +def simple_intersection_2(): + mesh_a = Mesh( + np.array([[4,3],[1,3]], dtype=float), + [[0, 1]], []) + + mesh_b = Mesh( + np.array([[3, 4], [3, 1]], dtype=float), + [[0, 1]], []) + + meshmerge(mesh_a, mesh_b) +def main(): + from voronoi_mesh import voronoi_mesh + + n1 = 100 + n2 = 100 + + m1 = voronoi_mesh(np.random.random(n1), np.random.random(n1)) + m2 = voronoi_mesh(np.random.random(n2), np.random.random(n2)) + + + meshmerge(m1, m2) + +if __name__ == "__main__": + main() + # simple_intersection() \ No newline at end of file diff --git a/sasdata/data_util/sample_polygons.py b/sasdata/data_util/slicing/sample_polygons.py similarity index 100% rename from sasdata/data_util/sample_polygons.py rename to sasdata/data_util/slicing/sample_polygons.py diff --git a/sasdata/data_util/transforms.py b/sasdata/data_util/slicing/transforms.py similarity index 100% rename from sasdata/data_util/transforms.py rename to sasdata/data_util/slicing/transforms.py diff --git a/sasdata/data_util/slicing/voronoi_mesh.py b/sasdata/data_util/slicing/voronoi_mesh.py new file mode 100644 index 000000000..34a8fd7d6 --- /dev/null +++ b/sasdata/data_util/slicing/voronoi_mesh.py @@ -0,0 +1,37 @@ +import numpy as np +from scipy.spatial import Voronoi + + +from sasdata.data_util.slicing.mesh import Mesh + +def voronoi_mesh(x, y) -> Mesh: + + input_data = np.array((x, y)).T + voronoi = Voronoi(input_data) + + edges = set() + + for point_index, points in enumerate(voronoi.points): + + region_index = voronoi.point_region[point_index] + region = voronoi.regions[region_index] + + wrapped = region + [region[0]] + for a, b in zip(wrapped[:-1], wrapped[1:]): + if not a == -1 and not b == -1: + + # make sure the representation is unique + if a > b: + edges.add((a, b)) + else: + edges.add((b, a)) + + edges = list(edges) + + return Mesh(points=voronoi.vertices, edges=edges, cells=[]) + + +if __name__ == "__main__": + points = np.random.random((100, 2)) + mesh = voronoi_mesh(points[:,0], points[:,1]) + mesh.show() \ No newline at end of file From ba792f278ddb02ad6bdf885bc7528895b1962a26 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Sun, 24 Sep 2023 12:54:29 +0100 Subject: [PATCH 135/675] Triangulated mesh --- sasdata/data_util/slicing/delaunay_mesh.py | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 sasdata/data_util/slicing/delaunay_mesh.py diff --git a/sasdata/data_util/slicing/delaunay_mesh.py b/sasdata/data_util/slicing/delaunay_mesh.py new file mode 100644 index 000000000..ef90e44df --- /dev/null +++ b/sasdata/data_util/slicing/delaunay_mesh.py @@ -0,0 +1,34 @@ +import numpy as np + +from scipy.spatial import Delaunay + +from sasdata.data_util.slicing.mesh import Mesh + + +def delaunay_mesh(x, y) -> Mesh: + + input_data = np.array((x, y)).T + delaunay = Delaunay(input_data) + + edges = set() + + for simplex_index, simplex in enumerate(delaunay.simplices): + + wrapped = list(simplex) + [simplex[0]] + + for a, b in zip(wrapped[:-1], wrapped[1:]): + # make sure the representation is unique + if a > b: + edges.add((a, b)) + else: + edges.add((b, a)) + + edges = list(edges) + + return Mesh(points=input_data, edges=edges, cells=[]) + + +if __name__ == "__main__": + points = np.random.random((100, 2)) + mesh = delaunay_mesh(points[:,0], points[:,1]) + mesh.show() \ No newline at end of file From 4c0a055e90f9639e63ad7b8327905c089a0cb9cb Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 25 Sep 2023 01:28:00 +0100 Subject: [PATCH 136/675] Mesh merging works --- sasdata/data_util/slicing/delaunay_mesh.py | 34 ------ sasdata/data_util/slicing/mesh.py | 28 ----- sasdata/data_util/slicing/meshes/__init__.py | 0 .../data_util/slicing/meshes/delaunay_mesh.py | 32 +++++ sasdata/data_util/slicing/meshes/mesh.py | 96 +++++++++++++++ .../slicing/{ => meshes}/meshmerge.py | 110 +++++++++++------- sasdata/data_util/slicing/meshes/util.py | 10 ++ .../data_util/slicing/meshes/voronoi_mesh.py | 20 ++++ sasdata/data_util/slicing/voronoi_mesh.py | 37 ------ test/slicers/meshes_for_testing.py | 80 ++++--------- test/slicers/utest_meshmerge.py | 16 +-- 11 files changed, 248 insertions(+), 215 deletions(-) delete mode 100644 sasdata/data_util/slicing/delaunay_mesh.py delete mode 100644 sasdata/data_util/slicing/mesh.py create mode 100644 sasdata/data_util/slicing/meshes/__init__.py create mode 100644 sasdata/data_util/slicing/meshes/delaunay_mesh.py create mode 100644 sasdata/data_util/slicing/meshes/mesh.py rename sasdata/data_util/slicing/{ => meshes}/meshmerge.py (51%) create mode 100644 sasdata/data_util/slicing/meshes/util.py create mode 100644 sasdata/data_util/slicing/meshes/voronoi_mesh.py delete mode 100644 sasdata/data_util/slicing/voronoi_mesh.py diff --git a/sasdata/data_util/slicing/delaunay_mesh.py b/sasdata/data_util/slicing/delaunay_mesh.py deleted file mode 100644 index ef90e44df..000000000 --- a/sasdata/data_util/slicing/delaunay_mesh.py +++ /dev/null @@ -1,34 +0,0 @@ -import numpy as np - -from scipy.spatial import Delaunay - -from sasdata.data_util.slicing.mesh import Mesh - - -def delaunay_mesh(x, y) -> Mesh: - - input_data = np.array((x, y)).T - delaunay = Delaunay(input_data) - - edges = set() - - for simplex_index, simplex in enumerate(delaunay.simplices): - - wrapped = list(simplex) + [simplex[0]] - - for a, b in zip(wrapped[:-1], wrapped[1:]): - # make sure the representation is unique - if a > b: - edges.add((a, b)) - else: - edges.add((b, a)) - - edges = list(edges) - - return Mesh(points=input_data, edges=edges, cells=[]) - - -if __name__ == "__main__": - points = np.random.random((100, 2)) - mesh = delaunay_mesh(points[:,0], points[:,1]) - mesh.show() \ No newline at end of file diff --git a/sasdata/data_util/slicing/mesh.py b/sasdata/data_util/slicing/mesh.py deleted file mode 100644 index c27be6030..000000000 --- a/sasdata/data_util/slicing/mesh.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import Sequence - -import numpy as np - -import matplotlib.pyplot as plt -from matplotlib.collections import LineCollection - -class Mesh: - def __init__(self, points: np.ndarray, edges: Sequence[Sequence[int]], cells: Sequence[Sequence[int]]): - self.points = points - self.edges = edges - self.cells = cells - - self._cells_to_points = None - - - def show(self, actually_show=True, **kwargs): - - ax = plt.gca() - segments = [[self.points[edge[0]], self.points[edge[1]]] for edge in self.edges] - line_collection = LineCollection(segments=segments, **kwargs) - ax.add_collection(line_collection) - - if actually_show: - plt.show() - - def show_data(self, data: np.ndarray): - raise NotImplementedError("Show data not implemented") \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/__init__.py b/sasdata/data_util/slicing/meshes/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/data_util/slicing/meshes/delaunay_mesh.py b/sasdata/data_util/slicing/meshes/delaunay_mesh.py new file mode 100644 index 000000000..45e208786 --- /dev/null +++ b/sasdata/data_util/slicing/meshes/delaunay_mesh.py @@ -0,0 +1,32 @@ +import numpy as np +from scipy.spatial import Delaunay + +from sasdata.data_util.slicing.meshes.mesh import Mesh + +def delaunay_mesh(x, y) -> Mesh: + """ Create a triangulated mesh based on input points """ + + input_data = np.array((x, y)).T + delaunay = Delaunay(input_data) + + return Mesh(points=input_data, cells=delaunay.simplices) + + +if __name__ == "__main__": + import matplotlib.pyplot as plt + + points = np.random.random((100, 2)) + mesh = delaunay_mesh(points[:,0], points[:,1]) + mesh.show(actually_show=False) + + print(mesh.cells[50]) + + # pick random cell to show + for cell in mesh.cells_to_edges[10]: + a, b = mesh.edges[cell] + plt.plot( + [mesh.points[a][0], mesh.points[b][0]], + [mesh.points[a][1], mesh.points[b][1]], + color='r') + + plt.show() diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py new file mode 100644 index 000000000..b52b3e8a9 --- /dev/null +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -0,0 +1,96 @@ +from typing import Sequence + +import numpy as np + +import matplotlib.pyplot as plt +from matplotlib.collections import LineCollection + +from sasdata.data_util.slicing.meshes.util import closed_loop_edges + +class Mesh: + def __init__(self, + points: np.ndarray, + cells: Sequence[Sequence[int]]): + + """ + Object representing a mesh. + + Parameters are the values: + mesh points + map from edge to points + map from cells to edges + + it is done this way to ensure a non-redundant representation of cells and edges, + however there are no checks for the topology of the mesh, this is assumed to be done by + whatever creates it. There are also no checks for ordering of cells. + + :param points: points in 2D forming vertices of the mesh + :param cells: ordered lists of indices of points forming each cell (face) + + """ + + self.points = points + self.cells = cells + + # Get edges + + edges = set() + for cell_index, cell in enumerate(cells): + + for a, b in closed_loop_edges(cell): + # make sure the representation is unique + if a > b: + edges.add((a, b)) + else: + edges.add((b, a)) + + self.edges = list(edges) + + # Associate edges with faces + + edge_lookup = {edge: i for i, edge in enumerate(self.edges)} + self.cells_to_edges = [] + + for cell in cells: + + this_cell_data = [] + + for a, b in closed_loop_edges(cell): + # make sure the representation is unique + if a > b: + this_cell_data.append(edge_lookup[(a, b)]) + else: + this_cell_data.append(edge_lookup[(b, a)]) + + self.cells_to_edges.append(this_cell_data) + + # Counts for elements + self.n_points = self.points.shape[0] + self.n_edges = len(self.edges) + self.n_cells = len(self.cells) + + def show(self, actually_show=True, show_labels=False, **kwargs): + """ Show on a plot """ + ax = plt.gca() + segments = [[self.points[edge[0]], self.points[edge[1]]] for edge in self.edges] + line_collection = LineCollection(segments=segments, **kwargs) + ax.add_collection(line_collection) + + if show_labels: + text_color = kwargs["color"] if "color" in kwargs else 'k' + for i, cell in enumerate(self.cells): + xy = np.sum(self.points[cell, :], axis=0)/len(cell) + ax.text(xy[0], xy[1], str(i), horizontalalignment="center", verticalalignment="center", color=text_color) + + x_limits = [np.min(self.points[:,0]), np.max(self.points[:,0])] + y_limits = [np.min(self.points[:,1]), np.max(self.points[:,1])] + + plt.xlim(x_limits) + plt.ylim(y_limits) + + if actually_show: + plt.show() + + def show_data(self, data: np.ndarray, show_mesh=True): + """ Show with data """ + raise NotImplementedError("Show data not implemented") \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshmerge.py b/sasdata/data_util/slicing/meshes/meshmerge.py similarity index 51% rename from sasdata/data_util/slicing/meshmerge.py rename to sasdata/data_util/slicing/meshes/meshmerge.py index 32cd8e1f5..3ce52ba56 100644 --- a/sasdata/data_util/slicing/meshmerge.py +++ b/sasdata/data_util/slicing/meshes/meshmerge.py @@ -1,13 +1,9 @@ -from typing import Sequence -from scipy.spatial import Delaunay - import numpy as np -from dataclasses import dataclass - -from sasdata.data_util.slicing.mesh import Mesh +from sasdata.data_util.slicing.meshes.mesh import Mesh +from sasdata.data_util.slicing.meshes.delaunay_mesh import delaunay_mesh +from sasdata.data_util.slicing.meshes.util import closed_loop_edges -import matplotlib.pyplot as plt def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: """ Take two lists of polygons and find their intersections @@ -15,7 +11,8 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] Polygons in each of the input variables should not overlap i.e. a point in space should be assignable to at most one polygon in mesh_a and at most one polygon in mesh_b - Mesh topology should be sensible, otherwise bad things might happen + Mesh topology should be sensible, otherwise bad things might happen, also, the cells of the input meshes + must be in order (which is assumed by the mesh class constructor anyway). :returns: 1) A triangulated mesh based on both sets of polygons together @@ -95,17 +92,6 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] # Build list of all input points, in a way that we can check for coincident points - # plt.scatter(mesh_a.points[:,0], mesh_a.points[:,1]) - # plt.scatter(mesh_b.points[:,0], mesh_b.points[:,1]) - # plt.scatter(new_x, new_y) - # - # mesh_a.show(False) - # mesh_b.show(False, color=(.8, .5, 0)) - # - # plt.xlim([0,1]) - # plt.ylim([0,1]) - # - # plt.show() points = np.concatenate(( mesh_a.points, @@ -113,8 +99,6 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] np.array((new_x, new_y)).T )) - # plt.scatter(points[:,0], points[:,1]) - # plt.show() # Remove coincident points @@ -122,37 +106,75 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] # Triangulate based on these intersections + output_mesh = delaunay_mesh(points[:, 0], points[:, 1]) + # Find centroids of all output triangles, and find which source cells they belong to - ## Assign -1 to all cells - ## Find centroids - they're just the closed voronoi cells? - ## Check whether within bounding box - ## If in bounding box, check cell properly using winding number, if inside, assign + ## step 1) Assign -1 to all cells of original meshes + assignments_a = -np.ones(output_mesh.n_cells, dtype=int) + assignments_b = -np.ones(output_mesh.n_cells, dtype=int) + + ## step 2) Find centroids of triangulated mesh (just needs to be a point inside, but this is a good one) + centroids = [] + for cell in output_mesh.cells: + centroid = np.sum(output_mesh.points[cell, :]/3, axis=0) + centroids.append(centroid) + + ## step 3) Perform checks based on winding number method (see wikipedia Point in Polygon). + for mesh, assignments in [ + (mesh_a, assignments_a), + (mesh_b, assignments_b)]: + + for centroid_index, centroid in enumerate(centroids): + for cell_index, cell in enumerate(mesh.cells): + # Bounding box check + points = mesh.points[cell, :] + if np.any(centroid < np.min(points, axis=0)): # x or y less than any in polygon + continue -def simple_intersection(): - mesh_a = Mesh( - np.array([[0, 0.5],[1,0.5]], dtype=float), - [[0, 1]], []) + if np.any(centroid > np.max(points, axis=0)): # x or y greater than any in polygon + continue - mesh_b = Mesh( - np.array([[0.5, 0], [0.5, 1]], dtype=float), - [[0, 1]], []) + # Winding number check - count directional crossings of vertical half line from centroid + winding_number = 0 + for i1, i2 in closed_loop_edges(cell): + p1 = mesh.points[i1, :] + p2 = mesh.points[i2, :] - meshmerge(mesh_a, mesh_b) + # if the section xs do not straddle the x=centroid_x coordinate, then the + # edge cannot cross the half line. + # If it does, then remember which way it was + # * Careful about ends + # * Also, note that the p1[0] == p2[0] -> (no contribution) case is covered by the strict inequality + if p1[0] > centroid[0] >= p2[0]: + left_right = -1 + elif p2[0] > centroid[0] >= p1[0]: + left_right = 1 + else: + continue + # Find the y point that it crosses x=centroid at + # note: denominator cannot be zero because of strict inequality above + gradient = (p2[1] - p1[1]) / (p2[0] - p1[0]) + x_delta = centroid[0] - p1[0] + y = p1[1] + x_delta * gradient + if y > centroid[1]: + winding_number += left_right -def simple_intersection_2(): - mesh_a = Mesh( - np.array([[4,3],[1,3]], dtype=float), - [[0, 1]], []) - mesh_b = Mesh( - np.array([[3, 4], [3, 1]], dtype=float), - [[0, 1]], []) + if abs(winding_number) > 0: + # Do assignment of input cell to output triangle index + assignments[centroid_index] = cell_index + + # end cell loop + + # end centroid loop + + return output_mesh, assignments_a, assignments_b + - meshmerge(mesh_a, mesh_b) def main(): from voronoi_mesh import voronoi_mesh @@ -163,8 +185,10 @@ def main(): m2 = voronoi_mesh(np.random.random(n2), np.random.random(n2)) - meshmerge(m1, m2) + mesh, _, _ = meshmerge(m1, m2) + + mesh.show() + if __name__ == "__main__": main() - # simple_intersection() \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/util.py b/sasdata/data_util/slicing/meshes/util.py new file mode 100644 index 000000000..b78a9e076 --- /dev/null +++ b/sasdata/data_util/slicing/meshes/util.py @@ -0,0 +1,10 @@ +from typing import Sequence, TypeVar + +T = TypeVar("T") + +def closed_loop_edges(values: Sequence[T]) -> tuple[T, T]: + """ Generator for a closed loop of edge pairs """ + for pair in zip(values, values[1:]): + yield pair + + yield values[-1], values[0] \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/voronoi_mesh.py b/sasdata/data_util/slicing/meshes/voronoi_mesh.py new file mode 100644 index 000000000..77db2a687 --- /dev/null +++ b/sasdata/data_util/slicing/meshes/voronoi_mesh.py @@ -0,0 +1,20 @@ +import numpy as np +from scipy.spatial import Voronoi + + +from sasdata.data_util.slicing.meshes.mesh import Mesh + +def voronoi_mesh(x, y) -> Mesh: + + input_data = np.array((x.reshape(-1), y.reshape(-1))).T + voronoi = Voronoi(input_data) + + finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] + + return Mesh(points=voronoi.vertices, cells=finite_cells) + + +if __name__ == "__main__": + points = np.random.random((100, 2)) + mesh = voronoi_mesh(points[:,0], points[:,1]) + mesh.show() \ No newline at end of file diff --git a/sasdata/data_util/slicing/voronoi_mesh.py b/sasdata/data_util/slicing/voronoi_mesh.py deleted file mode 100644 index 34a8fd7d6..000000000 --- a/sasdata/data_util/slicing/voronoi_mesh.py +++ /dev/null @@ -1,37 +0,0 @@ -import numpy as np -from scipy.spatial import Voronoi - - -from sasdata.data_util.slicing.mesh import Mesh - -def voronoi_mesh(x, y) -> Mesh: - - input_data = np.array((x, y)).T - voronoi = Voronoi(input_data) - - edges = set() - - for point_index, points in enumerate(voronoi.points): - - region_index = voronoi.point_region[point_index] - region = voronoi.regions[region_index] - - wrapped = region + [region[0]] - for a, b in zip(wrapped[:-1], wrapped[1:]): - if not a == -1 and not b == -1: - - # make sure the representation is unique - if a > b: - edges.add((a, b)) - else: - edges.add((b, a)) - - edges = list(edges) - - return Mesh(points=voronoi.vertices, edges=edges, cells=[]) - - -if __name__ == "__main__": - points = np.random.random((100, 2)) - mesh = voronoi_mesh(points[:,0], points[:,1]) - mesh.show() \ No newline at end of file diff --git a/test/slicers/meshes_for_testing.py b/test/slicers/meshes_for_testing.py index 7e39ec75c..ff87dc8fc 100644 --- a/test/slicers/meshes_for_testing.py +++ b/test/slicers/meshes_for_testing.py @@ -4,9 +4,9 @@ import numpy as np -from sasdata.slicing.meshes.mesh import Mesh -from sasdata.slicing.meshes.meshmerge import meshmerge -from sasdata.slicing.meshes.voronoi_mesh import voronoi_mesh +from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh +from sasdata.data_util.slicing.meshes.mesh import Mesh +from sasdata.data_util.slicing.meshes.meshmerge import meshmerge coords = np.arange(-4, 5) grid_mesh = voronoi_mesh(*np.meshgrid(coords, coords)) @@ -32,61 +32,25 @@ # Subset of the mappings that meshmerge should include # This can be read off the plots generated below - - expected_shape_mappings = [ - (100, -1), - (152, -1), - (141, -1), - (172, -1), - (170, -1), - (0, -1), + (98, -1), + (99, -1), + (12, 0), (1, -1), - (8, 0), - (9, 0), - (37, 0), - (83, 0), - (190, 1), - (186, 1), - (189, 1), - (193, 1) -] + (148, 1), + (149, 1), + (110, 1), + (144, -1), + (123, -1)] + expected_grid_mappings = [ - (89, 0), - (90, 1), - (148, 16), - (175, 35), - (60, 47), - (44, 47), - (80, 60) + (89, 1), + (146, 29), + (66, 34), + (112, 45) ] -# -# Mesh location tests -# - -location_test_mesh_points = np.array([ - [0, 0], # 0 - [0, 1], # 1 - [0, 2], # 2 - [1, 0], # 3 - [1, 1], # 4 - [1, 2], # 5 - [2, 0], # 6 - [2, 1], # 7 - [2, 2]], dtype=float) - -location_test_mesh_cells = [ - [0, 1, 4, 3], - [1, 2, 5, 4], - [3, 4, 7, 6], - [4, 5, 8, 7]] - -location_test_mesh = Mesh(location_test_mesh_points, location_test_mesh_cells) - -test_coords = 0.25 + 0.5*np.arange(4) -location_test_points_x, location_test_points_y = np.meshgrid(test_coords, test_coords) if __name__ == "__main__": @@ -98,18 +62,14 @@ combined_mesh.show(actually_show=False, show_labels=True, color='k') grid_mesh.show(actually_show=False, show_labels=True, color='r') - plt.xlim([-5, 5]) - plt.ylim([-5, 5]) + plt.xlim([-4, 4]) + plt.ylim([-4, 4]) plt.figure() combined_mesh.show(actually_show=False, show_labels=True, color='k') shape_mesh.show(actually_show=False, show_labels=True, color='r') - plt.xlim([-5, 5]) - plt.ylim([-5, 5]) - - plt.figure() - location_test_mesh.show(actually_show=False, show_labels=True) - plt.scatter(location_test_points_x, location_test_points_y) + plt.xlim([-4, 4]) + plt.ylim([-4, 4]) plt.show() diff --git a/test/slicers/utest_meshmerge.py b/test/slicers/utest_meshmerge.py index a4a1645c2..d1e16f2bf 100644 --- a/test/slicers/utest_meshmerge.py +++ b/test/slicers/utest_meshmerge.py @@ -4,22 +4,12 @@ It's pretty hard to test componentwise, but we can do some tests of the general behaviour """ -from sasdata.slicing.meshes.meshmerge import meshmerge -from test.slicers.meshes_for_testing import expected_grid_mappings, expected_shape_mappings, grid_mesh, shape_mesh +from sasdata.data_util.slicing.meshes.meshmerge import meshmerge +from test.slicers.meshes_for_testing import ( + grid_mesh, shape_mesh, expected_grid_mappings, expected_shape_mappings) def test_meshmerge_mappings(): - """ Test the output of meshmerge is correct - - IMPORTANT IF TESTS FAIL!!!... The docs for scipy.spatial.Voronoi and Delaunay - say that the ordering of faces might depend on machine precession. Thus, these - tests might not be reliable... we'll see how they play out - """ - - import sys - if sys.platform == "darwin": - # It does indeed rely on machine precision, only run on windows and linux - return combined_mesh, grid_mappings, shape_mappings = meshmerge(grid_mesh, shape_mesh) From da6b5ae47185dc6cc52f84b453af47dcb3deb3ef Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 28 Sep 2023 02:56:00 +0100 Subject: [PATCH 137/675] Implementation of Rebinner base class --- sasdata/data_util/slicing/meshes/mesh.py | 28 +++++ sasdata/data_util/slicing/rebinning.py | 128 +++++++++++++++++++++++ test/slicers/utest_meshmerge.py | 7 ++ 3 files changed, 163 insertions(+) create mode 100644 sasdata/data_util/slicing/rebinning.py diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py index b52b3e8a9..0f12102ce 100644 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -69,6 +69,34 @@ def __init__(self, self.n_edges = len(self.edges) self.n_cells = len(self.cells) + # Areas + self._areas = None + + @property + def areas(self): + """ Areas of cells """ + + if self._areas is None: + # Calculate areas + areas = [] + for cell in self.cells: + # Use triangle shoelace formula, basically calculate the + # determinant based on of triangles with one point at 0,0 + a_times_2 = 0.0 + for i1, i2 in closed_loop_edges(cell): + p1 = self.points[i1, :] + p2 = self.points[i2, :] + a_times_2 += p1[0]*p2[1] - p1[1]*p2[0] + + areas.append(0.5*np.abs(a_times_2)) + + # Save in cache + self._areas = np.ndarray(areas) + + # Return cache + return self._areas + + def show(self, actually_show=True, show_labels=False, **kwargs): """ Show on a plot """ ax = plt.gca() diff --git a/sasdata/data_util/slicing/rebinning.py b/sasdata/data_util/slicing/rebinning.py new file mode 100644 index 000000000..c6ba6079b --- /dev/null +++ b/sasdata/data_util/slicing/rebinning.py @@ -0,0 +1,128 @@ +from abc import ABC, abstractmethod +from typing import Optional +from dataclasses import dataclass + +import numpy as np + +from sasdata.data_util.slicing.meshes.mesh import Mesh +from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh +from sasdata.data_util.slicing.meshes.meshmerge import meshmerge + + +@dataclass +class CacheData: + """ Data cached for repeated calculations with the same coordinates """ + input_coordinates: np.ndarray # Input data + input_coordinates_mesh: Mesh # Mesh of the input data + merged_mesh_data: tuple[Mesh, np.ndarray, np.ndarray] # mesh information about the merging + + +class Rebinner(): + + allowable_orders = [-1,0,1] + + def __init__(self, order): + """ Base class for rebinning methods""" + + self._order = order + self._bin_mesh_cache: Optional[Mesh] = None # cached version of the output bin mesh + + # Output dependent caching + self._input_cache: Optional[CacheData] = None + + if order not in Rebinner.allowable_orders: + raise ValueError(f"Expected order to be in {Rebinner.allowable_orders}, got {order}") + + @abstractmethod + def _bin_coordinates(self) -> np.ndarray: + """ Coordinates for the output bins """ + + @abstractmethod + def _bin_mesh(self) -> Mesh: + """ Get the meshes used for binning """ + + @property + def bin_mesh(self): + if self._bin_mesh_cache is None: + bin_mesh = self._bin_mesh() + self._data_mesh_cache = bin_mesh + else: + return self._bin_mesh_cache + + def _post_processing(self, coordinates, values) -> tuple[np.ndarray, np.ndarray]: + """ Perform post-processing on the mesh binned values """ + # Default is to do nothing, override if needed + return coordinates, values + + def _do_binning(self, data): + """ Main binning algorithm """ + + def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> np.ndarray: + """ Main calculation """ + + if self._order == -1: + # Construct the input output mapping just based on input points being the output cells, + # Equivalent to the original binning method + + pass + + else: + # Use a mapping based on meshes + + # Either create de-cache the appropriate mesh + # Why not use a hash? Hashing takes time, equality checks are pretty fast, need to check equality + # when there is a hit anyway in case of very rare chance of collision, hits are the most common case, + # we want it to work 100% of the time, not 99.9999% + if self._input_cache is not None and np.all(self._input_cache.input_coordinates == input_coordinates): + + input_coordinate_mesh = self._input_cache.input_coordinates_mesh + merge_data = self._input_cache.merged_mesh_data + + else: + # Calculate mesh data + input_coordinate_mesh = voronoi_mesh(input_coordinates[:,0], input_coordinates[:, 1]) + self._data_mesh_cahce = input_coordinate_mesh + + merge_data = meshmerge(self.bin_mesh, input_coordinate_mesh) + + # Cache mesh data + self._input_cache = CacheData( + input_coordinates=input_coordinates, + input_coordinates_mesh=input_coordinate_mesh, + merged_mesh_data=merge_data) + + merged_mesh, merged_to_input, merged_to_output = merge_data + + # Calculate values according to the order parameter + + if self._order == 0: + # Based on the overlap of cells only + + input_areas = input_coordinate_mesh.areas + output = np.zeros(self.bin_mesh.n_cells, dtype=float) + + for input_index, output_index, area in zip(merged_to_input, merged_to_output, merged_mesh.areas): + output[output_index] += input_data[input_index] * area / input_areas[input_data] + + return output + + elif self._order == 1: + raise NotImplementedError("1st order (linear) interpolation currently not implemented") + + else: + raise ValueError(f"Expected order to be in {Rebinner.allowable_orders}, got {self._order}") + + def sum(self, input_coordinates: np.ndarray, data: np.ndarray) -> np.ndarray: + """ Return the summed data in the output bins """ + return self._calculate(input_coordinates, data) + + def error_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: + raise NotImplementedError("Error propagation not implemented yet") + + def resolution_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: + raise NotImplementedError("Resolution propagation not implemented yet") + + def average(self, input_coordinates: np.ndarray, data: np.ndarray) -> np.ndarray: + """ Return the averaged data in the output bins """ + return self._calculate(input_coordinates, data) / self.bin_mesh.areas + diff --git a/test/slicers/utest_meshmerge.py b/test/slicers/utest_meshmerge.py index d1e16f2bf..f745d0250 100644 --- a/test/slicers/utest_meshmerge.py +++ b/test/slicers/utest_meshmerge.py @@ -10,6 +10,13 @@ def test_meshmerge_mappings(): + """ Test the output of meshmerge is correct + + IMPORTANT IF TESTS FAIL!!!... The docs for scipy.spatial.Voronoi and Delaunay + say that the ordering of faces might depend on machine precession. Thus, these + tests might not be reliable... we'll see how they play out + """ + combined_mesh, grid_mappings, shape_mappings = meshmerge(grid_mesh, shape_mesh) From 37c6722935909bf93892561a28bb294a1236ee4d Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 28 Sep 2023 10:06:44 +0100 Subject: [PATCH 138/675] Work towards demo --- sasdata/data_util/slicing/meshes/mesh.py | 29 +++++++++++++-- .../data_util/slicing/meshes/voronoi_mesh.py | 11 ++++++ sasdata/data_util/slicing/rebinning.py | 21 ++++++++--- sasdata/data_util/slicing/slicer_demo.py | 19 ++++++++++ .../data_util/slicing/slicers/AnularSector.py | 35 +++++++++++++++++++ 5 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 sasdata/data_util/slicing/slicer_demo.py create mode 100644 sasdata/data_util/slicing/slicers/AnularSector.py diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py index 0f12102ce..cad7b5ffd 100644 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -3,6 +3,7 @@ import numpy as np import matplotlib.pyplot as plt +from matplotlib import cm from matplotlib.collections import LineCollection from sasdata.data_util.slicing.meshes.util import closed_loop_edges @@ -72,6 +73,12 @@ def __init__(self, # Areas self._areas = None + def find_locations(self, points): + """ Find indices of cells containing the input points """ + + + + @property def areas(self): """ Areas of cells """ @@ -119,6 +126,24 @@ def show(self, actually_show=True, show_labels=False, **kwargs): if actually_show: plt.show() - def show_data(self, data: np.ndarray, show_mesh=True): + def show_data(self, data: np.ndarray, cmap='winter', mesh_color='white', show_mesh=True, actually_show=True): """ Show with data """ - raise NotImplementedError("Show data not implemented") \ No newline at end of file + + colormap = cm.get_cmap(cmap, 256) + + cmin = np.min(data) + cmax = np.max(data) + + color_index_map = np.array(255 * (data - cmin) / (cmax - cmin), dtype=int) + + for cell, color_index in zip(self.cells, color_index_map): + + color = colormap(color_index) + + plt.fill(self.points[cell, 0], self.points[cell, 1], color=color) + + if show_mesh: + self.show(actually_show=False, color=mesh_color) + + if actually_show: + self.show() \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/voronoi_mesh.py b/sasdata/data_util/slicing/meshes/voronoi_mesh.py index 77db2a687..975488015 100644 --- a/sasdata/data_util/slicing/meshes/voronoi_mesh.py +++ b/sasdata/data_util/slicing/meshes/voronoi_mesh.py @@ -7,10 +7,21 @@ def voronoi_mesh(x, y) -> Mesh: input_data = np.array((x.reshape(-1), y.reshape(-1))).T + + # Need to make sure mesh covers a finite region, probably not important for + # much data stuff, but is important for plotting + # To do this first need to find an appropriate region + # Then we need to adjust the mesh to deal with these points + voronoi = Voronoi(input_data) + + + finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] + + return Mesh(points=voronoi.vertices, cells=finite_cells) diff --git a/sasdata/data_util/slicing/rebinning.py b/sasdata/data_util/slicing/rebinning.py index c6ba6079b..06ace87a9 100644 --- a/sasdata/data_util/slicing/rebinning.py +++ b/sasdata/data_util/slicing/rebinning.py @@ -19,7 +19,6 @@ class CacheData: class Rebinner(): - allowable_orders = [-1,0,1] def __init__(self, order): """ Base class for rebinning methods""" @@ -30,8 +29,9 @@ def __init__(self, order): # Output dependent caching self._input_cache: Optional[CacheData] = None - if order not in Rebinner.allowable_orders: - raise ValueError(f"Expected order to be in {Rebinner.allowable_orders}, got {order}") + if order not in self.allowable_orders: + raise ValueError(f"Expected order to be in {self.allowable_orders}, got {order}") + @abstractmethod def _bin_coordinates(self) -> np.ndarray: @@ -41,6 +41,10 @@ def _bin_coordinates(self) -> np.ndarray: def _bin_mesh(self) -> Mesh: """ Get the meshes used for binning """ + @property + def allowable_orders(self) -> list[int]: + return [-1, 0, 1] + @property def bin_mesh(self): if self._bin_mesh_cache is None: @@ -104,13 +108,22 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n for input_index, output_index, area in zip(merged_to_input, merged_to_output, merged_mesh.areas): output[output_index] += input_data[input_index] * area / input_areas[input_data] + return output elif self._order == 1: + # Linear interpolation requires the following relationship with the data, + # as the input data is the total over the whole input cell, the linear + # interpolation requires continuity at the vertices, and a constraint on the + # integral. + # + # We can take each of the input points, and the associated values, and solve a system + # of linear equations that gives a total value. + raise NotImplementedError("1st order (linear) interpolation currently not implemented") else: - raise ValueError(f"Expected order to be in {Rebinner.allowable_orders}, got {self._order}") + raise ValueError(f"Expected order to be in {self.allowable_orders}, got {self._order}") def sum(self, input_coordinates: np.ndarray, data: np.ndarray) -> np.ndarray: """ Return the summed data in the output bins """ diff --git a/sasdata/data_util/slicing/slicer_demo.py b/sasdata/data_util/slicing/slicer_demo.py new file mode 100644 index 000000000..775c1d9b7 --- /dev/null +++ b/sasdata/data_util/slicing/slicer_demo.py @@ -0,0 +1,19 @@ +""" Dev docs: """ + +import numpy as np + +from sasdata.data_util.slicing.slicers import AnularSector +from sasdata.data_util.slicing.meshes.mesh import Mesh +from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh + + + +if __name__ == "__main__": + + # Demo of sums, annular sector over some not very circular data + + q_range = 1.5 + + test_coordinates = (2*q_range)*(np.random.random((100, 2))-0.5) + + # Demo of averaging, annular sector over ring shaped data \ No newline at end of file diff --git a/sasdata/data_util/slicing/slicers/AnularSector.py b/sasdata/data_util/slicing/slicers/AnularSector.py new file mode 100644 index 000000000..bf3021d02 --- /dev/null +++ b/sasdata/data_util/slicing/slicers/AnularSector.py @@ -0,0 +1,35 @@ +import numpy as np + +from sasdata.data_util.slicing.rebinning import Rebinner +from sasdata.data_util.slicing.meshes.mesh import Mesh + +class AnularSector(Rebinner): + """ A single annular sector (wedge sum)""" + def __init__(self, q0: float, q1: float, phi0: float, phi1: float, order: int=1, points_per_degree: int=2): + super().__init__(order) + + self.q0 = q0 + self.q1 = q1 + self.phi0 = phi0 + self.phi1 = phi1 + + self.points_per_degree = points_per_degree + + def _bin_mesh(self) -> Mesh: + + n_points = 1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi + + angles = np.linspace(self.phi0, self.phi1, n_points) + + row1 = self.q0 * np.array([np.cos(angles), np.sin(angles)]) + row2 = self.q1 * np.array([np.cos(angles), np.sin(angles)])[:, ::-1] + + points = np.concatenate((row1, row2), axis=1) + + cells = [i for i in range(2*n_points)] + + return Mesh(points=points, cells=cells) + + def _bin_coordinates(self) -> np.ndarray: + return np.array([], dtype=float) + From a89b392deb5fa3f9f0002704697d44c8cfbb5422 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 28 Sep 2023 12:59:02 +0100 Subject: [PATCH 139/675] Voronoi mesh edges and ordering --- sasdata/data_util/slicing/meshes/mesh.py | 16 +++- .../data_util/slicing/meshes/voronoi_mesh.py | 80 +++++++++++++++++-- test/slicers/meshes_for_testing.py | 46 +++++++---- 3 files changed, 114 insertions(+), 28 deletions(-) diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py index cad7b5ffd..05f4d33bc 100644 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -76,7 +76,7 @@ def __init__(self, def find_locations(self, points): """ Find indices of cells containing the input points """ - + @property @@ -98,7 +98,7 @@ def areas(self): areas.append(0.5*np.abs(a_times_2)) # Save in cache - self._areas = np.ndarray(areas) + self._areas = np.array(areas) # Return cache return self._areas @@ -126,11 +126,21 @@ def show(self, actually_show=True, show_labels=False, **kwargs): if actually_show: plt.show() - def show_data(self, data: np.ndarray, cmap='winter', mesh_color='white', show_mesh=True, actually_show=True): + def show_data(self, + data: np.ndarray, + cmap='winter', + mesh_color='white', + show_mesh=True, + actually_show=True, + density=False): + """ Show with data """ colormap = cm.get_cmap(cmap, 256) + if density: + data = data / self.areas + cmin = np.min(data) cmax = np.max(data) diff --git a/sasdata/data_util/slicing/meshes/voronoi_mesh.py b/sasdata/data_util/slicing/meshes/voronoi_mesh.py index 975488015..3497fbba7 100644 --- a/sasdata/data_util/slicing/meshes/voronoi_mesh.py +++ b/sasdata/data_util/slicing/meshes/voronoi_mesh.py @@ -4,28 +4,92 @@ from sasdata.data_util.slicing.meshes.mesh import Mesh -def voronoi_mesh(x, y) -> Mesh: +def voronoi_mesh(x, y, debug_plot=False) -> Mesh: + """ Create a mesh based on a voronoi diagram of points """ input_data = np.array((x.reshape(-1), y.reshape(-1))).T # Need to make sure mesh covers a finite region, probably not important for # much data stuff, but is important for plotting - # To do this first need to find an appropriate region - # Then we need to adjust the mesh to deal with these points + # + # * We want the cells at the edge of the mesh to have a reasonable size, definitely not infinite + # * The exact size doesn't matter that much + # * It should work well with a grid, but also + # * ...it should be robust so that if the data isn't on a grid, it doesn't cause any serious problems + # + # Plan: Create a square border of points that are totally around the points, this is + # at the distance it would be if it was an extra row of grid points + # to do this we'll need + # 1) an estimate of the grid spacing + # 2) the bounding box of the grid + # + # Use the median area of finite voronoi cells as an estimate voronoi = Voronoi(input_data) + finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] + premesh = Mesh(points=voronoi.vertices, cells=finite_cells) + area_spacing = np.median(premesh.areas) + gap = np.sqrt(area_spacing) + # Bounding box is easy + x_min, y_min = np.min(input_data, axis=0) + x_max, y_max = np.max(input_data, axis=0) + # Create a border + n_x = np.round((x_max - x_min)/gap).astype(int) + n_y = np.round((y_max - y_min)/gap).astype(int) - finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] + top_bottom_xs = np.linspace(x_min - gap, x_max + gap, n_x + 3) + left_right_ys = np.linspace(y_min, y_max, n_y + 1) + top = np.array([top_bottom_xs, (y_max + gap) * np.ones_like(top_bottom_xs)]) + bottom = np.array([top_bottom_xs, (y_min - gap) * np.ones_like(top_bottom_xs)]) + left = np.array([(x_min - gap) * np.ones_like(left_right_ys), left_right_ys]) + right = np.array([(x_max + gap) * np.ones_like(left_right_ys), left_right_ys]) + added_points = np.concatenate((top, bottom, left, right), axis=1).T - return Mesh(points=voronoi.vertices, cells=finite_cells) + if debug_plot: + import matplotlib.pyplot as plt + plt.scatter(x, y) + plt.scatter(added_points[:, 0], added_points[:, 1]) + plt.show() + new_points = np.concatenate((input_data, added_points), axis=0) + voronoi = Voronoi(new_points) -if __name__ == "__main__": + # Remove the cells that correspond to the added edge points, + # Because the points on the edge of the square are (weakly) convex, these + # regions be infinite + + # finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] + + # ... however, we can just use .region_points + input_regions = voronoi.point_region[:input_data.shape[0]] + cells = [voronoi.regions[region_index] for region_index in input_regions] + + return Mesh(points=voronoi.vertices, cells=cells) + + +def square_grid_check(): + values = np.linspace(-10, 10, 21) + x, y = np.meshgrid(values, values) + + mesh = voronoi_mesh(x, y) + + mesh.show(show_labels=True) + +def random_grid_check(): + import matplotlib.pyplot as plt points = np.random.random((100, 2)) - mesh = voronoi_mesh(points[:,0], points[:,1]) - mesh.show() \ No newline at end of file + mesh = voronoi_mesh(points[:, 0], points[:, 1], True) + mesh.show(actually_show=False) + plt.scatter(points[:, 0], points[:, 1]) + plt.show() + + +if __name__ == "__main__": + square_grid_check() + # random_grid_check() + diff --git a/test/slicers/meshes_for_testing.py b/test/slicers/meshes_for_testing.py index ff87dc8fc..c7426245b 100644 --- a/test/slicers/meshes_for_testing.py +++ b/test/slicers/meshes_for_testing.py @@ -32,26 +32,38 @@ # Subset of the mappings that meshmerge should include # This can be read off the plots generated below + + expected_shape_mappings = [ - (98, -1), - (99, -1), - (12, 0), + (100, -1), + (152, -1), + (141, -1), + (172, -1), + (170, -1), + (0, -1), (1, -1), - (148, 1), - (149, 1), - (110, 1), - (144, -1), - (123, -1)] - + (8, 0), + (9, 0), + (37, 0), + (83, 0), + (190, 1), + (186, 1), + (189, 1), + (193, 1) +] expected_grid_mappings = [ - (89, 1), - (146, 29), - (66, 34), - (112, 45) + (89, 0), + (90, 1), + (148, 16), + (175, 35), + (60, 47), + (44, 47), + (80, 60) ] + if __name__ == "__main__": import matplotlib.pyplot as plt @@ -62,14 +74,14 @@ combined_mesh.show(actually_show=False, show_labels=True, color='k') grid_mesh.show(actually_show=False, show_labels=True, color='r') - plt.xlim([-4, 4]) - plt.ylim([-4, 4]) + plt.xlim([-5, 5]) + plt.ylim([-5, 5]) plt.figure() combined_mesh.show(actually_show=False, show_labels=True, color='k') shape_mesh.show(actually_show=False, show_labels=True, color='r') - plt.xlim([-4, 4]) - plt.ylim([-4, 4]) + plt.xlim([-5, 5]) + plt.ylim([-5, 5]) plt.show() From b2bfeda91652542d246b360805a0a296d1980a6c Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 28 Sep 2023 13:54:43 +0100 Subject: [PATCH 140/675] It works, needs benchmarking --- sasdata/data_util/slicing/meshes/mesh.py | 4 +- sasdata/data_util/slicing/rebinning.py | 28 ++++++++----- sasdata/data_util/slicing/slicer_demo.py | 42 +++++++++++++++++-- .../data_util/slicing/slicers/AnularSector.py | 14 +++++-- 4 files changed, 69 insertions(+), 19 deletions(-) diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py index 05f4d33bc..ba31c51ee 100644 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -130,7 +130,7 @@ def show_data(self, data: np.ndarray, cmap='winter', mesh_color='white', - show_mesh=True, + show_mesh=False, actually_show=True, density=False): @@ -150,7 +150,7 @@ def show_data(self, color = colormap(color_index) - plt.fill(self.points[cell, 0], self.points[cell, 1], color=color) + plt.fill(self.points[cell, 0], self.points[cell, 1], color=color, edgecolor=None) if show_mesh: self.show(actually_show=False, color=mesh_color) diff --git a/sasdata/data_util/slicing/rebinning.py b/sasdata/data_util/slicing/rebinning.py index 06ace87a9..86818f74f 100644 --- a/sasdata/data_util/slicing/rebinning.py +++ b/sasdata/data_util/slicing/rebinning.py @@ -46,12 +46,13 @@ def allowable_orders(self) -> list[int]: return [-1, 0, 1] @property - def bin_mesh(self): + def bin_mesh(self) -> Mesh: + if self._bin_mesh_cache is None: bin_mesh = self._bin_mesh() - self._data_mesh_cache = bin_mesh - else: - return self._bin_mesh_cache + self._bin_mesh_cache = bin_mesh + + return self._bin_mesh_cache def _post_processing(self, coordinates, values) -> tuple[np.ndarray, np.ndarray]: """ Perform post-processing on the mesh binned values """ @@ -95,7 +96,7 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n input_coordinates_mesh=input_coordinate_mesh, merged_mesh_data=merge_data) - merged_mesh, merged_to_input, merged_to_output = merge_data + merged_mesh, merged_to_output, merged_to_input = merge_data # Calculate values according to the order parameter @@ -105,8 +106,15 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n input_areas = input_coordinate_mesh.areas output = np.zeros(self.bin_mesh.n_cells, dtype=float) + print(np.max(merged_to_input)) + print(np.max(merged_to_output)) + for input_index, output_index, area in zip(merged_to_input, merged_to_output, merged_mesh.areas): - output[output_index] += input_data[input_index] * area / input_areas[input_data] + if input_index == -1 or output_index == -1: + # merged region does not correspond to anything of interest + continue + + output[output_index] += input_data[input_index] * area / input_areas[input_index] return output @@ -125,9 +133,9 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n else: raise ValueError(f"Expected order to be in {self.allowable_orders}, got {self._order}") - def sum(self, input_coordinates: np.ndarray, data: np.ndarray) -> np.ndarray: + def sum(self, x: np.ndarray, y: np.ndarray, data: np.ndarray) -> np.ndarray: """ Return the summed data in the output bins """ - return self._calculate(input_coordinates, data) + return self._calculate(np.array((x.reshape(-1), y.reshape(-1))).T, data) def error_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: raise NotImplementedError("Error propagation not implemented yet") @@ -135,7 +143,7 @@ def error_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, error def resolution_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: raise NotImplementedError("Resolution propagation not implemented yet") - def average(self, input_coordinates: np.ndarray, data: np.ndarray) -> np.ndarray: + def average(self, x: np.ndarray, y: np.ndarray, data: np.ndarray) -> np.ndarray: """ Return the averaged data in the output bins """ - return self._calculate(input_coordinates, data) / self.bin_mesh.areas + return self._calculate(np.array((x, y)).T, data) / self.bin_mesh.areas diff --git a/sasdata/data_util/slicing/slicer_demo.py b/sasdata/data_util/slicing/slicer_demo.py index 775c1d9b7..e76e1c4f4 100644 --- a/sasdata/data_util/slicing/slicer_demo.py +++ b/sasdata/data_util/slicing/slicer_demo.py @@ -1,19 +1,53 @@ -""" Dev docs: """ +""" Dev docs: Demo to show the behaviour of the re-binning methods """ import numpy as np -from sasdata.data_util.slicing.slicers import AnularSector +import matplotlib.pyplot as plt + +from sasdata.data_util.slicing.slicers.AnularSector import AnularSector from sasdata.data_util.slicing.meshes.mesh import Mesh from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh if __name__ == "__main__": + q_range = 1.5 + + + x = (2*q_range)*(np.random.random(400)-0.5) + y = (2*q_range)*(np.random.random(400)-0.5) + + display_mesh = voronoi_mesh(x, y) # Demo of sums, annular sector over some not very circular data - q_range = 1.5 - test_coordinates = (2*q_range)*(np.random.random((100, 2))-0.5) + def lobe_test_function(x, y): + return 1 + np.sin(x*np.pi/q_range)*np.sin(y*np.pi/q_range) + + + random_lobe_data = lobe_test_function(x, y) + + plt.figure("Input Dataset 1") + display_mesh.show_data(random_lobe_data, actually_show=False) + + data_order_0 = [] + + for index, size in enumerate(np.linspace(0.1, 1, 100)): + q0 = 0.75 - 0.6*size + q1 = 0.75 + 0.6*size + phi0 = np.pi/2 - size + phi1 = np.pi/2 + size + + rebinner = AnularSector(q0, q1, phi0, phi1, order=0) + + data_order_0.append(rebinner.sum(x, y, random_lobe_data)) + + if index % 10 == 0: + plt.figure("Regions") + rebinner.bin_mesh.show(actually_show=False) + + plt.show() + # Demo of averaging, annular sector over ring shaped data \ No newline at end of file diff --git a/sasdata/data_util/slicing/slicers/AnularSector.py b/sasdata/data_util/slicing/slicers/AnularSector.py index bf3021d02..e9f13774b 100644 --- a/sasdata/data_util/slicing/slicers/AnularSector.py +++ b/sasdata/data_util/slicing/slicers/AnularSector.py @@ -17,19 +17,27 @@ def __init__(self, q0: float, q1: float, phi0: float, phi1: float, order: int=1, def _bin_mesh(self) -> Mesh: - n_points = 1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi + n_points = int(1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi) angles = np.linspace(self.phi0, self.phi1, n_points) row1 = self.q0 * np.array([np.cos(angles), np.sin(angles)]) row2 = self.q1 * np.array([np.cos(angles), np.sin(angles)])[:, ::-1] - points = np.concatenate((row1, row2), axis=1) + points = np.concatenate((row1, row2), axis=1).T - cells = [i for i in range(2*n_points)] + cells = [[i for i in range(2*n_points)]] return Mesh(points=points, cells=cells) def _bin_coordinates(self) -> np.ndarray: return np.array([], dtype=float) + +def main(): + """ Just show a random example""" + AnularSector(1, 2, 1, 2).bin_mesh.show() + + +if __name__ == "__main__": + main() \ No newline at end of file From 96a7fdc98fa97caa727eb5bd2f44bd5686ae8c70 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 29 Sep 2023 14:47:01 +0100 Subject: [PATCH 141/675] Much faster assignment/merge method --- sasdata/data_util/slicing/meshes/mesh.py | 93 ++++++++++++- sasdata/data_util/slicing/meshes/meshmerge.py | 124 +++++++++++------- sasdata/data_util/slicing/rebinning.py | 7 +- sasdata/data_util/slicing/slicer_demo.py | 8 +- test/slicers/meshes_for_testing.py | 30 ++++- test/slicers/utest_point_assignment.py | 3 + 6 files changed, 204 insertions(+), 61 deletions(-) diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py index ba31c51ee..6b4df930c 100644 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -51,19 +51,24 @@ def __init__(self, edge_lookup = {edge: i for i, edge in enumerate(self.edges)} self.cells_to_edges = [] + self.cells_to_edges_signs = [] for cell in cells: this_cell_data = [] + this_sign_data = [] for a, b in closed_loop_edges(cell): # make sure the representation is unique if a > b: this_cell_data.append(edge_lookup[(a, b)]) + this_sign_data.append(1) else: this_cell_data.append(edge_lookup[(b, a)]) + this_sign_data.append(-1) self.cells_to_edges.append(this_cell_data) + self.cells_to_edges_signs.append(this_sign_data) # Counts for elements self.n_points = self.points.shape[0] @@ -73,11 +78,6 @@ def __init__(self, # Areas self._areas = None - def find_locations(self, points): - """ Find indices of cells containing the input points """ - - - @property def areas(self): @@ -126,6 +126,71 @@ def show(self, actually_show=True, show_labels=False, **kwargs): if actually_show: plt.show() + def locate_points(self, x: np.ndarray, y: np.ndarray): + """ Find the cells that contain the specified points""" + + x = x.reshape(-1) + y = y.reshape(-1) + + xy = np.concatenate(([x], [y]), axis=1) + + # The most simple implementation is not particularly fast, especially in python + # + # Less obvious, but hopefully faster strategy + # + # Ultimately, checking the inclusion of a point within a polygon + # requires checking the crossings of a half line with the polygon's + # edges. + # + # A fairly efficient thing to do is to check every edge for crossing + # the axis parallel lines x=point_x. + # Then these edges that cross can map back to the polygons they're in + # and a final check for inclusion can be done with the edge sign property + # and some explicit checking of the + # + # Basic idea is: + # 1) build a matrix for each point-edge pair + # True if the edge crosses the half-line above a point + # 2) for each cell get the winding number by evaluating the + # sum of the component edges, weighted 1/-1 according to direction + + + edges = np.array(self.edges) + + edge_xy_1 = self.points[edges[:, 0], :] + edge_xy_2 = self.points[edges[:, 1], :] + + edge_x_1 = edge_xy_1[:, 0] + edge_x_2 = edge_xy_2[:, 0] + + + + # Make an n_edges-by-n_inputs boolean matrix that indicates which of the + # edges cross x=points_x line + crossers = np.logical_xor( + edge_x_1.reshape(-1, 1) < x.reshape(1, -1), + edge_x_2.reshape(-1, 1) < x.reshape(1, -1)) + + # Calculate the gradients, some might be infs, but none that matter will be + # TODO: Disable warnings + gradients = (edge_xy_2[:, 1] - edge_xy_1[:, 1]) / (edge_xy_2[:, 0] - edge_xy_1[:, 0]) + + # Distance to crossing points edge 0 + delta_x = x.reshape(1, -1) - edge_x_1.reshape(-1, 1) + + # Signed distance from point to y (doesn't really matter which sign) + delta_y = gradients.reshape(-1, 1) * delta_x + edge_xy_1[:, 1:] - y.reshape(1, -1) + + score_matrix = np.logical_and(delta_y > 0, crossers) + + output = -np.ones(len(x), dtype=int) + for cell_index, (cell_edges, sign) in enumerate(zip(self.cells_to_edges, self.cells_to_edges_signs)): + cell_score = np.sum(score_matrix[cell_edges, :] * np.array(sign).reshape(-1, 1), axis=0) + points_in_cell = np.abs(cell_score) == 1 + output[points_in_cell] = cell_index + + return output + def show_data(self, data: np.ndarray, cmap='winter', @@ -156,4 +221,20 @@ def show_data(self, self.show(actually_show=False, color=mesh_color) if actually_show: - self.show() \ No newline at end of file + self.show() + + +if __name__ == "__main__": + from test.slicers.meshes_for_testing import location_test_mesh, location_test_points_x, location_test_points_y + + cell_indices = location_test_mesh.locate_points(location_test_points_x, location_test_points_y) + + print(cell_indices) + + for i in range(location_test_mesh.n_cells): + inds = cell_indices == i + plt.scatter( + location_test_points_x.reshape(-1)[inds], + location_test_points_y.reshape(-1)[inds]) + + location_test_mesh.show() \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/meshmerge.py b/sasdata/data_util/slicing/meshes/meshmerge.py index 3ce52ba56..2524c5169 100644 --- a/sasdata/data_util/slicing/meshes/meshmerge.py +++ b/sasdata/data_util/slicing/meshes/meshmerge.py @@ -5,6 +5,8 @@ from sasdata.data_util.slicing.meshes.util import closed_loop_edges +import time + def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: """ Take two lists of polygons and find their intersections @@ -21,6 +23,8 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] """ + t0 = time.time() + # Find intersections of all edges in mesh one with edges in mesh two new_x = [] @@ -89,6 +93,8 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] new_y.append(y) + t1 = time.time() + print("Edge intersections:", t1 - t0) # Build list of all input points, in a way that we can check for coincident points @@ -108,6 +114,11 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] output_mesh = delaunay_mesh(points[:, 0], points[:, 1]) + + t2 = time.time() + print("Delaunay:", t2 - t1) + + # Find centroids of all output triangles, and find which source cells they belong to ## step 1) Assign -1 to all cells of original meshes @@ -120,57 +131,72 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] centroid = np.sum(output_mesh.points[cell, :]/3, axis=0) centroids.append(centroid) - ## step 3) Perform checks based on winding number method (see wikipedia Point in Polygon). - for mesh, assignments in [ - (mesh_a, assignments_a), - (mesh_b, assignments_b)]: - - for centroid_index, centroid in enumerate(centroids): - for cell_index, cell in enumerate(mesh.cells): - - # Bounding box check - points = mesh.points[cell, :] - if np.any(centroid < np.min(points, axis=0)): # x or y less than any in polygon - continue + centroids = np.array(centroids) - if np.any(centroid > np.max(points, axis=0)): # x or y greater than any in polygon - continue + t3 = time.time() + print("Centroids:", t3 - t2) - # Winding number check - count directional crossings of vertical half line from centroid - winding_number = 0 - for i1, i2 in closed_loop_edges(cell): - p1 = mesh.points[i1, :] - p2 = mesh.points[i2, :] - # if the section xs do not straddle the x=centroid_x coordinate, then the - # edge cannot cross the half line. - # If it does, then remember which way it was - # * Careful about ends - # * Also, note that the p1[0] == p2[0] -> (no contribution) case is covered by the strict inequality - if p1[0] > centroid[0] >= p2[0]: - left_right = -1 - elif p2[0] > centroid[0] >= p1[0]: - left_right = 1 - else: - continue - - # Find the y point that it crosses x=centroid at - # note: denominator cannot be zero because of strict inequality above - gradient = (p2[1] - p1[1]) / (p2[0] - p1[0]) - x_delta = centroid[0] - p1[0] - y = p1[1] + x_delta * gradient - - if y > centroid[1]: - winding_number += left_right - - - if abs(winding_number) > 0: - # Do assignment of input cell to output triangle index - assignments[centroid_index] = cell_index - - # end cell loop - - # end centroid loop + ## step 3) Perform checks based on winding number method (see wikipedia Point in Polygon). + # + # # TODO: Brute force search is sllllloooooooowwwwww - keeping track of which points are where would be better + # for mesh, assignments in [ + # (mesh_a, assignments_a), + # (mesh_b, assignments_b)]: + # + # for centroid_index, centroid in enumerate(centroids): + # for cell_index, cell in enumerate(mesh.cells): + # + # # Bounding box check + # points = mesh.points[cell, :] + # if np.any(centroid < np.min(points, axis=0)): # x or y less than any in polygon + # continue + # + # if np.any(centroid > np.max(points, axis=0)): # x or y greater than any in polygon + # continue + # + # # Winding number check - count directional crossings of vertical half line from centroid + # winding_number = 0 + # for i1, i2 in closed_loop_edges(cell): + # p1 = mesh.points[i1, :] + # p2 = mesh.points[i2, :] + # + # # if the section xs do not straddle the x=centroid_x coordinate, then the + # # edge cannot cross the half line. + # # If it does, then remember which way it was + # # * Careful about ends + # # * Also, note that the p1[0] == p2[0] -> (no contribution) case is covered by the strict inequality + # if p1[0] > centroid[0] >= p2[0]: + # left_right = -1 + # elif p2[0] > centroid[0] >= p1[0]: + # left_right = 1 + # else: + # continue + # + # # Find the y point that it crosses x=centroid at + # # note: denominator cannot be zero because of strict inequality above + # gradient = (p2[1] - p1[1]) / (p2[0] - p1[0]) + # x_delta = centroid[0] - p1[0] + # y = p1[1] + x_delta * gradient + # + # if y > centroid[1]: + # winding_number += left_right + # + # + # if abs(winding_number) > 0: + # # Do assignment of input cell to output triangle index + # assignments[centroid_index] = cell_index + # break # point is assigned + # + # # end cell loop + # + # # end centroid loop + + assignments_a = mesh_a.locate_points(centroids[:, 0], centroids[:, 1]) + assignments_b = mesh_b.locate_points(centroids[:, 0], centroids[:, 1]) + + t4 = time.time() + print("Assignments:", t4 - t3) return output_mesh, assignments_a, assignments_b @@ -185,7 +211,7 @@ def main(): m2 = voronoi_mesh(np.random.random(n2), np.random.random(n2)) - mesh, _, _ = meshmerge(m1, m2) + mesh, assignement1, assignement2 = meshmerge(m1, m2) mesh.show() diff --git a/sasdata/data_util/slicing/rebinning.py b/sasdata/data_util/slicing/rebinning.py index 86818f74f..7b6eea938 100644 --- a/sasdata/data_util/slicing/rebinning.py +++ b/sasdata/data_util/slicing/rebinning.py @@ -8,6 +8,7 @@ from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh from sasdata.data_util.slicing.meshes.meshmerge import meshmerge +import time @dataclass class CacheData: @@ -99,16 +100,13 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n merged_mesh, merged_to_output, merged_to_input = merge_data # Calculate values according to the order parameter - + t0 = time.time() if self._order == 0: # Based on the overlap of cells only input_areas = input_coordinate_mesh.areas output = np.zeros(self.bin_mesh.n_cells, dtype=float) - print(np.max(merged_to_input)) - print(np.max(merged_to_output)) - for input_index, output_index, area in zip(merged_to_input, merged_to_output, merged_mesh.areas): if input_index == -1 or output_index == -1: # merged region does not correspond to anything of interest @@ -116,6 +114,7 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n output[output_index] += input_data[input_index] * area / input_areas[input_index] + print("Main calc:", time.time() - t0) return output diff --git a/sasdata/data_util/slicing/slicer_demo.py b/sasdata/data_util/slicing/slicer_demo.py index e76e1c4f4..d60c0acd2 100644 --- a/sasdata/data_util/slicing/slicer_demo.py +++ b/sasdata/data_util/slicing/slicer_demo.py @@ -33,7 +33,9 @@ def lobe_test_function(x, y): data_order_0 = [] - for index, size in enumerate(np.linspace(0.1, 1, 100)): + sizes = np.linspace(0.1, 1, 100) + + for index, size in enumerate(sizes): q0 = 0.75 - 0.6*size q1 = 0.75 + 0.6*size phi0 = np.pi/2 - size @@ -47,6 +49,10 @@ def lobe_test_function(x, y): plt.figure("Regions") rebinner.bin_mesh.show(actually_show=False) + plt.figure("Data") + + plt.plot(sizes, data_order_0) + plt.show() diff --git a/test/slicers/meshes_for_testing.py b/test/slicers/meshes_for_testing.py index c7426245b..7cb17b484 100644 --- a/test/slicers/meshes_for_testing.py +++ b/test/slicers/meshes_for_testing.py @@ -62,7 +62,31 @@ (80, 60) ] - +# +# Mesh location tests +# + +location_test_mesh_points = np.array([ + [0, 0], # 0 + [0, 1], # 1 + [0, 2], # 2 + [1, 0], # 3 + [1, 1], # 4 + [1, 2], # 5 + [2, 0], # 6 + [2, 1], # 7 + [2, 2]], dtype=float) + +location_test_mesh_cells = [ + [0, 1, 4, 3], + [1, 2, 5, 4], + [3, 4, 7, 6], + [4, 5, 8, 7]] + +location_test_mesh = Mesh(location_test_mesh_points, location_test_mesh_cells) + +test_coords = 0.25 + 0.5*np.arange(4) +location_test_points_x, location_test_points_y = np.meshgrid(test_coords, test_coords) if __name__ == "__main__": @@ -84,4 +108,8 @@ plt.xlim([-5, 5]) plt.ylim([-5, 5]) + plt.figure() + location_test_mesh.show(actually_show=False, show_labels=True) + plt.scatter(location_test_points_x, location_test_points_y) + plt.show() diff --git a/test/slicers/utest_point_assignment.py b/test/slicers/utest_point_assignment.py index c648edde3..64ed24e42 100644 --- a/test/slicers/utest_point_assignment.py +++ b/test/slicers/utest_point_assignment.py @@ -1,4 +1,7 @@ + +from test.slicers.meshes_for_testing import location_test_mesh, location_test_points_x, location_test_points_y + def test_location_assignment(): pass From 87dc552e6d155896e475ebc74a049dccc1127146 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 5 Oct 2023 12:49:13 +0100 Subject: [PATCH 142/675] Significantly faster edge crossing algorithm --- sasdata/data_util/slicing/meshes/meshmerge.py | 90 ++++++++----------- 1 file changed, 37 insertions(+), 53 deletions(-) diff --git a/sasdata/data_util/slicing/meshes/meshmerge.py b/sasdata/data_util/slicing/meshes/meshmerge.py index 2524c5169..c0235dcfe 100644 --- a/sasdata/data_util/slicing/meshes/meshmerge.py +++ b/sasdata/data_util/slicing/meshes/meshmerge.py @@ -26,72 +26,56 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] t0 = time.time() # Find intersections of all edges in mesh one with edges in mesh two + # TODO: Speed this up - new_x = [] - new_y = [] - for edge_a in mesh_a.edges: - for edge_b in mesh_b.edges: + # Fastest way might just be to calculate the intersections of all lines on edges, + # see whether we need filtering afterwards - p1 = mesh_a.points[edge_a[0]] - p2 = mesh_a.points[edge_a[1]] - p3 = mesh_b.points[edge_b[0]] - p4 = mesh_b.points[edge_b[1]] + edges_a = np.array(mesh_a.edges, dtype=int) + edges_b = np.array(mesh_b.edges, dtype=int) - # Bounding box check + edge_a_1 = mesh_a.points[edges_a[:, 0], :] + edge_a_2 = mesh_a.points[edges_a[:, 1], :] + edge_b_1 = mesh_b.points[edges_b[:, 0], :] + edge_b_2 = mesh_b.points[edges_b[:, 1], :] - # First edge entirely to left of other - if max((p1[0], p2[0])) < min((p3[0], p4[0])): - continue + a_grid, b_grid = np.mgrid[0:mesh_a.n_edges, 0:mesh_b.n_edges] + a_grid = a_grid.reshape(-1) + b_grid = b_grid.reshape(-1) - # First edge entirely below other - if max((p1[1], p2[1])) < min((p3[1], p4[1])): - continue - - # First edge entirely to right of other - if min((p1[0], p2[0])) > max((p3[0], p4[0])): - continue - - # First edge entirely above other - if min((p1[1], p2[1])) > max((p3[1], p4[1])): - continue - - # - # Parametric description of intersection in terms of position along lines - # - # Simultaneous eqns (to reflect current wiki notation) - # s(x2 - x1) - t(x4 - x3) = x3 - x1 - # s(y2 - y1) - t(y4 - y3) = y3 - y1 - # - # in matrix form: - # m.(s,t) = v - # + p1 = edge_a_1[a_grid, :] + p2 = edge_a_2[a_grid, :] + p3 = edge_b_1[b_grid, :] + p4 = edge_b_2[b_grid, :] + # + # Solve the equations + # + # z_a1 + s delta_z_a = z_b1 + t delta_z_b + # + # for z = (x, y) + # - m = np.array([ - [p2[0] - p1[0], p3[0] - p4[0]], - [p2[1] - p1[1], p3[1] - p4[1]]]) + start_point_diff = p1 - p3 - v = np.array([p3[0] - p1[0], p3[1] - p1[1]]) + delta1 = p2 - p1 + delta3 = p4 - p3 - if np.linalg.det(m) == 0: - # Lines don't intersect, or are colinear in a way that doesn't matter - continue + deltas = np.concatenate(([-delta1], [delta3]), axis=0) + deltas = np.moveaxis(deltas, 0, 2) - st = np.linalg.solve(m, v) + st = np.linalg.solve(deltas, start_point_diff) - # As the purpose of this is finding new points for the merged mesh, we don't - # want new points if they are right at the end of the lines, hence non-strict - # inequalities here - if np.any(st <= 0) or np.any(st >= 1): - # Exclude intection points, that are not on the *segments* - continue + # Find the points where s and t are in (0, 1) - x = p1[0] + (p2[0] - p1[0])*st[0] - y = p1[1] + (p2[1] - p1[1])*st[0] + intersection_inds = np.logical_and( + np.logical_and(0 < st[:, 0], st[:, 0] < 1), + np.logical_and(0 < st[:, 1], st[:, 1] < 1)) - new_x.append(x) - new_y.append(y) + start_points_for_intersections = p1[intersection_inds, :] + deltas_for_intersections = delta1[intersection_inds, :] + points_to_add = start_points_for_intersections + st[intersection_inds, 0].reshape(-1,1) * deltas_for_intersections t1 = time.time() print("Edge intersections:", t1 - t0) @@ -102,7 +86,7 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] points = np.concatenate(( mesh_a.points, mesh_b.points, - np.array((new_x, new_y)).T + points_to_add )) From 00a56c9609fc2a5111c00628eb32a8955c69dbcf Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 5 Oct 2023 13:57:08 +0100 Subject: [PATCH 143/675] Demo --- sasdata/data_util/slicing/meshes/mesh.py | 2 + sasdata/data_util/slicing/meshes/meshmerge.py | 68 ++--------- .../data_util/slicing/meshes/voronoi_mesh.py | 5 +- sasdata/data_util/slicing/rebinning.py | 41 +++---- sasdata/data_util/slicing/slicer_demo.py | 112 ++++++++++++++---- .../data_util/slicing/slicers/AnularSector.py | 6 +- 6 files changed, 126 insertions(+), 108 deletions(-) diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py index 6b4df930c..3ac23dafe 100644 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -203,6 +203,8 @@ def show_data(self, colormap = cm.get_cmap(cmap, 256) + data = data.reshape(-1) + if density: data = data / self.areas diff --git a/sasdata/data_util/slicing/meshes/meshmerge.py b/sasdata/data_util/slicing/meshes/meshmerge.py index c0235dcfe..161c1e5da 100644 --- a/sasdata/data_util/slicing/meshes/meshmerge.py +++ b/sasdata/data_util/slicing/meshes/meshmerge.py @@ -26,7 +26,6 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] t0 = time.time() # Find intersections of all edges in mesh one with edges in mesh two - # TODO: Speed this up # Fastest way might just be to calculate the intersections of all lines on edges, # see whether we need filtering afterwards @@ -48,6 +47,10 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] p3 = edge_b_1[b_grid, :] p4 = edge_b_2[b_grid, :] + # + # TODO: Investigate whether adding a bounding box check will help with speed, seems likely as most edges wont cross + # + # # Solve the equations # @@ -64,7 +67,9 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] deltas = np.concatenate(([-delta1], [delta3]), axis=0) deltas = np.moveaxis(deltas, 0, 2) - st = np.linalg.solve(deltas, start_point_diff) + non_singular = np.linalg.det(deltas) != 0 + + st = np.linalg.solve(deltas[non_singular], start_point_diff[non_singular]) # Find the points where s and t are in (0, 1) @@ -72,8 +77,8 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] np.logical_and(0 < st[:, 0], st[:, 0] < 1), np.logical_and(0 < st[:, 1], st[:, 1] < 1)) - start_points_for_intersections = p1[intersection_inds, :] - deltas_for_intersections = delta1[intersection_inds, :] + start_points_for_intersections = p1[non_singular][intersection_inds, :] + deltas_for_intersections = delta1[non_singular][intersection_inds, :] points_to_add = start_points_for_intersections + st[intersection_inds, 0].reshape(-1,1) * deltas_for_intersections @@ -121,60 +126,7 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] print("Centroids:", t3 - t2) - ## step 3) Perform checks based on winding number method (see wikipedia Point in Polygon). - # - # # TODO: Brute force search is sllllloooooooowwwwww - keeping track of which points are where would be better - # for mesh, assignments in [ - # (mesh_a, assignments_a), - # (mesh_b, assignments_b)]: - # - # for centroid_index, centroid in enumerate(centroids): - # for cell_index, cell in enumerate(mesh.cells): - # - # # Bounding box check - # points = mesh.points[cell, :] - # if np.any(centroid < np.min(points, axis=0)): # x or y less than any in polygon - # continue - # - # if np.any(centroid > np.max(points, axis=0)): # x or y greater than any in polygon - # continue - # - # # Winding number check - count directional crossings of vertical half line from centroid - # winding_number = 0 - # for i1, i2 in closed_loop_edges(cell): - # p1 = mesh.points[i1, :] - # p2 = mesh.points[i2, :] - # - # # if the section xs do not straddle the x=centroid_x coordinate, then the - # # edge cannot cross the half line. - # # If it does, then remember which way it was - # # * Careful about ends - # # * Also, note that the p1[0] == p2[0] -> (no contribution) case is covered by the strict inequality - # if p1[0] > centroid[0] >= p2[0]: - # left_right = -1 - # elif p2[0] > centroid[0] >= p1[0]: - # left_right = 1 - # else: - # continue - # - # # Find the y point that it crosses x=centroid at - # # note: denominator cannot be zero because of strict inequality above - # gradient = (p2[1] - p1[1]) / (p2[0] - p1[0]) - # x_delta = centroid[0] - p1[0] - # y = p1[1] + x_delta * gradient - # - # if y > centroid[1]: - # winding_number += left_right - # - # - # if abs(winding_number) > 0: - # # Do assignment of input cell to output triangle index - # assignments[centroid_index] = cell_index - # break # point is assigned - # - # # end cell loop - # - # # end centroid loop + ## step 3) Find where points belong based on Mesh classes point location algorithm assignments_a = mesh_a.locate_points(centroids[:, 0], centroids[:, 1]) assignments_b = mesh_b.locate_points(centroids[:, 0], centroids[:, 1]) diff --git a/sasdata/data_util/slicing/meshes/voronoi_mesh.py b/sasdata/data_util/slicing/meshes/voronoi_mesh.py index 3497fbba7..d3eb81d29 100644 --- a/sasdata/data_util/slicing/meshes/voronoi_mesh.py +++ b/sasdata/data_util/slicing/meshes/voronoi_mesh.py @@ -24,6 +24,7 @@ def voronoi_mesh(x, y, debug_plot=False) -> Mesh: # 2) the bounding box of the grid # + # Use the median area of finite voronoi cells as an estimate voronoi = Voronoi(input_data) finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] @@ -37,8 +38,8 @@ def voronoi_mesh(x, y, debug_plot=False) -> Mesh: x_max, y_max = np.max(input_data, axis=0) # Create a border - n_x = np.round((x_max - x_min)/gap).astype(int) - n_y = np.round((y_max - y_min)/gap).astype(int) + n_x = int(np.round((x_max - x_min)/gap)) + n_y = int(np.round((y_max - y_min)/gap)) top_bottom_xs = np.linspace(x_min - gap, x_max + gap, n_x + 3) left_right_ys = np.linspace(y_min, y_max, n_y + 1) diff --git a/sasdata/data_util/slicing/rebinning.py b/sasdata/data_util/slicing/rebinning.py index 7b6eea938..510535a9c 100644 --- a/sasdata/data_util/slicing/rebinning.py +++ b/sasdata/data_util/slicing/rebinning.py @@ -18,21 +18,17 @@ class CacheData: merged_mesh_data: tuple[Mesh, np.ndarray, np.ndarray] # mesh information about the merging -class Rebinner(): +class Rebinner(ABC): - def __init__(self, order): + def __init__(self): """ Base class for rebinning methods""" - self._order = order self._bin_mesh_cache: Optional[Mesh] = None # cached version of the output bin mesh # Output dependent caching self._input_cache: Optional[CacheData] = None - if order not in self.allowable_orders: - raise ValueError(f"Expected order to be in {self.allowable_orders}, got {order}") - @abstractmethod def _bin_coordinates(self) -> np.ndarray: @@ -60,17 +56,22 @@ def _post_processing(self, coordinates, values) -> tuple[np.ndarray, np.ndarray] # Default is to do nothing, override if needed return coordinates, values - def _do_binning(self, data): - """ Main binning algorithm """ - - def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> np.ndarray: + def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray, order: int) -> np.ndarray: """ Main calculation """ - if self._order == -1: + if order == -1: # Construct the input output mapping just based on input points being the output cells, # Equivalent to the original binning method - pass + mesh = self.bin_mesh + bin_identities = mesh.locate_points(input_coordinates[:,0], input_coordinates[:, 1]) + output_data = np.zeros(mesh.n_cells, dtype=float) + + for index, bin in enumerate(bin_identities): + if bin >= 0: + output_data[bin] += input_data[index] + + return output_data else: # Use a mapping based on meshes @@ -87,7 +88,7 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n else: # Calculate mesh data input_coordinate_mesh = voronoi_mesh(input_coordinates[:,0], input_coordinates[:, 1]) - self._data_mesh_cahce = input_coordinate_mesh + self._data_mesh_cache = input_coordinate_mesh merge_data = meshmerge(self.bin_mesh, input_coordinate_mesh) @@ -101,7 +102,7 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n # Calculate values according to the order parameter t0 = time.time() - if self._order == 0: + if order == 0: # Based on the overlap of cells only input_areas = input_coordinate_mesh.areas @@ -118,7 +119,7 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n return output - elif self._order == 1: + elif order == 1: # Linear interpolation requires the following relationship with the data, # as the input data is the total over the whole input cell, the linear # interpolation requires continuity at the vertices, and a constraint on the @@ -130,11 +131,11 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n raise NotImplementedError("1st order (linear) interpolation currently not implemented") else: - raise ValueError(f"Expected order to be in {self.allowable_orders}, got {self._order}") + raise ValueError(f"Expected order to be in {self.allowable_orders}, got {order}") - def sum(self, x: np.ndarray, y: np.ndarray, data: np.ndarray) -> np.ndarray: + def sum(self, x: np.ndarray, y: np.ndarray, data: np.ndarray, order: int = 0) -> np.ndarray: """ Return the summed data in the output bins """ - return self._calculate(np.array((x.reshape(-1), y.reshape(-1))).T, data) + return self._calculate(np.array((x.reshape(-1), y.reshape(-1))).T, data.reshape(-1), order) def error_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: raise NotImplementedError("Error propagation not implemented yet") @@ -142,7 +143,7 @@ def error_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, error def resolution_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: raise NotImplementedError("Resolution propagation not implemented yet") - def average(self, x: np.ndarray, y: np.ndarray, data: np.ndarray) -> np.ndarray: + def average(self, x: np.ndarray, y: np.ndarray, data: np.ndarray, order: int = 0) -> np.ndarray: """ Return the averaged data in the output bins """ - return self._calculate(np.array((x, y)).T, data) / self.bin_mesh.areas + return self._calculate(np.array((x, y)).T, data.reshape(-1), order) / self.bin_mesh.areas diff --git a/sasdata/data_util/slicing/slicer_demo.py b/sasdata/data_util/slicing/slicer_demo.py index d60c0acd2..6096ca905 100644 --- a/sasdata/data_util/slicing/slicer_demo.py +++ b/sasdata/data_util/slicing/slicer_demo.py @@ -12,48 +12,110 @@ if __name__ == "__main__": q_range = 1.5 + demo1 = True + demo2 = True + # Demo of sums, annular sector over some not very circular data - x = (2*q_range)*(np.random.random(400)-0.5) - y = (2*q_range)*(np.random.random(400)-0.5) + if demo1: - display_mesh = voronoi_mesh(x, y) + x = (2 * q_range) * (np.random.random(400) - 0.5) + y = (2 * q_range) * (np.random.random(400) - 0.5) - # Demo of sums, annular sector over some not very circular data + display_mesh = voronoi_mesh(x, y) - def lobe_test_function(x, y): - return 1 + np.sin(x*np.pi/q_range)*np.sin(y*np.pi/q_range) + def lobe_test_function(x, y): + return 1 + np.sin(x*np.pi/q_range)*np.sin(y*np.pi/q_range) - random_lobe_data = lobe_test_function(x, y) + random_lobe_data = lobe_test_function(x, y) - plt.figure("Input Dataset 1") - display_mesh.show_data(random_lobe_data, actually_show=False) + plt.figure("Input Dataset 1") + display_mesh.show_data(random_lobe_data, actually_show=False) - data_order_0 = [] + data_order_0 = [] + data_order_neg1 = [] - sizes = np.linspace(0.1, 1, 100) + sizes = np.linspace(0.1, 1, 100) - for index, size in enumerate(sizes): - q0 = 0.75 - 0.6*size - q1 = 0.75 + 0.6*size - phi0 = np.pi/2 - size - phi1 = np.pi/2 + size + for index, size in enumerate(sizes): + q0 = 0.75 - 0.6*size + q1 = 0.75 + 0.6*size + phi0 = np.pi/2 - size + phi1 = np.pi/2 + size - rebinner = AnularSector(q0, q1, phi0, phi1, order=0) + rebinner = AnularSector(q0, q1, phi0, phi1) - data_order_0.append(rebinner.sum(x, y, random_lobe_data)) + data_order_neg1.append(rebinner.sum(x, y, random_lobe_data, order=-1)) + data_order_0.append(rebinner.sum(x, y, random_lobe_data, order=0)) - if index % 10 == 0: - plt.figure("Regions") - rebinner.bin_mesh.show(actually_show=False) + if index % 10 == 0: + plt.figure("Regions 1") + rebinner.bin_mesh.show(actually_show=False) - plt.figure("Data") + plt.title("Regions") - plt.plot(sizes, data_order_0) + plt.figure("Sum of region, dataset 1") - plt.show() + plt.plot(sizes, data_order_neg1) + plt.plot(sizes, data_order_0) + + plt.legend(["Order -1", "Order 0"]) + plt.title("Sum over region") + + + # Demo of averaging, annular sector over ring shaped data + + if demo2: + + x, y = np.meshgrid(np.linspace(-q_range, q_range, 41), np.linspace(-q_range, q_range, 41)) + x = x.reshape(-1) + y = y.reshape(-1) + + display_mesh = voronoi_mesh(x, y) + + + def ring_test_function(x, y): + r = np.sqrt(x**2 + y**2) + return np.log(np.sinc(r*1.5)**2) + + + grid_ring_data = ring_test_function(x, y) + plt.figure("Input Dataset 2") + display_mesh.show_data(grid_ring_data, actually_show=False) + + data_order_0 = [] + data_order_neg1 = [] + + sizes = np.linspace(0.1, 1, 100) + + for index, size in enumerate(sizes): + q0 = 0.25 + q1 = 1.25 + + phi0 = np.pi/2 - size + phi1 = np.pi/2 + size + + rebinner = AnularSector(q0, q1, phi0, phi1) + + data_order_neg1.append(rebinner.average(x, y, grid_ring_data, order=-1)) + data_order_0.append(rebinner.average(x, y, grid_ring_data, order=0)) + + if index % 10 == 0: + plt.figure("Regions 2") + rebinner.bin_mesh.show(actually_show=False) + + plt.title("Regions") + + plt.figure("Average of region 2") + + plt.plot(sizes, data_order_neg1) + plt.plot(sizes, data_order_0) + + plt.legend(["Order -1", "Order 0"]) + plt.title("Sum over region") + + plt.show() - # Demo of averaging, annular sector over ring shaped data \ No newline at end of file diff --git a/sasdata/data_util/slicing/slicers/AnularSector.py b/sasdata/data_util/slicing/slicers/AnularSector.py index e9f13774b..6d034dad3 100644 --- a/sasdata/data_util/slicing/slicers/AnularSector.py +++ b/sasdata/data_util/slicing/slicers/AnularSector.py @@ -5,8 +5,8 @@ class AnularSector(Rebinner): """ A single annular sector (wedge sum)""" - def __init__(self, q0: float, q1: float, phi0: float, phi1: float, order: int=1, points_per_degree: int=2): - super().__init__(order) + def __init__(self, q0: float, q1: float, phi0: float, phi1: float, points_per_degree: int=2): + super().__init__() self.q0 = q0 self.q1 = q1 @@ -17,7 +17,7 @@ def __init__(self, q0: float, q1: float, phi0: float, phi1: float, order: int=1, def _bin_mesh(self) -> Mesh: - n_points = int(1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi) + n_points = np.max([int(1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi), 2]) angles = np.linspace(self.phi0, self.phi1, n_points) From 93c3a0cd1d75b121b748f9243643a16cc3c0fd56 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 5 Oct 2023 14:38:34 +0100 Subject: [PATCH 144/675] Requirements --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 64bfe29a4..a1609faa1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,4 +16,3 @@ html5lib # Other stuff matplotlib -pre-commit \ No newline at end of file From aae2eed87e4ab62d77b72b09903063269914de0c Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 15 Oct 2024 17:45:32 +0100 Subject: [PATCH 145/675] Notes --- sasdata/transforms/rebinning.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index 3335216ac..d5120b709 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -26,7 +26,11 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], order: InterpolationOptions = InterpolationOptions.LINEAR, is_density=False): + """ Calculate the matrix that converts values recorded at points specified by input_axis to + values recorded at points specified by output_axis""" + # We want the input values in terms of the output units, will implicitly check compatability + # TODO: incorporate mask working_units = output_axis.units @@ -136,6 +140,8 @@ def calculate_interpolation_matrix_2d_axis_axis(input_1: Quantity[ArrayLike], order: InterpolationOptions = InterpolationOptions.LINEAR, is_density: bool = False): + # If it wasn't for the mask, this would be the same as just two sets of 1D interpolation + match order: case InterpolationOptions.NEAREST_NEIGHBOUR: pass From e7d8d89f4733416d676adb4c44a5883c584ae05d Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 15 Oct 2024 17:46:18 +0100 Subject: [PATCH 146/675] No error --- sasdata/transforms/rebinning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index d5120b709..662a0b132 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -177,7 +177,7 @@ def calculate_interpolation_matrix(input_axes: list[Quantity[ArrayLike]], if len(output_axes) != len(input_axes): # Input or output axes might be 2D matrices - + pass From 747c58a5492f7c55e47b6534f953b4b15c2623df Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 22 Oct 2024 15:36:48 +0100 Subject: [PATCH 147/675] Interpolation stuff --- sasdata/manual_tests/interpolation.py | 4 ++-- sasdata/quantities/math.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sasdata/manual_tests/interpolation.py b/sasdata/manual_tests/interpolation.py index 9edee8a2e..c46078ba7 100644 --- a/sasdata/manual_tests/interpolation.py +++ b/sasdata/manual_tests/interpolation.py @@ -34,11 +34,11 @@ def linear_interpolation_check(): quantity_plot(new_x, new_y) - # print(new_y.history.summary()) + print(new_y.history.summary()) plt.show() -linear_interpolation_check() +linear_interpolation_check() \ No newline at end of file diff --git a/sasdata/quantities/math.py b/sasdata/quantities/math.py index d252ccc0d..6ef5b2983 100644 --- a/sasdata/quantities/math.py +++ b/sasdata/quantities/math.py @@ -2,4 +2,3 @@ # TODO Implementations for trig and exp # TODO Implementations for linear algebra stuff - From a0ea7442a7bf2de4d60e0970c2fba2b0cf711eb3 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 17 Oct 2024 14:21:59 +0100 Subject: [PATCH 148/675] Moving some things around --- sasdata/data_util/slicing/__init__.py | 0 sasdata/data_util/slicing/geometry.py | 0 sasdata/data_util/slicing/meshes/__init__.py | 0 .../data_util/slicing/meshes/delaunay_mesh.py | 32 --- sasdata/data_util/slicing/meshes/mesh.py | 242 ------------------ sasdata/data_util/slicing/meshes/meshmerge.py | 156 ----------- sasdata/data_util/slicing/meshes/util.py | 10 - .../data_util/slicing/meshes/voronoi_mesh.py | 96 ------- sasdata/data_util/slicing/rebinning.py | 149 ----------- sasdata/data_util/slicing/sample_polygons.py | 31 --- sasdata/data_util/slicing/slicer_demo.py | 121 --------- .../data_util/slicing/slicers/AnularSector.py | 43 ---- sasdata/data_util/slicing/transforms.py | 58 ----- sasdata/slicing/meshes/mesh.py | 8 +- sasdata/slicing/meshes/meshmerge.py | 22 +- sasdata/slicing/meshes/util.py | 3 +- sasdata/slicing/rebinning.py | 9 +- sasdata/slicing/slicer_demo.py | 7 +- sasdata/slicing/slicers/AnularSector.py | 3 +- sasdata/slicing/transforms.py | 66 ++--- sasdata/transforms/rebinning.py | 14 +- test/slicers/meshes_for_testing.py | 6 +- test/slicers/utest_meshmerge.py | 2 +- 23 files changed, 71 insertions(+), 1007 deletions(-) delete mode 100644 sasdata/data_util/slicing/__init__.py delete mode 100644 sasdata/data_util/slicing/geometry.py delete mode 100644 sasdata/data_util/slicing/meshes/__init__.py delete mode 100644 sasdata/data_util/slicing/meshes/delaunay_mesh.py delete mode 100644 sasdata/data_util/slicing/meshes/mesh.py delete mode 100644 sasdata/data_util/slicing/meshes/meshmerge.py delete mode 100644 sasdata/data_util/slicing/meshes/util.py delete mode 100644 sasdata/data_util/slicing/meshes/voronoi_mesh.py delete mode 100644 sasdata/data_util/slicing/rebinning.py delete mode 100644 sasdata/data_util/slicing/sample_polygons.py delete mode 100644 sasdata/data_util/slicing/slicer_demo.py delete mode 100644 sasdata/data_util/slicing/slicers/AnularSector.py delete mode 100644 sasdata/data_util/slicing/transforms.py diff --git a/sasdata/data_util/slicing/__init__.py b/sasdata/data_util/slicing/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/sasdata/data_util/slicing/geometry.py b/sasdata/data_util/slicing/geometry.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/sasdata/data_util/slicing/meshes/__init__.py b/sasdata/data_util/slicing/meshes/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/sasdata/data_util/slicing/meshes/delaunay_mesh.py b/sasdata/data_util/slicing/meshes/delaunay_mesh.py deleted file mode 100644 index 45e208786..000000000 --- a/sasdata/data_util/slicing/meshes/delaunay_mesh.py +++ /dev/null @@ -1,32 +0,0 @@ -import numpy as np -from scipy.spatial import Delaunay - -from sasdata.data_util.slicing.meshes.mesh import Mesh - -def delaunay_mesh(x, y) -> Mesh: - """ Create a triangulated mesh based on input points """ - - input_data = np.array((x, y)).T - delaunay = Delaunay(input_data) - - return Mesh(points=input_data, cells=delaunay.simplices) - - -if __name__ == "__main__": - import matplotlib.pyplot as plt - - points = np.random.random((100, 2)) - mesh = delaunay_mesh(points[:,0], points[:,1]) - mesh.show(actually_show=False) - - print(mesh.cells[50]) - - # pick random cell to show - for cell in mesh.cells_to_edges[10]: - a, b = mesh.edges[cell] - plt.plot( - [mesh.points[a][0], mesh.points[b][0]], - [mesh.points[a][1], mesh.points[b][1]], - color='r') - - plt.show() diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py deleted file mode 100644 index 3ac23dafe..000000000 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ /dev/null @@ -1,242 +0,0 @@ -from typing import Sequence - -import numpy as np - -import matplotlib.pyplot as plt -from matplotlib import cm -from matplotlib.collections import LineCollection - -from sasdata.data_util.slicing.meshes.util import closed_loop_edges - -class Mesh: - def __init__(self, - points: np.ndarray, - cells: Sequence[Sequence[int]]): - - """ - Object representing a mesh. - - Parameters are the values: - mesh points - map from edge to points - map from cells to edges - - it is done this way to ensure a non-redundant representation of cells and edges, - however there are no checks for the topology of the mesh, this is assumed to be done by - whatever creates it. There are also no checks for ordering of cells. - - :param points: points in 2D forming vertices of the mesh - :param cells: ordered lists of indices of points forming each cell (face) - - """ - - self.points = points - self.cells = cells - - # Get edges - - edges = set() - for cell_index, cell in enumerate(cells): - - for a, b in closed_loop_edges(cell): - # make sure the representation is unique - if a > b: - edges.add((a, b)) - else: - edges.add((b, a)) - - self.edges = list(edges) - - # Associate edges with faces - - edge_lookup = {edge: i for i, edge in enumerate(self.edges)} - self.cells_to_edges = [] - self.cells_to_edges_signs = [] - - for cell in cells: - - this_cell_data = [] - this_sign_data = [] - - for a, b in closed_loop_edges(cell): - # make sure the representation is unique - if a > b: - this_cell_data.append(edge_lookup[(a, b)]) - this_sign_data.append(1) - else: - this_cell_data.append(edge_lookup[(b, a)]) - this_sign_data.append(-1) - - self.cells_to_edges.append(this_cell_data) - self.cells_to_edges_signs.append(this_sign_data) - - # Counts for elements - self.n_points = self.points.shape[0] - self.n_edges = len(self.edges) - self.n_cells = len(self.cells) - - # Areas - self._areas = None - - - @property - def areas(self): - """ Areas of cells """ - - if self._areas is None: - # Calculate areas - areas = [] - for cell in self.cells: - # Use triangle shoelace formula, basically calculate the - # determinant based on of triangles with one point at 0,0 - a_times_2 = 0.0 - for i1, i2 in closed_loop_edges(cell): - p1 = self.points[i1, :] - p2 = self.points[i2, :] - a_times_2 += p1[0]*p2[1] - p1[1]*p2[0] - - areas.append(0.5*np.abs(a_times_2)) - - # Save in cache - self._areas = np.array(areas) - - # Return cache - return self._areas - - - def show(self, actually_show=True, show_labels=False, **kwargs): - """ Show on a plot """ - ax = plt.gca() - segments = [[self.points[edge[0]], self.points[edge[1]]] for edge in self.edges] - line_collection = LineCollection(segments=segments, **kwargs) - ax.add_collection(line_collection) - - if show_labels: - text_color = kwargs["color"] if "color" in kwargs else 'k' - for i, cell in enumerate(self.cells): - xy = np.sum(self.points[cell, :], axis=0)/len(cell) - ax.text(xy[0], xy[1], str(i), horizontalalignment="center", verticalalignment="center", color=text_color) - - x_limits = [np.min(self.points[:,0]), np.max(self.points[:,0])] - y_limits = [np.min(self.points[:,1]), np.max(self.points[:,1])] - - plt.xlim(x_limits) - plt.ylim(y_limits) - - if actually_show: - plt.show() - - def locate_points(self, x: np.ndarray, y: np.ndarray): - """ Find the cells that contain the specified points""" - - x = x.reshape(-1) - y = y.reshape(-1) - - xy = np.concatenate(([x], [y]), axis=1) - - # The most simple implementation is not particularly fast, especially in python - # - # Less obvious, but hopefully faster strategy - # - # Ultimately, checking the inclusion of a point within a polygon - # requires checking the crossings of a half line with the polygon's - # edges. - # - # A fairly efficient thing to do is to check every edge for crossing - # the axis parallel lines x=point_x. - # Then these edges that cross can map back to the polygons they're in - # and a final check for inclusion can be done with the edge sign property - # and some explicit checking of the - # - # Basic idea is: - # 1) build a matrix for each point-edge pair - # True if the edge crosses the half-line above a point - # 2) for each cell get the winding number by evaluating the - # sum of the component edges, weighted 1/-1 according to direction - - - edges = np.array(self.edges) - - edge_xy_1 = self.points[edges[:, 0], :] - edge_xy_2 = self.points[edges[:, 1], :] - - edge_x_1 = edge_xy_1[:, 0] - edge_x_2 = edge_xy_2[:, 0] - - - - # Make an n_edges-by-n_inputs boolean matrix that indicates which of the - # edges cross x=points_x line - crossers = np.logical_xor( - edge_x_1.reshape(-1, 1) < x.reshape(1, -1), - edge_x_2.reshape(-1, 1) < x.reshape(1, -1)) - - # Calculate the gradients, some might be infs, but none that matter will be - # TODO: Disable warnings - gradients = (edge_xy_2[:, 1] - edge_xy_1[:, 1]) / (edge_xy_2[:, 0] - edge_xy_1[:, 0]) - - # Distance to crossing points edge 0 - delta_x = x.reshape(1, -1) - edge_x_1.reshape(-1, 1) - - # Signed distance from point to y (doesn't really matter which sign) - delta_y = gradients.reshape(-1, 1) * delta_x + edge_xy_1[:, 1:] - y.reshape(1, -1) - - score_matrix = np.logical_and(delta_y > 0, crossers) - - output = -np.ones(len(x), dtype=int) - for cell_index, (cell_edges, sign) in enumerate(zip(self.cells_to_edges, self.cells_to_edges_signs)): - cell_score = np.sum(score_matrix[cell_edges, :] * np.array(sign).reshape(-1, 1), axis=0) - points_in_cell = np.abs(cell_score) == 1 - output[points_in_cell] = cell_index - - return output - - def show_data(self, - data: np.ndarray, - cmap='winter', - mesh_color='white', - show_mesh=False, - actually_show=True, - density=False): - - """ Show with data """ - - colormap = cm.get_cmap(cmap, 256) - - data = data.reshape(-1) - - if density: - data = data / self.areas - - cmin = np.min(data) - cmax = np.max(data) - - color_index_map = np.array(255 * (data - cmin) / (cmax - cmin), dtype=int) - - for cell, color_index in zip(self.cells, color_index_map): - - color = colormap(color_index) - - plt.fill(self.points[cell, 0], self.points[cell, 1], color=color, edgecolor=None) - - if show_mesh: - self.show(actually_show=False, color=mesh_color) - - if actually_show: - self.show() - - -if __name__ == "__main__": - from test.slicers.meshes_for_testing import location_test_mesh, location_test_points_x, location_test_points_y - - cell_indices = location_test_mesh.locate_points(location_test_points_x, location_test_points_y) - - print(cell_indices) - - for i in range(location_test_mesh.n_cells): - inds = cell_indices == i - plt.scatter( - location_test_points_x.reshape(-1)[inds], - location_test_points_y.reshape(-1)[inds]) - - location_test_mesh.show() \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/meshmerge.py b/sasdata/data_util/slicing/meshes/meshmerge.py deleted file mode 100644 index 161c1e5da..000000000 --- a/sasdata/data_util/slicing/meshes/meshmerge.py +++ /dev/null @@ -1,156 +0,0 @@ -import numpy as np - -from sasdata.data_util.slicing.meshes.mesh import Mesh -from sasdata.data_util.slicing.meshes.delaunay_mesh import delaunay_mesh -from sasdata.data_util.slicing.meshes.util import closed_loop_edges - - -import time - -def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: - """ Take two lists of polygons and find their intersections - - Polygons in each of the input variables should not overlap i.e. a point in space should be assignable to - at most one polygon in mesh_a and at most one polygon in mesh_b - - Mesh topology should be sensible, otherwise bad things might happen, also, the cells of the input meshes - must be in order (which is assumed by the mesh class constructor anyway). - - :returns: - 1) A triangulated mesh based on both sets of polygons together - 2) The indices of the mesh_a polygon that corresponds to each triangle, -1 for nothing - 3) The indices of the mesh_b polygon that corresponds to each triangle, -1 for nothing - - """ - - t0 = time.time() - - # Find intersections of all edges in mesh one with edges in mesh two - - # Fastest way might just be to calculate the intersections of all lines on edges, - # see whether we need filtering afterwards - - edges_a = np.array(mesh_a.edges, dtype=int) - edges_b = np.array(mesh_b.edges, dtype=int) - - edge_a_1 = mesh_a.points[edges_a[:, 0], :] - edge_a_2 = mesh_a.points[edges_a[:, 1], :] - edge_b_1 = mesh_b.points[edges_b[:, 0], :] - edge_b_2 = mesh_b.points[edges_b[:, 1], :] - - a_grid, b_grid = np.mgrid[0:mesh_a.n_edges, 0:mesh_b.n_edges] - a_grid = a_grid.reshape(-1) - b_grid = b_grid.reshape(-1) - - p1 = edge_a_1[a_grid, :] - p2 = edge_a_2[a_grid, :] - p3 = edge_b_1[b_grid, :] - p4 = edge_b_2[b_grid, :] - - # - # TODO: Investigate whether adding a bounding box check will help with speed, seems likely as most edges wont cross - # - - # - # Solve the equations - # - # z_a1 + s delta_z_a = z_b1 + t delta_z_b - # - # for z = (x, y) - # - - start_point_diff = p1 - p3 - - delta1 = p2 - p1 - delta3 = p4 - p3 - - deltas = np.concatenate(([-delta1], [delta3]), axis=0) - deltas = np.moveaxis(deltas, 0, 2) - - non_singular = np.linalg.det(deltas) != 0 - - st = np.linalg.solve(deltas[non_singular], start_point_diff[non_singular]) - - # Find the points where s and t are in (0, 1) - - intersection_inds = np.logical_and( - np.logical_and(0 < st[:, 0], st[:, 0] < 1), - np.logical_and(0 < st[:, 1], st[:, 1] < 1)) - - start_points_for_intersections = p1[non_singular][intersection_inds, :] - deltas_for_intersections = delta1[non_singular][intersection_inds, :] - - points_to_add = start_points_for_intersections + st[intersection_inds, 0].reshape(-1,1) * deltas_for_intersections - - t1 = time.time() - print("Edge intersections:", t1 - t0) - - # Build list of all input points, in a way that we can check for coincident points - - - points = np.concatenate(( - mesh_a.points, - mesh_b.points, - points_to_add - )) - - - # Remove coincident points - - points = np.unique(points, axis=0) - - # Triangulate based on these intersections - - output_mesh = delaunay_mesh(points[:, 0], points[:, 1]) - - - t2 = time.time() - print("Delaunay:", t2 - t1) - - - # Find centroids of all output triangles, and find which source cells they belong to - - ## step 1) Assign -1 to all cells of original meshes - assignments_a = -np.ones(output_mesh.n_cells, dtype=int) - assignments_b = -np.ones(output_mesh.n_cells, dtype=int) - - ## step 2) Find centroids of triangulated mesh (just needs to be a point inside, but this is a good one) - centroids = [] - for cell in output_mesh.cells: - centroid = np.sum(output_mesh.points[cell, :]/3, axis=0) - centroids.append(centroid) - - centroids = np.array(centroids) - - t3 = time.time() - print("Centroids:", t3 - t2) - - - ## step 3) Find where points belong based on Mesh classes point location algorithm - - assignments_a = mesh_a.locate_points(centroids[:, 0], centroids[:, 1]) - assignments_b = mesh_b.locate_points(centroids[:, 0], centroids[:, 1]) - - t4 = time.time() - print("Assignments:", t4 - t3) - - return output_mesh, assignments_a, assignments_b - - -def main(): - from voronoi_mesh import voronoi_mesh - - n1 = 100 - n2 = 100 - - m1 = voronoi_mesh(np.random.random(n1), np.random.random(n1)) - m2 = voronoi_mesh(np.random.random(n2), np.random.random(n2)) - - - mesh, assignement1, assignement2 = meshmerge(m1, m2) - - mesh.show() - - -if __name__ == "__main__": - main() diff --git a/sasdata/data_util/slicing/meshes/util.py b/sasdata/data_util/slicing/meshes/util.py deleted file mode 100644 index b78a9e076..000000000 --- a/sasdata/data_util/slicing/meshes/util.py +++ /dev/null @@ -1,10 +0,0 @@ -from typing import Sequence, TypeVar - -T = TypeVar("T") - -def closed_loop_edges(values: Sequence[T]) -> tuple[T, T]: - """ Generator for a closed loop of edge pairs """ - for pair in zip(values, values[1:]): - yield pair - - yield values[-1], values[0] \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/voronoi_mesh.py b/sasdata/data_util/slicing/meshes/voronoi_mesh.py deleted file mode 100644 index d3eb81d29..000000000 --- a/sasdata/data_util/slicing/meshes/voronoi_mesh.py +++ /dev/null @@ -1,96 +0,0 @@ -import numpy as np -from scipy.spatial import Voronoi - - -from sasdata.data_util.slicing.meshes.mesh import Mesh - -def voronoi_mesh(x, y, debug_plot=False) -> Mesh: - """ Create a mesh based on a voronoi diagram of points """ - - input_data = np.array((x.reshape(-1), y.reshape(-1))).T - - # Need to make sure mesh covers a finite region, probably not important for - # much data stuff, but is important for plotting - # - # * We want the cells at the edge of the mesh to have a reasonable size, definitely not infinite - # * The exact size doesn't matter that much - # * It should work well with a grid, but also - # * ...it should be robust so that if the data isn't on a grid, it doesn't cause any serious problems - # - # Plan: Create a square border of points that are totally around the points, this is - # at the distance it would be if it was an extra row of grid points - # to do this we'll need - # 1) an estimate of the grid spacing - # 2) the bounding box of the grid - # - - - # Use the median area of finite voronoi cells as an estimate - voronoi = Voronoi(input_data) - finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] - premesh = Mesh(points=voronoi.vertices, cells=finite_cells) - - area_spacing = np.median(premesh.areas) - gap = np.sqrt(area_spacing) - - # Bounding box is easy - x_min, y_min = np.min(input_data, axis=0) - x_max, y_max = np.max(input_data, axis=0) - - # Create a border - n_x = int(np.round((x_max - x_min)/gap)) - n_y = int(np.round((y_max - y_min)/gap)) - - top_bottom_xs = np.linspace(x_min - gap, x_max + gap, n_x + 3) - left_right_ys = np.linspace(y_min, y_max, n_y + 1) - - top = np.array([top_bottom_xs, (y_max + gap) * np.ones_like(top_bottom_xs)]) - bottom = np.array([top_bottom_xs, (y_min - gap) * np.ones_like(top_bottom_xs)]) - left = np.array([(x_min - gap) * np.ones_like(left_right_ys), left_right_ys]) - right = np.array([(x_max + gap) * np.ones_like(left_right_ys), left_right_ys]) - - added_points = np.concatenate((top, bottom, left, right), axis=1).T - - if debug_plot: - import matplotlib.pyplot as plt - plt.scatter(x, y) - plt.scatter(added_points[:, 0], added_points[:, 1]) - plt.show() - - new_points = np.concatenate((input_data, added_points), axis=0) - voronoi = Voronoi(new_points) - - # Remove the cells that correspond to the added edge points, - # Because the points on the edge of the square are (weakly) convex, these - # regions be infinite - - # finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] - - # ... however, we can just use .region_points - input_regions = voronoi.point_region[:input_data.shape[0]] - cells = [voronoi.regions[region_index] for region_index in input_regions] - - return Mesh(points=voronoi.vertices, cells=cells) - - -def square_grid_check(): - values = np.linspace(-10, 10, 21) - x, y = np.meshgrid(values, values) - - mesh = voronoi_mesh(x, y) - - mesh.show(show_labels=True) - -def random_grid_check(): - import matplotlib.pyplot as plt - points = np.random.random((100, 2)) - mesh = voronoi_mesh(points[:, 0], points[:, 1], True) - mesh.show(actually_show=False) - plt.scatter(points[:, 0], points[:, 1]) - plt.show() - - -if __name__ == "__main__": - square_grid_check() - # random_grid_check() - diff --git a/sasdata/data_util/slicing/rebinning.py b/sasdata/data_util/slicing/rebinning.py deleted file mode 100644 index 510535a9c..000000000 --- a/sasdata/data_util/slicing/rebinning.py +++ /dev/null @@ -1,149 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Optional -from dataclasses import dataclass - -import numpy as np - -from sasdata.data_util.slicing.meshes.mesh import Mesh -from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh -from sasdata.data_util.slicing.meshes.meshmerge import meshmerge - -import time - -@dataclass -class CacheData: - """ Data cached for repeated calculations with the same coordinates """ - input_coordinates: np.ndarray # Input data - input_coordinates_mesh: Mesh # Mesh of the input data - merged_mesh_data: tuple[Mesh, np.ndarray, np.ndarray] # mesh information about the merging - - -class Rebinner(ABC): - - - def __init__(self): - """ Base class for rebinning methods""" - - self._bin_mesh_cache: Optional[Mesh] = None # cached version of the output bin mesh - - # Output dependent caching - self._input_cache: Optional[CacheData] = None - - - @abstractmethod - def _bin_coordinates(self) -> np.ndarray: - """ Coordinates for the output bins """ - - @abstractmethod - def _bin_mesh(self) -> Mesh: - """ Get the meshes used for binning """ - - @property - def allowable_orders(self) -> list[int]: - return [-1, 0, 1] - - @property - def bin_mesh(self) -> Mesh: - - if self._bin_mesh_cache is None: - bin_mesh = self._bin_mesh() - self._bin_mesh_cache = bin_mesh - - return self._bin_mesh_cache - - def _post_processing(self, coordinates, values) -> tuple[np.ndarray, np.ndarray]: - """ Perform post-processing on the mesh binned values """ - # Default is to do nothing, override if needed - return coordinates, values - - def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray, order: int) -> np.ndarray: - """ Main calculation """ - - if order == -1: - # Construct the input output mapping just based on input points being the output cells, - # Equivalent to the original binning method - - mesh = self.bin_mesh - bin_identities = mesh.locate_points(input_coordinates[:,0], input_coordinates[:, 1]) - output_data = np.zeros(mesh.n_cells, dtype=float) - - for index, bin in enumerate(bin_identities): - if bin >= 0: - output_data[bin] += input_data[index] - - return output_data - - else: - # Use a mapping based on meshes - - # Either create de-cache the appropriate mesh - # Why not use a hash? Hashing takes time, equality checks are pretty fast, need to check equality - # when there is a hit anyway in case of very rare chance of collision, hits are the most common case, - # we want it to work 100% of the time, not 99.9999% - if self._input_cache is not None and np.all(self._input_cache.input_coordinates == input_coordinates): - - input_coordinate_mesh = self._input_cache.input_coordinates_mesh - merge_data = self._input_cache.merged_mesh_data - - else: - # Calculate mesh data - input_coordinate_mesh = voronoi_mesh(input_coordinates[:,0], input_coordinates[:, 1]) - self._data_mesh_cache = input_coordinate_mesh - - merge_data = meshmerge(self.bin_mesh, input_coordinate_mesh) - - # Cache mesh data - self._input_cache = CacheData( - input_coordinates=input_coordinates, - input_coordinates_mesh=input_coordinate_mesh, - merged_mesh_data=merge_data) - - merged_mesh, merged_to_output, merged_to_input = merge_data - - # Calculate values according to the order parameter - t0 = time.time() - if order == 0: - # Based on the overlap of cells only - - input_areas = input_coordinate_mesh.areas - output = np.zeros(self.bin_mesh.n_cells, dtype=float) - - for input_index, output_index, area in zip(merged_to_input, merged_to_output, merged_mesh.areas): - if input_index == -1 or output_index == -1: - # merged region does not correspond to anything of interest - continue - - output[output_index] += input_data[input_index] * area / input_areas[input_index] - - print("Main calc:", time.time() - t0) - - return output - - elif order == 1: - # Linear interpolation requires the following relationship with the data, - # as the input data is the total over the whole input cell, the linear - # interpolation requires continuity at the vertices, and a constraint on the - # integral. - # - # We can take each of the input points, and the associated values, and solve a system - # of linear equations that gives a total value. - - raise NotImplementedError("1st order (linear) interpolation currently not implemented") - - else: - raise ValueError(f"Expected order to be in {self.allowable_orders}, got {order}") - - def sum(self, x: np.ndarray, y: np.ndarray, data: np.ndarray, order: int = 0) -> np.ndarray: - """ Return the summed data in the output bins """ - return self._calculate(np.array((x.reshape(-1), y.reshape(-1))).T, data.reshape(-1), order) - - def error_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: - raise NotImplementedError("Error propagation not implemented yet") - - def resolution_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: - raise NotImplementedError("Resolution propagation not implemented yet") - - def average(self, x: np.ndarray, y: np.ndarray, data: np.ndarray, order: int = 0) -> np.ndarray: - """ Return the averaged data in the output bins """ - return self._calculate(np.array((x, y)).T, data.reshape(-1), order) / self.bin_mesh.areas - diff --git a/sasdata/data_util/slicing/sample_polygons.py b/sasdata/data_util/slicing/sample_polygons.py deleted file mode 100644 index e12fb1e80..000000000 --- a/sasdata/data_util/slicing/sample_polygons.py +++ /dev/null @@ -1,31 +0,0 @@ -import numpy as np - -def wedge(q0, q1, theta0, theta1, clockwise=False, n_points_per_degree=2): - - # Traverse a rectangle in curvilinear coordinates (q0, theta0), (q0, theta1), (q1, theta1), (q1, theta0) - if clockwise: - if theta1 > theta0: - theta0 += 2*np.pi - - else: - if theta0 > theta1: - theta1 += 2*np.pi - - subtended_angle = np.abs(theta1 - theta0) - n_points = int(subtended_angle*180*n_points_per_degree/np.pi)+1 - - angles = np.linspace(theta0, theta1, n_points) - - xs = np.concatenate((q0*np.cos(angles), q1*np.cos(angles[::-1]))) - ys = np.concatenate((q0*np.sin(angles), q1*np.sin(angles[::-1]))) - - return np.array((xs, ys)).T - - -if __name__ == "__main__": - import matplotlib.pyplot as plt - xy = wedge(0.3, 0.6, 2, 3) - - plt.plot(xy[:,0], xy[:,1]) - plt.show() - diff --git a/sasdata/data_util/slicing/slicer_demo.py b/sasdata/data_util/slicing/slicer_demo.py deleted file mode 100644 index 6096ca905..000000000 --- a/sasdata/data_util/slicing/slicer_demo.py +++ /dev/null @@ -1,121 +0,0 @@ -""" Dev docs: Demo to show the behaviour of the re-binning methods """ - -import numpy as np - -import matplotlib.pyplot as plt - -from sasdata.data_util.slicing.slicers.AnularSector import AnularSector -from sasdata.data_util.slicing.meshes.mesh import Mesh -from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh - - - -if __name__ == "__main__": - q_range = 1.5 - demo1 = True - demo2 = True - - # Demo of sums, annular sector over some not very circular data - - if demo1: - - x = (2 * q_range) * (np.random.random(400) - 0.5) - y = (2 * q_range) * (np.random.random(400) - 0.5) - - display_mesh = voronoi_mesh(x, y) - - - def lobe_test_function(x, y): - return 1 + np.sin(x*np.pi/q_range)*np.sin(y*np.pi/q_range) - - - random_lobe_data = lobe_test_function(x, y) - - plt.figure("Input Dataset 1") - display_mesh.show_data(random_lobe_data, actually_show=False) - - data_order_0 = [] - data_order_neg1 = [] - - sizes = np.linspace(0.1, 1, 100) - - for index, size in enumerate(sizes): - q0 = 0.75 - 0.6*size - q1 = 0.75 + 0.6*size - phi0 = np.pi/2 - size - phi1 = np.pi/2 + size - - rebinner = AnularSector(q0, q1, phi0, phi1) - - data_order_neg1.append(rebinner.sum(x, y, random_lobe_data, order=-1)) - data_order_0.append(rebinner.sum(x, y, random_lobe_data, order=0)) - - if index % 10 == 0: - plt.figure("Regions 1") - rebinner.bin_mesh.show(actually_show=False) - - plt.title("Regions") - - plt.figure("Sum of region, dataset 1") - - plt.plot(sizes, data_order_neg1) - plt.plot(sizes, data_order_0) - - plt.legend(["Order -1", "Order 0"]) - plt.title("Sum over region") - - - # Demo of averaging, annular sector over ring shaped data - - if demo2: - - x, y = np.meshgrid(np.linspace(-q_range, q_range, 41), np.linspace(-q_range, q_range, 41)) - x = x.reshape(-1) - y = y.reshape(-1) - - display_mesh = voronoi_mesh(x, y) - - - def ring_test_function(x, y): - r = np.sqrt(x**2 + y**2) - return np.log(np.sinc(r*1.5)**2) - - - grid_ring_data = ring_test_function(x, y) - - plt.figure("Input Dataset 2") - display_mesh.show_data(grid_ring_data, actually_show=False) - - data_order_0 = [] - data_order_neg1 = [] - - sizes = np.linspace(0.1, 1, 100) - - for index, size in enumerate(sizes): - q0 = 0.25 - q1 = 1.25 - - phi0 = np.pi/2 - size - phi1 = np.pi/2 + size - - rebinner = AnularSector(q0, q1, phi0, phi1) - - data_order_neg1.append(rebinner.average(x, y, grid_ring_data, order=-1)) - data_order_0.append(rebinner.average(x, y, grid_ring_data, order=0)) - - if index % 10 == 0: - plt.figure("Regions 2") - rebinner.bin_mesh.show(actually_show=False) - - plt.title("Regions") - - plt.figure("Average of region 2") - - plt.plot(sizes, data_order_neg1) - plt.plot(sizes, data_order_0) - - plt.legend(["Order -1", "Order 0"]) - plt.title("Sum over region") - - plt.show() - diff --git a/sasdata/data_util/slicing/slicers/AnularSector.py b/sasdata/data_util/slicing/slicers/AnularSector.py deleted file mode 100644 index 6d034dad3..000000000 --- a/sasdata/data_util/slicing/slicers/AnularSector.py +++ /dev/null @@ -1,43 +0,0 @@ -import numpy as np - -from sasdata.data_util.slicing.rebinning import Rebinner -from sasdata.data_util.slicing.meshes.mesh import Mesh - -class AnularSector(Rebinner): - """ A single annular sector (wedge sum)""" - def __init__(self, q0: float, q1: float, phi0: float, phi1: float, points_per_degree: int=2): - super().__init__() - - self.q0 = q0 - self.q1 = q1 - self.phi0 = phi0 - self.phi1 = phi1 - - self.points_per_degree = points_per_degree - - def _bin_mesh(self) -> Mesh: - - n_points = np.max([int(1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi), 2]) - - angles = np.linspace(self.phi0, self.phi1, n_points) - - row1 = self.q0 * np.array([np.cos(angles), np.sin(angles)]) - row2 = self.q1 * np.array([np.cos(angles), np.sin(angles)])[:, ::-1] - - points = np.concatenate((row1, row2), axis=1).T - - cells = [[i for i in range(2*n_points)]] - - return Mesh(points=points, cells=cells) - - def _bin_coordinates(self) -> np.ndarray: - return np.array([], dtype=float) - - -def main(): - """ Just show a random example""" - AnularSector(1, 2, 1, 2).bin_mesh.show() - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/sasdata/data_util/slicing/transforms.py b/sasdata/data_util/slicing/transforms.py deleted file mode 100644 index d04742d3c..000000000 --- a/sasdata/data_util/slicing/transforms.py +++ /dev/null @@ -1,58 +0,0 @@ -import numpy as np -from scipy.spatial import Voronoi, Delaunay -import matplotlib.pyplot as plt -from matplotlib import cm - - -# Some test data - -qx_base_values = np.linspace(-10, 10, 21) -qy_base_values = np.linspace(-10, 10, 21) - -qx, qy = np.meshgrid(qx_base_values, qy_base_values) - -include = np.logical_not((np.abs(qx) < 2) & (np.abs(qy) < 2)) - -qx = qx[include] -qy = qy[include] - -r = np.sqrt(qx**2 + qy**2) - -data = np.log((1+np.cos(3*r))*np.exp(-r*r)) - -colormap = cm.get_cmap('winter', 256) - -def get_data_mesh(x, y, data): - - input_data = np.array((x, y)).T - voronoi = Voronoi(input_data) - - # plt.scatter(voronoi.vertices[:,0], voronoi.vertices[:,1]) - # plt.scatter(voronoi.points[:,0], voronoi.points[:,1]) - - cmin = np.min(data) - cmax = np.max(data) - - color_index_map = np.array(255 * (data - cmin) / (cmax - cmin), dtype=int) - - for point_index, points in enumerate(voronoi.points): - - region_index = voronoi.point_region[point_index] - region = voronoi.regions[region_index] - - if len(region) > 0: - - if -1 in region: - - pass - - else: - - color = colormap(color_index_map[point_index]) - - circly = region + [region[0]] - plt.fill(voronoi.vertices[circly, 0], voronoi.vertices[circly, 1], color=color, edgecolor="white") - - plt.show() - -get_data_mesh(qx.reshape(-1), qy.reshape(-1), data) \ No newline at end of file diff --git a/sasdata/slicing/meshes/mesh.py b/sasdata/slicing/meshes/mesh.py index a3e8c0fa0..e1d6c8a96 100644 --- a/sasdata/slicing/meshes/mesh.py +++ b/sasdata/slicing/meshes/mesh.py @@ -1,13 +1,13 @@ -from collections.abc import Sequence +from typing import Sequence -import matplotlib.pyplot as plt import numpy as np + +import matplotlib.pyplot as plt from matplotlib import cm from matplotlib.collections import LineCollection from sasdata.slicing.meshes.util import closed_loop_edges - class Mesh: def __init__(self, points: np.ndarray, @@ -132,6 +132,8 @@ def locate_points(self, x: np.ndarray, y: np.ndarray): x = x.reshape(-1) y = y.reshape(-1) + xy = np.concatenate(([x], [y]), axis=1) + # The most simple implementation is not particularly fast, especially in python # # Less obvious, but hopefully faster strategy diff --git a/sasdata/slicing/meshes/meshmerge.py b/sasdata/slicing/meshes/meshmerge.py index 882699c0d..2060cc7b4 100644 --- a/sasdata/slicing/meshes/meshmerge.py +++ b/sasdata/slicing/meshes/meshmerge.py @@ -1,10 +1,9 @@ -import time - import numpy as np -from sasdata.slicing.meshes.delaunay_mesh import delaunay_mesh from sasdata.slicing.meshes.mesh import Mesh +from sasdata.slicing.meshes.delaunay_mesh import delaunay_mesh +import time def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: """ Take two lists of polygons and find their intersections @@ -68,24 +67,13 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] non_singular = np.linalg.det(deltas) != 0 - st = np.linalg.solve( - deltas[non_singular], - # Reshape is required because solve accepts matrices of shape - # (M) or (..., M, K) for the second parameter, but ours shape - # is (..., M). We add an extra dimension to force our matrix - # into the shape (..., M, 1), which meets the expectations. - # - # - # Due to the reshaping work mentioned above, the final result - # has an extra element of length 1. We then index this extra - # dimension to get back to the result we wanted. - np.expand_dims(start_point_diff[non_singular], axis=2))[:, :, 0] + st = np.linalg.solve(deltas[non_singular], start_point_diff[non_singular]) # Find the points where s and t are in (0, 1) intersection_inds = np.logical_and( - np.logical_and(0 < st[:, 0], st[:, 0] < 1), # noqa SIM300 - np.logical_and(0 < st[:, 1], st[:, 1] < 1)) # noqa SIM300 + np.logical_and(0 < st[:, 0], st[:, 0] < 1), + np.logical_and(0 < st[:, 1], st[:, 1] < 1)) start_points_for_intersections = p1[non_singular][intersection_inds, :] deltas_for_intersections = delta1[non_singular][intersection_inds, :] diff --git a/sasdata/slicing/meshes/util.py b/sasdata/slicing/meshes/util.py index da5b6e370..56584434d 100644 --- a/sasdata/slicing/meshes/util.py +++ b/sasdata/slicing/meshes/util.py @@ -1,5 +1,4 @@ -from collections.abc import Sequence -from typing import TypeVar +from typing import Sequence, TypeVar T = TypeVar("T") diff --git a/sasdata/slicing/rebinning.py b/sasdata/slicing/rebinning.py index 060b2e0ca..f2c76de64 100644 --- a/sasdata/slicing/rebinning.py +++ b/sasdata/slicing/rebinning.py @@ -1,13 +1,14 @@ -import time from abc import ABC, abstractmethod +from typing import Optional from dataclasses import dataclass import numpy as np from sasdata.slicing.meshes.mesh import Mesh -from sasdata.slicing.meshes.meshmerge import meshmerge from sasdata.slicing.meshes.voronoi_mesh import voronoi_mesh +from sasdata.slicing.meshes.meshmerge import meshmerge +import time @dataclass class CacheData: @@ -23,10 +24,10 @@ class Rebinner(ABC): def __init__(self): """ Base class for rebinning methods""" - self._bin_mesh_cache: Mesh | None = None # cached version of the output bin mesh + self._bin_mesh_cache: Optional[Mesh] = None # cached version of the output bin mesh # Output dependent caching - self._input_cache: CacheData | None = None + self._input_cache: Optional[CacheData] = None @abstractmethod diff --git a/sasdata/slicing/slicer_demo.py b/sasdata/slicing/slicer_demo.py index 5626ded4d..af3ee985f 100644 --- a/sasdata/slicing/slicer_demo.py +++ b/sasdata/slicing/slicer_demo.py @@ -1,10 +1,13 @@ """ Dev docs: Demo to show the behaviour of the re-binning methods """ -import matplotlib.pyplot as plt import numpy as np -from sasdata.slicing.meshes.voronoi_mesh import voronoi_mesh +import matplotlib.pyplot as plt + from sasdata.slicing.slicers.AnularSector import AnularSector +from sasdata.slicing.meshes.voronoi_mesh import voronoi_mesh + + if __name__ == "__main__": q_range = 1.5 diff --git a/sasdata/slicing/slicers/AnularSector.py b/sasdata/slicing/slicers/AnularSector.py index 56ed5f262..06ee3fe32 100644 --- a/sasdata/slicing/slicers/AnularSector.py +++ b/sasdata/slicing/slicers/AnularSector.py @@ -1,8 +1,7 @@ import numpy as np -from sasdata.slicing.meshes.mesh import Mesh from sasdata.slicing.rebinning import Rebinner - +from sasdata.slicing.meshes.mesh import Mesh class AnularSector(Rebinner): """ A single annular sector (wedge sum)""" diff --git a/sasdata/slicing/transforms.py b/sasdata/slicing/transforms.py index 724c53ff4..8c9b2c69c 100644 --- a/sasdata/slicing/transforms.py +++ b/sasdata/slicing/transforms.py @@ -1,58 +1,58 @@ -import matplotlib.pyplot as plt import numpy as np +from scipy.spatial import Voronoi, Delaunay +import matplotlib.pyplot as plt from matplotlib import cm -from scipy.spatial import Voronoi -if __name__ == "__main__": - # Some test data - qx_base_values = np.linspace(-10, 10, 21) - qy_base_values = np.linspace(-10, 10, 21) +# Some test data + +qx_base_values = np.linspace(-10, 10, 21) +qy_base_values = np.linspace(-10, 10, 21) - qx, qy = np.meshgrid(qx_base_values, qy_base_values) +qx, qy = np.meshgrid(qx_base_values, qy_base_values) - include = np.logical_not((np.abs(qx) < 2) & (np.abs(qy) < 2)) +include = np.logical_not((np.abs(qx) < 2) & (np.abs(qy) < 2)) - qx = qx[include] - qy = qy[include] +qx = qx[include] +qy = qy[include] - r = np.sqrt(qx**2 + qy**2) +r = np.sqrt(qx**2 + qy**2) - data = np.log((1+np.cos(3*r))*np.exp(-r*r)) +data = np.log((1+np.cos(3*r))*np.exp(-r*r)) - colormap = cm.get_cmap('winter', 256) +colormap = cm.get_cmap('winter', 256) - def get_data_mesh(x, y, data): +def get_data_mesh(x, y, data): - input_data = np.array((x, y)).T - voronoi = Voronoi(input_data) + input_data = np.array((x, y)).T + voronoi = Voronoi(input_data) - # plt.scatter(voronoi.vertices[:,0], voronoi.vertices[:,1]) - # plt.scatter(voronoi.points[:,0], voronoi.points[:,1]) + # plt.scatter(voronoi.vertices[:,0], voronoi.vertices[:,1]) + # plt.scatter(voronoi.points[:,0], voronoi.points[:,1]) - cmin = np.min(data) - cmax = np.max(data) + cmin = np.min(data) + cmax = np.max(data) - color_index_map = np.array(255 * (data - cmin) / (cmax - cmin), dtype=int) + color_index_map = np.array(255 * (data - cmin) / (cmax - cmin), dtype=int) - for point_index, points in enumerate(voronoi.points): + for point_index, points in enumerate(voronoi.points): - region_index = voronoi.point_region[point_index] - region = voronoi.regions[region_index] + region_index = voronoi.point_region[point_index] + region = voronoi.regions[region_index] - if len(region) > 0: + if len(region) > 0: - if -1 in region: + if -1 in region: - pass + pass - else: + else: - color = colormap(color_index_map[point_index]) + color = colormap(color_index_map[point_index]) - circly = region + [region[0]] - plt.fill(voronoi.vertices[circly, 0], voronoi.vertices[circly, 1], color=color, edgecolor="white") + circly = region + [region[0]] + plt.fill(voronoi.vertices[circly, 0], voronoi.vertices[circly, 1], color=color, edgecolor="white") - plt.show() + plt.show() - get_data_mesh(qx.reshape(-1), qy.reshape(-1), data) +get_data_mesh(qx.reshape(-1), qy.reshape(-1), data) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index 662a0b132..7bdc66209 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -130,7 +130,17 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], raise InterpolationError(f"Unsupported interpolation order: {order}") - return conversion_matrix + if mask is None: + return conversion_matrix, None + else: + # Create a new mask + + # Convert to numerical values + # Conservative masking: anything touched by the previous mask is now masked + new_mask = (np.array(mask, dtype=float) @ conversion_matrix) != 0.0 + + return conversion_matrix, new_mask + def calculate_interpolation_matrix_2d_axis_axis(input_1: Quantity[ArrayLike], input_2: Quantity[ArrayLike], @@ -140,7 +150,7 @@ def calculate_interpolation_matrix_2d_axis_axis(input_1: Quantity[ArrayLike], order: InterpolationOptions = InterpolationOptions.LINEAR, is_density: bool = False): - # If it wasn't for the mask, this would be the same as just two sets of 1D interpolation + # This is just the same 1D matrices things match order: case InterpolationOptions.NEAREST_NEIGHBOUR: diff --git a/test/slicers/meshes_for_testing.py b/test/slicers/meshes_for_testing.py index 7cb17b484..fb346e79c 100644 --- a/test/slicers/meshes_for_testing.py +++ b/test/slicers/meshes_for_testing.py @@ -4,9 +4,9 @@ import numpy as np -from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh -from sasdata.data_util.slicing.meshes.mesh import Mesh -from sasdata.data_util.slicing.meshes.meshmerge import meshmerge +from sasdata.slicing.meshes.voronoi_mesh import voronoi_mesh +from sasdata.slicing.meshes.mesh import Mesh +from sasdata.slicing.meshes.meshmerge import meshmerge coords = np.arange(-4, 5) grid_mesh = voronoi_mesh(*np.meshgrid(coords, coords)) diff --git a/test/slicers/utest_meshmerge.py b/test/slicers/utest_meshmerge.py index f745d0250..21071c049 100644 --- a/test/slicers/utest_meshmerge.py +++ b/test/slicers/utest_meshmerge.py @@ -4,7 +4,7 @@ It's pretty hard to test componentwise, but we can do some tests of the general behaviour """ -from sasdata.data_util.slicing.meshes.meshmerge import meshmerge +from sasdata.slicing.meshes import meshmerge from test.slicers.meshes_for_testing import ( grid_mesh, shape_mesh, expected_grid_mappings, expected_shape_mappings) From 48958dd1cc2db2b66de2aa4826d5b2e3d3086676 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 21 Oct 2024 14:26:04 +0100 Subject: [PATCH 149/675] Move math and operations into quantity --- sasdata/model_requirements.py | 2 +- sasdata/quantities/math.py | 4 - sasdata/quantities/operations.py | 821 --------------------- sasdata/quantities/operations_examples.py | 2 +- sasdata/quantities/operations_test.py | 2 +- sasdata/quantities/quantity.py | 857 +++++++++++++++++++++- 6 files changed, 858 insertions(+), 830 deletions(-) delete mode 100644 sasdata/quantities/math.py delete mode 100644 sasdata/quantities/operations.py diff --git a/sasdata/model_requirements.py b/sasdata/model_requirements.py index 40b6ac728..12ad54565 100644 --- a/sasdata/model_requirements.py +++ b/sasdata/model_requirements.py @@ -3,7 +3,7 @@ import numpy as np from sasdata.metadata import Metadata -from sasdata.quantities.operations import Operation +from sasdata.quantities.quantity import Operation @dataclass diff --git a/sasdata/quantities/math.py b/sasdata/quantities/math.py deleted file mode 100644 index 6ef5b2983..000000000 --- a/sasdata/quantities/math.py +++ /dev/null @@ -1,4 +0,0 @@ -""" Math module extended to allow operations on quantities """ - -# TODO Implementations for trig and exp -# TODO Implementations for linear algebra stuff diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py deleted file mode 100644 index 4c143b40f..000000000 --- a/sasdata/quantities/operations.py +++ /dev/null @@ -1,821 +0,0 @@ -from typing import Any, TypeVar, Union -import numpy as np - -import json - -T = TypeVar("T") - -def hash_and_name(hash_or_name: int | str): - """ Infer the name of a variable from a hash, or the hash from the name - - Note: hash_and_name(hash_and_name(number)[1]) is not the identity - however: hash_and_name(hash_and_name(number)) is - """ - - if isinstance(hash_or_name, str): - hash_value = hash(hash_or_name) - name = hash_or_name - - return hash_value, name - - elif isinstance(hash_or_name, int): - hash_value = hash_or_name - name = f"#{hash_or_name}" - - return hash_value, name - - elif isinstance(hash_or_name, tuple): - return hash_or_name - - else: - raise TypeError("Variable name_or_hash_value must be either str or int") - - -class Operation: - - serialisation_name = "unknown" - def summary(self, indent_amount: int = 0, indent: str=" "): - """ Summary of the operation tree""" - - s = f"{indent_amount*indent}{self._summary_open()}(\n" - - for chunk in self._summary_components(): - s += chunk.summary(indent_amount+1, indent) + "\n" - - s += f"{indent_amount*indent})" - - return s - def _summary_open(self): - """ First line of summary """ - - def _summary_components(self) -> list["Operation"]: - return [] - def evaluate(self, variables: dict[int, T]) -> T: - - """ Evaluate this operation """ - - def _derivative(self, hash_value: int) -> "Operation": - """ Get the derivative of this operation """ - - def _clean(self): - """ Clean up this operation - i.e. remove silly things like 1*x """ - return self - - def derivative(self, variable: Union[str, int, "Variable"], simplify=True): - if isinstance(variable, Variable): - hash_value = variable.hash_value - else: - hash_value, _ = hash_and_name(variable) - - derivative = self._derivative(hash_value) - - if not simplify: - return derivative - - derivative_string = derivative.serialise() - - # print("---------------") - # print("Base") - # print("---------------") - # print(derivative.summary()) - - # Inefficient way of doing repeated simplification, but it will work - for i in range(100): # set max iterations - - derivative = derivative._clean() - # - # print("-------------------") - # print("Iteration", i+1) - # print("-------------------") - # print(derivative.summary()) - # print("-------------------") - - new_derivative_string = derivative.serialise() - - if derivative_string == new_derivative_string: - break - - derivative_string = new_derivative_string - - return derivative - - @staticmethod - def deserialise(data: str) -> "Operation": - json_data = json.loads(data) - return Operation.deserialise_json(json_data) - - @staticmethod - def deserialise_json(json_data: dict) -> "Operation": - - operation = json_data["operation"] - parameters = json_data["parameters"] - cls = _serialisation_lookup[operation] - - try: - return cls._deserialise(parameters) - - except NotImplementedError: - raise NotImplementedError(f"No method to deserialise {operation} with {parameters} (cls={cls})") - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - raise NotImplementedError(f"Deserialise not implemented for this class") - - def serialise(self) -> str: - return json.dumps(self._serialise_json()) - - def _serialise_json(self) -> dict[str, Any]: - return {"operation": self.serialisation_name, - "parameters": self._serialise_parameters()} - - def _serialise_parameters(self) -> dict[str, Any]: - raise NotImplementedError("_serialise_parameters not implemented") - - def __eq__(self, other: "Operation"): - return NotImplemented - -class ConstantBase(Operation): - pass - -class AdditiveIdentity(ConstantBase): - - serialisation_name = "zero" - def evaluate(self, variables: dict[int, T]) -> T: - return 0 - - def _derivative(self, hash_value: int) -> Operation: - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return AdditiveIdentity() - - def _serialise_parameters(self) -> dict[str, Any]: - return {} - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}0 [Add.Id.]" - - def __eq__(self, other): - if isinstance(other, AdditiveIdentity): - return True - elif isinstance(other, Constant): - if other.value == 0: - return True - - return False - - - -class MultiplicativeIdentity(ConstantBase): - - serialisation_name = "one" - - def evaluate(self, variables: dict[int, T]) -> T: - return 1 - - def _derivative(self, hash_value: int): - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return MultiplicativeIdentity() - - - def _serialise_parameters(self) -> dict[str, Any]: - return {} - - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}1 [Mul.Id.]" - - def __eq__(self, other): - if isinstance(other, MultiplicativeIdentity): - return True - elif isinstance(other, Constant): - if other.value == 1: - return True - - return False - - -class Constant(ConstantBase): - - serialisation_name = "constant" - def __init__(self, value): - self.value = value - - def summary(self, indent_amount: int = 0, indent: str=" "): - pass - - def evaluate(self, variables: dict[int, T]) -> T: - return self.value - - def _derivative(self, hash_value: int): - return AdditiveIdentity() - - def _clean(self): - - if self.value == 0: - return AdditiveIdentity() - - elif self.value == 1: - return MultiplicativeIdentity() - - else: - return self - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - value = parameters["value"] - return Constant(value) - - - def _serialise_parameters(self) -> dict[str, Any]: - return {"value": self.value} - - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}{self.value}" - - def __eq__(self, other): - if isinstance(other, AdditiveIdentity): - return self.value == 0 - - elif isinstance(other, MultiplicativeIdentity): - return self.value == 1 - - elif isinstance(other, Constant): - if other.value == self.value: - return True - - return False - - -class Variable(Operation): - - serialisation_name = "variable" - def __init__(self, name_or_hash_value: int | str | tuple[int, str]): - self.hash_value, self.name = hash_and_name(name_or_hash_value) - - def evaluate(self, variables: dict[int, T]) -> T: - try: - return variables[self.hash_value] - except KeyError: - raise ValueError(f"Variable dictionary didn't have an entry for {self.name} (hash={self.hash_value})") - - def _derivative(self, hash_value: int) -> Operation: - if hash_value == self.hash_value: - return MultiplicativeIdentity() - else: - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - hash_value = parameters["hash_value"] - name = parameters["name"] - - return Variable((hash_value, name)) - - def _serialise_parameters(self) -> dict[str, Any]: - return {"hash_value": self.hash_value, - "name": self.name} - - def summary(self, indent_amount: int = 0, indent: str=" "): - return f"{indent_amount*indent}{self.name}" - - def __eq__(self, other): - if isinstance(other, Variable): - return self.hash_value == other.hash_value - - return False - -class UnaryOperation(Operation): - - def __init__(self, a: Operation): - self.a = a - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": self.a._serialise_json()} - - def _summary_components(self) -> list["Operation"]: - return [self.a] - - - - -class Neg(UnaryOperation): - - serialisation_name = "neg" - def evaluate(self, variables: dict[int, T]) -> T: - return -self.a.evaluate(variables) - - def _derivative(self, hash_value: int): - return Neg(self.a._derivative(hash_value)) - - def _clean(self): - - clean_a = self.a._clean() - - if isinstance(clean_a, Neg): - # Removes double negations - return clean_a.a - - elif isinstance(clean_a, Constant): - return Constant(-clean_a.value)._clean() - - else: - return Neg(clean_a) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Neg(Operation.deserialise_json(parameters["a"])) - - - def _summary_open(self): - return "Neg" - - def __eq__(self, other): - if isinstance(other, Neg): - return other.a == self.a - - -class Inv(UnaryOperation): - - serialisation_name = "reciprocal" - - def evaluate(self, variables: dict[int, T]) -> T: - return 1/self.a.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Neg(Div(self.a._derivative(hash_value), Mul(self.a, self.a))) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, Inv): - # Removes double negations - return clean_a.a - - elif isinstance(clean_a, Neg): - # cannonicalise 1/-a to -(1/a) - # over multiple iterations this should have the effect of ordering and gathering Neg and Inv - return Neg(Inv(clean_a.a)) - - elif isinstance(clean_a, Constant): - return Constant(1/clean_a.value)._clean() - - else: - return Inv(clean_a) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Inv(Operation.deserialise_json(parameters["a"])) - - def _summary_open(self): - return "Inv" - - - def __eq__(self, other): - if isinstance(other, Inv): - return other.a == self.a - -class BinaryOperation(Operation): - def __init__(self, a: Operation, b: Operation): - self.a = a - self.b = b - - def _clean(self): - return self._clean_ab(self.a._clean(), self.b._clean()) - - def _clean_ab(self, a, b): - raise NotImplementedError("_clean_ab not implemented") - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": self.a._serialise_json(), - "b": self.b._serialise_json()} - - @staticmethod - def _deserialise_ab(parameters) -> tuple[Operation, Operation]: - return (Operation.deserialise_json(parameters["a"]), - Operation.deserialise_json(parameters["b"])) - - - def _summary_components(self) -> list["Operation"]: - return [self.a, self.b] - - def _self_cls(self) -> type: - """ Own class""" - def __eq__(self, other): - if isinstance(other, self._self_cls()): - return other.a == self.a and self.b == other.b - -class Add(BinaryOperation): - - serialisation_name = "add" - - def _self_cls(self) -> type: - return Add - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) + self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add(self.a._derivative(hash_value), self.b._derivative(hash_value)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity): - # Convert 0 + b to b - return b - - elif isinstance(b, AdditiveIdentity): - # Convert a + 0 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"+"b" to "a+b" - return Constant(a.evaluate({}) + b.evaluate({}))._clean() - - elif isinstance(a, Neg): - if isinstance(b, Neg): - # Convert (-a)+(-b) to -(a+b) - return Neg(Add(a.a, b.a)) - else: - # Convert (-a) + b to b-a - return Sub(b, a.a) - - elif isinstance(b, Neg): - # Convert a+(-b) to a-b - return Sub(a, b.a) - - elif a == b: - return Mul(Constant(2), a) - - else: - return Add(a, b) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Add(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Add" - -class Sub(BinaryOperation): - - serialisation_name = "sub" - - - def _self_cls(self) -> type: - return Sub - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) - self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Sub(self.a._derivative(hash_value), self.b._derivative(hash_value)) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity): - # Convert 0 - b to -b - return Neg(b) - - elif isinstance(b, AdditiveIdentity): - # Convert a - 0 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant pair "a" - "b" to "a-b" - return Constant(a.evaluate({}) - b.evaluate({}))._clean() - - elif isinstance(a, Neg): - if isinstance(b, Neg): - # Convert (-a)-(-b) to b-a - return Sub(b.a, a.a) - else: - # Convert (-a)-b to -(a+b) - return Neg(Add(a.a, b)) - - elif isinstance(b, Neg): - # Convert a-(-b) to a+b - return Add(a, b.a) - - elif a == b: - return AdditiveIdentity() - - else: - return Sub(a, b) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Sub(*BinaryOperation._deserialise_ab(parameters)) - - - def _summary_open(self): - return "Sub" - -class Mul(BinaryOperation): - - serialisation_name = "mul" - - - def _self_cls(self) -> type: - return Mul - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) * self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add(Mul(self.a, self.b._derivative(hash_value)), Mul(self.a._derivative(hash_value), self.b)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): - # Convert 0*b or a*0 to 0 - return AdditiveIdentity() - - elif isinstance(a, MultiplicativeIdentity): - # Convert 1*b to b - return b - - elif isinstance(b, MultiplicativeIdentity): - # Convert a*1 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"*"b" to "a*b" - return Constant(a.evaluate({}) * b.evaluate({}))._clean() - - elif isinstance(a, Inv) and isinstance(b, Inv): - return Inv(Mul(a.a, b.a)) - - elif isinstance(a, Inv) and not isinstance(b, Inv): - return Div(b, a.a) - - elif not isinstance(a, Inv) and isinstance(b, Inv): - return Div(a, b.a) - - elif isinstance(a, Neg): - return Neg(Mul(a.a, b)) - - elif isinstance(b, Neg): - return Neg(Mul(a, b.a)) - - elif a == b: - return Pow(a, 2) - - elif isinstance(a, Pow) and a.a == b: - return Pow(b, a.power + 1) - - elif isinstance(b, Pow) and b.a == a: - return Pow(a, b.power + 1) - - elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: - return Pow(a.a, a.power + b.power) - - else: - return Mul(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Mul(*BinaryOperation._deserialise_ab(parameters)) - - - def _summary_open(self): - return "Mul" - -class Div(BinaryOperation): - - serialisation_name = "div" - - - def _self_cls(self) -> type: - return Div - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) / self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Sub(Div(self.a.derivative(hash_value), self.b), - Div(Mul(self.a, self.b.derivative(hash_value)), Mul(self.b, self.b))) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity): - # Convert 0/b to 0 - return AdditiveIdentity() - - elif isinstance(a, MultiplicativeIdentity): - # Convert 1/b to inverse of b - return Inv(b) - - elif isinstance(b, MultiplicativeIdentity): - # Convert a/1 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constants "a"/"b" to "a/b" - return Constant(self.a.evaluate({}) / self.b.evaluate({}))._clean() - - - elif isinstance(a, Inv) and isinstance(b, Inv): - return Div(b.a, a.a) - - elif isinstance(a, Inv) and not isinstance(b, Inv): - return Inv(Mul(a.a, b)) - - elif not isinstance(a, Inv) and isinstance(b, Inv): - return Mul(a, b.a) - - elif a == b: - return MultiplicativeIdentity() - - elif isinstance(a, Pow) and a.a == b: - return Pow(b, a.power - 1) - - elif isinstance(b, Pow) and b.a == a: - return Pow(a, 1 - b.power) - - elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: - return Pow(a.a, a.power - b.power) - - else: - return Div(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Div(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Div" - -class Pow(Operation): - - serialisation_name = "pow" - - def __init__(self, a: Operation, power: float): - self.a = a - self.power = power - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) ** self.power - - def _derivative(self, hash_value: int) -> Operation: - if self.power == 0: - return AdditiveIdentity() - - elif self.power == 1: - return self.a._derivative(hash_value) - - else: - return Mul(Constant(self.power), Mul(Pow(self.a, self.power-1), self.a._derivative(hash_value))) - - def _clean(self) -> Operation: - a = self.a._clean() - - if self.power == 1: - return a - - elif self.power == 0: - return MultiplicativeIdentity() - - elif self.power == -1: - return Inv(a) - - else: - return Pow(a, self.power) - - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": Operation._serialise_json(self.a), - "power": self.power} - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Pow(Operation.deserialise_json(parameters["a"]), parameters["power"]) - - def summary(self, indent_amount: int=0, indent=" "): - return (f"{indent_amount*indent}Pow\n" + - self.a.summary(indent_amount+1, indent) + "\n" + - f"{(indent_amount+1)*indent}{self.power}\n" + - f"{indent_amount*indent})") - - def __eq__(self, other): - if isinstance(other, Pow): - return self.a == other.a and self.power == other.power - - - -# -# Matrix operations -# - -class Transpose(UnaryOperation): - """ Transpose operation - as per numpy""" - - serialisation_name = "transpose" - - def evaluate(self, variables: dict[int, T]) -> T: - return np.transpose(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Transpose(self.a.derivative(hash_value)) # TODO: Check! - - def _clean(self): - clean_a = self.a._clean() - return Transpose(clean_a) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Transpose(Operation.deserialise_json(parameters["a"])) - - def _summary_open(self): - return "Transpose" - - def __eq__(self, other): - if isinstance(other, Transpose): - return other.a == self.a - - -class Dot(BinaryOperation): - """ Dot product - backed by numpy's dot method""" - - serialisation_name = "dot" - - def _self_cls(self) -> type: - return Dot - - def evaluate(self, variables: dict[int, T]) -> T: - return np.dot(self.a.evaluate(variables) + self.b.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Add( - Dot(self.a, - self.b._derivative(hash_value)), - Dot(self.a._derivative(hash_value), - self.b)) - - def _clean_ab(self, a, b): - return Dot(a, b) # Do nothing for now - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Dot(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Dot" - - -# TODO: Add to base operation class, and to quantities -class MatMul(BinaryOperation): - """ Matrix multiplication, using __matmul__ dunder""" - - serialisation_name = "matmul" - - def _self_cls(self) -> type: - return MatMul - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) @ self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add( - MatMul(self.a, - self.b._derivative(hash_value)), - MatMul(self.a._derivative(hash_value), - self.b)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): - # Convert 0*b or a*0 to 0 - return AdditiveIdentity() - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"@"b" to "a@b" - return Constant(a.evaluate({}) @ b.evaluate({}))._clean() - - elif isinstance(a, Neg): - return Neg(Mul(a.a, b)) - - elif isinstance(b, Neg): - return Neg(Mul(a, b.a)) - - return MatMul(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return MatMul(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "MatMul" - - - -_serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, - Variable, - Neg, Inv, - Add, Sub, Mul, Div, Pow, - Transpose, Dot, MatMul] - -_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} \ No newline at end of file diff --git a/sasdata/quantities/operations_examples.py b/sasdata/quantities/operations_examples.py index 4509a86ac..e2e25666f 100644 --- a/sasdata/quantities/operations_examples.py +++ b/sasdata/quantities/operations_examples.py @@ -1,4 +1,4 @@ -from sasdata.quantities.operations import Variable, Mul +from sasdata.quantities.quantity import Variable, Mul x = Variable("x") y = Variable("y") diff --git a/sasdata/quantities/operations_test.py b/sasdata/quantities/operations_test.py index 0899eee7f..6767e32a0 100644 --- a/sasdata/quantities/operations_test.py +++ b/sasdata/quantities/operations_test.py @@ -1,6 +1,6 @@ import pytest -from sasdata.quantities.operations import Operation, \ +from sasdata.quantities.quantity import Operation, \ Neg, Inv, \ Add, Sub, Mul, Div, Pow, \ Variable, Constant, AdditiveIdentity, MultiplicativeIdentity diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index ffb6fe474..4738a9d9c 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -4,13 +4,866 @@ import numpy as np from numpy._typing import ArrayLike -from sasdata.quantities.operations import Operation, Variable -from quantities import operations, units +from sasdata.quantities import units from sasdata.quantities.units import Unit, NamedUnit import hashlib +from typing import Any, TypeVar, Union +import numpy as np + +import json + +T = TypeVar("T") + + + + + +################### Quantity based operations, need to be here to avoid cyclic dependencies ##################### + + +def transpose(a: Union["Quantity[ArrayLike]", ArrayLike]): + if isinstance(a, Quantity): + return + + +def tensordot(a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, b: Union["Quantity[ArrayLike]", ArrayLike], a_index: int, b_index: int): + pass + + +################### Operation Definitions ####################################### + +def hash_and_name(hash_or_name: int | str): + """ Infer the name of a variable from a hash, or the hash from the name + + Note: hash_and_name(hash_and_name(number)[1]) is not the identity + however: hash_and_name(hash_and_name(number)) is + """ + + if isinstance(hash_or_name, str): + hash_value = hash(hash_or_name) + name = hash_or_name + + return hash_value, name + + elif isinstance(hash_or_name, int): + hash_value = hash_or_name + name = f"#{hash_or_name}" + + return hash_value, name + + elif isinstance(hash_or_name, tuple): + return hash_or_name + + else: + raise TypeError("Variable name_or_hash_value must be either str or int") + + +class Operation: + + serialisation_name = "unknown" + def summary(self, indent_amount: int = 0, indent: str=" "): + """ Summary of the operation tree""" + + s = f"{indent_amount*indent}{self._summary_open()}(\n" + + for chunk in self._summary_components(): + s += chunk.summary(indent_amount+1, indent) + "\n" + + s += f"{indent_amount*indent})" + + return s + def _summary_open(self): + """ First line of summary """ + + def _summary_components(self) -> list["Operation"]: + return [] + def evaluate(self, variables: dict[int, T]) -> T: + + """ Evaluate this operation """ + + def _derivative(self, hash_value: int) -> "Operation": + """ Get the derivative of this operation """ + + def _clean(self): + """ Clean up this operation - i.e. remove silly things like 1*x """ + return self + + def derivative(self, variable: Union[str, int, "Variable"], simplify=True): + if isinstance(variable, Variable): + hash_value = variable.hash_value + else: + hash_value, _ = hash_and_name(variable) + + derivative = self._derivative(hash_value) + + if not simplify: + return derivative + + derivative_string = derivative.serialise() + + # print("---------------") + # print("Base") + # print("---------------") + # print(derivative.summary()) + + # Inefficient way of doing repeated simplification, but it will work + for i in range(100): # set max iterations + + derivative = derivative._clean() + # + # print("-------------------") + # print("Iteration", i+1) + # print("-------------------") + # print(derivative.summary()) + # print("-------------------") + + new_derivative_string = derivative.serialise() + + if derivative_string == new_derivative_string: + break + + derivative_string = new_derivative_string + + return derivative + + @staticmethod + def deserialise(data: str) -> "Operation": + json_data = json.loads(data) + return Operation.deserialise_json(json_data) + + @staticmethod + def deserialise_json(json_data: dict) -> "Operation": + + operation = json_data["operation"] + parameters = json_data["parameters"] + cls = _serialisation_lookup[operation] + + try: + return cls._deserialise(parameters) + + except NotImplementedError: + raise NotImplementedError(f"No method to deserialise {operation} with {parameters} (cls={cls})") + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + raise NotImplementedError(f"Deserialise not implemented for this class") + + def serialise(self) -> str: + return json.dumps(self._serialise_json()) + + def _serialise_json(self) -> dict[str, Any]: + return {"operation": self.serialisation_name, + "parameters": self._serialise_parameters()} + + def _serialise_parameters(self) -> dict[str, Any]: + raise NotImplementedError("_serialise_parameters not implemented") + + def __eq__(self, other: "Operation"): + return NotImplemented + +class ConstantBase(Operation): + pass + +class AdditiveIdentity(ConstantBase): + + serialisation_name = "zero" + def evaluate(self, variables: dict[int, T]) -> T: + return 0 + + def _derivative(self, hash_value: int) -> Operation: + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return AdditiveIdentity() + + def _serialise_parameters(self) -> dict[str, Any]: + return {} + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}0 [Add.Id.]" + + def __eq__(self, other): + if isinstance(other, AdditiveIdentity): + return True + elif isinstance(other, Constant): + if other.value == 0: + return True + + return False + + + +class MultiplicativeIdentity(ConstantBase): + + serialisation_name = "one" + + def evaluate(self, variables: dict[int, T]) -> T: + return 1 + + def _derivative(self, hash_value: int): + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return MultiplicativeIdentity() + + + def _serialise_parameters(self) -> dict[str, Any]: + return {} + + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}1 [Mul.Id.]" + + def __eq__(self, other): + if isinstance(other, MultiplicativeIdentity): + return True + elif isinstance(other, Constant): + if other.value == 1: + return True + + return False + + +class Constant(ConstantBase): + + serialisation_name = "constant" + def __init__(self, value): + self.value = value + + def summary(self, indent_amount: int = 0, indent: str=" "): + pass + + def evaluate(self, variables: dict[int, T]) -> T: + return self.value + + def _derivative(self, hash_value: int): + return AdditiveIdentity() + + def _clean(self): + + if self.value == 0: + return AdditiveIdentity() + + elif self.value == 1: + return MultiplicativeIdentity() + + else: + return self + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + value = parameters["value"] + return Constant(value) + + + def _serialise_parameters(self) -> dict[str, Any]: + return {"value": self.value} + + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}{self.value}" + + def __eq__(self, other): + if isinstance(other, AdditiveIdentity): + return self.value == 0 + + elif isinstance(other, MultiplicativeIdentity): + return self.value == 1 + + elif isinstance(other, Constant): + if other.value == self.value: + return True + + return False + + +class Variable(Operation): + + serialisation_name = "variable" + def __init__(self, name_or_hash_value: int | str | tuple[int, str]): + self.hash_value, self.name = hash_and_name(name_or_hash_value) + + def evaluate(self, variables: dict[int, T]) -> T: + try: + return variables[self.hash_value] + except KeyError: + raise ValueError(f"Variable dictionary didn't have an entry for {self.name} (hash={self.hash_value})") + + def _derivative(self, hash_value: int) -> Operation: + if hash_value == self.hash_value: + return MultiplicativeIdentity() + else: + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + hash_value = parameters["hash_value"] + name = parameters["name"] + + return Variable((hash_value, name)) + + def _serialise_parameters(self) -> dict[str, Any]: + return {"hash_value": self.hash_value, + "name": self.name} + + def summary(self, indent_amount: int = 0, indent: str=" "): + return f"{indent_amount*indent}{self.name}" + + def __eq__(self, other): + if isinstance(other, Variable): + return self.hash_value == other.hash_value + + return False + +class UnaryOperation(Operation): + + def __init__(self, a: Operation): + self.a = a + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": self.a._serialise_json()} + + def _summary_components(self) -> list["Operation"]: + return [self.a] + + + + +class Neg(UnaryOperation): + + serialisation_name = "neg" + def evaluate(self, variables: dict[int, T]) -> T: + return -self.a.evaluate(variables) + + def _derivative(self, hash_value: int): + return Neg(self.a._derivative(hash_value)) + + def _clean(self): + + clean_a = self.a._clean() + + if isinstance(clean_a, Neg): + # Removes double negations + return clean_a.a + + elif isinstance(clean_a, Constant): + return Constant(-clean_a.value)._clean() + + else: + return Neg(clean_a) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Neg(Operation.deserialise_json(parameters["a"])) + + + def _summary_open(self): + return "Neg" + + def __eq__(self, other): + if isinstance(other, Neg): + return other.a == self.a + + +class Inv(UnaryOperation): + + serialisation_name = "reciprocal" + + def evaluate(self, variables: dict[int, T]) -> T: + return 1/self.a.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Neg(Div(self.a._derivative(hash_value), Mul(self.a, self.a))) + + def _clean(self): + clean_a = self.a._clean() + + if isinstance(clean_a, Inv): + # Removes double negations + return clean_a.a + + elif isinstance(clean_a, Neg): + # cannonicalise 1/-a to -(1/a) + # over multiple iterations this should have the effect of ordering and gathering Neg and Inv + return Neg(Inv(clean_a.a)) + + elif isinstance(clean_a, Constant): + return Constant(1/clean_a.value)._clean() + + else: + return Inv(clean_a) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Inv(Operation.deserialise_json(parameters["a"])) + + def _summary_open(self): + return "Inv" + + + def __eq__(self, other): + if isinstance(other, Inv): + return other.a == self.a + +class BinaryOperation(Operation): + def __init__(self, a: Operation, b: Operation): + self.a = a + self.b = b + + def _clean(self): + return self._clean_ab(self.a._clean(), self.b._clean()) + + def _clean_ab(self, a, b): + raise NotImplementedError("_clean_ab not implemented") + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": self.a._serialise_json(), + "b": self.b._serialise_json()} + + @staticmethod + def _deserialise_ab(parameters) -> tuple[Operation, Operation]: + return (Operation.deserialise_json(parameters["a"]), + Operation.deserialise_json(parameters["b"])) + + + def _summary_components(self) -> list["Operation"]: + return [self.a, self.b] + + def _self_cls(self) -> type: + """ Own class""" + def __eq__(self, other): + if isinstance(other, self._self_cls()): + return other.a == self.a and self.b == other.b + +class Add(BinaryOperation): + + serialisation_name = "add" + + def _self_cls(self) -> type: + return Add + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) + self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add(self.a._derivative(hash_value), self.b._derivative(hash_value)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity): + # Convert 0 + b to b + return b + + elif isinstance(b, AdditiveIdentity): + # Convert a + 0 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"+"b" to "a+b" + return Constant(a.evaluate({}) + b.evaluate({}))._clean() + + elif isinstance(a, Neg): + if isinstance(b, Neg): + # Convert (-a)+(-b) to -(a+b) + return Neg(Add(a.a, b.a)) + else: + # Convert (-a) + b to b-a + return Sub(b, a.a) + + elif isinstance(b, Neg): + # Convert a+(-b) to a-b + return Sub(a, b.a) + + elif a == b: + return Mul(Constant(2), a) + + else: + return Add(a, b) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Add(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Add" + +class Sub(BinaryOperation): + + serialisation_name = "sub" + + + def _self_cls(self) -> type: + return Sub + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) - self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Sub(self.a._derivative(hash_value), self.b._derivative(hash_value)) + + def _clean_ab(self, a, b): + if isinstance(a, AdditiveIdentity): + # Convert 0 - b to -b + return Neg(b) + + elif isinstance(b, AdditiveIdentity): + # Convert a - 0 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant pair "a" - "b" to "a-b" + return Constant(a.evaluate({}) - b.evaluate({}))._clean() + + elif isinstance(a, Neg): + if isinstance(b, Neg): + # Convert (-a)-(-b) to b-a + return Sub(b.a, a.a) + else: + # Convert (-a)-b to -(a+b) + return Neg(Add(a.a, b)) + + elif isinstance(b, Neg): + # Convert a-(-b) to a+b + return Add(a, b.a) + + elif a == b: + return AdditiveIdentity() + + else: + return Sub(a, b) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Sub(*BinaryOperation._deserialise_ab(parameters)) + + + def _summary_open(self): + return "Sub" + +class Mul(BinaryOperation): + + serialisation_name = "mul" + + + def _self_cls(self) -> type: + return Mul + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) * self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add(Mul(self.a, self.b._derivative(hash_value)), Mul(self.a._derivative(hash_value), self.b)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): + # Convert 0*b or a*0 to 0 + return AdditiveIdentity() + + elif isinstance(a, MultiplicativeIdentity): + # Convert 1*b to b + return b + + elif isinstance(b, MultiplicativeIdentity): + # Convert a*1 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"*"b" to "a*b" + return Constant(a.evaluate({}) * b.evaluate({}))._clean() + + elif isinstance(a, Inv) and isinstance(b, Inv): + return Inv(Mul(a.a, b.a)) + + elif isinstance(a, Inv) and not isinstance(b, Inv): + return Div(b, a.a) + + elif not isinstance(a, Inv) and isinstance(b, Inv): + return Div(a, b.a) + + elif isinstance(a, Neg): + return Neg(Mul(a.a, b)) + + elif isinstance(b, Neg): + return Neg(Mul(a, b.a)) + + elif a == b: + return Pow(a, 2) + + elif isinstance(a, Pow) and a.a == b: + return Pow(b, a.power + 1) + + elif isinstance(b, Pow) and b.a == a: + return Pow(a, b.power + 1) + + elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: + return Pow(a.a, a.power + b.power) + + else: + return Mul(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Mul(*BinaryOperation._deserialise_ab(parameters)) + + + def _summary_open(self): + return "Mul" + +class Div(BinaryOperation): + + serialisation_name = "div" + + + def _self_cls(self) -> type: + return Div + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) / self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Sub(Div(self.a.derivative(hash_value), self.b), + Div(Mul(self.a, self.b.derivative(hash_value)), Mul(self.b, self.b))) + + def _clean_ab(self, a, b): + if isinstance(a, AdditiveIdentity): + # Convert 0/b to 0 + return AdditiveIdentity() + + elif isinstance(a, MultiplicativeIdentity): + # Convert 1/b to inverse of b + return Inv(b) + + elif isinstance(b, MultiplicativeIdentity): + # Convert a/1 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constants "a"/"b" to "a/b" + return Constant(self.a.evaluate({}) / self.b.evaluate({}))._clean() + + + elif isinstance(a, Inv) and isinstance(b, Inv): + return Div(b.a, a.a) + + elif isinstance(a, Inv) and not isinstance(b, Inv): + return Inv(Mul(a.a, b)) + + elif not isinstance(a, Inv) and isinstance(b, Inv): + return Mul(a, b.a) + + elif a == b: + return MultiplicativeIdentity() + + elif isinstance(a, Pow) and a.a == b: + return Pow(b, a.power - 1) + + elif isinstance(b, Pow) and b.a == a: + return Pow(a, 1 - b.power) + + elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: + return Pow(a.a, a.power - b.power) + + else: + return Div(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Div(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Div" + +class Pow(Operation): + + serialisation_name = "pow" + + def __init__(self, a: Operation, power: float): + self.a = a + self.power = power + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) ** self.power + + def _derivative(self, hash_value: int) -> Operation: + if self.power == 0: + return AdditiveIdentity() + + elif self.power == 1: + return self.a._derivative(hash_value) + + else: + return Mul(Constant(self.power), Mul(Pow(self.a, self.power-1), self.a._derivative(hash_value))) + + def _clean(self) -> Operation: + a = self.a._clean() + + if self.power == 1: + return a + + elif self.power == 0: + return MultiplicativeIdentity() + + elif self.power == -1: + return Inv(a) + + else: + return Pow(a, self.power) + + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": Operation._serialise_json(self.a), + "power": self.power} + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Pow(Operation.deserialise_json(parameters["a"]), parameters["power"]) + + def summary(self, indent_amount: int=0, indent=" "): + return (f"{indent_amount*indent}Pow\n" + + self.a.summary(indent_amount+1, indent) + "\n" + + f"{(indent_amount+1)*indent}{self.power}\n" + + f"{indent_amount*indent})") + + def __eq__(self, other): + if isinstance(other, Pow): + return self.a == other.a and self.power == other.power + + + +# +# Matrix operations +# + +class Transpose(UnaryOperation): + """ Transpose operation - as per numpy""" + + serialisation_name = "transpose" + + def evaluate(self, variables: dict[int, T]) -> T: + return np.transpose(self.a.evaluate(variables)) + + def _derivative(self, hash_value: int) -> Operation: + return Transpose(self.a.derivative(hash_value)) # TODO: Check! + + def _clean(self): + clean_a = self.a._clean() + return Transpose(clean_a) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Transpose(Operation.deserialise_json(parameters["a"])) + + def _summary_open(self): + return "Transpose" + + def __eq__(self, other): + if isinstance(other, Transpose): + return other.a == self.a + + +class Dot(BinaryOperation): + """ Dot product - backed by numpy's dot method""" + + serialisation_name = "dot" + + def evaluate(self, variables: dict[int, T]) -> T: + return np.dot(self.a.evaluate(variables) + self.b.evaluate(variables)) + + def _derivative(self, hash_value: int) -> Operation: + return Add( + Dot(self.a, + self.b._derivative(hash_value)), + Dot(self.a._derivative(hash_value), + self.b)) + + def _clean_ab(self, a, b): + return Dot(a, b) # Do nothing for now + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Dot(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Dot" + + +# TODO: Add to base operation class, and to quantities +class MatMul(BinaryOperation): + """ Matrix multiplication, using __matmul__ dunder""" + + serialisation_name = "matmul" + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) @ self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add( + MatMul(self.a, + self.b._derivative(hash_value)), + MatMul(self.a._derivative(hash_value), + self.b)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): + # Convert 0*b or a*0 to 0 + return AdditiveIdentity() + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"@"b" to "a@b" + return Constant(a.evaluate({}) @ b.evaluate({}))._clean() + + elif isinstance(a, Neg): + return Neg(Mul(a.a, b)) + + elif isinstance(b, Neg): + return Neg(Mul(a, b.a)) + + return MatMul(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return MatMul(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "MatMul" + +class TensorProduct(Operation): + serialisation_name = "tensor_product" + + def __init__(self, a: Operation, b: Operation, a_index: int, b_index: int): + self.a = a + self.b = b + self.a_index = a_index + self.b_index = b_index + + def evaluate(self, variables: dict[int, T]) -> T: + return np.tensordot(self.a, self.b, axes=(self.a_index, self.b_index)) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + pass + + def _summary_open(self): + return "TensorProduct" + + +_serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, + Variable, + Neg, Inv, + Add, Sub, Mul, Div, Pow, + Transpose, Dot, MatMul] + +_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} + + class UnitError(Exception): """Errors caused by unit specification not being correct""" From c4552c443dcaf1ede78bc9eaf3f937f41037eb44 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 21 Oct 2024 14:27:53 +0100 Subject: [PATCH 150/675] Fixes from move --- sasdata/quantities/quantity.py | 42 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 4738a9d9c..67aab002b 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1068,14 +1068,14 @@ def __mul__(self: Self, other: ArrayLike | Self ) -> Self: return DerivedQuantity( self.value * other.value, self.units * other.units, - history=QuantityHistory.apply_operation(operations.Mul, self.history, other.history)) + history=QuantityHistory.apply_operation(Mul, self.history, other.history)) else: return DerivedQuantity(self.value * other, self.units, QuantityHistory( - operations.Mul( + Mul( self.history.operation_tree, - operations.Constant(other)), + Constant(other)), self.history.references)) def __rmul__(self: Self, other: ArrayLike | Self): @@ -1084,15 +1084,15 @@ def __rmul__(self: Self, other: ArrayLike | Self): other.value * self.value, other.units * self.units, history=QuantityHistory.apply_operation( - operations.Mul, + Mul, other.history, self.history)) else: return DerivedQuantity(other * self.value, self.units, QuantityHistory( - operations.Mul( - operations.Constant(other), + Mul( + Constant(other), self.history.operation_tree), self.history.references)) @@ -1103,7 +1103,7 @@ def __matmul__(self, other: ArrayLike | Self): self.value @ other.value, self.units * other.units, history=QuantityHistory.apply_operation( - operations.MatMul, + MatMul, self.history, other.history)) else: @@ -1111,9 +1111,9 @@ def __matmul__(self, other: ArrayLike | Self): self.value @ other, self.units, QuantityHistory( - operations.MatMul( + MatMul( self.history.operation_tree, - operations.Constant(other)), + Constant(other)), self.history.references)) def __rmatmul__(self, other: ArrayLike | Self): @@ -1122,15 +1122,15 @@ def __rmatmul__(self, other: ArrayLike | Self): other.value @ self.value, other.units * self.units, history=QuantityHistory.apply_operation( - operations.MatMul, + MatMul, other.history, self.history)) else: return DerivedQuantity(other @ self.value, self.units, QuantityHistory( - operations.MatMul( - operations.Constant(other), + MatMul( + Constant(other), self.history.operation_tree), self.history.references)) @@ -1141,15 +1141,15 @@ def __truediv__(self: Self, other: float | Self) -> Self: self.value / other.value, self.units / other.units, history=QuantityHistory.apply_operation( - operations.Div, + Div, self.history, other.history)) else: return DerivedQuantity(self.value / other, self.units, QuantityHistory( - operations.Div( - operations.Constant(other), + Div( + Constant(other), self.history.operation_tree), self.history.references)) @@ -1159,7 +1159,7 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: other.value / self.value, other.units / self.units, history=QuantityHistory.apply_operation( - operations.Div, + Div, other.history, self.history )) @@ -1169,8 +1169,8 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: other / self.value, self.units ** -1, QuantityHistory( - operations.Div( - operations.Constant(other), + Div( + Constant(other), self.history.operation_tree), self.history.references)) @@ -1181,7 +1181,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: self.value + (other.value * other.units.scale) / self.units.scale, self.units, QuantityHistory.apply_operation( - operations.Add, + Add, self.history, other.history)) else: @@ -1195,7 +1195,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: def __neg__(self): return DerivedQuantity(-self.value, self.units, QuantityHistory.apply_operation( - operations.Neg, + Neg, self.history )) @@ -1209,7 +1209,7 @@ def __pow__(self: Self, other: int | float): return DerivedQuantity(self.value ** other, self.units ** other, QuantityHistory( - operations.Pow( + Pow( self.history.operation_tree, other), self.history.references)) From 21255f3a2db0b7ba2c7aa6560eff4a11a4099089 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 21 Oct 2024 17:07:42 +0100 Subject: [PATCH 151/675] Tensor product implementation --- sasdata/quantities/quantity.py | 102 +++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 12 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 67aab002b..b17684b1a 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -25,12 +25,63 @@ def transpose(a: Union["Quantity[ArrayLike]", ArrayLike]): + """ Transpose an array or an array based quantity """ if isinstance(a, Quantity): - return + return DerivedQuantity(value=np.transpose(a.value), + units=a.units, + history=QuantityHistory.apply_operation(Transpose, a.history)) + else: + return np.transpose(a) + +def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike]", ArrayLike]): + """ Dot product of two arrays or two array based quantities """ + a_is_quantity = isinstance(a, Quantity) + b_is_quantity = isinstance(b, Quantity) + + if a_is_quantity or b_is_quantity: + + # If its only one of them that is a quantity, convert the other one + + if not a_is_quantity: + a = Quantity(a, units.dimensionless) + + if not b_is_quantity: + b = Quantity(b, units.dimensionless) + + return DerivedQuantity( + value=np.dot(a.value, b.value), + units=a.units * b.units, + history=QuantityHistory.apply_operation(Dot, a.history, b.history)) + else: + return np.dot(a, b) def tensordot(a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, b: Union["Quantity[ArrayLike]", ArrayLike], a_index: int, b_index: int): - pass + a_is_quantity = isinstance(a, Quantity) + b_is_quantity = isinstance(b, Quantity) + + if a_is_quantity or b_is_quantity: + + # If its only one of them that is a quantity, convert the other one + + if not a_is_quantity: + a = Quantity(a, units.dimensionless) + + if not b_is_quantity: + b = Quantity(b, units.dimensionless) + + return DerivedQuantity( + value=np.tensordot(a.value, b.value, axes=(a_index, b_index)), + units=a.units * b.units, + history=QuantityHistory.apply_operation( + TensorDot, + a.history, + b.history, + a_index=a_index, + b_index=b_index)) + + else: + return np.tensordot(a, b, axes=(a_index, b_index)) ################### Operation Definitions ####################################### @@ -773,7 +824,7 @@ class Dot(BinaryOperation): serialisation_name = "dot" def evaluate(self, variables: dict[int, T]) -> T: - return np.dot(self.a.evaluate(variables) + self.b.evaluate(variables)) + return dot(self.a.evaluate(variables), self.b.evaluate(variables)) def _derivative(self, hash_value: int) -> Operation: return Add( @@ -785,6 +836,7 @@ def _derivative(self, hash_value: int) -> Operation: def _clean_ab(self, a, b): return Dot(a, b) # Do nothing for now + @staticmethod def _deserialise(parameters: dict) -> "Operation": return Dot(*BinaryOperation._deserialise_ab(parameters)) @@ -835,7 +887,7 @@ def _deserialise(parameters: dict) -> "Operation": def _summary_open(self): return "MatMul" -class TensorProduct(Operation): +class TensorDot(Operation): serialisation_name = "tensor_product" def __init__(self, a: Operation, b: Operation, a_index: int, b_index: int): @@ -845,21 +897,32 @@ def __init__(self, a: Operation, b: Operation, a_index: int, b_index: int): self.b_index = b_index def evaluate(self, variables: dict[int, T]) -> T: - return np.tensordot(self.a, self.b, axes=(self.a_index, self.b_index)) + return tensordot(self.a, self.b, self.a_index, self.b_index) + + + def _serialise_parameters(self) -> dict[str, Any]: + return { + "a": self.a._serialise_json(), + "b": self.b._serialise_json(), + "a_index": self.a_index, + "b_index": self.b_index } @staticmethod def _deserialise(parameters: dict) -> "Operation": - pass + return TensorDot(a = Operation.deserialise_json(parameters["a"]), + b = Operation.deserialise_json(parameters["b"]), + a_index=int(parameters["a_index"]), + b_index=int(parameters["b_index"])) def _summary_open(self): return "TensorProduct" _serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, - Variable, - Neg, Inv, - Add, Sub, Mul, Div, Pow, - Transpose, Dot, MatMul] + Variable, + Neg, Inv, + Add, Sub, Mul, Div, Pow, + Transpose, Dot, MatMul, TensorDot] _serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} @@ -879,10 +942,25 @@ def hash_data_via_numpy(*data: ArrayLike): return int(md5_hash.hexdigest(), 16) + +##################################### +# # +# # +# # +# Quantities begin here # +# # +# # +# # +##################################### + + + QuantityType = TypeVar("QuantityType") class QuantityHistory: + """ Class that holds the information for keeping track of operations done on quantities """ + def __init__(self, operation_tree: Operation, references: dict[int, "Quantity"]): self.operation_tree = operation_tree self.references = references @@ -936,7 +1014,7 @@ def variable(quantity: "Quantity"): return QuantityHistory(Variable(quantity.hash_value), {quantity.hash_value: quantity}) @staticmethod - def apply_operation(operation: type[Operation], *histories: "QuantityHistory") -> "QuantityHistory": + def apply_operation(operation: type[Operation], *histories: "QuantityHistory", **extra_parameters) -> "QuantityHistory": """ Apply an operation to the history This is slightly unsafe as it is possible to attempt to apply an n-ary operation to a number of trees other @@ -953,7 +1031,7 @@ def apply_operation(operation: type[Operation], *histories: "QuantityHistory") - references.update(history.references) return QuantityHistory( - operation(*[history.operation_tree for history in histories]), + operation(*[history.operation_tree for history in histories], **extra_parameters), references) def has_variance(self): From 7e0ca556f249f9b90ce8c0e9f6ecad481f857fa1 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 21 Oct 2024 19:39:47 +0100 Subject: [PATCH 152/675] Extended transpose, and tensor tests --- sasdata/quantities/math_operations_test.py | 152 +++++++++++++++++++++ sasdata/quantities/quantity.py | 80 ++++++++--- 2 files changed, 211 insertions(+), 21 deletions(-) create mode 100644 sasdata/quantities/math_operations_test.py diff --git a/sasdata/quantities/math_operations_test.py b/sasdata/quantities/math_operations_test.py new file mode 100644 index 000000000..5bda5a2cd --- /dev/null +++ b/sasdata/quantities/math_operations_test.py @@ -0,0 +1,152 @@ +""" Tests for math operations """ + +import pytest + +import numpy as np +from sasdata.quantities.quantity import NamedQuantity, tensordot, transpose +from sasdata.quantities import units + +order_list = [ + [0, 1, 2, 3], + [0, 2, 1], + [1, 0], + [0, 1], + [2, 0, 1], + [3, 1, 2, 0] +] + +@pytest.mark.parametrize("order", order_list) +def test_transpose_raw(order: list[int]): + """ Check that the transpose operation changes the order of indices correctly - uses sizes as way of tracking""" + + input_shape = tuple([i+1 for i in range(len(order))]) + expected_shape = tuple([i+1 for i in order]) + + input_mat = np.zeros(input_shape) + + measured_mat = transpose(input_mat, axes=tuple(order)) + + assert measured_mat.shape == expected_shape + + +@pytest.mark.parametrize("order", order_list) +def test_transpose_raw(order: list[int]): + """ Check that the transpose operation changes the order of indices correctly - uses sizes as way of tracking""" + input_shape = tuple([i + 1 for i in range(len(order))]) + expected_shape = tuple([i + 1 for i in order]) + + input_mat = NamedQuantity("testmat", np.zeros(input_shape), units=units.none) + + measured_mat = transpose(input_mat, axes=tuple(order)) + + assert measured_mat.value.shape == expected_shape + + +rng_seed = 1979 +tensor_product_with_identity_sizes = (4,6,5) + +@pytest.mark.parametrize("index, size", [tup for tup in enumerate(tensor_product_with_identity_sizes)]) +def test_tensor_product_with_identity_quantities(index, size): + """ Check the correctness of the tensor product by multiplying by the identity (quantity, quantity)""" + np.random.seed(rng_seed) + + x = NamedQuantity("x", np.random.rand(*tensor_product_with_identity_sizes), units=units.meters) + y = NamedQuantity("y", np.eye(size), units.seconds) + + z = tensordot(x, y, index, 0) + + # Check units + assert z.units == units.meters * units.seconds + + # Expected sizes - last index gets moved to end + output_order = [i for i in (0, 1, 2) if i != index] + [index] + output_sizes = [tensor_product_with_identity_sizes[i] for i in output_order] + + assert z.value.shape == tuple(output_sizes) + + # Restore original order and check + reverse_order = [-1, -1, -1] + for to_index, from_index in enumerate(output_order): + reverse_order[from_index] = to_index + + z_reordered = transpose(z, axes = tuple(reverse_order)) + + assert z_reordered.value.shape == tensor_product_with_identity_sizes + + # Check values + + mat_in = x.in_si() + mat_out = transpose(z, axes=tuple(reverse_order)).in_si() + + assert np.all(np.abs(mat_in - mat_out) < 1e-10) + + +@pytest.mark.parametrize("index, size", [tup for tup in enumerate(tensor_product_with_identity_sizes)]) +def test_tensor_product_with_identity_quantity_matrix(index, size): + """ Check the correctness of the tensor product by multiplying by the identity (quantity, matrix)""" + np.random.seed(rng_seed) + + x = NamedQuantity("x", np.random.rand(*tensor_product_with_identity_sizes), units.meters) + y = np.eye(size) + + z = tensordot(x, y, index, 0) + + assert z.units == units.meters + + # Expected sizes - last index gets moved to end + output_order = [i for i in (0, 1, 2) if i != index] + [index] + output_sizes = [tensor_product_with_identity_sizes[i] for i in output_order] + + assert z.value.shape == tuple(output_sizes) + + # Restore original order and check + reverse_order = [-1, -1, -1] + for to_index, from_index in enumerate(output_order): + reverse_order[from_index] = to_index + + z_reordered = transpose(z, axes = tuple(reverse_order)) + + assert z_reordered.value.shape == tensor_product_with_identity_sizes + + # Check values + + mat_in = x.in_si() + mat_out = transpose(z, axes=tuple(reverse_order)).in_si() + + assert np.all(np.abs(mat_in - mat_out) < 1e-10) + + +@pytest.mark.parametrize("index, size", [tup for tup in enumerate(tensor_product_with_identity_sizes)]) +def test_tensor_product_with_identity_matrix_quantity(index, size): + """ Check the correctness of the tensor product by multiplying by the identity (matrix, quantity)""" + np.random.seed(rng_seed) + + x = np.random.rand(*tensor_product_with_identity_sizes) + y = NamedQuantity("y", np.eye(size), units.seconds) + + z = tensordot(x, y, index, 0) + + assert z.units == units.seconds + + + # Expected sizes - last index gets moved to end + output_order = [i for i in (0, 1, 2) if i != index] + [index] + output_sizes = [tensor_product_with_identity_sizes[i] for i in output_order] + + assert z.value.shape == tuple(output_sizes) + + # Restore original order and check + reverse_order = [-1, -1, -1] + for to_index, from_index in enumerate(output_order): + reverse_order[from_index] = to_index + + z_reordered = transpose(z, axes = tuple(reverse_order)) + + assert z_reordered.value.shape == tensor_product_with_identity_sizes + + # Check values + + mat_in = x + mat_out = transpose(z, axes=tuple(reverse_order)).in_si() + + assert np.all(np.abs(mat_in - mat_out) < 1e-10) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index b17684b1a..c87452678 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -23,15 +23,23 @@ ################### Quantity based operations, need to be here to avoid cyclic dependencies ##################### - -def transpose(a: Union["Quantity[ArrayLike]", ArrayLike]): - """ Transpose an array or an array based quantity """ +def transpose(a: Union["Quantity[ArrayLike]", ArrayLike], axes: tuple | None = None): + """ Transpose an array or an array based quantity, can also do reordering of axes""" if isinstance(a, Quantity): - return DerivedQuantity(value=np.transpose(a.value), - units=a.units, - history=QuantityHistory.apply_operation(Transpose, a.history)) + + if axes is None: + return DerivedQuantity(value=np.transpose(a.value, axes=axes), + units=a.units, + history=QuantityHistory.apply_operation(Transpose, a.history)) + + else: + return DerivedQuantity(value=np.transpose(a.value, axes=axes), + units=a.units, + history=QuantityHistory.apply_operation(Transpose, a.history, axes=axes)) + else: - return np.transpose(a) + return np.transpose(a, axes=axes) + def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike]", ArrayLike]): """ Dot product of two arrays or two array based quantities """ @@ -43,10 +51,10 @@ def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike # If its only one of them that is a quantity, convert the other one if not a_is_quantity: - a = Quantity(a, units.dimensionless) + a = Quantity(a, units.none) if not b_is_quantity: - b = Quantity(b, units.dimensionless) + b = Quantity(b, units.none) return DerivedQuantity( value=np.dot(a.value, b.value), @@ -57,6 +65,18 @@ def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike return np.dot(a, b) def tensordot(a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, b: Union["Quantity[ArrayLike]", ArrayLike], a_index: int, b_index: int): + """ Tensor dot product - equivalent to contracting two tensors, such as + + A_{i0, i1, i2, i3...} and B_{j0, j1, j2...} + + e.g. if a_index is 1 and b_index is zero, it will be the sum + + C_{i0, i2, i3 ..., j1, j2 ...} = sum_k A_{i0, k, i2, i3 ...} B_{k, j1, j2 ...} + + (I think, have to check what happens with indices TODO!) + + """ + a_is_quantity = isinstance(a, Quantity) b_is_quantity = isinstance(b, Quantity) @@ -65,10 +85,10 @@ def tensordot(a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, b: Union[" # If its only one of them that is a quantity, convert the other one if not a_is_quantity: - a = Quantity(a, units.dimensionless) + a = Quantity(a, units.none) if not b_is_quantity: - b = Quantity(b, units.dimensionless) + b = Quantity(b, units.none) return DerivedQuantity( value=np.tensordot(a.value, b.value, axes=(a_index, b_index)), @@ -791,11 +811,15 @@ def __eq__(self, other): # Matrix operations # -class Transpose(UnaryOperation): +class Transpose(Operation): """ Transpose operation - as per numpy""" serialisation_name = "transpose" + def __init__(self, a: Operation, axes: tuple[int] | None = None): + self.a = a + self.axes = axes + def evaluate(self, variables: dict[int, T]) -> T: return np.transpose(self.a.evaluate(variables)) @@ -806,9 +830,27 @@ def _clean(self): clean_a = self.a._clean() return Transpose(clean_a) + + def _serialise_parameters(self) -> dict[str, Any]: + if self.axes is None: + return { "a": self.a._serialise_json() } + else: + return { + "a": self.a._serialise_json(), + "axes": list(self.axes) + } + + @staticmethod def _deserialise(parameters: dict) -> "Operation": - return Transpose(Operation.deserialise_json(parameters["a"])) + if "axes" in parameters: + return Transpose( + a=Operation.deserialise_json(parameters["a"]), + axes=tuple(parameters["axes"])) + else: + return Transpose( + a=Operation.deserialise_json(parameters["a"])) + def _summary_open(self): return "Transpose" @@ -974,6 +1016,10 @@ def jacobian(self) -> list[Operation]: # Use the hash value to specify the variable of differentiation return [self.operation_tree.derivative(key) for key in self.reference_key_list] + def _recalculate(self): + """ Recalculate the value of this object - primary use case is for testing """ + return self.operation_tree.evaluate(self.references) + def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, int]: "Quantity"] = {}): """ Do standard error propagation to calculate the uncertainties associated with this quantity @@ -985,14 +1031,6 @@ def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, raise NotImplementedError("User specified covariances not currently implemented") jacobian = self.jacobian() - # jacobian_units = [quantity_units / self.references[key].units for key in self.reference_key_list] - # - # # Evaluate the jacobian - # # TODO: should we use quantities here, does that work automatically? - # evaluated_jacobian = [Quantity( - # value=entry.evaluate(self.si_reference_values), - # units=unit.si_equivalent()) - # for entry, unit in zip(jacobian, jacobian_units)] evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] From e0255c415ee416aadb289d7adc625a10e623e6c6 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 22 Oct 2024 19:03:30 +0100 Subject: [PATCH 153/675] Encodings for numerical values --- sasdata/quantities/numerical_encoding.py | 48 ++++--------------- sasdata/quantities/quantity.py | 6 ++- sasdata/quantities/test_numerical_encoding.py | 14 +----- 3 files changed, 14 insertions(+), 54 deletions(-) diff --git a/sasdata/quantities/numerical_encoding.py b/sasdata/quantities/numerical_encoding.py index 6e2e53265..63a888d5d 100644 --- a/sasdata/quantities/numerical_encoding.py +++ b/sasdata/quantities/numerical_encoding.py @@ -1,11 +1,12 @@ -import base64 -import struct + import numpy as np -from scipy.sparse import coo_array, coo_matrix, csc_array, csc_matrix, csr_array, csr_matrix + +import base64 +import struct -def numerical_encode(obj: int | float | np.ndarray | coo_matrix | coo_array | csr_matrix | csr_array | csc_matrix | csc_array): +def numerical_encode(obj: int | float | np.ndarray): if isinstance(obj, int): return {"type": "int", @@ -13,48 +14,21 @@ def numerical_encode(obj: int | float | np.ndarray | coo_matrix | coo_array | cs elif isinstance(obj, float): return {"type": "float", - "value": base64.b64encode(bytearray(struct.pack('d', obj))).decode("utf-8")} + "value": base64.b64encode(bytearray(struct.pack('d', obj)))} elif isinstance(obj, np.ndarray): return { "type": "numpy", - "value": base64.b64encode(obj.tobytes()).decode("utf-8"), + "value": base64.b64encode(obj.tobytes()), "dtype": obj.dtype.str, "shape": list(obj.shape) } - elif isinstance(obj, (coo_matrix, coo_array, csr_matrix, csr_array, csc_matrix, csc_array)): - - output = { - "type": obj.__class__.__name__, # not robust to name changes, but more concise - "dtype": obj.dtype.str, - "shape": list(obj.shape) - } - - if isinstance(obj, (coo_array, coo_matrix)): - - output["data"] = numerical_encode(obj.data) - output["coords"] = [numerical_encode(coord) for coord in obj.coords] - - - elif isinstance(obj, (csr_array, csr_matrix)): - pass - - - elif isinstance(obj, (csc_array, csc_matrix)): - - pass - - - return output - else: raise TypeError(f"Cannot serialise object of type: {type(obj)}") -def numerical_decode(data: dict[str, str | int | list[int]]) -> int | float | np.ndarray | coo_matrix | coo_array | csr_matrix | csr_array | csc_matrix | csc_array: - obj_type = data["type"] - - match obj_type: +def numerical_decode(data: dict[str, str | int | list[int]]) -> int | float | np.ndarray: + match data["type"]: case "int": return int(data["value"]) @@ -66,7 +40,3 @@ def numerical_decode(data: dict[str, str | int | list[int]]) -> int | float | np dtype = np.dtype(data["dtype"]) shape = tuple(data["shape"]) return np.frombuffer(value, dtype=dtype).reshape(*shape) - - case _: - raise ValueError(f"Cannot decode objects of type '{obj_type}'") - diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index c87452678..da4ef6798 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,14 +1,17 @@ +from encodings.base64_codec import base64_decode from typing import Collection, Sequence, TypeVar, Generic, Self from dataclasses import dataclass import numpy as np +from lxml.etree import SerialisationError from numpy._typing import ArrayLike from sasdata.quantities import units from sasdata.quantities.units import Unit, NamedUnit import hashlib - +import base64 +import struct from typing import Any, TypeVar, Union import numpy as np @@ -131,7 +134,6 @@ def hash_and_name(hash_or_name: int | str): else: raise TypeError("Variable name_or_hash_value must be either str or int") - class Operation: serialisation_name = "unknown" diff --git a/sasdata/quantities/test_numerical_encoding.py b/sasdata/quantities/test_numerical_encoding.py index b7fb7cfed..4b170584e 100644 --- a/sasdata/quantities/test_numerical_encoding.py +++ b/sasdata/quantities/test_numerical_encoding.py @@ -1,9 +1,7 @@ -""" Tests for the encoding and decoding of numerical data""" - import numpy as np import pytest -from sasdata.quantities.numerical_encoding import numerical_decode, numerical_encode +from sasdata.quantities.numerical_encoding import numerical_encode, numerical_decode @pytest.mark.parametrize("value", [-100.0, -10.0, -1.0, 0.0, 0.5, 1.0, 10.0, 100.0, 1e100]) @@ -54,13 +52,3 @@ def test_numpy_dtypes_encode_decode(dtype): decoded = numerical_decode(encoded) assert decoded.dtype == test_matrix.dtype - -@pytest.mark.parametrize("dtype", [int, float, complex]) -@pytest.mark.parametrize("shape, n, m", [ - ((8, 8), (1,3,5),(2,5,7)), - ((6, 8), (1,0,5),(0,5,0)), - ((6, 1), (1, 0, 5), (0, 0, 0)), -]) -def test_coo_matrix_encode_decode(shape, n, m, dtype): - - values = np.arange(10) From 70882c55d5520c309980b00cc69ea0b05f64085b Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 22 Oct 2024 19:08:50 +0100 Subject: [PATCH 154/675] Tidying up --- sasdata/quantities/quantity.py | 18 +++++++----------- sasdata/quantities/test_numerical_encoding.py | 2 ++ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index da4ef6798..2c7a7b10e 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,20 +1,17 @@ -from encodings.base64_codec import base64_decode -from typing import Collection, Sequence, TypeVar, Generic, Self -from dataclasses import dataclass + + +from typing import Self import numpy as np -from lxml.etree import SerialisationError from numpy._typing import ArrayLike from sasdata.quantities import units +from sasdata.quantities.numerical_encoding import numerical_decode, numerical_encode from sasdata.quantities.units import Unit, NamedUnit import hashlib -import base64 -import struct from typing import Any, TypeVar, Union -import numpy as np import json @@ -309,7 +306,7 @@ def __init__(self, value): self.value = value def summary(self, indent_amount: int = 0, indent: str=" "): - pass + return repr(self.value) def evaluate(self, variables: dict[int, T]) -> T: return self.value @@ -330,13 +327,12 @@ def _clean(self): @staticmethod def _deserialise(parameters: dict) -> "Operation": - value = parameters["value"] + value = numerical_decode(parameters["value"]) return Constant(value) def _serialise_parameters(self) -> dict[str, Any]: - return {"value": self.value} - + return {"value": numerical_encode(self.value)} def summary(self, indent_amount: int=0, indent=" "): return f"{indent_amount*indent}{self.value}" diff --git a/sasdata/quantities/test_numerical_encoding.py b/sasdata/quantities/test_numerical_encoding.py index 4b170584e..e1166eed6 100644 --- a/sasdata/quantities/test_numerical_encoding.py +++ b/sasdata/quantities/test_numerical_encoding.py @@ -1,3 +1,5 @@ +""" Tests for the encoding and decoding of numerical data""" + import numpy as np import pytest From 69b607b2bb0e71e0b1e1e09a039f497b48ae2eca Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 23 Oct 2024 08:05:42 +0100 Subject: [PATCH 155/675] Work on sparse matrix serialisation --- sasdata/quantities/numerical_encoding.py | 38 +++++++++++++++++-- sasdata/quantities/test_numerical_encoding.py | 9 +++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/sasdata/quantities/numerical_encoding.py b/sasdata/quantities/numerical_encoding.py index 63a888d5d..66b24b8ff 100644 --- a/sasdata/quantities/numerical_encoding.py +++ b/sasdata/quantities/numerical_encoding.py @@ -1,12 +1,13 @@ import numpy as np +from scipy.sparse import coo_matrix, csr_matrix, csc_matrix, coo_array, csr_array, csc_array import base64 import struct -def numerical_encode(obj: int | float | np.ndarray): +def numerical_encode(obj: int | float | np.ndarray | coo_matrix | coo_array | csr_matrix | csr_array | csc_matrix | csc_array): if isinstance(obj, int): return {"type": "int", @@ -24,11 +25,38 @@ def numerical_encode(obj: int | float | np.ndarray): "shape": list(obj.shape) } + elif isinstance(obj, (coo_matrix, coo_array, csr_matrix, csr_array, csc_matrix, csc_array)): + + output = { + "type": obj.__class__.__name__, # not robust to name changes, but more concise + "dtype": obj.dtype.str, + "shape": list(obj.shape) + } + + if isinstance(obj, (coo_array, coo_matrix)): + + output["data"] = numerical_encode(obj.data) + output["coords"] = [numerical_encode(coord) for coord in obj.coords] + + + elif isinstance(obj, (csr_array, csr_matrix)): + pass + + + elif isinstance(obj, (csc_array, csc_matrix)): + + pass + + + return output + else: raise TypeError(f"Cannot serialise object of type: {type(obj)}") -def numerical_decode(data: dict[str, str | int | list[int]]) -> int | float | np.ndarray: - match data["type"]: +def numerical_decode(data: dict[str, str | int | list[int]]) -> int | float | np.ndarray | coo_matrix | coo_array | csr_matrix | csr_array | csc_matrix | csc_array: + obj_type = data["type"] + + match obj_type: case "int": return int(data["value"]) @@ -40,3 +68,7 @@ def numerical_decode(data: dict[str, str | int | list[int]]) -> int | float | np dtype = np.dtype(data["dtype"]) shape = tuple(data["shape"]) return np.frombuffer(value, dtype=dtype).reshape(*shape) + + case _: + raise ValueError(f"Cannot decode objects of type '{obj_type}'") + diff --git a/sasdata/quantities/test_numerical_encoding.py b/sasdata/quantities/test_numerical_encoding.py index e1166eed6..83fa5fe2a 100644 --- a/sasdata/quantities/test_numerical_encoding.py +++ b/sasdata/quantities/test_numerical_encoding.py @@ -54,3 +54,12 @@ def test_numpy_dtypes_encode_decode(dtype): decoded = numerical_decode(encoded) assert decoded.dtype == test_matrix.dtype + +@pytest.mark.parametrize("dtype", [int, float, complex]) +@pytest.mark.parametrize("shape, n, m", [ + ((8, 8), (1,3,5),(2,5,7)), + ((6, 8), (1,0,5),(0,5,0)), + ((6, 1), (1, 0, 5), (0, 0, 0)), +]) +def test_coo_matrix_encode_decode(shape, n, m, dtype): + test_matrix = np.arange() From 92969a224928f26472e234dd4b5730c5195c93cc Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 23 Oct 2024 10:02:47 +0100 Subject: [PATCH 156/675] Updated final line endings --- sasdata/quantities/quantity.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 2c7a7b10e..f27875cf3 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,5 +1,4 @@ - from typing import Self import numpy as np From 0a6a0f30e9081e3f82823d679873491cb390f458 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 23 Oct 2024 10:10:03 +0100 Subject: [PATCH 157/675] Fix test import --- test/slicers/utest_meshmerge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/slicers/utest_meshmerge.py b/test/slicers/utest_meshmerge.py index 21071c049..4e4ee83f1 100644 --- a/test/slicers/utest_meshmerge.py +++ b/test/slicers/utest_meshmerge.py @@ -4,7 +4,7 @@ It's pretty hard to test componentwise, but we can do some tests of the general behaviour """ -from sasdata.slicing.meshes import meshmerge +from sasdata.slicing.meshes.meshmerge import meshmerge from test.slicers.meshes_for_testing import ( grid_mesh, shape_mesh, expected_grid_mappings, expected_shape_mappings) From 37f3d45dc06dcd6169c1fa86e7966f88828856e0 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 25 Oct 2024 10:46:07 +0100 Subject: [PATCH 158/675] Work on tests for sparse matrix encoding --- sasdata/quantities/test_numerical_encoding.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/test_numerical_encoding.py b/sasdata/quantities/test_numerical_encoding.py index 83fa5fe2a..80cfbad9a 100644 --- a/sasdata/quantities/test_numerical_encoding.py +++ b/sasdata/quantities/test_numerical_encoding.py @@ -62,4 +62,7 @@ def test_numpy_dtypes_encode_decode(dtype): ((6, 1), (1, 0, 5), (0, 0, 0)), ]) def test_coo_matrix_encode_decode(shape, n, m, dtype): - test_matrix = np.arange() + + i_indices = + + values = np.arange(10) \ No newline at end of file From 9729a20a5a80edbfd06fd5df3185468428259901 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 9 Jan 2025 09:57:04 +0100 Subject: [PATCH 159/675] Fix tests --- sasdata/transforms/rebinning.py | 1 + sasdata/transforms/test_interpolation.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index 7bdc66209..fe96bcb72 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -132,6 +132,7 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], if mask is None: return conversion_matrix, None + else: # Create a new mask diff --git a/sasdata/transforms/test_interpolation.py b/sasdata/transforms/test_interpolation.py index 688da65fd..97b4f7911 100644 --- a/sasdata/transforms/test_interpolation.py +++ b/sasdata/transforms/test_interpolation.py @@ -23,7 +23,7 @@ def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Q test_points = NamedQuantity("x_test", np.linspace(-5, 5, 11), units.meters) - mapping = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) + mapping, _ = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) y_original = fun(original_points) y_test = y_original @ mapping @@ -53,7 +53,7 @@ def test_linear_interpolate_different_units(fun: Callable[[Quantity[ArrayLike]], original_points = NamedQuantity("x_base", np.linspace(-10,10, 107), units.meters) test_points = NamedQuantity("x_test", np.linspace(-5000, 5000, 11), units.millimeters) - mapping = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) + mapping, _ = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) y_original = fun(original_points) y_test = y_original @ mapping @@ -83,7 +83,7 @@ def test_linearity_linear(): x_and_y = NamedQuantity("x_base", np.linspace(-10, 10, 2), units.meters) new_x = NamedQuantity("x_test", np.linspace(-5000, 5000, 101), units.millimeters) - mapping = calculate_interpolation_matrix_1d(x_and_y, new_x, order=InterpolationOptions.LINEAR) + mapping, _ = calculate_interpolation_matrix_1d(x_and_y, new_x, order=InterpolationOptions.LINEAR) linear_points = x_and_y @ mapping From 705b0eb1e8b2e0ed6dcff869185be0d3b1f45256 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 9 Jan 2025 10:02:11 +0100 Subject: [PATCH 160/675] Disable machine specific mesh test for MacOS --- test/slicers/utest_meshmerge.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/slicers/utest_meshmerge.py b/test/slicers/utest_meshmerge.py index 4e4ee83f1..d83892def 100644 --- a/test/slicers/utest_meshmerge.py +++ b/test/slicers/utest_meshmerge.py @@ -17,6 +17,10 @@ def test_meshmerge_mappings(): tests might not be reliable... we'll see how they play out """ + import sys + if sys.platform == "darwin": + # It does indeed rely on machine precision, only run on windows and linux + return combined_mesh, grid_mappings, shape_mappings = meshmerge(grid_mesh, shape_mesh) From e3340f0790b293a7e42924494f59f58864a95d47 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 14 Jan 2025 15:26:30 +0000 Subject: [PATCH 161/675] Sasdata contains a list of quantities. Not named quantities. --- sasdata/data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 378cb72ff..801cb9504 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -4,7 +4,7 @@ import numpy as np -from sasdata.quantities.quantity import NamedQuantity +from sasdata.quantities.quantity import NamedQuantity, Quantity from sasdata.metadata import Metadata from sasdata.quantities.accessors import AccessorTarget from sasdata.data_backing import Group, key_tree @@ -12,7 +12,7 @@ class SasData: def __init__(self, name: str, - data_contents: list[NamedQuantity], + data_contents: list[Quantity], raw_metadata: Group, verbose: bool=False): From 50694c89e1f17b99d9338885ee57d90df90a277c Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 14 Jan 2025 15:30:21 +0000 Subject: [PATCH 162/675] Make these properties. --- sasdata/data.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 801cb9504..80c789688 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -24,11 +24,18 @@ def __init__(self, name: str, self.metadata = Metadata(AccessorTarget(raw_metadata, verbose=verbose)) # Components that need to be organised after creation - self.ordinate: NamedQuantity[np.ndarray] = None # TODO: fill out - self.abscissae: list[NamedQuantity[np.ndarray]] = None # TODO: fill out self.mask = None # TODO: fill out self.model_requirements = None # TODO: fill out + #TODO: This seems oriented around 1D I vs Q data. What about 2D data? + @property + def ordinate() -> Quantity: + raise NotImplementedError() + + @property + def abscissae() -> list[Quantity]: + raise NotImplementedError() + def summary(self, indent = " ", include_raw=False): s = f"{self.name}\n" @@ -42,4 +49,4 @@ def summary(self, indent = " ", include_raw=False): if include_raw: s += key_tree(self._raw_metadata) - return s \ No newline at end of file + return s From 3797ef5867782a575ba1ee7ffe2cf215fb887f47 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 15 Jan 2025 13:38:30 +0000 Subject: [PATCH 163/675] Use full function name here. --- sasdata/metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index ab580b3e5..611d35b03 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -400,4 +400,4 @@ def summary(self): self.process.summary() + self.sample.summary() + self.instrument.summary() + - self.transmission_spectrum.summary()) \ No newline at end of file + self.transmission_spectrum.summary()) From 34418eb44886cc633bad4f6b10ffeab759489e22 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 17 Jan 2025 07:52:44 +0000 Subject: [PATCH 164/675] Add with standard error for quantities. --- sasdata/quantities/quantity.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index f27875cf3..4701df6d5 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,4 +1,5 @@ + from typing import Self import numpy as np @@ -1119,6 +1120,18 @@ def __init__(self, self.history = QuantityHistory.variable(self) + # TODO: Adding this method as a temporary measure but we need a single + # method that does this. + def with_standard_error(self, standard_error: Quantity): + if standard_error.units.equivalent(self.units): + return NamedQuantity( + value=self.value, + units=self.units, + standard_error=standard_error.in_units_of(self.units),) + else: + raise UnitError(f"Standard error units ({standard_error.units}) " + f"are not compatible with value units ({self.units})") + @property def has_variance(self): return self._variance is not None From b8fde12681402a984f4e8c31af8ee1cde313bd10 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 28 Jan 2025 08:38:02 +0000 Subject: [PATCH 165/675] Make import paths absolute. --- sasdata/metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 611d35b03..ab580b3e5 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -400,4 +400,4 @@ def summary(self): self.process.summary() + self.sample.summary() + self.instrument.summary() + - self.transmission_spectrum.summary()) + self.transmission_spectrum.summary()) \ No newline at end of file From d6382b79a127dd0eed69bf5e75b4c0d4586d1984 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 12 Feb 2025 11:06:39 +0000 Subject: [PATCH 166/675] Function for making the guesses. Useful for testing. --- sasdata/temp_ascii_reader.py | 42 +++++++++++++++--------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/sasdata/temp_ascii_reader.py b/sasdata/temp_ascii_reader.py index 96e8634bf..53e86adf8 100644 --- a/sasdata/temp_ascii_reader.py +++ b/sasdata/temp_ascii_reader.py @@ -1,3 +1,16 @@ +from sasdata.ascii_reader_metadata import AsciiMetadataCategory, AsciiReaderMetadata, pairings, bidirectional_pairings +from sasdata.data import SasData +from sasdata.dataset_types import DatasetType, one_dim +from sasdata.dataset_types import DatasetType +from sasdata.guess import guess_column_count, guess_columns, guess_starting_position +from sasdata.quantities.units import NamedUnit +from sasdata.quantities.quantity import NamedQuantity, Quantity +from sasdata.quantities.accessors import AccessorTarget, Group +from sasdata.metadata import Metadata +from sasdata.data_backing import Dataset, Group +from enum import Enum +from dataclasses import dataclass, field +import numpy as np import re from dataclasses import dataclass, field, replace from enum import Enum @@ -63,19 +76,9 @@ def initialise_metadata(self): self.metadata.filename_separator[basename] = "_" self.metadata.filename_specific_metadata[basename] = {} - @property - def columns_included(self) -> list[tuple[str, NamedUnit]]: - return [ - column - for column in self.columns - if column[0] != "" and isinstance(column[1], NamedUnit) - ] - - # TODO: Should I make this work on a list of filenames as well? -def guess_params_from_filename( - filename: str, dataset_type: DatasetType -) -> AsciiReaderParams: +def guess_params_from_filename(filename: str, dataset_type: DatasetType) -> AsciiReaderParams: + # Lets assume that all the separators are to be enabled. # Lets just assume we want all of the seaprators on. This seems to work for most files. separator_dict = initialise_separator_dict(True) with open(filename) as file: @@ -83,19 +86,8 @@ def guess_params_from_filename( lines_split = [split_line(separator_dict, line) for line in lines] startpos = guess_starting_position(lines_split) colcount = guess_column_count(lines_split, startpos) - columns = [ - (x, get_default_unit(x, unit_kinds[x])) - for x in guess_columns(colcount, dataset_type) - if x in unit_kinds - ] - params = AsciiReaderParams( - [filename], - columns, - starting_line=startpos, - separator_dict=separator_dict, - dataset_type=guess_dataset_type(filename), - ) - return params + columns = guess_columns(colcount, dataset_type) + params = AsciiReaderParams([filename], columns, starting_line=startpos, separator_dict=separator_dict) def split_line(separator_dict: dict[str, bool], line: str) -> list[str]: From ea1b2a1b0516d49c251df60542976aefbcbd58b5 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 17 Feb 2025 10:56:36 +0000 Subject: [PATCH 167/675] Create test option for displaying diagnostic plots --- sasdata/transforms/test_interpolation.py | 40 +++++++++++++----------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/sasdata/transforms/test_interpolation.py b/sasdata/transforms/test_interpolation.py index 97b4f7911..7c8573faf 100644 --- a/sasdata/transforms/test_interpolation.py +++ b/sasdata/transforms/test_interpolation.py @@ -18,7 +18,7 @@ @pytest.mark.parametrize("fun", test_functions) -def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]]): +def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]], order: InterpolationOptions, show_plots: bool): original_points = NamedQuantity("x_base", np.linspace(-10,10, 31), units.meters) test_points = NamedQuantity("x_test", np.linspace(-5, 5, 11), units.meters) @@ -34,13 +34,14 @@ def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Q y_values_test = y_test.in_units_of(test_units) y_values_expected = y_expected.in_units_of(test_units) - # print(y_values_test) - # print(y_values_expected) - # - # quantity_plot(original_points, y_original) - # quantity_plot(test_points, y_test) - # quantity_plot(test_points, y_expected) - # plt.show() + if show_plots: + print(y_values_test) + print(y_values_expected) + + quantity_plot(original_points, y_original) + quantity_plot(test_points, y_test) + quantity_plot(test_points, y_expected) + plt.show() assert len(y_values_test) == len(y_values_expected) @@ -49,7 +50,7 @@ def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Q @pytest.mark.parametrize("fun", test_functions) -def test_linear_interpolate_different_units(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]]): +def test_linear_interpolate_different_units(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]], order: InterpolationOptions, show_plots: bool): original_points = NamedQuantity("x_base", np.linspace(-10,10, 107), units.meters) test_points = NamedQuantity("x_test", np.linspace(-5000, 5000, 11), units.millimeters) @@ -63,15 +64,16 @@ def test_linear_interpolate_different_units(fun: Callable[[Quantity[ArrayLike]], y_values_test = y_test.in_units_of(test_units) y_values_expected = y_expected.in_units_of(test_units) - # - # print(y_values_test) - # print(y_test.in_si()) - # print(y_values_expected) - # - # plt.plot(original_points.in_si(), y_original.in_si()) - # plt.plot(test_points.in_si(), y_test.in_si(), "x") - # plt.plot(test_points.in_si(), y_expected.in_si(), "o") - # plt.show() + + if show_plots: + print(y_values_test) + print(y_test.in_si()) + print(y_values_expected) + + plt.plot(original_points.in_si(), y_original.in_si()) + plt.plot(test_points.in_si(), y_test.in_si(), "x") + plt.plot(test_points.in_si(), y_expected.in_si(), "o") + plt.show() assert len(y_values_test) == len(y_values_expected) @@ -88,4 +90,4 @@ def test_linearity_linear(): linear_points = x_and_y @ mapping for t, e in zip(new_x.in_si(), linear_points.in_si()): - assert t == pytest.approx(e, rel=1e-3) \ No newline at end of file + assert t == pytest.approx(e, rel=1e-3) From 24a73984257f3b82b2b99f5a14a24535372298fc Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 17 Feb 2025 11:05:41 +0000 Subject: [PATCH 168/675] More interpolation test into main test directory --- sasdata/transforms/test_interpolation.py | 93 ------------------------ test/transforms/utest_interpolation.py | 10 +-- 2 files changed, 5 insertions(+), 98 deletions(-) delete mode 100644 sasdata/transforms/test_interpolation.py diff --git a/sasdata/transforms/test_interpolation.py b/sasdata/transforms/test_interpolation.py deleted file mode 100644 index 7c8573faf..000000000 --- a/sasdata/transforms/test_interpolation.py +++ /dev/null @@ -1,93 +0,0 @@ -import pytest -import numpy as np -from matplotlib import pyplot as plt -from numpy.typing import ArrayLike -from typing import Callable - -from sasdata.quantities.plotting import quantity_plot -from sasdata.quantities.quantity import NamedQuantity, Quantity -from sasdata.quantities import units - -from sasdata.transforms.rebinning import calculate_interpolation_matrix_1d, InterpolationOptions - -test_functions = [ - lambda x: x**2, - lambda x: 2*x, - lambda x: x**3 -] - - -@pytest.mark.parametrize("fun", test_functions) -def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]], order: InterpolationOptions, show_plots: bool): - original_points = NamedQuantity("x_base", np.linspace(-10,10, 31), units.meters) - test_points = NamedQuantity("x_test", np.linspace(-5, 5, 11), units.meters) - - - mapping, _ = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) - - y_original = fun(original_points) - y_test = y_original @ mapping - y_expected = fun(test_points) - - test_units = y_expected.units - - y_values_test = y_test.in_units_of(test_units) - y_values_expected = y_expected.in_units_of(test_units) - - if show_plots: - print(y_values_test) - print(y_values_expected) - - quantity_plot(original_points, y_original) - quantity_plot(test_points, y_test) - quantity_plot(test_points, y_expected) - plt.show() - - assert len(y_values_test) == len(y_values_expected) - - for t, e in zip(y_values_test, y_values_expected): - assert t == pytest.approx(e, abs=2) - - -@pytest.mark.parametrize("fun", test_functions) -def test_linear_interpolate_different_units(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]], order: InterpolationOptions, show_plots: bool): - original_points = NamedQuantity("x_base", np.linspace(-10,10, 107), units.meters) - test_points = NamedQuantity("x_test", np.linspace(-5000, 5000, 11), units.millimeters) - - mapping, _ = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) - - y_original = fun(original_points) - y_test = y_original @ mapping - y_expected = fun(test_points) - - test_units = y_expected.units - - y_values_test = y_test.in_units_of(test_units) - y_values_expected = y_expected.in_units_of(test_units) - - if show_plots: - print(y_values_test) - print(y_test.in_si()) - print(y_values_expected) - - plt.plot(original_points.in_si(), y_original.in_si()) - plt.plot(test_points.in_si(), y_test.in_si(), "x") - plt.plot(test_points.in_si(), y_expected.in_si(), "o") - plt.show() - - assert len(y_values_test) == len(y_values_expected) - - for t, e in zip(y_values_test, y_values_expected): - assert t == pytest.approx(e, rel=5e-2) - -def test_linearity_linear(): - """ Test linear interpolation between two points""" - x_and_y = NamedQuantity("x_base", np.linspace(-10, 10, 2), units.meters) - new_x = NamedQuantity("x_test", np.linspace(-5000, 5000, 101), units.millimeters) - - mapping, _ = calculate_interpolation_matrix_1d(x_and_y, new_x, order=InterpolationOptions.LINEAR) - - linear_points = x_and_y @ mapping - - for t, e in zip(new_x.in_si(), linear_points.in_si()): - assert t == pytest.approx(e, rel=1e-3) diff --git a/test/transforms/utest_interpolation.py b/test/transforms/utest_interpolation.py index 9ac701ceb..7011d48a3 100644 --- a/test/transforms/utest_interpolation.py +++ b/test/transforms/utest_interpolation.py @@ -1,14 +1,14 @@ -from collections.abc import Callable - -import numpy as np import pytest +import numpy as np from matplotlib import pyplot as plt from numpy.typing import ArrayLike +from typing import Callable -from sasdata.quantities import units from sasdata.quantities.plotting import quantity_plot from sasdata.quantities.quantity import NamedQuantity, Quantity -from sasdata.transforms.rebinning import InterpolationOptions, calculate_interpolation_matrix_1d +from sasdata.quantities import units + +from sasdata.transforms.rebinning import calculate_interpolation_matrix_1d, InterpolationOptions test_functions = [ lambda x: x**2, From f98dc3814c450a5bcd78523b02b2b7fbb116717e Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 5 Mar 2025 10:06:18 +0000 Subject: [PATCH 169/675] Fixed typo. --- sasdata/data.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 80c789688..ee1f764f2 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -33,8 +33,25 @@ def ordinate() -> Quantity: raise NotImplementedError() @property - def abscissae() -> list[Quantity]: - raise NotImplementedError() + def abscissae(self) -> Quantity: + if self.dataset_type == one_dim: + return self._data_contents['Q'] + elif self.dataset_type == two_dim: + # Type hinting is a bit lacking. Assume each part of the zip is a scalar value. + data_contents = zip(self._data_contents['Qx'].value, self._data_contents['Qy'].value) + # Use this value to extract units etc. Assume they will be the same for Qy. + reference_data_content = self._data_contents['Qx'] + # TODO: If this is a derived quantity then we are going to lose that + # information. + # + # TODO: Won't work when there's errors involved. On reflection, we + # probably want to avoid creating a new Quantity but at the moment I + # can't see a way around it. + return Quantity(data_contents, reference_data_content.units) + return None + + def __getitem__(self, item: str): + return self._data_contents[item] def summary(self, indent = " ", include_raw=False): s = f"{self.name}\n" @@ -49,4 +66,4 @@ def summary(self, indent = " ", include_raw=False): if include_raw: s += key_tree(self._raw_metadata) - return s + return s \ No newline at end of file From e48a328cdb5efc063e15521a38aa669ea2813eaa Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Tue, 4 Mar 2025 14:16:14 +0000 Subject: [PATCH 170/675] Add tests for dataset --- sasdata/temp_hdf5_reader.py | 8 +- test/sasdataloader/reference/14250.txt | 51 ++ test/sasdataloader/reference/33837.txt | 50 ++ test/sasdataloader/reference/33837_v3.txt | 50 ++ test/sasdataloader/reference/BAM.txt | 51 ++ .../sasdataloader/reference/MAR07232_rest.txt | 25 +- .../nxcansas_1Dand2D_multisasdata.txt | 33 +- .../nxcansas_1Dand2D_multisasentry.txt | 63 ++- .../reference/simpleexamplefile.txt | 47 +- test/sasdataloader/reference/x25000_no_di.txt | 24 +- test/sasdataloader/utest_sasdataload.py | 475 ++---------------- 11 files changed, 388 insertions(+), 489 deletions(-) create mode 100644 test/sasdataloader/reference/14250.txt create mode 100644 test/sasdataloader/reference/33837.txt create mode 100644 test/sasdataloader/reference/33837_v3.txt create mode 100644 test/sasdataloader/reference/BAM.txt diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index f96b2a4cb..8afebcbe2 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -144,8 +144,8 @@ def load_data(filename) -> list[SasData]: +if __name__ == "__main__": + data = load_data(test_file) -data = load_data(test_file) - -for dataset in data: - print(dataset.summary(include_raw=False)) \ No newline at end of file + for dataset in data: + print(dataset.summary(include_raw=False)) diff --git a/test/sasdataloader/reference/14250.txt b/test/sasdataloader/reference/14250.txt new file mode 100644 index 000000000..6f11aba78 --- /dev/null +++ b/test/sasdataloader/reference/14250.txt @@ -0,0 +1,51 @@ +sasentry01 + [FILE_ID_HERE/sasentry01/sasdata/I] [[0.0 ... 0.0]] ± [[0.0 ... 0.0]] cm⁻¹ + [FILE_ID_HERE/sasentry01/sasdata/Qx] [[-0.11925 ... 0.11925000000000001]] Å⁻¹ + [FILE_ID_HERE/sasentry01/sasdata/Qy] [[-0.11925 ... 0.11925000000000001]] Å⁻¹ +Metadata: + + High C high V 63, 900oC, 10h 1.65T_SANS, Run: 14250 + =================================================== + +Definition: High C high V 63, 900oC, 10h 1.65T_SANS +Process: + Name: Mantid_generated_NXcanSAS + Date: 2016-12-06T17:15:48 + Description: None + Term: None + Notes: None +Sample: + ID: None + Transmission: None + Thickness: None + Temperature: None + Position: None + Orientation: None +Aperture: + Name: None + Aperture size: None + Aperture distance: None +Collimation: + Length: None +Detector: + Name: None + Distance: None + Offset: None + Orientation: None + Beam center: None + Pixel size: None + Slit length: None +Source: + Radiation: Spallation Neutron Source + Shape: None + Wavelength: None + Min. Wavelength: None + Max. Wavelength: None + Wavelength Spread: None + Beam Size: None +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None + diff --git a/test/sasdataloader/reference/33837.txt b/test/sasdataloader/reference/33837.txt new file mode 100644 index 000000000..26f36b96b --- /dev/null +++ b/test/sasdataloader/reference/33837.txt @@ -0,0 +1,50 @@ +sasentry01 + [FILE_ID_HERE/sasentry01/sasdata/I] [5.416094671273121 ... 0.33697913143947616] ± [0.6152247543248875 ... 0.19365125082205084] m + [FILE_ID_HERE/sasentry01/sasdata/Q] [0.0041600000000000005 ... 0.6189241619415587] m +Metadata: + + MH4_5deg_16T_SLOW, Run: 33837 + ============================= + +Definition: MH4_5deg_16T_SLOW +Process: + Name: Mantid_generated_NXcanSAS + Date: 11-May-2016 12:20:43 + Description: None + Term: None + Notes: None +Sample: + ID: None + Transmission: None + Thickness: None + Temperature: None + Position: None + Orientation: None +Aperture: + Name: None + Aperture size: None + Aperture distance: None +Collimation: + Length: None +Detector: + Name: None + Distance: None + Offset: None + Orientation: None + Beam center: None + Pixel size: None + Slit length: None +Source: + Radiation: Spallation Neutron Source + Shape: None + Wavelength: None + Min. Wavelength: None + Max. Wavelength: None + Wavelength Spread: None + Beam Size: None +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None + diff --git a/test/sasdataloader/reference/33837_v3.txt b/test/sasdataloader/reference/33837_v3.txt new file mode 100644 index 000000000..f4d205b2a --- /dev/null +++ b/test/sasdataloader/reference/33837_v3.txt @@ -0,0 +1,50 @@ +sasentry01 + [FILE_ID_HERE/sasentry01/sasdata/I] [5.416094671273121 ... 0.33697913143947616] ± [0.6152247543248875 ... 0.19365125082205084] none + [FILE_ID_HERE/sasentry01/sasdata/Q] [0.0041600000000000005 ... 0.6189241619415587] Å⁻¹ +Metadata: + + MH4_5deg_16T_SLOW, Run: 33837 + ============================= + +Definition: MH4_5deg_16T_SLOW +Process: + Name: Mantid_generated_NXcanSAS + Date: 2016-07-04T10:34:34 + Description: None + Term: None + Notes: None +Sample: + ID: None + Transmission: None + Thickness: None + Temperature: None + Position: None + Orientation: None +Aperture: + Name: None + Aperture size: None + Aperture distance: None +Collimation: + Length: None +Detector: + Name: None + Distance: None + Offset: None + Orientation: None + Beam center: None + Pixel size: None + Slit length: None +Source: + Radiation: Spallation Neutron Source + Shape: None + Wavelength: None + Min. Wavelength: None + Max. Wavelength: None + Wavelength Spread: None + Beam Size: None +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None + diff --git a/test/sasdataloader/reference/BAM.txt b/test/sasdataloader/reference/BAM.txt new file mode 100644 index 000000000..bf6328b1c --- /dev/null +++ b/test/sasdataloader/reference/BAM.txt @@ -0,0 +1,51 @@ +sasentry01 + [FILE_ID_HERE/sasentry01/data/I] [[-4132.585671758142 ... -4139.954861346877]] ± [[1000000000.0 ... 1000000000.0]] m⁻¹ + [FILE_ID_HERE/sasentry01/data/Imask] [[True ... True]] m⁻¹ + [FILE_ID_HERE/sasentry01/data/Q] [[[-0.10733919639695527 ... 0.0]]] Å⁻¹ +Metadata: + + Qais oriented iron test 1, Run: 12345 + ===================================== + +Definition: Qais oriented iron test 1 +Process: + Name: None + Date: None + Description: None + Term: None + Notes: None +Sample: + ID: None + Transmission: None + Thickness: None + Temperature: None + Position: None + Orientation: None +Aperture: + Name: None + Aperture size: None + Aperture distance: None +Collimation: + Length: None +Detector: + Name: None + Distance: None + Offset: None + Orientation: None + Beam center: None + Pixel size: None + Slit length: None +Source: + Radiation: None + Shape: None + Wavelength: None + Min. Wavelength: None + Max. Wavelength: None + Wavelength Spread: None + Beam Size: None +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None + diff --git a/test/sasdataloader/reference/MAR07232_rest.txt b/test/sasdataloader/reference/MAR07232_rest.txt index e98b20bd2..52b350363 100644 --- a/test/sasdataloader/reference/MAR07232_rest.txt +++ b/test/sasdataloader/reference/MAR07232_rest.txt @@ -1,24 +1,31 @@ sasentry01 - Qy - Qx - I Metadata: MAR07232_rest_out.dat, Run: 2 ============================= Definition: MAR07232_rest_out.dat +Process: + Name: None + Date: None + Description: None + Term: None + Notes: None Sample: - ID: - Transmission: 0.84357 + ID: None + Transmission: [0.84357] Thickness: None Temperature: None Position: None Orientation: None +Aperture: + Name: None + Aperture size: None + Aperture distance: None Collimation: Length: None Detector: - Name: + Name: None Distance: None Offset: None Orientation: None @@ -33,3 +40,9 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None + diff --git a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt index ad56786dd..eaee62960 100644 --- a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt +++ b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt @@ -1,7 +1,4 @@ sasentry01 - Qy - Qx - I Metadata: MH4_5deg_16T_SLOW, Run: 33837 @@ -9,29 +6,27 @@ Metadata: Definition: MH4_5deg_16T_SLOW Process: - Name: Mantid generated CanSAS1D XML - Date: 11-May-2016 12:15:34 + Name: None + Date: None Description: None + Term: None + Notes: None Sample: - ID: + ID: None Transmission: None Thickness: None Temperature: None Position: None Orientation: None +Aperture: + Name: None + Aperture size: None + Aperture distance: None Collimation: Length: None Detector: - Name: front-detector - Distance: 2845.26 mm - Offset: None - Orientation: None - Beam center: None - Pixel size: None - Slit length: None -Detector: - Name: rear-detector - Distance: 4385.28 mm + Name: None + Distance: None Offset: None Orientation: None Beam center: None @@ -45,3 +40,9 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None + diff --git a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt index 9353db922..1123f39b0 100644 --- a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt +++ b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt @@ -1,12 +1,55 @@ sasentry01 - dQ - Q - I Metadata: MH4_5deg_16T_SLOW, Run: 33837 ============================= +Definition: MH4_5deg_16T_SLOW +Process: + Name: None + Date: None + Description: None + Term: None + Notes: None +Sample: + ID: None + Transmission: None + Thickness: None + Temperature: None + Position: None + Orientation: None +Aperture: + Name: None + Aperture size: None + Aperture distance: None +Collimation: + Length: None +Detector: + Name: None + Distance: None + Offset: None + Orientation: None + Beam center: None + Pixel size: None + Slit length: None +Source: + Radiation: Spallation Neutron Source + Shape: None + Wavelength: None + Min. Wavelength: None + Max. Wavelength: None + Wavelength Spread: None + Beam Size: None +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None + +<<<<<<< HEAD + MH4_5deg_16T_SLOW, Run: 33837 + ============================= + Definition: MH4_5deg_16T_SLOW Process: Name: Mantid generated CanSAS1D XML @@ -45,12 +88,8 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None -sasentry02 - Qy - Qx - I -Metadata: +||||||| parent of 556c3b69 (Add tests for dataset) MH4_5deg_16T_SLOW, Run: 33837 ============================= @@ -70,7 +109,7 @@ Collimation: Length: None Detector: Name: front-detector - Distance: 2845.26 mm + Distance: 2845.260009765625 mm Offset: None Orientation: None Beam center: None @@ -78,7 +117,7 @@ Detector: Slit length: None Detector: Name: rear-detector - Distance: 4385.28 mm + Distance: 4385.27978515625 mm Offset: None Orientation: None Beam center: None @@ -92,3 +131,7 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None + +======= + +>>>>>>> 556c3b69 (Add tests for dataset) \ No newline at end of file diff --git a/test/sasdataloader/reference/simpleexamplefile.txt b/test/sasdataloader/reference/simpleexamplefile.txt index 58c9c66b6..a81384082 100644 --- a/test/sasdataloader/reference/simpleexamplefile.txt +++ b/test/sasdataloader/reference/simpleexamplefile.txt @@ -1,9 +1,48 @@ sasentry01 - Q - I Metadata: - None, Run: [] - ============= + None, Run: None + =============== Definition: None +Process: + Name: None + Date: None + Description: None + Term: None + Notes: None +Sample: + ID: None + Transmission: None + Thickness: None + Temperature: None + Position: None + Orientation: None +Aperture: + Name: None + Aperture size: None + Aperture distance: None +Collimation: + Length: None +Detector: + Name: None + Distance: None + Offset: None + Orientation: None + Beam center: None + Pixel size: None + Slit length: None +Source: + Radiation: None + Shape: None + Wavelength: None + Min. Wavelength: None + Max. Wavelength: None + Wavelength Spread: None + Beam Size: None +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None + diff --git a/test/sasdataloader/reference/x25000_no_di.txt b/test/sasdataloader/reference/x25000_no_di.txt index b0cad2ee3..37f524e97 100644 --- a/test/sasdataloader/reference/x25000_no_di.txt +++ b/test/sasdataloader/reference/x25000_no_di.txt @@ -1,25 +1,31 @@ sasentry01 - mask - Qy - Qx - I Metadata: , Run: ======= Definition: +Process: + Name: None + Date: None + Description: None + Term: None + Notes: None Sample: - ID: + ID: None Transmission: None Thickness: None Temperature: None Position: None Orientation: None +Aperture: + Name: None + Aperture size: None + Aperture distance: None Collimation: Length: None Detector: - Name: + Name: None Distance: None Offset: None Orientation: None @@ -34,3 +40,9 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None + diff --git a/test/sasdataloader/utest_sasdataload.py b/test/sasdataloader/utest_sasdataload.py index 735904352..6acc7aa17 100644 --- a/test/sasdataloader/utest_sasdataload.py +++ b/test/sasdataloader/utest_sasdataload.py @@ -2,455 +2,44 @@ Unit tests for the new recursive cansas reader """ -import io -import json import os -from dataclasses import dataclass, field -from typing import Any - -import numpy as np +import unittest import pytest - -import sasdata.quantities.units as units -from sasdata.data import SasData, SasDataEncoder -from sasdata.dataset_types import one_dim -from sasdata.guess import guess_columns -from sasdata.quantities.quantity import Quantity -from sasdata.quantities.units import per_angstrom -from sasdata.temp_ascii_reader import ( - AsciiMetadataCategory, - AsciiReaderMetadata, - AsciiReaderParams, - load_data_default_params, -) -from sasdata.temp_ascii_reader import load_data as ascii_load_data -from sasdata.temp_hdf5_reader import load_data as hdf_load_data -from sasdata.temp_sesans_reader import load_data as sesans_load_data -from sasdata.temp_xml_reader import load_data as xml_load_data +import logging +import warnings +from io import StringIO + +from lxml import etree +from lxml.etree import XMLSyntaxError +from xml.dom import minidom + +from sasdata.dataloader.filereader import decode +from sasdata.dataloader.loader import Loader +from sasdata.dataloader.data_info import Data1D, Data2D +from sasdata.dataloader.readers.xml_reader import XMLreader +from sasdata.dataloader.readers.cansas_reader import Reader +from sasdata.dataloader.readers.cansas_constants import CansasConstants +from sasdata.temp_hdf5_reader import load_data + +test_file_names = [ + "simpleexamplefile", + "nxcansas_1Dand2D_multisasentry", + "nxcansas_1Dand2D_multisasdata", + "MAR07232_rest", + "x25000_no_di", +] def local_load(path: str): """Get local file path""" - base = os.path.join(os.path.dirname(__file__), path) - if os.path.exists(f"{base}.h5"): - return f"{base}.h5" - if os.path.exists(f"{base}.xml"): - return f"{base}.xml" - return f"{base}" - - -def local_reference_load(path: str): - return local_load(f"{os.path.join('reference', path)}") - - -def local_data_load(path: str): - return local_load(f"{os.path.join('data', path)}") - - -def local_json_load(path: str): - return local_load(f"{os.path.join('json', path)}") - - -def local_sesans_load(path: str): - return local_load(f"{os.path.join('sesans_data', path)}") - - -@pytest.mark.sasdata -def test_filter_data(): - data = xml_load_data(local_load("data/cansas1d_notitle")) - for k, v in data.items(): - assert v.metadata.raw.filter("transmission") == ["0.327"] - assert v.metadata.raw.filter("wavelength")[0] == Quantity(6.0, units.angstroms) - assert v.metadata.raw.filter("SDD")[0] == Quantity(4.15, units.meters) - data = hdf_load_data(local_load("data/nxcansas_1Dand2D_multisasentry")) - for k, v in data.items(): - assert v.metadata.raw.filter("radiation") == ["Spallation Neutron Source"] - assert v.metadata.raw.filter("SDD") == [ - Quantity(np.array([2845.26], dtype=np.float32), units.millimeters), - Quantity(np.array([4385.28], dtype=np.float32), units.millimeters), - ] - - -@dataclass(kw_only=True) -class BaseTestCase: - expected_values: dict[int, dict[str, float]] - expected_metadata: dict[str, Any] = field(default_factory=dict) - metadata_file: None | str = None - json_file: None | str = None - round_trip: bool = False - - -@dataclass(kw_only=True) -class AsciiTestCase(BaseTestCase): - # If this is a string of strings then the other params will be guessed. - reader_params: AsciiReaderParams | str - - -@dataclass(kw_only=True) -class BulkAsciiTestCase(AsciiTestCase): - reader_params: AsciiReaderParams - expected_values: dict[str, dict[int, dict[str, float]]] - expected_metadata: dict[str, dict[str, Any]] = field(default_factory=dict) - - -@dataclass(kw_only=True) -class XmlTestCase(BaseTestCase): - filename: str - entry: str = "sasentry01" - round_trip: bool = True - - -@dataclass(kw_only=True) -class Hdf5TestCase(BaseTestCase): - filename: str - entry: str = "sasentry01" - round_trip: bool = True - - -@dataclass(kw_only=True) -class SesansTestCase(BaseTestCase): - filename: str - - -test_cases = [ - pytest.param( - AsciiTestCase( - reader_params=local_data_load("ascii_test_1.txt"), - expected_values={ - 0: {"Q": 0.002618, "I": 0.02198, "dI": 0.002704}, - -1: {"Q": 0.0497, "I": 8.346, "dI": 0.191}, - }, - ), - marks=pytest.mark.xfail(reason="The ASCII reader cannot make the right guesses for this file."), - ), - AsciiTestCase( - reader_params=local_data_load("test_3_columns.txt"), - expected_values={ - 0: {"Q": 0, "I": 2.83954, "dI": 0.6}, - -1: {"Q": 1.22449, "I": 7.47487, "dI": 1.05918}, - }, - ), - pytest.param( - AsciiTestCase( - reader_params=local_data_load("detector_rectangular.DAT"), - expected_values={ - 0: { - "Qx": -0.009160664, - "Qy": -0.1683881, - "I": 16806.79, - "dI": 0.01366757, - }, - -1: { - "Qx": 0.2908819, - "Qy": 0.1634992, - "I": 8147.779, - "dI": 0.05458562, - }, - }, - ), - marks=pytest.mark.xfail( - reason="Guesses for 2D ASCII files are currently wrong, so the data loaded won't be correct." - ), - ), - BulkAsciiTestCase( - reader_params=AsciiReaderParams( - filenames=[ - local_data_load(filename) - for filename in [ - "1_33_1640_22.874115.csv", - "2_42_1640_23.456895.csv", - "3_61_1640_23.748285.csv", - "4_103_1640_24.039675.csv", - "5_312_1640_24.331065.csv", - "6_1270_1640_24.331065.csv", - ] - ], - columns=[(column, per_angstrom) for column in guess_columns(3, one_dim)], - separator_dict={"Comma": True}, - metadata=AsciiReaderMetadata( - master_metadata={ - "magnetic": AsciiMetadataCategory( - values={ - "counting_index": 0, - "applied_magnetic_field": 1, - "saturation_magnetization": 2, - "demagnetizing_field": 3, - } - ) - } - ), - ), - expected_values={}, - expected_metadata={ - "1_33_1640_22.874115.csv": { - "counting_index": ["1"], - "applied_magnetic_field": ["33"], - "saturation_magnetization": ["1640"], - "demagnetizing_field": ["22"], - }, - "6_1270_1640_24.331065.csv": { - "counting_index": ["6"], - "applied_magnetic_field": ["1270"], - "saturation_magnetization": ["1640"], - "demagnetizing_field": ["24"], - }, - }, - ), - XmlTestCase( - filename=local_data_load("ISIS_1_0.xml"), - entry="79680main_1D_2.2_10.0", - expected_values={ - 0: {"Q": 0.009, "I": 85.3333, "dI": 0.852491, "dQ": 0}, - -2: {"Q": 0.281, "I": 0.408902, "dQ": 0}, - -1: {"Q": 0.283, "I": 0, "dI": 0, "dQ": 0}, - }, - expected_metadata={ - # TODO: Add more. - "radiation": "neutron" - }, - ), - Hdf5TestCase( - filename=local_data_load("simpleexamplefile.h5"), - metadata_file=local_reference_load("simpleexamplefile.txt"), - expected_values={ - 0: {"Q": 0.5488135039273248, "I": 0.6778165367962301}, - -1: {"Q": 0.004695476192547066, "I": 0.4344166255581208}, - }, - ), - Hdf5TestCase( - filename=local_data_load("MAR07232_rest.h5"), - metadata_file=local_reference_load("MAR07232_rest.txt"), - expected_values={}, - ), - Hdf5TestCase( - filename=local_data_load("x25000_no_di.h5"), - expected_values={}, - ), - Hdf5TestCase( - filename=local_data_load("nxcansas_1Dand2D_multisasentry.h5"), - metadata_file=local_reference_load("nxcansas_1Dand2D_multisasentry.txt"), - expected_values={}, - ), - Hdf5TestCase( - filename=local_data_load("nxcansas_1Dand2D_multisasdata.h5"), - metadata_file=local_reference_load("nxcansas_1Dand2D_multisasdata.txt"), - expected_values={}, - ), - XmlTestCase( - filename=local_data_load("ISIS_1_0.xml"), - entry="79680main_1D_2.2_10.0", - metadata_file=local_reference_load("ISIS_1_0.txt"), - json_file=local_json_load("ISIS_1_0.json"), - expected_values={}, - ), - XmlTestCase( - filename=local_data_load("ISIS_1_1.xml"), - entry="79680main_1D_2.2_10.0", - metadata_file=local_reference_load("ISIS_1_1.txt"), - json_file=local_json_load("ISIS_1_1.json"), - expected_values={}, - ), - XmlTestCase( - filename=local_data_load("ISIS_1_1_doubletrans.xml"), - entry="79680main_1D_2.2_10.0", - metadata_file=local_reference_load("ISIS_1_1_doubletrans.txt"), - json_file=local_json_load("ISIS_1_1_doubletrans.json"), - expected_values={}, - ), - XmlTestCase( - filename=local_data_load("ISIS_1_1_notrans.xml"), - entry="79680main_1D_2.2_10.0", - metadata_file=local_reference_load("ISIS_1_1_notrans.txt"), - json_file=local_json_load("ISIS_1_1_notrans.json"), - expected_values={}, - ), - XmlTestCase( - filename=local_data_load("TestExtensions.xml"), - entry="TK49 c10_SANS", - metadata_file=local_reference_load("TestExtensions.txt"), - json_file=local_json_load("TestExtensions.json"), - expected_values={}, - ), - XmlTestCase( - filename=local_data_load("cansas1d.xml"), - entry="Test title", - metadata_file=local_reference_load("cansas1d.txt"), - json_file=local_json_load("cansas1d.json"), - expected_values={}, - ), - XmlTestCase( - filename=local_data_load("cansas1d_badunits.xml"), - entry="Test title", - metadata_file=local_reference_load("cansas1d_badunits.txt"), - json_file=local_json_load("cansas1d_badunits.json"), - expected_values={}, - ), - XmlTestCase( - filename=local_data_load("cansas1d_notitle.xml"), - entry="SasData01", - metadata_file=local_reference_load("cansas1d_notitle.txt"), - json_file=local_json_load("cansas1d_notitle.json"), - expected_values={}, - ), - XmlTestCase( - filename=local_data_load("cansas1d_slit.xml"), - entry="Test title", - metadata_file=local_reference_load("cansas1d_slit.txt"), - json_file=local_json_load("cansas1d_slit.json"), - expected_values={}, - ), - XmlTestCase( - filename=local_data_load("cansas1d_units.xml"), - entry="Test title", - metadata_file=local_reference_load("cansas1d_units.txt"), - json_file=local_json_load("cansas1d_units.json"), - expected_values={}, - ), - XmlTestCase( - filename=local_data_load("cansas_test.xml"), - entry="ILL-D11 example1: 2A 5mM 0%D2O", - metadata_file=local_reference_load("cansas_test.txt"), - json_file=local_json_load("cansas_test.json"), - expected_values={}, - ), - XmlTestCase( - filename=local_data_load("cansas_test_modified.xml"), - entry="ILL-D11 example1: 2A 5mM 0%D2O", - metadata_file=local_reference_load("cansas_test_modified.txt"), - json_file=local_json_load("cansas_test_modified.json"), - expected_values={}, - ), - XmlTestCase( - filename=local_data_load("valid_cansas_xml.xml"), - entry="80514main_1D_2.2_10.0", - metadata_file=local_reference_load("valid_cansas_xml.txt"), - json_file=local_json_load("valid_cansas_xml.json"), - expected_values={}, - ), - SesansTestCase( - filename=local_sesans_load("sphere2micron.ses"), - metadata_file=local_reference_load("sphere2micron.txt"), - expected_values={ - 0: {"SpinEchoLength": 391.56, "Depolarisation": 0.0041929}, - -1: {"SpinEchoLength": 46099, "Depolarisation": -0.19956}, - }, - ), -] - - -def join_actual_expected( - actual: list[SasData], expected: dict[str, dict[int, dict[str, float]]] -) -> list[tuple[SasData, dict[int, dict[str, float]]]]: - return_value = [] - for actual_datum in actual: - matching_expected_datum = expected.get(actual_datum.name) - if matching_expected_datum is None: - continue - return_value.append((actual_datum, matching_expected_datum)) - return return_value - - -def is_uncertainty(column: str) -> bool: - for uncertainty_str in ["I", "Q", "Qx", "Qy"]: - if column == "d" + uncertainty_str: - return True - return False - - -@pytest.mark.dataload -@pytest.mark.parametrize("test_case", test_cases) -def test_load_file(test_case: BaseTestCase): - match test_case: - case BulkAsciiTestCase(): - loaded_data = ascii_load_data(test_case.reader_params) - case AsciiTestCase(): - if isinstance(test_case.reader_params, str): - loaded_data = load_data_default_params(test_case.reader_params)[0] - elif isinstance(test_case.reader_params, AsciiReaderParams): - loaded_data = ascii_load_data(test_case.reader_params)[0] - else: - raise TypeError("Invalid type for reader_params.") - case Hdf5TestCase(): - combined_data = hdf_load_data(test_case.filename) - loaded_data = combined_data[test_case.entry] - # TODO: Support SESANS - case XmlTestCase(): - # Not bulk, so just assume we get one dataset. - combined_data = xml_load_data(test_case.filename) - loaded_data = combined_data[test_case.entry] - case SesansTestCase(): - loaded_data = sesans_load_data(test_case.filename) - combined_data = {"only": loaded_data} - case _: - raise ValueError("Invalid loader") - if isinstance(test_case, BulkAsciiTestCase): - loaded_expected_pairs = join_actual_expected(loaded_data, test_case.expected_values) - metadata_filenames = test_case.expected_metadata.keys() - else: - loaded_expected_pairs = [(loaded_data, test_case.expected_values)] - metadata_filenames = [loaded_data.name] - for loaded, expected in loaded_expected_pairs: - for index, values in expected.items(): - for column, expected_value in values.items(): - if is_uncertainty(column): - assert loaded._data_contents[column[1::]]._variance[index] == pytest.approx(expected_value**2) - else: - assert loaded._data_contents[column].value[index] == pytest.approx(expected_value) - - for filename in metadata_filenames: - current_metadata_dict = test_case.expected_metadata.get(filename) - current_datum = ( - next(filter(lambda d: d.name == filename, loaded_data)) if isinstance(loaded_data, list) else loaded_data - ) - if current_metadata_dict is None: - continue - for metadata_key, value in current_metadata_dict.items(): - assert current_datum.metadata.raw.filter(metadata_key) == value - - if test_case.metadata_file is not None: - with open(test_case.metadata_file, encoding="utf-8") as infile: - expected = "".join(infile.readlines()) - keys = sorted([d for d in combined_data]) - assert "".join(combined_data[k].summary() for k in keys) == expected - - if test_case.json_file is not None: - # Test serialisation - with open(test_case.json_file, encoding="utf-8") as infile: - expected = json.loads("".join(infile.readlines())) - assert json.loads(SasDataEncoder().encode(combined_data)) == expected - - # Test deserialisation - with open(test_case.json_file, encoding="utf-8") as infile: - raw = json.loads("".join(infile.readlines())) - parsed = {} - for k in raw: - parsed[k] = SasData.from_json(raw[k]) - - for k in combined_data: - expect = combined_data[k] - pars = parsed[k] - assert pars.name == expect.name - # assert pars._data_contents == expect._data_contents - assert pars.dataset_type == expect.dataset_type - assert pars.mask == expect.mask - assert pars.model_requirements == expect.model_requirements + return os.path.join(os.path.dirname(__file__), path) - if test_case.round_trip: - bio = io.BytesIO() - SasData.save_h5(combined_data, bio) - bio.seek(0) - result = hdf_load_data(bio) - bio.close() +@pytest.mark.current +@pytest.mark.parametrize("f", test_file_names) +def test_load_file(f): + data = load_data(local_load(f"data/{f}.h5")) - for name, entry in result.items(): - assert combined_data[name].metadata.title == entry.metadata.title - assert combined_data[name].metadata.run == entry.metadata.run - assert combined_data[name].metadata.definition == entry.metadata.definition - assert combined_data[name].metadata.process == entry.metadata.process - assert combined_data[name].metadata.instrument == entry.metadata.instrument - assert combined_data[name].metadata.sample == entry.metadata.sample - assert combined_data[name].ordinate.units == entry.ordinate.units - assert np.all(combined_data[name].ordinate.value == entry.ordinate.value) - assert combined_data[name].abscissae.units == entry.abscissae.units - assert np.all(combined_data[name].abscissae.value == entry.abscissae.value) + with open(local_load(f"reference/{f}.txt")) as infile: + expected = "".join(infile.readlines()) + assert data[0].summary() == expected From 3f6bfe7c89da96452aa420076766f356d6c7a323 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Wed, 5 Mar 2025 11:39:22 +0000 Subject: [PATCH 171/675] Handle collimations directly --- sasdata/data.py | 7 +++-- sasdata/metadata.py | 28 +++++++----------- sasdata/temp_hdf5_reader.py | 29 +++++++++++++++---- .../sasdataloader/reference/MAR07232_rest.txt | 2 -- .../nxcansas_1Dand2D_multisasdata.txt | 2 -- .../nxcansas_1Dand2D_multisasentry.txt | 2 -- .../reference/simpleexamplefile.txt | 2 -- test/sasdataloader/reference/x25000_no_di.txt | 2 -- 8 files changed, 38 insertions(+), 36 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index ee1f764f2..9a05276d2 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -5,7 +5,7 @@ import numpy as np from sasdata.quantities.quantity import NamedQuantity, Quantity -from sasdata.metadata import Metadata +from sasdata.metadata import Metadata, Instrument from sasdata.quantities.accessors import AccessorTarget from sasdata.data_backing import Group, key_tree @@ -14,6 +14,7 @@ class SasData: def __init__(self, name: str, data_contents: list[Quantity], raw_metadata: Group, + instrument: Instrument, verbose: bool=False): self.name = name @@ -21,7 +22,7 @@ def __init__(self, name: str, self._raw_metadata = raw_metadata self._verbose = verbose - self.metadata = Metadata(AccessorTarget(raw_metadata, verbose=verbose)) + self.metadata = Metadata(AccessorTarget(raw_metadata, verbose=verbose), instrument) # Components that need to be organised after creation self.mask = None # TODO: fill out @@ -66,4 +67,4 @@ def summary(self, indent = " ", include_raw=False): if include_raw: s += key_tree(self._raw_metadata) - return s \ No newline at end of file + return s diff --git a/sasdata/metadata.py b/sasdata/metadata.py index ab580b3e5..31f107c27 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -102,26 +102,20 @@ class Collimation: Class to hold collimation information """ - def __init__(self, target_object: AccessorTarget): + def __init__(self, name, length): # Name - self.name = StringAccessor(target_object, "name") + self.name = name # Length [float] [mm] - self.length = LengthAccessor[float](target_object, - "length", - "length.units", - default_unit=units.millimeters) - - - # Todo - how do we handle this - # self.collimator = Collimation(target_object) + self.length = length + # TODO - parse units properly def summary(self): #TODO collimation stuff return ( f"Collimation:\n" - f" Length: {self.length.value}\n") + f" Length: {self.length}\n") @@ -337,15 +331,15 @@ def summary(self) -> str: class Instrument: - def __init__(self, target: AccessorTarget): + def __init__(self, target: AccessorTarget, collimations: list[Collimation]): self.aperture = Aperture(target.with_path_prefix("sasaperture|aperture")) - self.collimation = Collimation(target.with_path_prefix("sascollimation|collimation")) + self.collimations = collimations self.detector = Detector(target.with_path_prefix("sasdetector|detector")) self.source = Source(target.with_path_prefix("sassource|source")) def summary(self): return ( self.aperture.summary() + - self.collimation.summary() + + "\n".join([c.summary for c in self.collimations]) + self.detector.summary() + self.source.summary()) @@ -374,10 +368,10 @@ def decode_string(data): return str(data) class Metadata: - def __init__(self, target: AccessorTarget): + def __init__(self, target: AccessorTarget, instrument: Instrument): self._target = target - self.instrument = Instrument(target.with_path_prefix("sasinstrument|instrument")) + self.instrument = instrument self.process = Process(target.with_path_prefix("sasprocess|process")) self.sample = Sample(target.with_path_prefix("sassample|sample")) self.transmission_spectrum = TransmissionSpectrum(target.with_path_prefix("sastransmission_spectrum|transmission_spectrum")) @@ -400,4 +394,4 @@ def summary(self): self.process.summary() + self.sample.summary() + self.instrument.summary() + - self.transmission_spectrum.summary()) \ No newline at end of file + self.transmission_spectrum.summary()) diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 8afebcbe2..060a45439 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -13,6 +13,8 @@ from sasdata.data import SasData from sasdata.data_backing import Dataset as SASDataDataset, Group as SASDataGroup +from sasdata.metadata import Instrument, Collimation +from sasdata.quantities.accessors import AccessorTarget from sasdata.quantities.quantity import NamedQuantity from sasdata.quantities import units @@ -104,13 +106,25 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: return output -def load_data(filename) -> list[SasData]: - with h5py.File(filename, 'r') as f: +def parse_collimations(node) -> list[Collimation]: + if "sasinstrument" not in node["sasentry01"]: + return [] + return [ + Collimation(name=None, length=x) + for x in node["sasentry01"]["sasinstrument"]["sascollimation01"] + ] + + +def parse_instrument(raw, node) -> Instrument: + collimations = parse_collimations(node) + return Instrument(raw, collimations=collimations) + +def load_data(filename) -> list[SasData]: + with h5py.File(filename, "r") as f: loaded_data: list[SasData] = [] for root_key in f.keys(): - entry = f[root_key] data_contents = [] @@ -132,18 +146,21 @@ def load_data(filename) -> list[SasData]: else: raw_metadata[key] = recurse_hdf5(component) - loaded_data.append( SasData( name=root_key, data_contents=data_contents, raw_metadata=SASDataGroup("root", raw_metadata), - verbose=False)) + instrument=parse_instrument( + AccessorTarget(SASDataGroup("root", raw_metadata)).with_path_prefix("sasinstrument|instrument"), f + ), + verbose=False, + ) + ) return loaded_data - if __name__ == "__main__": data = load_data(test_file) diff --git a/test/sasdataloader/reference/MAR07232_rest.txt b/test/sasdataloader/reference/MAR07232_rest.txt index 52b350363..97c4c5045 100644 --- a/test/sasdataloader/reference/MAR07232_rest.txt +++ b/test/sasdataloader/reference/MAR07232_rest.txt @@ -22,8 +22,6 @@ Aperture: Name: None Aperture size: None Aperture distance: None -Collimation: - Length: None Detector: Name: None Distance: None diff --git a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt index eaee62960..d68cddeb8 100644 --- a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt +++ b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt @@ -22,8 +22,6 @@ Aperture: Name: None Aperture size: None Aperture distance: None -Collimation: - Length: None Detector: Name: None Distance: None diff --git a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt index 1123f39b0..8a8eeda8b 100644 --- a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt +++ b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt @@ -22,8 +22,6 @@ Aperture: Name: None Aperture size: None Aperture distance: None -Collimation: - Length: None Detector: Name: None Distance: None diff --git a/test/sasdataloader/reference/simpleexamplefile.txt b/test/sasdataloader/reference/simpleexamplefile.txt index a81384082..10301146a 100644 --- a/test/sasdataloader/reference/simpleexamplefile.txt +++ b/test/sasdataloader/reference/simpleexamplefile.txt @@ -22,8 +22,6 @@ Aperture: Name: None Aperture size: None Aperture distance: None -Collimation: - Length: None Detector: Name: None Distance: None diff --git a/test/sasdataloader/reference/x25000_no_di.txt b/test/sasdataloader/reference/x25000_no_di.txt index 37f524e97..cfb87ad49 100644 --- a/test/sasdataloader/reference/x25000_no_di.txt +++ b/test/sasdataloader/reference/x25000_no_di.txt @@ -22,8 +22,6 @@ Aperture: Name: None Aperture size: None Aperture distance: None -Collimation: - Length: None Detector: Name: None Distance: None From 4f5f3593ecfeaf483b74eb35a4dab26dabc584f0 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 17 Mar 2025 12:45:42 +0000 Subject: [PATCH 172/675] Parse source --- sasdata/metadata.py | 98 +++++---------- sasdata/temp_hdf5_reader.py | 160 +++++++++++++++++++++--- test/sasdataloader/utest_sasdataload.py | 2 +- 3 files changed, 175 insertions(+), 85 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 31f107c27..d52c40c88 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -117,80 +117,40 @@ def summary(self): f"Collimation:\n" f" Length: {self.length}\n") +@dataclass +class BeamSize: + name: Optional[str] + x: Optional[Quantity[float]] + y: Optional[Quantity[float]] + z: Optional[Quantity[float]] +@dataclass class Source: - """ - Class to hold source information - """ - - def __init__(self, target_object: AccessorTarget): - # Name - self.name = StringAccessor(target_object, "name") - - # Generic radiation type (Type and probe give more specific info) [string] - self.radiation = StringAccessor(target_object, "radiation") - - # Type and probe are only written to by the NXcanSAS reader - # Specific radiation type (Synchotron X-ray, Reactor neutron, etc) [string] - self.type = StringAccessor(target_object, "type") - - # Radiation probe (generic probe such as neutron, x-ray, muon, etc) [string] - self.probe_particle = StringAccessor(target_object, "probe") - - # Beam size name - self.beam_size_name = StringAccessor(target_object, "beam_size_name") - - # Beam size [Vector] [mm] - self.beam_size = LengthAccessor[ArrayLike](target_object, - "beam_size", - "beam_size.units", - default_unit=units.millimeters) - - # Beam shape [string] - self.beam_shape = StringAccessor(target_object, "beam_shape") - - # Wavelength [float] [Angstrom] - self.wavelength = LengthAccessor[float](target_object, - "wavelength", - "wavelength.units", - default_unit=units.angstroms) - - # Minimum wavelength [float] [Angstrom] - self.wavelength_min = LengthAccessor[float](target_object, - "wavelength_min", - "wavelength_min.units", - default_unit=units.angstroms) - - # Maximum wavelength [float] [Angstrom] - self.wavelength_max = LengthAccessor[float](target_object, - "wavelength_min", - "wavelength_max.units", - default_unit=units.angstroms) - - # Wavelength spread [float] [Angstrom] - # Quantity because it might have other units, such as percent - self.wavelength_spread = QuantityAccessor[float](target_object, - "wavelength_spread", - "wavelength_spread.units", - default_unit=units.angstroms) + radiation: str + beam_shape: str + beam_size: Optional[BeamSize] + wavelength : Quantity[float] + wavelength_min : Quantity[float] + wavelength_max : Quantity[float] + wavelength_spread : Quantity[float] def summary(self) -> str: - - if self.radiation.value is None and self.type.value and self.probe_particle.value: + if self.radiation is None and self.type.value and self.probe_particle.value: radiation = f"{self.type.value} {self.probe_particle.value}" else: - radiation = f"{self.radiation.value}" - - return (f"Source:\n" - f" Radiation: {radiation}\n" - f" Shape: {self.beam_shape.value}\n" - f" Wavelength: {self.wavelength.value}\n" - f" Min. Wavelength: {self.wavelength_min.value}\n" - f" Max. Wavelength: {self.wavelength_max.value}\n" - f" Wavelength Spread: {self.wavelength_spread.value}\n" - f" Beam Size: {self.beam_size.value}\n") + radiation = f"{self.radiation}" + return ( + f"Source:\n" + f" Radiation: {radiation}\n" + f" Shape: {self.beam_shape}\n" + f" Wavelength: {self.wavelength}\n" + f" Min. Wavelength: {self.wavelength_min}\n" + f" Max. Wavelength: {self.wavelength_max}\n" + f" Wavelength Spread: {self.wavelength_spread}\n" + f" Beam Size: {self.beam_size}\n" + ) """ @@ -331,11 +291,11 @@ def summary(self) -> str: class Instrument: - def __init__(self, target: AccessorTarget, collimations: list[Collimation]): + def __init__(self, target: AccessorTarget, collimations: list[Collimation], source: Source): self.aperture = Aperture(target.with_path_prefix("sasaperture|aperture")) self.collimations = collimations self.detector = Detector(target.with_path_prefix("sasdetector|detector")) - self.source = Source(target.with_path_prefix("sassource|source")) + self.source = source def summary(self): return ( self.aperture.summary() + @@ -393,5 +353,5 @@ def summary(self): f"Definition: {self.title}\n" + self.process.summary() + self.sample.summary() + - self.instrument.summary() + + (self.instrument.summary() if self.instrument else "") + self.transmission_spectrum.summary()) diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 060a45439..2fd2fa227 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -13,7 +13,7 @@ from sasdata.data import SasData from sasdata.data_backing import Dataset as SASDataDataset, Group as SASDataGroup -from sasdata.metadata import Instrument, Collimation +from sasdata.metadata import Instrument, Collimation, Aperture, Source from sasdata.quantities.accessors import AccessorTarget from sasdata.quantities.quantity import NamedQuantity @@ -38,29 +38,33 @@ def recurse_hdf5(hdf5_entry): data = hdf5_entry[()][0].decode("utf-8") return SASDataDataset[str]( - name=hdf5_entry.name, - data=data, - attributes=attributes) + name=hdf5_entry.name, data=data, attributes=attributes + ) else: data = np.array(hdf5_entry, dtype=hdf5_entry.dtype) return SASDataDataset[np.ndarray]( - name=hdf5_entry.name, - data=data, - attributes=attributes) + name=hdf5_entry.name, data=data, attributes=attributes + ) elif isinstance(hdf5_entry, HDF5Group): return SASDataGroup( name=hdf5_entry.name, - children={key: recurse_hdf5(hdf5_entry[key]) for key in hdf5_entry.keys()}) + children={key: recurse_hdf5(hdf5_entry[key]) for key in hdf5_entry.keys()}, + ) else: - raise TypeError(f"Unknown type found during HDF5 parsing: {type(hdf5_entry)} ({hdf5_entry})") + raise TypeError( + f"Unknown type found during HDF5 parsing: {type(hdf5_entry)} ({hdf5_entry})" + ) + GET_UNITS_FROM_ELSEWHERE = units.meters + + def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: - """ In the context of NeXus files, load a group of data entries that are organised together + """In the context of NeXus files, load a group of data entries that are organised together match up the units and errors with their values""" # Gather together data with its error terms @@ -69,7 +73,6 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: entries = {} for name in node.children: - child = node.children[name] if "units" in child.attributes: @@ -77,9 +80,9 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: else: units = GET_UNITS_FROM_ELSEWHERE - quantity = NamedQuantity(name=name_prefix+child.name, - value=child.data, - units=units) + quantity = NamedQuantity( + name=name_prefix + child.name, value=child.data, units=units + ) # Turns out people can't be trusted to use the same keys here if "uncertainty" in child.attributes or "uncertainties" in child.attributes: @@ -105,7 +108,53 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: return output - +<<<<<<< HEAD +||||||| parent of a1a66a4a (Parse source) +def parse_apertures(node) -> list[Aperture]: + result = [] + aps = [a for a in node if "aperture" in a] + for ap in aps: + distance = None + size = None + if "distance" in node[ap]: + distance = node[ap]["distance"] + if "size" in node[ap]: + x = y = z = None + if "x" in node[ap]: + x = node[ap]["size"]["x"] + if "y" in node[ap]: + y = node[ap]["size"]["y"] + if "z" in node[ap]: + z = node[ap]["size"]["z"] + if x is not None or y is not None or z is not None: + size = (x, y, z) + result.append(Aperture(distance=distance, size=size, size_name=size_name, name=name, apType=apType)) + return result +======= + +def parse_apertures(node) -> list[Aperture]: + result = [] + aps = [a for a in node if "aperture" in a] + for ap in aps: + distance = None + size = None + if "distance" in node[ap]: + distance = node[ap]["distance"] + if "size" in node[ap]: + x = y = z = None + if "x" in node[ap]: + x = node[ap]["size"]["x"] + if "y" in node[ap]: + y = node[ap]["size"]["y"] + if "z" in node[ap]: + z = node[ap]["size"]["z"] + if x is not None or y is not None or z is not None: + size = (x, y, z) + result.append(Aperture(distance=distance, size=size, size_name=size_name, name=name, apType=apType)) + return result +>>>>>>> a1a66a4a (Parse source) + +<<<<<<< HEAD def parse_collimations(node) -> list[Collimation]: if "sasinstrument" not in node["sasentry01"]: return [] @@ -113,11 +162,75 @@ def parse_collimations(node) -> list[Collimation]: Collimation(name=None, length=x) for x in node["sasentry01"]["sasinstrument"]["sascollimation01"] ] +||||||| parent of a1a66a4a (Parse source) + +def parse_collimation(node) -> Collimation: + if "length" in node: + length = node["length"] + else: + length = None + return Collimation(length=length, apertures=parse_apertures(node)) +======= + +def parse_source(node) -> Source: + beam_shape = None + beam_size = None + wavelength = None + wavelength_min = None + wavelength_max = None + wavelength_spread = None + if "beam_shape" in node: + beam_shape = node["beam_shape"] + if "wavelength" in node: + wavelength = node["wavelength"] + if "wavelength_min" in node: + wavelength = node["wavelength_min"] + if "wavelength_max" in node: + wavelength = node["wavelength_max"] + if "wavelength_spread" in node: + wavelength = node["wavelength_spread"] + return Source( + radiation=node["radiation"].asstr()[0], + beam_shape=beam_shape, + beam_size=beam_size, + wavelength=wavelength, + wavelength_min=wavelength_min, + wavelength_max=wavelength_max, + wavelength_spread=wavelength_spread, + ) + + +def parse_collimation(node) -> Collimation: + if "length" in node: + length = node["length"] + else: + length = None + return Collimation(length=length, apertures=parse_apertures(node)) +>>>>>>> a1a66a4a (Parse source) def parse_instrument(raw, node) -> Instrument: +<<<<<<< HEAD collimations = parse_collimations(node) return Instrument(raw, collimations=collimations) +||||||| parent of a1a66a4a (Parse source) + if "sasinstrument" in node: + collimations = [parse_collimation(node["sasinstrument"][x]) for x in node["sasinstrument"] if "collimation" in x] + else: + collimations=[] + return Instrument(raw, collimations=collimations) +======= + collimations = [ + parse_collimation(node[x]) + for x in node + if "collimation" in x + ] + return Instrument( + raw, + collimations=collimations, + source=parse_source(node["sassource"]), + ) +>>>>>>> a1a66a4a (Parse source) def load_data(filename) -> list[SasData]: @@ -146,14 +259,31 @@ def load_data(filename) -> list[SasData]: else: raw_metadata[key] = recurse_hdf5(component) + instrument = None + if "sasinstrument" in f["sasentry01"]: + instrument = parse_instrument( + AccessorTarget(SASDataGroup("root", raw_metadata)).with_path_prefix( + "sasinstrument|instrument" + ), + f["sasentry01"]["sasinstrument"], + ) + loaded_data.append( SasData( name=root_key, data_contents=data_contents, raw_metadata=SASDataGroup("root", raw_metadata), +<<<<<<< HEAD instrument=parse_instrument( AccessorTarget(SASDataGroup("root", raw_metadata)).with_path_prefix("sasinstrument|instrument"), f ), +||||||| parent of a1a66a4a (Parse source) + instrument=parse_instrument( + AccessorTarget(SASDataGroup("root", raw_metadata)).with_path_prefix("sasinstrument|instrument"), f["sasentry01"] + ), +======= + instrument=instrument, +>>>>>>> a1a66a4a (Parse source) verbose=False, ) ) diff --git a/test/sasdataloader/utest_sasdataload.py b/test/sasdataloader/utest_sasdataload.py index 6acc7aa17..09aeef807 100644 --- a/test/sasdataloader/utest_sasdataload.py +++ b/test/sasdataloader/utest_sasdataload.py @@ -22,7 +22,7 @@ from sasdata.temp_hdf5_reader import load_data test_file_names = [ - "simpleexamplefile", + # "simpleexamplefile", "nxcansas_1Dand2D_multisasentry", "nxcansas_1Dand2D_multisasdata", "MAR07232_rest", From 881a7d163e9f08d1dba51f238e5d9a71310155ca Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 17 Mar 2025 14:34:42 +0000 Subject: [PATCH 173/675] Instrument is a data class --- sasdata/metadata.py | 10 +++---- sasdata/temp_hdf5_reader.py | 60 +++++++++++-------------------------- 2 files changed, 23 insertions(+), 47 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index d52c40c88..abe4db7f2 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -290,12 +290,12 @@ def summary(self) -> str: f" Transmission: {self.transmission.value}\n") +@dataclass class Instrument: - def __init__(self, target: AccessorTarget, collimations: list[Collimation], source: Source): - self.aperture = Aperture(target.with_path_prefix("sasaperture|aperture")) - self.collimations = collimations - self.detector = Detector(target.with_path_prefix("sasdetector|detector")) - self.source = source + collimations : list[Collimation] + source : Source + detector : list[Detector] + def summary(self): return ( self.aperture.summary() + diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 2fd2fa227..467f8de1c 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -108,29 +108,6 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: return output -<<<<<<< HEAD -||||||| parent of a1a66a4a (Parse source) -def parse_apertures(node) -> list[Aperture]: - result = [] - aps = [a for a in node if "aperture" in a] - for ap in aps: - distance = None - size = None - if "distance" in node[ap]: - distance = node[ap]["distance"] - if "size" in node[ap]: - x = y = z = None - if "x" in node[ap]: - x = node[ap]["size"]["x"] - if "y" in node[ap]: - y = node[ap]["size"]["y"] - if "z" in node[ap]: - z = node[ap]["size"]["z"] - if x is not None or y is not None or z is not None: - size = (x, y, z) - result.append(Aperture(distance=distance, size=size, size_name=size_name, name=name, apType=apType)) - return result -======= def parse_apertures(node) -> list[Aperture]: result = [] @@ -152,25 +129,7 @@ def parse_apertures(node) -> list[Aperture]: size = (x, y, z) result.append(Aperture(distance=distance, size=size, size_name=size_name, name=name, apType=apType)) return result ->>>>>>> a1a66a4a (Parse source) -<<<<<<< HEAD -def parse_collimations(node) -> list[Collimation]: - if "sasinstrument" not in node["sasentry01"]: - return [] - return [ - Collimation(name=None, length=x) - for x in node["sasentry01"]["sasinstrument"]["sascollimation01"] - ] -||||||| parent of a1a66a4a (Parse source) - -def parse_collimation(node) -> Collimation: - if "length" in node: - length = node["length"] - else: - length = None - return Collimation(length=length, apertures=parse_apertures(node)) -======= def parse_source(node) -> Source: beam_shape = None @@ -206,10 +165,10 @@ def parse_collimation(node) -> Collimation: else: length = None return Collimation(length=length, apertures=parse_apertures(node)) ->>>>>>> a1a66a4a (Parse source) def parse_instrument(raw, node) -> Instrument: +<<<<<<< HEAD <<<<<<< HEAD collimations = parse_collimations(node) return Instrument(raw, collimations=collimations) @@ -225,9 +184,26 @@ def parse_instrument(raw, node) -> Instrument: for x in node if "collimation" in x ] +||||||| parent of aedb35f2 (Instrument is a data class) + collimations = [ + parse_collimation(node[x]) + for x in node + if "collimation" in x + ] +======= +>>>>>>> aedb35f2 (Instrument is a data class) return Instrument( +<<<<<<< HEAD raw, collimations=collimations, +||||||| parent of aedb35f2 (Instrument is a data class) + raw, + collimations=collimations, + detector=[parse_detector(node[d]) for d in node if "detector" in d], +======= + collimations= [parse_collimation(node[x]) for x in node if "collimation" in x], + detector=[parse_detector(node[d]) for d in node if "detector" in d], +>>>>>>> aedb35f2 (Instrument is a data class) source=parse_source(node["sassource"]), ) >>>>>>> a1a66a4a (Parse source) From f90477c4cbce8f648e7b44273c49f4841a06172c Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 17 Mar 2025 16:41:32 +0000 Subject: [PATCH 174/675] Metadata is a dataclass --- sasdata/metadata.py | 45 +++------------ sasdata/temp_hdf5_reader.py | 55 +++---------------- .../sasdataloader/reference/MAR07232_rest.txt | 6 -- .../nxcansas_1Dand2D_multisasdata.txt | 12 ++++ .../nxcansas_1Dand2D_multisasentry.txt | 9 +-- test/sasdataloader/reference/x25000_no_di.txt | 6 -- 6 files changed, 33 insertions(+), 100 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index abe4db7f2..1597535ee 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -254,41 +254,6 @@ def summary(self): f" Notes: {self.notes.value}\n" ) -class TransmissionSpectrum: - """ - Class that holds information about transmission spectrum - for white beams and spallation sources. - """ - def __init__(self, target_object: AccessorTarget): - # TODO: Needs to be multiple instances - self.name = StringAccessor(target_object, "name") - self.timestamp = StringAccessor(target_object, "timestamp") - - # Wavelength (float) [A] - self.wavelength = LengthAccessor[ArrayLike](target_object, - "wavelength", - "wavelength.units") - - # Transmission (float) [unit less] - self.transmission = DimensionlessAccessor[ArrayLike](target_object, - "transmission", - "units", - default_unit=units.none) - - # Transmission Deviation (float) [unit less] - self.transmission_deviation = DimensionlessAccessor[ArrayLike](target_object, - "transmission_deviation", - "transmission_deviation.units", - default_unit=units.none) - - - def summary(self) -> str: - return (f"Transmission Spectrum:\n" - f" Name: {self.name.value}\n" - f" Timestamp: {self.timestamp.value}\n" - f" Wavelengths: {self.wavelength.value}\n" - f" Transmission: {self.transmission.value}\n") - @dataclass class Instrument: @@ -327,6 +292,7 @@ def decode_string(data): else: return str(data) +@dataclass(kw_only=True) class Metadata: def __init__(self, target: AccessorTarget, instrument: Instrument): self._target = target @@ -343,6 +309,12 @@ def __init__(self, target: AccessorTarget, instrument: Instrument): self.title: str = decode_string(self._title.value) self.run: str = decode_string(self._run.value) self.definition: str = decode_string(self._definition.value) + title: Optional[str] + run: list[str] + definition: Optional[str] + process: list[str] + sample: Optional[Sample] + instrument: Optional[Instrument] def summary(self): return ( @@ -353,5 +325,4 @@ def summary(self): f"Definition: {self.title}\n" + self.process.summary() + self.sample.summary() + - (self.instrument.summary() if self.instrument else "") + - self.transmission_spectrum.summary()) + (self.instrument.summary() if self.instrument else "")) diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 467f8de1c..a8f275b74 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -168,45 +168,16 @@ def parse_collimation(node) -> Collimation: def parse_instrument(raw, node) -> Instrument: -<<<<<<< HEAD -<<<<<<< HEAD - collimations = parse_collimations(node) - return Instrument(raw, collimations=collimations) -||||||| parent of a1a66a4a (Parse source) - if "sasinstrument" in node: - collimations = [parse_collimation(node["sasinstrument"][x]) for x in node["sasinstrument"] if "collimation" in x] - else: - collimations=[] - return Instrument(raw, collimations=collimations) -======= collimations = [ parse_collimation(node[x]) for x in node if "collimation" in x ] -||||||| parent of aedb35f2 (Instrument is a data class) - collimations = [ - parse_collimation(node[x]) - for x in node - if "collimation" in x - ] -======= ->>>>>>> aedb35f2 (Instrument is a data class) return Instrument( -<<<<<<< HEAD - raw, - collimations=collimations, -||||||| parent of aedb35f2 (Instrument is a data class) - raw, - collimations=collimations, - detector=[parse_detector(node[d]) for d in node if "detector" in d], -======= collimations= [parse_collimation(node[x]) for x in node if "collimation" in x], detector=[parse_detector(node[d]) for d in node if "detector" in d], ->>>>>>> aedb35f2 (Instrument is a data class) source=parse_source(node["sassource"]), ) ->>>>>>> a1a66a4a (Parse source) def load_data(filename) -> list[SasData]: @@ -235,31 +206,21 @@ def load_data(filename) -> list[SasData]: else: raw_metadata[key] = recurse_hdf5(component) - instrument = None - if "sasinstrument" in f["sasentry01"]: - instrument = parse_instrument( - AccessorTarget(SASDataGroup("root", raw_metadata)).with_path_prefix( - "sasinstrument|instrument" - ), - f["sasentry01"]["sasinstrument"], - ) + instrument = opt_parse(f["sasentry01"], "sasinstrument", parse_instrument) + sample = opt_parse(f["sasentry01"], "sassample", parse_sample) + process = [parse_process(f["sasentry01"][p]) for p in f["sasentry01"] if "sasprocess" in p] + title = opt_parse(f["sasentry01"], "title", parse_string) + run = [parse_string(f["sasentry01"][r]) for r in f["sasentry01"] if "run" in r] + definition = opt_parse(f["sasentry01"], "definition", parse_string) + + metadata = Metadata(process=process, instrument=instrument, sample=sample, title=title, run=run, definition=definition) loaded_data.append( SasData( name=root_key, data_contents=data_contents, raw_metadata=SASDataGroup("root", raw_metadata), -<<<<<<< HEAD - instrument=parse_instrument( - AccessorTarget(SASDataGroup("root", raw_metadata)).with_path_prefix("sasinstrument|instrument"), f - ), -||||||| parent of a1a66a4a (Parse source) - instrument=parse_instrument( - AccessorTarget(SASDataGroup("root", raw_metadata)).with_path_prefix("sasinstrument|instrument"), f["sasentry01"] - ), -======= instrument=instrument, ->>>>>>> a1a66a4a (Parse source) verbose=False, ) ) diff --git a/test/sasdataloader/reference/MAR07232_rest.txt b/test/sasdataloader/reference/MAR07232_rest.txt index 97c4c5045..881665f79 100644 --- a/test/sasdataloader/reference/MAR07232_rest.txt +++ b/test/sasdataloader/reference/MAR07232_rest.txt @@ -38,9 +38,3 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None -Transmission Spectrum: - Name: None - Timestamp: None - Wavelengths: None - Transmission: None - diff --git a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt index d68cddeb8..f3b00b0cf 100644 --- a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt +++ b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt @@ -38,9 +38,21 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None +<<<<<<< HEAD Transmission Spectrum: Name: None Timestamp: None Wavelengths: None Transmission: None + +||||||| parent of c9f83a89 (Metadata is a dataclass) +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None + +======= + +>>>>>>> c9f83a89 (Metadata is a dataclass) \ No newline at end of file diff --git a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt index 8a8eeda8b..a12583e88 100644 --- a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt +++ b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt @@ -129,7 +129,8 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None - -======= - ->>>>>>> 556c3b69 (Add tests for dataset) \ No newline at end of file +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None diff --git a/test/sasdataloader/reference/x25000_no_di.txt b/test/sasdataloader/reference/x25000_no_di.txt index cfb87ad49..08a7142e2 100644 --- a/test/sasdataloader/reference/x25000_no_di.txt +++ b/test/sasdataloader/reference/x25000_no_di.txt @@ -38,9 +38,3 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None -Transmission Spectrum: - Name: None - Timestamp: None - Wavelengths: None - Transmission: None - From e84b1164f6f8b3d7135eab041f2f8247d34f5b91 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 23 Jan 2025 10:38:26 -0500 Subject: [PATCH 175/675] Create database --- sasdata/fair_database/db.sqlite3 | 0 .../fair_database/fair_database/__init__.py | 0 sasdata/fair_database/fair_database/asgi.py | 16 +++ .../fair_database/fair_database/settings.py | 123 ++++++++++++++++++ sasdata/fair_database/fair_database/urls.py | 22 ++++ sasdata/fair_database/fair_database/wsgi.py | 16 +++ sasdata/fair_database/manage.py | 22 ++++ 7 files changed, 199 insertions(+) create mode 100644 sasdata/fair_database/db.sqlite3 create mode 100644 sasdata/fair_database/fair_database/__init__.py create mode 100644 sasdata/fair_database/fair_database/asgi.py create mode 100644 sasdata/fair_database/fair_database/settings.py create mode 100644 sasdata/fair_database/fair_database/urls.py create mode 100644 sasdata/fair_database/fair_database/wsgi.py create mode 100755 sasdata/fair_database/manage.py diff --git a/sasdata/fair_database/db.sqlite3 b/sasdata/fair_database/db.sqlite3 new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/fair_database/fair_database/__init__.py b/sasdata/fair_database/fair_database/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/fair_database/fair_database/asgi.py b/sasdata/fair_database/fair_database/asgi.py new file mode 100644 index 000000000..f47618a3e --- /dev/null +++ b/sasdata/fair_database/fair_database/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for fair_database project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fair_database.settings') + +application = get_asgi_application() diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py new file mode 100644 index 000000000..2ff2160cb --- /dev/null +++ b/sasdata/fair_database/fair_database/settings.py @@ -0,0 +1,123 @@ +""" +Django settings for fair_database project. + +Generated by 'django-admin startproject' using Django 5.1.5. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.1/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure--f-t5!pdhq&4)^&xenr^k0e8n%-h06jx9d0&2kft(!+1$xzig)' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'fair_database.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'fair_database.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/5.1/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.1/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.1/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/sasdata/fair_database/fair_database/urls.py b/sasdata/fair_database/fair_database/urls.py new file mode 100644 index 000000000..30dc31227 --- /dev/null +++ b/sasdata/fair_database/fair_database/urls.py @@ -0,0 +1,22 @@ +""" +URL configuration for fair_database project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path + +urlpatterns = [ + path('admin/', admin.site.urls), +] diff --git a/sasdata/fair_database/fair_database/wsgi.py b/sasdata/fair_database/fair_database/wsgi.py new file mode 100644 index 000000000..cb0870868 --- /dev/null +++ b/sasdata/fair_database/fair_database/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for fair_database project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fair_database.settings') + +application = get_wsgi_application() diff --git a/sasdata/fair_database/manage.py b/sasdata/fair_database/manage.py new file mode 100755 index 000000000..c74d5f9c3 --- /dev/null +++ b/sasdata/fair_database/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fair_database.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() From 833180d0ee5fbfade3fe2f49e319bc659ee7a7ee Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 23 Jan 2025 12:50:03 -0500 Subject: [PATCH 176/675] Create data application in database --- sasdata/fair_database/data/__init__.py | 0 sasdata/fair_database/data/admin.py | 3 +++ sasdata/fair_database/data/apps.py | 6 ++++++ sasdata/fair_database/data/migrations/__init__.py | 0 sasdata/fair_database/data/models.py | 3 +++ sasdata/fair_database/data/tests.py | 3 +++ sasdata/fair_database/data/urls.py | 7 +++++++ sasdata/fair_database/data/views.py | 7 +++++++ sasdata/fair_database/fair_database/urls.py | 3 ++- 9 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 sasdata/fair_database/data/__init__.py create mode 100644 sasdata/fair_database/data/admin.py create mode 100644 sasdata/fair_database/data/apps.py create mode 100644 sasdata/fair_database/data/migrations/__init__.py create mode 100644 sasdata/fair_database/data/models.py create mode 100644 sasdata/fair_database/data/tests.py create mode 100644 sasdata/fair_database/data/urls.py create mode 100644 sasdata/fair_database/data/views.py diff --git a/sasdata/fair_database/data/__init__.py b/sasdata/fair_database/data/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/fair_database/data/admin.py b/sasdata/fair_database/data/admin.py new file mode 100644 index 000000000..8c38f3f3d --- /dev/null +++ b/sasdata/fair_database/data/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/sasdata/fair_database/data/apps.py b/sasdata/fair_database/data/apps.py new file mode 100644 index 000000000..f6b7ef7fa --- /dev/null +++ b/sasdata/fair_database/data/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class DataConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'data' diff --git a/sasdata/fair_database/data/migrations/__init__.py b/sasdata/fair_database/data/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py new file mode 100644 index 000000000..71a836239 --- /dev/null +++ b/sasdata/fair_database/data/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py new file mode 100644 index 000000000..7ce503c2d --- /dev/null +++ b/sasdata/fair_database/data/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/sasdata/fair_database/data/urls.py b/sasdata/fair_database/data/urls.py new file mode 100644 index 000000000..97ba2b8fb --- /dev/null +++ b/sasdata/fair_database/data/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path("list/", views.list_data, name="list public files"), +] \ No newline at end of file diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py new file mode 100644 index 000000000..08219af8d --- /dev/null +++ b/sasdata/fair_database/data/views.py @@ -0,0 +1,7 @@ +from django.shortcuts import render + +# Create your views here +from django.http import HttpResponse + +def list_data(request): + return HttpResponse("Hello World! This is going to display data later.") \ No newline at end of file diff --git a/sasdata/fair_database/fair_database/urls.py b/sasdata/fair_database/fair_database/urls.py index 30dc31227..e223d9d81 100644 --- a/sasdata/fair_database/fair_database/urls.py +++ b/sasdata/fair_database/fair_database/urls.py @@ -15,8 +15,9 @@ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import path +from django.urls import include, path urlpatterns = [ + path('data/', include("data.urls")), path('admin/', admin.site.urls), ] From 7a003a7d8616d201cf05f4c777320885560d21e9 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 23 Jan 2025 13:31:55 -0500 Subject: [PATCH 177/675] Add Data model class from webfit --- .../data/migrations/0001_initial.py | 28 +++++++++++++++++++ sasdata/fair_database/data/models.py | 19 +++++++++++++ .../fair_database/fair_database/settings.py | 1 + 3 files changed, 48 insertions(+) create mode 100644 sasdata/fair_database/data/migrations/0001_initial.py diff --git a/sasdata/fair_database/data/migrations/0001_initial.py b/sasdata/fair_database/data/migrations/0001_initial.py new file mode 100644 index 000000000..1c7c9df3c --- /dev/null +++ b/sasdata/fair_database/data/migrations/0001_initial.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.5 on 2025-01-23 18:41 + +import django.core.files.storage +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Data', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('file_name', models.CharField(blank=True, default=None, help_text='File name', max_length=200, null=True)), + ('file', models.FileField(default=None, help_text='This is a file', storage=django.core.files.storage.FileSystemStorage(), upload_to='uploaded_files')), + ('is_public', models.BooleanField(default=False, help_text='opt in to submit your data into example pool')), + ('current_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 71a836239..e902193c3 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -1,3 +1,22 @@ from django.db import models +from django.contrib.auth.models import User +from django.core.files.storage import FileSystemStorage # Create your models here. +class Data(models.Model): + #username + current_user = models.ForeignKey(User, blank=True, + null=True, on_delete=models.CASCADE) + + #file name + file_name = models.CharField(max_length=200, default=None, + blank=True, null=True, help_text="File name") + + #imported data + #user can either import a file path or actual file + file = models.FileField(blank=False, default=None, help_text="This is a file", + upload_to="uploaded_files", storage=FileSystemStorage()) + + #is the data public? + is_public = models.BooleanField(default=False, + help_text= "opt in to submit your data into example pool") \ No newline at end of file diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index 2ff2160cb..42b466258 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -31,6 +31,7 @@ # Application definition INSTALLED_APPS = [ + 'data.apps.DataConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', From eec3942669406b9b9b43b694100b341cbe05c937 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 23 Jan 2025 13:49:58 -0500 Subject: [PATCH 178/675] Add sqlite file to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ff18e7a00..a24aa7e04 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ **/build /dist .mplconfig +**/db.sqlite3 # INSTALL.md recommends a venv that should not be committed venv From e7c9f9353c8ddf85698eb78d80aa1c3fa9e296c2 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 23 Jan 2025 14:30:32 -0500 Subject: [PATCH 179/675] Create urls for database. --- sasdata/fair_database/data/urls.py | 8 +++++++- sasdata/fair_database/data/views.py | 13 +++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/data/urls.py b/sasdata/fair_database/data/urls.py index 97ba2b8fb..cba7e3edc 100644 --- a/sasdata/fair_database/data/urls.py +++ b/sasdata/fair_database/data/urls.py @@ -3,5 +3,11 @@ from . import views urlpatterns = [ - path("list/", views.list_data, name="list public files"), + path("", views.list_data, name = "list public file_ids"), + path("/", views.list_data, name = "view users file_ids"), + path("load//", views.data_info, name = "views data using file id"), + + path("upload/", views.upload, name = "upload data into db"), + path("upload//", views.upload, name = "update file in data"), + path("/download/", views.download, name = "download data from db"), ] \ No newline at end of file diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 08219af8d..86da092b9 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -3,5 +3,14 @@ # Create your views here from django.http import HttpResponse -def list_data(request): - return HttpResponse("Hello World! This is going to display data later.") \ No newline at end of file +def list_data(request, username = None): + return HttpResponse("Hello World! This is going to display data later.") + +def data_info(request, db_id): + return HttpResponse("This is going to allow viewing data file %s." % db_id) + +def upload(request, db_id = None): + return HttpResponse("This is going to allow data uploads.") + +def download(request, data_id): + return HttpResponse("This is going to allow downloads of data %s." % data_id) \ No newline at end of file From 3262deebc49b533c187eddf122742777f01b1c43 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 23 Jan 2025 15:16:53 -0500 Subject: [PATCH 180/675] Add requirements.txt --- sasdata/fair_database/requirements.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 sasdata/fair_database/requirements.txt diff --git a/sasdata/fair_database/requirements.txt b/sasdata/fair_database/requirements.txt new file mode 100644 index 000000000..d80bd1382 --- /dev/null +++ b/sasdata/fair_database/requirements.txt @@ -0,0 +1,2 @@ +django +djangorestframework \ No newline at end of file From 4ea76eea144012c81317b0cfa8ff02cffcba2a3b Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 23 Jan 2025 15:46:32 -0500 Subject: [PATCH 181/675] View for listing data --- sasdata/fair_database/data/views.py | 24 +++++++++++++++++-- .../fair_database/fair_database/settings.py | 1 + 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 86da092b9..533b5654b 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -1,10 +1,30 @@ from django.shortcuts import render # Create your views here -from django.http import HttpResponse +from django.http import HttpResponse, HttpResponseBadRequest +from rest_framework.decorators import api_view +from rest_framework.response import Response +from .models import Data + +@api_view(['GET']) def list_data(request, username = None): - return HttpResponse("Hello World! This is going to display data later.") + if request.method == 'GET': + if username: + data_list = {"user_data_ids": {}} + if username == request.user.username and request.user.is_authenticated: + private_data = Data.objects.filter(current_user=request.user.id) + for x in private_data: + data_list["user_data_ids"][x.id] = x.file_name + else: + return HttpResponseBadRequest("user is not logged in, or username is not same as current user") + else: + public_data = Data.objects.filter(is_public=True) + data_list = {"public_data_ids": {}} + for x in public_data: + data_list["public_data_ids"][x.id] = x.file_name + return Response(data_list) + return HttpResponseBadRequest("not get method") def data_info(request, db_id): return HttpResponse("This is going to allow viewing data file %s." % db_id) diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index 42b466258..f262b2627 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -38,6 +38,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'rest_framework', ] MIDDLEWARE = [ From 7902e551d203cf36893026305e065ccd95d5ea9f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 23 Jan 2025 16:16:45 -0500 Subject: [PATCH 182/675] View for specific data --- sasdata/fair_database/data/views.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 533b5654b..325a57a2e 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -1,10 +1,12 @@ from django.shortcuts import render +from django.shortcuts import get_object_or_404 # Create your views here from django.http import HttpResponse, HttpResponseBadRequest from rest_framework.decorators import api_view from rest_framework.response import Response +from sasdata.dataloader.loader import Loader from .models import Data @api_view(['GET']) @@ -26,8 +28,24 @@ def list_data(request, username = None): return Response(data_list) return HttpResponseBadRequest("not get method") +@api_view(['GET']) def data_info(request, db_id): - return HttpResponse("This is going to allow viewing data file %s." % db_id) + if request.method == 'GET': + loader = Loader() + data_db = get_object_or_404(Data, id=db_id) + if data_db.is_public: + data_list = loader.load(data_db.file.path) + contents = [str(data) for data in data_list] + return_data = {data_db.file_name: contents} + # rewrite with "user.is_authenticated" + elif (data_db.current_user == request.user) and request.user.is_authenticated: + data_list = loader.load(data_db.file.path) + contents = [str(data) for data in data_list] + return_data = {data_db.file_name: contents} + else: + return HttpResponseBadRequest("Database is either not public or wrong auth token") + return Response(return_data) + return HttpResponseBadRequest() def upload(request, db_id = None): return HttpResponse("This is going to allow data uploads.") From 92c600eaef77ff38d773523df23a5d6265dccf6a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 24 Jan 2025 11:28:52 -0500 Subject: [PATCH 183/675] Remove db.sqlite3 --- sasdata/fair_database/db.sqlite3 | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 sasdata/fair_database/db.sqlite3 diff --git a/sasdata/fair_database/db.sqlite3 b/sasdata/fair_database/db.sqlite3 deleted file mode 100644 index e69de29bb..000000000 From 001464f94daf910cb2245fa2c9de2bb5d65864d3 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 24 Jan 2025 15:18:19 -0500 Subject: [PATCH 184/675] Enable file upload --- sasdata/fair_database/data/forms.py | 8 ++++ sasdata/fair_database/data/serializers.py | 8 ++++ sasdata/fair_database/data/urls.py | 4 +- sasdata/fair_database/data/views.py | 43 +++++++++++++++++-- .../fair_database/fair_database/settings.py | 11 ++++- 5 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 sasdata/fair_database/data/forms.py create mode 100644 sasdata/fair_database/data/serializers.py diff --git a/sasdata/fair_database/data/forms.py b/sasdata/fair_database/data/forms.py new file mode 100644 index 000000000..e336efabd --- /dev/null +++ b/sasdata/fair_database/data/forms.py @@ -0,0 +1,8 @@ +from django import forms +from .models import Data + +# Create the form class. +class DataForm(forms.ModelForm): + class Meta: + model = Data + fields = ["file", "is_public"] \ No newline at end of file diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py new file mode 100644 index 000000000..c90249c36 --- /dev/null +++ b/sasdata/fair_database/data/serializers.py @@ -0,0 +1,8 @@ +from rest_framework import serializers + +from .models import Data + +class DataSerializer(serializers.ModelSerializer): + class Meta: + model = Data + fields = "__all__" \ No newline at end of file diff --git a/sasdata/fair_database/data/urls.py b/sasdata/fair_database/data/urls.py index cba7e3edc..abe4ffdb3 100644 --- a/sasdata/fair_database/data/urls.py +++ b/sasdata/fair_database/data/urls.py @@ -3,8 +3,8 @@ from . import views urlpatterns = [ - path("", views.list_data, name = "list public file_ids"), - path("/", views.list_data, name = "view users file_ids"), + path("list/", views.list_data, name = "list public file_ids"), + path("list//", views.list_data, name = "view users file_ids"), path("load//", views.data_info, name = "views data using file id"), path("upload/", views.upload, name = "upload data into db"), diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 325a57a2e..898041e1f 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -1,13 +1,17 @@ +import os + from django.shortcuts import render from django.shortcuts import get_object_or_404 # Create your views here -from django.http import HttpResponse, HttpResponseBadRequest +from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden from rest_framework.decorators import api_view from rest_framework.response import Response from sasdata.dataloader.loader import Loader +from .serializers import DataSerializer from .models import Data +from .forms import DataForm @api_view(['GET']) def list_data(request, username = None): @@ -47,8 +51,41 @@ def data_info(request, db_id): return Response(return_data) return HttpResponseBadRequest() -def upload(request, db_id = None): - return HttpResponse("This is going to allow data uploads.") +@api_view(['POST', 'PUT']) +def upload(request, data_id = None, version = None): + #saves file + if request.method == 'POST': + form = DataForm(request.data, request.FILES) + if form.is_valid(): + form.save() + db = Data.objects.get(pk = form.instance.pk) + + if request.user.is_authenticated: + serializer = DataSerializer(db, data={"file_name":os.path.basename(form.instance.file.path), "current_user" : request.user.id}) + else: + serializer = DataSerializer(db, data={"file_name":os.path.basename(form.instance.file.path)}) + + + #saves or updates file + elif request.method == 'PUT': + #require data_id + if data_id != None and request.user: + if request.user.is_authenticated: + db = get_object_or_404(Data, current_user = request.user.id, id = data_id) + form = DataForm(request.data, request.FILES, instance=db) + if form.is_valid(): + form.save() + serializer = DataSerializer(db, data={"file_name":os.path.basename(form.instance.file.path)}, partial = True) + else: + return HttpResponseForbidden("user is not logged in") + else: + return HttpResponseBadRequest() + + if serializer.is_valid(): + serializer.save() + #TODO get warnings/errors later + return_data = {"current_user":request.user.username, "authenticated" : request.user.is_authenticated, "file_id" : db.id, "file_alternative_name":serializer.data["file_name"],"is_public" : serializer.data["is_public"]} + return Response(return_data) def download(request, data_id): return HttpResponse("This is going to allow downloads of data %s." % data_id) \ No newline at end of file diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index f262b2627..61ee9dbae 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -10,6 +10,7 @@ https://docs.djangoproject.com/en/5.1/ref/settings/ """ +import os from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -115,9 +116,15 @@ # Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/5.1/howto/static-files/ +# https://docs.djangoproject.com/en/4.2/howto/static-files/ -STATIC_URL = 'static/' + +STATIC_ROOT = os.path.join(BASE_DIR, 'static') +STATIC_URL = '/static/' + +#instead of doing this, create a create a new media_root +MEDIA_ROOT = os.path.join(BASE_DIR, "media") +MEDIA_URL = '/media/' # Default primary key field type # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field From c70aab0aac745494d0abb2ae230f81286099bf0e Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 24 Jan 2025 15:30:38 -0500 Subject: [PATCH 185/675] File upload test script --- .../fair_database/upload_example_data.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 sasdata/fair_database/fair_database/upload_example_data.py diff --git a/sasdata/fair_database/fair_database/upload_example_data.py b/sasdata/fair_database/fair_database/upload_example_data.py new file mode 100644 index 000000000..bc9164c0c --- /dev/null +++ b/sasdata/fair_database/fair_database/upload_example_data.py @@ -0,0 +1,41 @@ +import os +import logging +import requests + +from glob import glob + +EXAMPLE_DATA_DIR = os.environ.get("EXAMPLE_DATA_DIR", '../../example_data') + +def parse_1D(): + dir_1d = os.path.join(EXAMPLE_DATA_DIR, "1d_data") + if not os.path.isdir(dir_1d): + logging.error("1D Data directory not found at: {}".format(dir_1d)) + return + for file_path in glob(os.path.join(dir_1d, "*")): + upload_file(file_path) + +def parse_2D(): + dir_2d = os.path.join(EXAMPLE_DATA_DIR, "2d_data") + if not os.path.isdir(dir_2d): + logging.error("2D Data directory not found at: {}".format(dir_2d)) + return + for file_path in glob(os.path.join(dir_2d, "*")): + upload_file(file_path) + +def parse_sesans(): + sesans_dir = os.path.join(EXAMPLE_DATA_DIR, "sesans_data") + if not os.path.isdir(sesans_dir): + logging.error("Sesans Data directory not found at: {}".format(sesans_dir)) + return + for file_path in glob(os.path.join(sesans_dir, "*")): + upload_file(file_path) + +def upload_file(file_path): + url = 'http://localhost:8000/data/upload/' + file = open(file_path, 'rb') + requests.request('POST', url, data={'is_public': True}, files={'file':file}) + +if __name__ == '__main__': + parse_1D() + parse_2D() + parse_sesans() From 486e9aaef155fdfc8ed240f5f4965f15203886bf Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 27 Jan 2025 10:43:17 -0500 Subject: [PATCH 186/675] Add version to url --- sasdata/fair_database/data/views.py | 6 +++--- sasdata/fair_database/fair_database/upload_example_data.py | 2 +- sasdata/fair_database/fair_database/urls.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 898041e1f..56206c2f4 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -14,7 +14,7 @@ from .forms import DataForm @api_view(['GET']) -def list_data(request, username = None): +def list_data(request, username = None, version = None): if request.method == 'GET': if username: data_list = {"user_data_ids": {}} @@ -33,7 +33,7 @@ def list_data(request, username = None): return HttpResponseBadRequest("not get method") @api_view(['GET']) -def data_info(request, db_id): +def data_info(request, db_id, version = None): if request.method == 'GET': loader = Loader() data_db = get_object_or_404(Data, id=db_id) @@ -87,5 +87,5 @@ def upload(request, data_id = None, version = None): return_data = {"current_user":request.user.username, "authenticated" : request.user.is_authenticated, "file_id" : db.id, "file_alternative_name":serializer.data["file_name"],"is_public" : serializer.data["is_public"]} return Response(return_data) -def download(request, data_id): +def download(request, data_id, version = None): return HttpResponse("This is going to allow downloads of data %s." % data_id) \ No newline at end of file diff --git a/sasdata/fair_database/fair_database/upload_example_data.py b/sasdata/fair_database/fair_database/upload_example_data.py index bc9164c0c..bf014cf20 100644 --- a/sasdata/fair_database/fair_database/upload_example_data.py +++ b/sasdata/fair_database/fair_database/upload_example_data.py @@ -31,7 +31,7 @@ def parse_sesans(): upload_file(file_path) def upload_file(file_path): - url = 'http://localhost:8000/data/upload/' + url = 'http://localhost:8000/v1/data/upload/' file = open(file_path, 'rb') requests.request('POST', url, data={'is_public': True}, files={'file':file}) diff --git a/sasdata/fair_database/fair_database/urls.py b/sasdata/fair_database/fair_database/urls.py index e223d9d81..e1f9149a1 100644 --- a/sasdata/fair_database/fair_database/urls.py +++ b/sasdata/fair_database/fair_database/urls.py @@ -15,9 +15,9 @@ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import include, path +from django.urls import include, path, re_path urlpatterns = [ - path('data/', include("data.urls")), - path('admin/', admin.site.urls), + re_path(r"^(?P(v1))/data/", include("data.urls")), + path("admin/", admin.site.urls), ] From 80d2095c7c44a033254435ce85d619a6b228c825 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 27 Jan 2025 11:22:05 -0500 Subject: [PATCH 187/675] Add file download functionality --- sasdata/fair_database/data/views.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 56206c2f4..b7da2ed6c 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -2,9 +2,7 @@ from django.shortcuts import render from django.shortcuts import get_object_or_404 - -# Create your views here -from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden +from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, Http404, FileResponse from rest_framework.decorators import api_view from rest_framework.response import Response @@ -87,5 +85,20 @@ def upload(request, data_id = None, version = None): return_data = {"current_user":request.user.username, "authenticated" : request.user.is_authenticated, "file_id" : db.id, "file_alternative_name":serializer.data["file_name"],"is_public" : serializer.data["is_public"]} return Response(return_data) +#downloads a file def download(request, data_id, version = None): - return HttpResponse("This is going to allow downloads of data %s." % data_id) \ No newline at end of file + if request.method == 'GET': + data = get_object_or_404(Data, id=data_id) + if not data.is_public: + # add session key later + if not request.user.is_authenticated: + return HttpResponseBadRequest("data is private, must log in") + # TODO add issues later + try: + file = open(data.file.path, 'rb') + except Exception as e: + return HttpResponseBadRequest(str(e)) + if file is None: + raise Http404("File not found.") + return FileResponse(file, as_attachment=True) + return HttpResponseBadRequest() \ No newline at end of file From e9451c93a59c0eb20f371bdc17565d2db4e5c793 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 27 Jan 2025 11:47:46 -0500 Subject: [PATCH 188/675] Allow admin page to view data --- sasdata/fair_database/data/admin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/admin.py b/sasdata/fair_database/data/admin.py index 8c38f3f3d..bfe8c7d93 100644 --- a/sasdata/fair_database/data/admin.py +++ b/sasdata/fair_database/data/admin.py @@ -1,3 +1,4 @@ from django.contrib import admin +from .models import Data -# Register your models here. +admin.site.register(Data) \ No newline at end of file From 1ebf1be0cdaf4a7a40080035956e2f54d49d5732 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 27 Jan 2025 14:11:54 -0500 Subject: [PATCH 189/675] Tests for data list from webfit --- sasdata/fair_database/data/tests.py | 28 +++++++++++++++++++++++++++- sasdata/fair_database/data/views.py | 1 - 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 7ce503c2d..d1f3ef133 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -1,3 +1,29 @@ +import os + from django.test import TestCase +from django.contrib.auth.models import User +from rest_framework.test import APIClient + +from .models import Data + +def find(filename): + return os.path.join(os.path.dirname(__file__), "../../example_data/1d_data", filename) + +class TestLists(TestCase): + def setUp(self): + public_test_data = Data.objects.create(id = 1, file_name = "cyl_400_40.txt", is_public = True) + public_test_data.file.save("cyl_400_40.txt", open(find("cyl_400_40.txt"), 'rb')) + self.user = User.objects.create_user(username="testUser", password="secret", id = 2) + private_test_data = Data.objects.create(id = 3, current_user = self.user, file_name = "cyl_400_20.txt", is_public = False) + private_test_data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), 'rb')) + self.client = APIClient() + self.client.force_authenticate(user=self.user) + + #working + def test_does_list_public(self): + request = self.client.get('/v1/data/list/') + self.assertEqual(request.data, {"public_data_ids":{1:"cyl_400_40.txt"}}) -# Create your tests here. + def test_does_list_user(self): + request = self.client.get('/v1/data/list/testUser/', user = self.user) + self.assertEqual(request.data, {"user_data_ids":{3:"cyl_400_20.txt"}}) \ No newline at end of file diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index b7da2ed6c..72f1fa3d5 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -1,6 +1,5 @@ import os -from django.shortcuts import render from django.shortcuts import get_object_or_404 from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, Http404, FileResponse from rest_framework.decorators import api_view From 5f9d88ad750d144b0d6ff9dd7a74e10128e1c5cf Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 27 Jan 2025 14:29:02 -0500 Subject: [PATCH 190/675] Tests for data upload from webfit --- sasdata/fair_database/data/tests.py | 72 ++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index d1f3ef133..e416ac840 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -1,8 +1,11 @@ import os +import shutil +from django.conf import settings from django.test import TestCase from django.contrib.auth.models import User -from rest_framework.test import APIClient +from rest_framework.test import APIClient, APITestCase +from rest_framework import status from .models import Data @@ -26,4 +29,69 @@ def test_does_list_public(self): def test_does_list_user(self): request = self.client.get('/v1/data/list/testUser/', user = self.user) - self.assertEqual(request.data, {"user_data_ids":{3:"cyl_400_20.txt"}}) \ No newline at end of file + self.assertEqual(request.data, {"user_data_ids":{3:"cyl_400_20.txt"}}) + + def test_does_load_data_info_public(self): + request = self.client.get('/v1/data/load/1/') + print(request.data) + self.assertEqual(request.status_code, status.HTTP_200_OK) + + def test_does_load_data_info_private(self): + request = self.client.get('/v1/data/load/3/') + print(request.data) + self.assertEqual(request.status_code, status.HTTP_200_OK) + + def tearDown(self): + shutil.rmtree(settings.MEDIA_ROOT) + +class TestingDatabase(APITestCase): + def setUp(self): + self.user = User.objects.create_user(username="testUser", password="secret", id = 1) + self.data = Data.objects.create(id = 2, current_user = self.user, file_name = "cyl_400_20.txt", is_public = False) + self.data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), 'rb')) + self.client = APIClient() + self.client.force_authenticate(user=self.user) + self.client2 = APIClient() + + def test_is_data_being_created(self): + file = open(find("cyl_400_40.txt"), 'rb') + data = { + "is_public":False, + "file":file + } + request = self.client.post('/v1/data/upload/', data=data) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(request.data, {"current_user":'testUser', "authenticated" : True, "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) + Data.objects.get(id = 3).delete() + + def test_is_data_being_created_no_user(self): + file = open(find("cyl_400_40.txt"), 'rb') + data = { + "is_public":False, + "file":file + } + request = self.client2.post('/v1/data/upload/', data=data) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(request.data, {"current_user":'', "authenticated" : False, "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) + Data.objects.get(id = 3).delete() + + def test_does_file_upload_update(self): + file = open(find("cyl_400_40.txt")) + data = { + "file":file, + "is_public":False + } + request = self.client.put('/v1/data/upload/2/', data = data) + request2 = self.client2.put('/v1/data/upload/2/', data = data) + self.assertEqual(request.data, {"current_user":'testUser', "authenticated" : True, "file_id" : 2, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + Data.objects.get(id = 2).delete() + + #TODO write tests for download + ''' + def test_does_download(self): + self.client.get() + ''' + + def tearDown(self): + shutil.rmtree(settings.MEDIA_ROOT) \ No newline at end of file From a7b05bca23c1b75677f38962e27d40a504883a62 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 27 Jan 2025 14:58:16 -0500 Subject: [PATCH 191/675] Disallow downloading unowned private data --- sasdata/fair_database/data/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 72f1fa3d5..6cba51919 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -92,6 +92,8 @@ def download(request, data_id, version = None): # add session key later if not request.user.is_authenticated: return HttpResponseBadRequest("data is private, must log in") + if not request.user == data.current_user: + return HttpResponseBadRequest("data is private") # TODO add issues later try: file = open(data.file.path, 'rb') From 8d73613d2e760da717e5806a534f5fcc734a0933 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 3 Feb 2025 12:57:54 -0500 Subject: [PATCH 192/675] Create app for authentication-related stuff --- sasdata/fair_database/user_app/__init__.py | 0 sasdata/fair_database/user_app/admin.py | 3 +++ sasdata/fair_database/user_app/apps.py | 6 ++++++ sasdata/fair_database/user_app/migrations/__init__.py | 0 sasdata/fair_database/user_app/models.py | 3 +++ sasdata/fair_database/user_app/tests.py | 3 +++ sasdata/fair_database/user_app/views.py | 3 +++ 7 files changed, 18 insertions(+) create mode 100644 sasdata/fair_database/user_app/__init__.py create mode 100644 sasdata/fair_database/user_app/admin.py create mode 100644 sasdata/fair_database/user_app/apps.py create mode 100644 sasdata/fair_database/user_app/migrations/__init__.py create mode 100644 sasdata/fair_database/user_app/models.py create mode 100644 sasdata/fair_database/user_app/tests.py create mode 100644 sasdata/fair_database/user_app/views.py diff --git a/sasdata/fair_database/user_app/__init__.py b/sasdata/fair_database/user_app/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/fair_database/user_app/admin.py b/sasdata/fair_database/user_app/admin.py new file mode 100644 index 000000000..8c38f3f3d --- /dev/null +++ b/sasdata/fair_database/user_app/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/sasdata/fair_database/user_app/apps.py b/sasdata/fair_database/user_app/apps.py new file mode 100644 index 000000000..f2d1d4178 --- /dev/null +++ b/sasdata/fair_database/user_app/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class UserAppConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'user_app' diff --git a/sasdata/fair_database/user_app/migrations/__init__.py b/sasdata/fair_database/user_app/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/fair_database/user_app/models.py b/sasdata/fair_database/user_app/models.py new file mode 100644 index 000000000..71a836239 --- /dev/null +++ b/sasdata/fair_database/user_app/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py new file mode 100644 index 000000000..7ce503c2d --- /dev/null +++ b/sasdata/fair_database/user_app/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/sasdata/fair_database/user_app/views.py b/sasdata/fair_database/user_app/views.py new file mode 100644 index 000000000..91ea44a21 --- /dev/null +++ b/sasdata/fair_database/user_app/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. From d20b2865cb191ee9d5caa0d60b47e039a3cd1f68 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 3 Feb 2025 14:53:12 -0500 Subject: [PATCH 193/675] Install allauth --- sasdata/fair_database/fair_database/settings.py | 9 +++++++++ sasdata/fair_database/fair_database/urls.py | 1 + 2 files changed, 10 insertions(+) diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index 61ee9dbae..26eafcc45 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -40,6 +40,10 @@ 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', + 'allauth', + 'allauth.account', + 'allauth.socialaccount', + 'allauth.socialaccount.providers.orcid', ] MIDDLEWARE = [ @@ -50,6 +54,7 @@ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'allauth.account.middleware.AccountMiddleware', ] ROOT_URLCONF = 'fair_database.urls' @@ -72,6 +77,10 @@ WSGI_APPLICATION = 'fair_database.wsgi.application' +AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', + 'allauth.account.auth_backends.AuthenticationBackend', +) # Database # https://docs.djangoproject.com/en/5.1/ref/settings/#databases diff --git a/sasdata/fair_database/fair_database/urls.py b/sasdata/fair_database/fair_database/urls.py index e1f9149a1..dae6d9a77 100644 --- a/sasdata/fair_database/fair_database/urls.py +++ b/sasdata/fair_database/fair_database/urls.py @@ -20,4 +20,5 @@ urlpatterns = [ re_path(r"^(?P(v1))/data/", include("data.urls")), path("admin/", admin.site.urls), + path("accounts/", include("allauth.urls")), ] From bbfc879e1798b6beb03c48a9e9d451eeba51f8b6 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 3 Feb 2025 16:13:40 -0500 Subject: [PATCH 194/675] Headless allauth --- sasdata/fair_database/fair_database/settings.py | 1 + sasdata/fair_database/fair_database/urls.py | 1 + 2 files changed, 2 insertions(+) diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index 26eafcc45..aafc984fe 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -42,6 +42,7 @@ 'rest_framework', 'allauth', 'allauth.account', + 'allauth.headless', 'allauth.socialaccount', 'allauth.socialaccount.providers.orcid', ] diff --git a/sasdata/fair_database/fair_database/urls.py b/sasdata/fair_database/fair_database/urls.py index dae6d9a77..43acdfc5a 100644 --- a/sasdata/fair_database/fair_database/urls.py +++ b/sasdata/fair_database/fair_database/urls.py @@ -21,4 +21,5 @@ re_path(r"^(?P(v1))/data/", include("data.urls")), path("admin/", admin.site.urls), path("accounts/", include("allauth.urls")), + path("_allauth/", include("allauth.headless.urls")), ] From bb915d72c117479fab61204b30ad8b840641acb3 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 3 Feb 2025 16:14:14 -0500 Subject: [PATCH 195/675] Test for download --- sasdata/fair_database/data/tests.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index e416ac840..b9140f60f 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -88,10 +88,15 @@ def test_does_file_upload_update(self): Data.objects.get(id = 2).delete() #TODO write tests for download - ''' + def test_does_download(self): - self.client.get() - ''' + request = self.client.get('/v1/data/2/download/') + print('Starting download tests') + self.assertEqual(request.status_code, status.HTTP_200_OK) + file_contents = b''.join(request.streaming_content) + test_file = open(find('cyl_400_20.txt'), 'rb') + self.assertEqual(file_contents, test_file.read()) + def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT) \ No newline at end of file From 7ec9856aae65f89e0bfbdba585650e5e499bc6f6 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 3 Feb 2025 16:24:58 -0500 Subject: [PATCH 196/675] Change unauthorized download response to 403 forbidden --- sasdata/fair_database/data/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 6cba51919..958724ab4 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -85,15 +85,16 @@ def upload(request, data_id = None, version = None): return Response(return_data) #downloads a file +@api_view(['GET']) def download(request, data_id, version = None): if request.method == 'GET': data = get_object_or_404(Data, id=data_id) if not data.is_public: # add session key later if not request.user.is_authenticated: - return HttpResponseBadRequest("data is private, must log in") + return HttpResponseForbidden("data is private, must log in") if not request.user == data.current_user: - return HttpResponseBadRequest("data is private") + return HttpResponseForbidden("data is private") # TODO add issues later try: file = open(data.file.path, 'rb') From 6ed1faeb2b1bd6f3a65ca4b52a0151b2c57b7f65 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 3 Feb 2025 16:25:41 -0500 Subject: [PATCH 197/675] Add unauthorized download test --- sasdata/fair_database/data/tests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index b9140f60f..7add1db9f 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -91,8 +91,9 @@ def test_does_file_upload_update(self): def test_does_download(self): request = self.client.get('/v1/data/2/download/') - print('Starting download tests') + request2 = self.client2.get('/v1/data/2/download/') self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) file_contents = b''.join(request.streaming_content) test_file = open(find('cyl_400_20.txt'), 'rb') self.assertEqual(file_contents, test_file.read()) From 002a0845d83cf6628604b57189879b8dafc3678d Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 3 Feb 2025 16:36:47 -0500 Subject: [PATCH 198/675] Start authentication tests --- sasdata/fair_database/user_app/tests.py | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index 7ce503c2d..b761dede0 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -1,3 +1,36 @@ +import requests + from django.test import TestCase +from rest_framework import status +from rest_framework.test import RequestsClient # Create your tests here. +class AuthTests(TestCase): + + def setup(self): + self.client = RequestsClient() + + def test_register(self): + data = { + 'email': 'test@test.com', + 'username': 'testUser', + 'password': 'testPassword' + } + response = self.client.post('/_allauth/app/v1/auth/signup',data=data) + print(response.content) + self.assertEqual(response.status_code, status.HTTP_200_OK) + +#can register a user, user is w/in User model +# user is logged in after registration +# logged-in user can create Data, is data's current_user +# test log out + + +# Permissions +# Any user can access public data +# logged-in user can access and modify their own private data +# unauthenticated user cannot access private data +# unauthenticated user cannot modify data +# logged-in user cannot modify data other than their own +# logged-in user cannot access the private data of others + From ac3001dd409fb6e6b37ffa2fb14af53bb806be58 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 4 Feb 2025 14:32:55 -0500 Subject: [PATCH 199/675] Install dj-rest-auth --- sasdata/fair_database/fair_database/settings.py | 11 ++++++++++- sasdata/fair_database/fair_database/urls.py | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index aafc984fe..1d3f94550 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -39,14 +39,20 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'django.contrib.sites', 'rest_framework', + 'rest_framework.authtoken', 'allauth', 'allauth.account', - 'allauth.headless', + #'allauth.headless', 'allauth.socialaccount', 'allauth.socialaccount.providers.orcid', + 'dj_rest_auth', + 'dj_rest_auth.registration', ] +SITE_ID = 1 + MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', @@ -83,6 +89,9 @@ 'allauth.account.auth_backends.AuthenticationBackend', ) +HEADLESS_ONLY = False +ACCOUNT_EMAIL_VERIFICATION = 'none' + # Database # https://docs.djangoproject.com/en/5.1/ref/settings/#databases diff --git a/sasdata/fair_database/fair_database/urls.py b/sasdata/fair_database/fair_database/urls.py index 43acdfc5a..ebb1f4f85 100644 --- a/sasdata/fair_database/fair_database/urls.py +++ b/sasdata/fair_database/fair_database/urls.py @@ -22,4 +22,6 @@ path("admin/", admin.site.urls), path("accounts/", include("allauth.urls")), path("_allauth/", include("allauth.headless.urls")), + path('dj-rest-auth/', include('dj_rest_auth.urls')), + path('dj-rest-auth/registration/', include('dj_rest_auth.registration.urls')), ] From ee4a1d1364b16bff8b5699bc7075c2c3ce17bbea Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 4 Feb 2025 15:28:42 -0500 Subject: [PATCH 200/675] Tests for register and login --- sasdata/fair_database/fair_database/urls.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/fair_database/urls.py b/sasdata/fair_database/fair_database/urls.py index ebb1f4f85..ac37b3cb7 100644 --- a/sasdata/fair_database/fair_database/urls.py +++ b/sasdata/fair_database/fair_database/urls.py @@ -20,8 +20,8 @@ urlpatterns = [ re_path(r"^(?P(v1))/data/", include("data.urls")), path("admin/", admin.site.urls), - path("accounts/", include("allauth.urls")), - path("_allauth/", include("allauth.headless.urls")), + path("accounts/", include("allauth.urls")), #needed for social auth + #path("_allauth/", include("allauth.headless.urls")), path('dj-rest-auth/', include('dj_rest_auth.urls')), path('dj-rest-auth/registration/', include('dj_rest_auth.registration.urls')), ] From 4810d5fe11fc54866d150576c879a7151556dbfd Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 4 Feb 2025 15:54:43 -0500 Subject: [PATCH 201/675] Tests for logout --- sasdata/fair_database/user_app/tests.py | 55 ++++++++++++++++++++----- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index b761dede0..c0e19f754 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -2,24 +2,59 @@ from django.test import TestCase from rest_framework import status -from rest_framework.test import RequestsClient +from rest_framework.test import APIClient + +from django.contrib.auth.models import User # Create your tests here. class AuthTests(TestCase): - def setup(self): - self.client = RequestsClient() + def setUp(self): + self.client = APIClient() + self.register_data = { + "email": "email@domain.org", + "username": "testUser", + "password1": "sasview!", + "password2": "sasview!" + } + self.login_data = { + "username": "testUser", + "email": "email@domain.org", + "password": "sasview!" + } def test_register(self): - data = { - 'email': 'test@test.com', - 'username': 'testUser', - 'password': 'testPassword' - } - response = self.client.post('/_allauth/app/v1/auth/signup',data=data) - print(response.content) + response = self.client.post('/dj-rest-auth/registration/',data=self.register_data) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + user = User.objects.get(username="testUser") + self.assertEquals(user.email, self.register_data["email"]) + + def test_login(self): + user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + response = self.client.post('/dj-rest-auth/login', data=self.login_data) self.assertEqual(response.status_code, status.HTTP_200_OK) + def test_login_logout(self): + user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + self.client.post('/dj-rest-auth/login', data=self.login_data) + response = self.client.post('/dj-rest-auth/logout') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') + + def test_register_logout(self): + self.client.post('/dj-rest-auth/registration/', data=self.register_data) + response = self.client.post('/dj-rest-auth/logout') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') + + def test_register_login(self): + register_response = self.client.post('/dj-rest-auth/registration/', data=self.register_data) + logout_response = self.client.post('/dj-rest-auth/logout') + login_response = self.client.post('/dj-rest-auth/login', data=self.login_data) + self.assertEqual(register_response.status_code, status.HTTP_201_CREATED) + self.assertEqual(logout_response.status_code, status.HTTP_200_OK) + self.assertEqual(login_response.status_code, status.HTTP_200_OK) + #can register a user, user is w/in User model # user is logged in after registration # logged-in user can create Data, is data's current_user From 088835816f0a6a6f2fa8850b0288712f20bae3d4 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 4 Feb 2025 16:14:21 -0500 Subject: [PATCH 202/675] Test for password change --- sasdata/fair_database/user_app/tests.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index c0e19f754..c9cd1943d 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -23,6 +23,9 @@ def setUp(self): "password": "sasview!" } + def tearDown(self): + self.client.post('/dj-rest-auth/logout') + def test_register(self): response = self.client.post('/dj-rest-auth/registration/',data=self.register_data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) @@ -55,6 +58,16 @@ def test_register_login(self): self.assertEqual(logout_response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) + def test_password_change(self): + self.client.post('/dj-rest-auth/registration/', data=self.register_data) + data = { + "new_password1": "sasview?", + "new_password2": "sasview?", + "old_password": "sasview!" + } + response = self.client.post('/dj-rest-auth/password/change', data=data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + #can register a user, user is w/in User model # user is logged in after registration # logged-in user can create Data, is data's current_user From c4f0e21b55245b8966ccfa454b9a5b063799478f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 4 Feb 2025 16:26:55 -0500 Subject: [PATCH 203/675] Test for password change --- sasdata/fair_database/user_app/tests.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index c9cd1943d..4fb7827a2 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -65,8 +65,14 @@ def test_password_change(self): "new_password2": "sasview?", "old_password": "sasview!" } + l_data = self.login_data + l_data["password"] = "sasview?" response = self.client.post('/dj-rest-auth/password/change', data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) + logout_response = self.client.post('/dj-rest-auth/logout') + login_response = self.client.post('/dj-rest-auth/login', data=l_data) + self.assertEqual(logout_response.status_code, status.HTTP_200_OK) + self.assertEqual(login_response.status_code, status.HTTP_200_OK) #can register a user, user is w/in User model # user is logged in after registration From 21212e61015fcf4e2a15b398b70d43e742ed3bea Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 5 Feb 2025 11:20:56 -0500 Subject: [PATCH 204/675] Tests for user endpoint --- sasdata/fair_database/user_app/tests.py | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index 4fb7827a2..6fd314dcf 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -37,6 +37,38 @@ def test_login(self): response = self.client.post('/dj-rest-auth/login', data=self.login_data) self.assertEqual(response.status_code, status.HTTP_200_OK) + def test_user_get(self): + user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + self.client.force_authenticate(user=user) + response = self.client.get('/dj-rest-auth/user') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.content, + b'{"pk":1,"username":"testUser","email":"email@domain.org","first_name":"","last_name":""}') + + def test_user_put_username(self): + user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + self.client.force_authenticate(user=user) + data = { + "username": "newName" + } + response = self.client.put('/dj-rest-auth/user', data=data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.content, + b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"","last_name":""}') + + def test_user_put_name(self): + user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + self.client.force_authenticate(user=user) + data = { + "username": "newName", + "first_name": "Clark", + "last_name": "Kent" + } + response = self.client.put('/dj-rest-auth/user', data=data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.content, + b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"Clark","last_name":"Kent"}') + def test_login_logout(self): user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") self.client.post('/dj-rest-auth/login', data=self.login_data) From 0649530a64331aeec758159c982dc48959600b41 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 5 Feb 2025 11:28:49 -0500 Subject: [PATCH 205/675] Test user endpoint unauthenticated --- sasdata/fair_database/user_app/tests.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index 6fd314dcf..ba0df62c1 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -54,7 +54,7 @@ def test_user_put_username(self): response = self.client.put('/dj-rest-auth/user', data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, - b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"","last_name":""}') + b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"","last_name":""}') def test_user_put_name(self): user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") @@ -67,7 +67,14 @@ def test_user_put_name(self): response = self.client.put('/dj-rest-auth/user', data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, - b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"Clark","last_name":"Kent"}') + b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"Clark","last_name":"Kent"}') + + def test_user_unauthenticated(self): + response = self.client.get('/dj-rest-auth/user') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + print(response.content) + self.assertEqual(response.content, + b'{"detail":"Authentication credentials were not provided."}') def test_login_logout(self): user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") From b0be49c10deb2c6dcc9904a8a63a1ed87acdffca Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 5 Feb 2025 11:34:58 -0500 Subject: [PATCH 206/675] Add checks to register/login/logout tests --- sasdata/fair_database/user_app/tests.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index ba0df62c1..b66c47e76 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -31,11 +31,15 @@ def test_register(self): self.assertEqual(response.status_code, status.HTTP_201_CREATED) user = User.objects.get(username="testUser") self.assertEquals(user.email, self.register_data["email"]) + response2 = self.client.get('/dj-rest-auth/user') + self.assertEquals(response2.status_code, status.HTTP_200_OK) def test_login(self): - user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") response = self.client.post('/dj-rest-auth/login', data=self.login_data) self.assertEqual(response.status_code, status.HTTP_200_OK) + response2 = self.client.get('/dj-rest-auth/user') + self.assertEquals(response2.status_code, status.HTTP_200_OK) def test_user_get(self): user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") @@ -72,22 +76,25 @@ def test_user_put_name(self): def test_user_unauthenticated(self): response = self.client.get('/dj-rest-auth/user') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - print(response.content) self.assertEqual(response.content, b'{"detail":"Authentication credentials were not provided."}') def test_login_logout(self): - user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") self.client.post('/dj-rest-auth/login', data=self.login_data) response = self.client.post('/dj-rest-auth/logout') + response2 = self.client.get('/dj-rest-auth/user') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') + self.assertEquals(response2.status_code, status.HTTP_403_FORBIDDEN) def test_register_logout(self): self.client.post('/dj-rest-auth/registration/', data=self.register_data) response = self.client.post('/dj-rest-auth/logout') + response2 = self.client.get('/dj-rest-auth/user') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') + self.assertEquals(response2.status_code, status.HTTP_403_FORBIDDEN) def test_register_login(self): register_response = self.client.post('/dj-rest-auth/registration/', data=self.register_data) From 748725f9dbd3c126e0994854deecec9068edf089 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 5 Feb 2025 11:54:50 -0500 Subject: [PATCH 207/675] Reorganize auth url patterns --- sasdata/fair_database/fair_database/urls.py | 4 +- sasdata/fair_database/user_app/tests.py | 44 ++++++++++----------- sasdata/fair_database/user_app/urls.py | 12 ++++++ 3 files changed, 35 insertions(+), 25 deletions(-) create mode 100644 sasdata/fair_database/user_app/urls.py diff --git a/sasdata/fair_database/fair_database/urls.py b/sasdata/fair_database/fair_database/urls.py index ac37b3cb7..89bac77cf 100644 --- a/sasdata/fair_database/fair_database/urls.py +++ b/sasdata/fair_database/fair_database/urls.py @@ -21,7 +21,5 @@ re_path(r"^(?P(v1))/data/", include("data.urls")), path("admin/", admin.site.urls), path("accounts/", include("allauth.urls")), #needed for social auth - #path("_allauth/", include("allauth.headless.urls")), - path('dj-rest-auth/', include('dj_rest_auth.urls')), - path('dj-rest-auth/registration/', include('dj_rest_auth.registration.urls')), + path('auth/', include('user_app.urls')), ] diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index b66c47e76..6262201a7 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -24,27 +24,27 @@ def setUp(self): } def tearDown(self): - self.client.post('/dj-rest-auth/logout') + self.client.post('/auth/logout') def test_register(self): - response = self.client.post('/dj-rest-auth/registration/',data=self.register_data) + response = self.client.post('/auth/registration/',data=self.register_data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) user = User.objects.get(username="testUser") self.assertEquals(user.email, self.register_data["email"]) - response2 = self.client.get('/dj-rest-auth/user') + response2 = self.client.get('/auth/user') self.assertEquals(response2.status_code, status.HTTP_200_OK) def test_login(self): User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") - response = self.client.post('/dj-rest-auth/login', data=self.login_data) + response = self.client.post('/auth/login', data=self.login_data) self.assertEqual(response.status_code, status.HTTP_200_OK) - response2 = self.client.get('/dj-rest-auth/user') + response2 = self.client.get('/auth/user') self.assertEquals(response2.status_code, status.HTTP_200_OK) def test_user_get(self): user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") self.client.force_authenticate(user=user) - response = self.client.get('/dj-rest-auth/user') + response = self.client.get('/auth/user') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"pk":1,"username":"testUser","email":"email@domain.org","first_name":"","last_name":""}') @@ -55,7 +55,7 @@ def test_user_put_username(self): data = { "username": "newName" } - response = self.client.put('/dj-rest-auth/user', data=data) + response = self.client.put('/auth/user', data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"","last_name":""}') @@ -68,44 +68,44 @@ def test_user_put_name(self): "first_name": "Clark", "last_name": "Kent" } - response = self.client.put('/dj-rest-auth/user', data=data) + response = self.client.put('/auth/user', data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"Clark","last_name":"Kent"}') def test_user_unauthenticated(self): - response = self.client.get('/dj-rest-auth/user') + response = self.client.get('/auth/user') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.content, b'{"detail":"Authentication credentials were not provided."}') def test_login_logout(self): User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") - self.client.post('/dj-rest-auth/login', data=self.login_data) - response = self.client.post('/dj-rest-auth/logout') - response2 = self.client.get('/dj-rest-auth/user') + self.client.post('/auth/login', data=self.login_data) + response = self.client.post('/auth/logout') + response2 = self.client.get('/auth/user') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') self.assertEquals(response2.status_code, status.HTTP_403_FORBIDDEN) def test_register_logout(self): - self.client.post('/dj-rest-auth/registration/', data=self.register_data) - response = self.client.post('/dj-rest-auth/logout') - response2 = self.client.get('/dj-rest-auth/user') + self.client.post('/auth/registration/', data=self.register_data) + response = self.client.post('/auth/logout') + response2 = self.client.get('/auth/user') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') self.assertEquals(response2.status_code, status.HTTP_403_FORBIDDEN) def test_register_login(self): - register_response = self.client.post('/dj-rest-auth/registration/', data=self.register_data) - logout_response = self.client.post('/dj-rest-auth/logout') - login_response = self.client.post('/dj-rest-auth/login', data=self.login_data) + register_response = self.client.post('/auth/registration/', data=self.register_data) + logout_response = self.client.post('/auth/logout') + login_response = self.client.post('/auth/login', data=self.login_data) self.assertEqual(register_response.status_code, status.HTTP_201_CREATED) self.assertEqual(logout_response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) def test_password_change(self): - self.client.post('/dj-rest-auth/registration/', data=self.register_data) + self.client.post('/auth/registration/', data=self.register_data) data = { "new_password1": "sasview?", "new_password2": "sasview?", @@ -113,10 +113,10 @@ def test_password_change(self): } l_data = self.login_data l_data["password"] = "sasview?" - response = self.client.post('/dj-rest-auth/password/change', data=data) + response = self.client.post('/auth/password/change', data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) - logout_response = self.client.post('/dj-rest-auth/logout') - login_response = self.client.post('/dj-rest-auth/login', data=l_data) + logout_response = self.client.post('/auth/logout') + login_response = self.client.post('/auth/login', data=l_data) self.assertEqual(logout_response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) diff --git a/sasdata/fair_database/user_app/urls.py b/sasdata/fair_database/user_app/urls.py new file mode 100644 index 000000000..e6188dd32 --- /dev/null +++ b/sasdata/fair_database/user_app/urls.py @@ -0,0 +1,12 @@ +from django.urls import path +from dj_rest_auth.views import (LoginView, LogoutView, + UserDetailsView, PasswordChangeView) +from dj_rest_auth.registration.views import RegisterView + +urlpatterns = [ + path('register/', RegisterView.as_view(), name='register'), + path('login/', LoginView.as_view(), name='login'), + path('logout/', LogoutView.as_view(), name='logout'), + path('user/', UserDetailsView.as_view(), name='view user information'), + path('password/change/', PasswordChangeView.as_view(), name='change password'), +] \ No newline at end of file From 7e8a7cc1a5935ee2d9c8a83dcf9a276dee600dbb Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 5 Feb 2025 13:50:04 -0500 Subject: [PATCH 208/675] Create views for knox auth --- .../fair_database/fair_database/settings.py | 19 +++++++++- sasdata/fair_database/user_app/serializers.py | 11 ++++++ sasdata/fair_database/user_app/views.py | 37 ++++++++++++++++++- 3 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 sasdata/fair_database/user_app/serializers.py diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index 1d3f94550..c0ab3a019 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -44,11 +44,11 @@ 'rest_framework.authtoken', 'allauth', 'allauth.account', - #'allauth.headless', 'allauth.socialaccount', 'allauth.socialaccount.providers.orcid', 'dj_rest_auth', 'dj_rest_auth.registration', + 'knox', ] SITE_ID = 1 @@ -89,7 +89,22 @@ 'allauth.account.auth_backends.AuthenticationBackend', ) -HEADLESS_ONLY = False +REST_FRAMEWORK = { + 'DEFAULT ATHENTICATION CLASSES': ('knox.auth.TokenAuthentication'), + 'DEFAULT_FILTER_BACKENDS': ( + 'django_filters.rest_framework.DjangoFilterBackend', + ), +} + +REST_AUTH_TOKEN_MODEL = 'knox.models.AuthToken' +REST_AUTH_TOKEN_CREATOR = 'project.apps.accounts.utils.create_knox_token' + +REST_AUTH_SERIALIZERS = { + 'USER_DETAILS_SERIALIZER': 'project.apps.accounts.serializers.UserDetailsSerializer', + 'TOKEN_SERIALIZER': 'project.apps.accounts.serializers.KnoxSerializer', +} + +HEADLESS_ONLY = True ACCOUNT_EMAIL_VERIFICATION = 'none' # Database diff --git a/sasdata/fair_database/user_app/serializers.py b/sasdata/fair_database/user_app/serializers.py new file mode 100644 index 000000000..c443afd1e --- /dev/null +++ b/sasdata/fair_database/user_app/serializers.py @@ -0,0 +1,11 @@ +from rest_framework import serializers + +from rest_auth.serializers import UserDetailsSerializer + + +class KnoxSerializer(serializers.Serializer): + """ + Serializer for Knox authentication. + """ + token = serializers.CharField() + user = UserDetailsSerializer() \ No newline at end of file diff --git a/sasdata/fair_database/user_app/views.py b/sasdata/fair_database/user_app/views.py index 91ea44a21..4474ef28b 100644 --- a/sasdata/fair_database/user_app/views.py +++ b/sasdata/fair_database/user_app/views.py @@ -1,3 +1,36 @@ -from django.shortcuts import render +from rest_framework.response import Response -# Create your views here. +from dj_rest_auth.views import LoginView +from dj_rest_auth.registration.views import RegisterView +from knox.models import AuthToken + +from allauth.account.utils import complete_signup +from allauth.account import app_settings as allauth_settings + +from serializers import KnoxSerializer + + +class KnoxLoginView(LoginView): + + def get_response(self): + serializer_class = self.get_response_serializer() + + data = { + 'user': self.user, + 'token': self.token + } + serializer = serializer_class(instance=data, context={'request': self.request}) + + return Response(serializer.data, status=200) + +#do we want to use email? +class KnoxRegisterView(RegisterView): + + def get_response_data(self, user): + return KnoxSerializer({'user': user, 'token': self.token}).data + + def perform_create(self, serializer): + user = serializer.save(self.request) + self.token = AuthToken.objects.create(user=user) + complete_signup(self.request._request, user, allauth_settings.EMAIL_VERIFICATION, None) + return user \ No newline at end of file From b373c8f3d4bc7a0427b2494315fca5112e0013c7 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 5 Feb 2025 13:52:28 -0500 Subject: [PATCH 209/675] Add authentication app to installed apps --- sasdata/fair_database/fair_database/settings.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index c0ab3a019..6b03229c9 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -49,6 +49,7 @@ 'dj_rest_auth', 'dj_rest_auth.registration', 'knox', + 'user_app.apps.UserAppConfig', ] SITE_ID = 1 @@ -91,9 +92,6 @@ REST_FRAMEWORK = { 'DEFAULT ATHENTICATION CLASSES': ('knox.auth.TokenAuthentication'), - 'DEFAULT_FILTER_BACKENDS': ( - 'django_filters.rest_framework.DjangoFilterBackend', - ), } REST_AUTH_TOKEN_MODEL = 'knox.models.AuthToken' From 14e1b8835e7759285db52e11bcd775fb9e117b49 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 5 Feb 2025 16:32:46 -0500 Subject: [PATCH 210/675] Switch auth to use knox --- sasdata/fair_database/fair_database/settings.py | 11 +++++------ sasdata/fair_database/user_app/serializers.py | 7 +++++-- sasdata/fair_database/user_app/urls.py | 8 ++++---- sasdata/fair_database/user_app/util.py | 6 ++++++ sasdata/fair_database/user_app/views.py | 13 +++++++++---- 5 files changed, 29 insertions(+), 16 deletions(-) create mode 100644 sasdata/fair_database/user_app/util.py diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index 6b03229c9..633a0c9bd 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -94,12 +94,11 @@ 'DEFAULT ATHENTICATION CLASSES': ('knox.auth.TokenAuthentication'), } -REST_AUTH_TOKEN_MODEL = 'knox.models.AuthToken' -REST_AUTH_TOKEN_CREATOR = 'project.apps.accounts.utils.create_knox_token' - -REST_AUTH_SERIALIZERS = { - 'USER_DETAILS_SERIALIZER': 'project.apps.accounts.serializers.UserDetailsSerializer', - 'TOKEN_SERIALIZER': 'project.apps.accounts.serializers.KnoxSerializer', +REST_AUTH = { + 'TOKEN_SERIALIZER': 'user_app.serializers.KnoxSerializer', + 'USER_DETAILS_SERIALIZER': 'dj_rest_auth.serializers.UserDetailsSerializer', + 'TOKEN_MODEL': 'knox.models.AuthToken', + 'TOKEN_CREATOR': 'user_app.util.create_knox_token', } HEADLESS_ONLY = True diff --git a/sasdata/fair_database/user_app/serializers.py b/sasdata/fair_database/user_app/serializers.py index c443afd1e..5181a7354 100644 --- a/sasdata/fair_database/user_app/serializers.py +++ b/sasdata/fair_database/user_app/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from rest_auth.serializers import UserDetailsSerializer +from dj_rest_auth.serializers import UserDetailsSerializer class KnoxSerializer(serializers.Serializer): @@ -8,4 +8,7 @@ class KnoxSerializer(serializers.Serializer): Serializer for Knox authentication. """ token = serializers.CharField() - user = UserDetailsSerializer() \ No newline at end of file + user = UserDetailsSerializer() + + def get_token(self, obj): + return obj["token"][1] \ No newline at end of file diff --git a/sasdata/fair_database/user_app/urls.py b/sasdata/fair_database/user_app/urls.py index e6188dd32..791a778d9 100644 --- a/sasdata/fair_database/user_app/urls.py +++ b/sasdata/fair_database/user_app/urls.py @@ -1,11 +1,11 @@ from django.urls import path -from dj_rest_auth.views import (LoginView, LogoutView, +from dj_rest_auth.views import (LogoutView, UserDetailsView, PasswordChangeView) -from dj_rest_auth.registration.views import RegisterView +from .views import KnoxLoginView, KnoxRegisterView urlpatterns = [ - path('register/', RegisterView.as_view(), name='register'), - path('login/', LoginView.as_view(), name='login'), + path('register/', KnoxRegisterView.as_view(), name='register'), + path('login/', KnoxLoginView.as_view(), name='login'), path('logout/', LogoutView.as_view(), name='logout'), path('user/', UserDetailsView.as_view(), name='view user information'), path('password/change/', PasswordChangeView.as_view(), name='change password'), diff --git a/sasdata/fair_database/user_app/util.py b/sasdata/fair_database/user_app/util.py new file mode 100644 index 000000000..ab9bcd0d9 --- /dev/null +++ b/sasdata/fair_database/user_app/util.py @@ -0,0 +1,6 @@ +from knox.models import AuthToken + + +def create_knox_token(token_model, user, serializer): + token = AuthToken.objects.create(user=user) + return token \ No newline at end of file diff --git a/sasdata/fair_database/user_app/views.py b/sasdata/fair_database/user_app/views.py index 4474ef28b..b32e0c26b 100644 --- a/sasdata/fair_database/user_app/views.py +++ b/sasdata/fair_database/user_app/views.py @@ -1,17 +1,22 @@ -from rest_framework.response import Response +from django.conf import settings +from rest_framework.response import Response from dj_rest_auth.views import LoginView from dj_rest_auth.registration.views import RegisterView from knox.models import AuthToken - from allauth.account.utils import complete_signup from allauth.account import app_settings as allauth_settings -from serializers import KnoxSerializer +from .serializers import KnoxSerializer +from .util import create_knox_token class KnoxLoginView(LoginView): + '''def get_response_serializer(self): + response_serializer = settings.REST_AUTH_SERIALIZERS['TOKEN_SERIALIZER'] + return response_serializer''' + def get_response(self): serializer_class = self.get_response_serializer() @@ -31,6 +36,6 @@ def get_response_data(self, user): def perform_create(self, serializer): user = serializer.save(self.request) - self.token = AuthToken.objects.create(user=user) + self.token = create_knox_token(None,user,None) complete_signup(self.request._request, user, allauth_settings.EMAIL_VERIFICATION, None) return user \ No newline at end of file From 58bd8880180e05d8854ed958f18db2a3e5c09ae6 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 5 Feb 2025 16:33:39 -0500 Subject: [PATCH 211/675] Fix tests to match url changes --- sasdata/fair_database/user_app/tests.py | 45 +++++++++++++------------ 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index 6262201a7..7cfb80f50 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -23,28 +23,29 @@ def setUp(self): "password": "sasview!" } + ''' def tearDown(self): - self.client.post('/auth/logout') + self.client.post('/auth/logout/') ''' def test_register(self): - response = self.client.post('/auth/registration/',data=self.register_data) + response = self.client.post('/auth/register/',data=self.register_data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) user = User.objects.get(username="testUser") self.assertEquals(user.email, self.register_data["email"]) - response2 = self.client.get('/auth/user') + response2 = self.client.get('/auth/user/') self.assertEquals(response2.status_code, status.HTTP_200_OK) def test_login(self): User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") - response = self.client.post('/auth/login', data=self.login_data) + response = self.client.post('/auth/login/', data=self.login_data) self.assertEqual(response.status_code, status.HTTP_200_OK) - response2 = self.client.get('/auth/user') + response2 = self.client.get('/auth/user/') self.assertEquals(response2.status_code, status.HTTP_200_OK) def test_user_get(self): user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") self.client.force_authenticate(user=user) - response = self.client.get('/auth/user') + response = self.client.get('/auth/user/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"pk":1,"username":"testUser","email":"email@domain.org","first_name":"","last_name":""}') @@ -55,7 +56,7 @@ def test_user_put_username(self): data = { "username": "newName" } - response = self.client.put('/auth/user', data=data) + response = self.client.put('/auth/user/', data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"","last_name":""}') @@ -68,44 +69,44 @@ def test_user_put_name(self): "first_name": "Clark", "last_name": "Kent" } - response = self.client.put('/auth/user', data=data) + response = self.client.put('/auth/user/', data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"Clark","last_name":"Kent"}') def test_user_unauthenticated(self): - response = self.client.get('/auth/user') + response = self.client.get('/auth/user/') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.content, b'{"detail":"Authentication credentials were not provided."}') def test_login_logout(self): User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") - self.client.post('/auth/login', data=self.login_data) - response = self.client.post('/auth/logout') - response2 = self.client.get('/auth/user') + self.client.post('/auth/login/', data=self.login_data) + response = self.client.post('/auth/logout/') + response2 = self.client.get('/auth/user/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') self.assertEquals(response2.status_code, status.HTTP_403_FORBIDDEN) def test_register_logout(self): - self.client.post('/auth/registration/', data=self.register_data) - response = self.client.post('/auth/logout') - response2 = self.client.get('/auth/user') + self.client.post('/auth/register/', data=self.register_data) + response = self.client.post('/auth/logout/') + response2 = self.client.get('/auth/user/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') self.assertEquals(response2.status_code, status.HTTP_403_FORBIDDEN) def test_register_login(self): - register_response = self.client.post('/auth/registration/', data=self.register_data) - logout_response = self.client.post('/auth/logout') - login_response = self.client.post('/auth/login', data=self.login_data) + register_response = self.client.post('/auth/register/', data=self.register_data) + logout_response = self.client.post('/auth/logout/') + login_response = self.client.post('/auth/login/', data=self.login_data) self.assertEqual(register_response.status_code, status.HTTP_201_CREATED) self.assertEqual(logout_response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) def test_password_change(self): - self.client.post('/auth/registration/', data=self.register_data) + self.client.post('/auth/register/', data=self.register_data) data = { "new_password1": "sasview?", "new_password2": "sasview?", @@ -113,10 +114,10 @@ def test_password_change(self): } l_data = self.login_data l_data["password"] = "sasview?" - response = self.client.post('/auth/password/change', data=data) + response = self.client.post('/auth/password/change/', data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) - logout_response = self.client.post('/auth/logout') - login_response = self.client.post('/auth/login', data=l_data) + logout_response = self.client.post('/auth/logout/') + login_response = self.client.post('/auth/login/', data=l_data) self.assertEqual(logout_response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) From 637cf498abb576ab2b83a92f6f80385b34eec719 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 5 Feb 2025 16:52:27 -0500 Subject: [PATCH 212/675] Add view for orcid login --- sasdata/fair_database/user_app/views.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/user_app/views.py b/sasdata/fair_database/user_app/views.py index b32e0c26b..8772ac5cb 100644 --- a/sasdata/fair_database/user_app/views.py +++ b/sasdata/fair_database/user_app/views.py @@ -2,10 +2,10 @@ from rest_framework.response import Response from dj_rest_auth.views import LoginView -from dj_rest_auth.registration.views import RegisterView -from knox.models import AuthToken +from dj_rest_auth.registration.views import RegisterView, SocialLoginView from allauth.account.utils import complete_signup from allauth.account import app_settings as allauth_settings +from allauth.socialaccount.providers.orcid.view import OrcidOAuth2Adapter from .serializers import KnoxSerializer from .util import create_knox_token @@ -38,4 +38,7 @@ def perform_create(self, serializer): user = serializer.save(self.request) self.token = create_knox_token(None,user,None) complete_signup(self.request._request, user, allauth_settings.EMAIL_VERIFICATION, None) - return user \ No newline at end of file + return user + +class OrcidLoginView(SocialLoginView): + adapter_class = OrcidOAuth2Adapter \ No newline at end of file From df51cfe558d37aa3e14d343d64c5f0f097fb743e Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Feb 2025 10:20:07 -0500 Subject: [PATCH 213/675] Set up future ORCID support --- .../fair_database/fair_database/settings.py | 25 +++++++++++++++++++ sasdata/fair_database/user_app/urls.py | 3 ++- sasdata/fair_database/user_app/views.py | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index 633a0c9bd..c298e1bdd 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -85,6 +85,7 @@ WSGI_APPLICATION = 'fair_database.wsgi.application' +#Authentication AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', 'allauth.account.auth_backends.AuthenticationBackend', @@ -104,6 +105,30 @@ HEADLESS_ONLY = True ACCOUNT_EMAIL_VERIFICATION = 'none' +#to enable ORCID, register for credentials through ORCID and fill out client_id and secret +SOCIALACCOUNT_PROVIDERS = { + 'orcid': { + 'APPS': [ + { + 'client_id': '', + 'secret': '', + 'key': '', + } + + ], + 'SCOPE': [ + 'profile', 'email', + ], + 'AUTH_PARAMETERS': { + 'access_type': 'online' + }, + # Base domain of the API. Default value: 'orcid.org', for the production API + 'BASE_DOMAIN':'sandbox.orcid.org', # for the sandbox API + # Member API or Public API? Default: False (for the public API) + 'MEMBER_API': False, + } +} + # Database # https://docs.djangoproject.com/en/5.1/ref/settings/#databases diff --git a/sasdata/fair_database/user_app/urls.py b/sasdata/fair_database/user_app/urls.py index 791a778d9..078051822 100644 --- a/sasdata/fair_database/user_app/urls.py +++ b/sasdata/fair_database/user_app/urls.py @@ -1,7 +1,7 @@ from django.urls import path from dj_rest_auth.views import (LogoutView, UserDetailsView, PasswordChangeView) -from .views import KnoxLoginView, KnoxRegisterView +from .views import KnoxLoginView, KnoxRegisterView, OrcidLoginView urlpatterns = [ path('register/', KnoxRegisterView.as_view(), name='register'), @@ -9,4 +9,5 @@ path('logout/', LogoutView.as_view(), name='logout'), path('user/', UserDetailsView.as_view(), name='view user information'), path('password/change/', PasswordChangeView.as_view(), name='change password'), + path('login/orcid/', OrcidLoginView.as_view(), name='orcid login') ] \ No newline at end of file diff --git a/sasdata/fair_database/user_app/views.py b/sasdata/fair_database/user_app/views.py index 8772ac5cb..d8a4d20fb 100644 --- a/sasdata/fair_database/user_app/views.py +++ b/sasdata/fair_database/user_app/views.py @@ -5,7 +5,7 @@ from dj_rest_auth.registration.views import RegisterView, SocialLoginView from allauth.account.utils import complete_signup from allauth.account import app_settings as allauth_settings -from allauth.socialaccount.providers.orcid.view import OrcidOAuth2Adapter +from allauth.socialaccount.providers.orcid.views import OrcidOAuth2Adapter from .serializers import KnoxSerializer from .util import create_knox_token From 1ff560049945d5de8f1cd8a416cf71a9bfcecc42 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Feb 2025 10:35:38 -0500 Subject: [PATCH 214/675] Documentation of authentication stuff --- sasdata/fair_database/fair_database/settings.py | 5 +++-- sasdata/fair_database/user_app/urls.py | 2 ++ sasdata/fair_database/user_app/views.py | 8 +++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index c298e1bdd..c96d7b934 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -85,7 +85,7 @@ WSGI_APPLICATION = 'fair_database.wsgi.application' -#Authentication +# Authentication AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', 'allauth.account.auth_backends.AuthenticationBackend', @@ -102,10 +102,11 @@ 'TOKEN_CREATOR': 'user_app.util.create_knox_token', } +# allauth settings HEADLESS_ONLY = True ACCOUNT_EMAIL_VERIFICATION = 'none' -#to enable ORCID, register for credentials through ORCID and fill out client_id and secret +# to enable ORCID, register for credentials through ORCID and fill out client_id and secret SOCIALACCOUNT_PROVIDERS = { 'orcid': { 'APPS': [ diff --git a/sasdata/fair_database/user_app/urls.py b/sasdata/fair_database/user_app/urls.py index 078051822..339d7d8b8 100644 --- a/sasdata/fair_database/user_app/urls.py +++ b/sasdata/fair_database/user_app/urls.py @@ -3,6 +3,8 @@ UserDetailsView, PasswordChangeView) from .views import KnoxLoginView, KnoxRegisterView, OrcidLoginView +'''Urls for authentication. Orcid login not functional.''' + urlpatterns = [ path('register/', KnoxRegisterView.as_view(), name='register'), path('login/', KnoxLoginView.as_view(), name='login'), diff --git a/sasdata/fair_database/user_app/views.py b/sasdata/fair_database/user_app/views.py index d8a4d20fb..88eb2eece 100644 --- a/sasdata/fair_database/user_app/views.py +++ b/sasdata/fair_database/user_app/views.py @@ -10,13 +10,10 @@ from .serializers import KnoxSerializer from .util import create_knox_token +#Login using knox tokens rather than django-rest-framework tokens. class KnoxLoginView(LoginView): - '''def get_response_serializer(self): - response_serializer = settings.REST_AUTH_SERIALIZERS['TOKEN_SERIALIZER'] - return response_serializer''' - def get_response(self): serializer_class = self.get_response_serializer() @@ -28,7 +25,7 @@ def get_response(self): return Response(serializer.data, status=200) -#do we want to use email? +# Registration using knox tokens rather than django-rest-framework tokens. class KnoxRegisterView(RegisterView): def get_response_data(self, user): @@ -40,5 +37,6 @@ def perform_create(self, serializer): complete_signup(self.request._request, user, allauth_settings.EMAIL_VERIFICATION, None) return user +# For ORCID login class OrcidLoginView(SocialLoginView): adapter_class = OrcidOAuth2Adapter \ No newline at end of file From 9ce289aa35a8d8d65ed96b812d1c4b266697c3bc Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Feb 2025 11:55:40 -0500 Subject: [PATCH 215/675] Authentication test documentation --- sasdata/fair_database/user_app/tests.py | 30 ++++++++++++++++--------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index 7cfb80f50..cd8163803 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -27,21 +27,24 @@ def setUp(self): def tearDown(self): self.client.post('/auth/logout/') ''' + # Test if registration successfully creates a new user and logs in def test_register(self): response = self.client.post('/auth/register/',data=self.register_data) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) user = User.objects.get(username="testUser") - self.assertEquals(user.email, self.register_data["email"]) response2 = self.client.get('/auth/user/') - self.assertEquals(response2.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(user.email, self.register_data["email"]) + self.assertEqual(response2.status_code, status.HTTP_200_OK) + # Test if login successful def test_login(self): User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") response = self.client.post('/auth/login/', data=self.login_data) - self.assertEqual(response.status_code, status.HTTP_200_OK) response2 = self.client.get('/auth/user/') - self.assertEquals(response2.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response2.status_code, status.HTTP_200_OK) + # Test get user information def test_user_get(self): user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") self.client.force_authenticate(user=user) @@ -50,6 +53,7 @@ def test_user_get(self): self.assertEqual(response.content, b'{"pk":1,"username":"testUser","email":"email@domain.org","first_name":"","last_name":""}') + # Test changing username def test_user_put_username(self): user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") self.client.force_authenticate(user=user) @@ -61,6 +65,7 @@ def test_user_put_username(self): self.assertEqual(response.content, b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"","last_name":""}') + # Test changing username and first and last name def test_user_put_name(self): user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") self.client.force_authenticate(user=user) @@ -74,12 +79,14 @@ def test_user_put_name(self): self.assertEqual(response.content, b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"Clark","last_name":"Kent"}') + # Test user info inaccessible when unauthenticated def test_user_unauthenticated(self): response = self.client.get('/auth/user/') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.content, b'{"detail":"Authentication credentials were not provided."}') + # Test logout is successful after login def test_login_logout(self): User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") self.client.post('/auth/login/', data=self.login_data) @@ -87,16 +94,18 @@ def test_login_logout(self): response2 = self.client.get('/auth/user/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') - self.assertEquals(response2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) + # Test logout is successful after registration def test_register_logout(self): self.client.post('/auth/register/', data=self.register_data) response = self.client.post('/auth/logout/') response2 = self.client.get('/auth/user/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') - self.assertEquals(response2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) + # Test login is successful after registering then logging out def test_register_login(self): register_response = self.client.post('/auth/register/', data=self.register_data) logout_response = self.client.post('/auth/logout/') @@ -105,6 +114,7 @@ def test_register_login(self): self.assertEqual(logout_response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) + # Test password is successfully changed def test_password_change(self): self.client.post('/auth/register/', data=self.register_data) data = { @@ -115,16 +125,14 @@ def test_password_change(self): l_data = self.login_data l_data["password"] = "sasview?" response = self.client.post('/auth/password/change/', data=data) - self.assertEqual(response.status_code, status.HTTP_200_OK) logout_response = self.client.post('/auth/logout/') login_response = self.client.post('/auth/login/', data=l_data) + self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(logout_response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) -#can register a user, user is w/in User model -# user is logged in after registration + # logged-in user can create Data, is data's current_user -# test log out # Permissions From 9e2252596704821c1f8580e4361691c6511158c6 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Feb 2025 12:08:22 -0500 Subject: [PATCH 216/675] Documentation for data tests --- sasdata/fair_database/data/tests.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 7add1db9f..a415308e7 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -22,20 +22,23 @@ def setUp(self): self.client = APIClient() self.client.force_authenticate(user=self.user) - #working + # Test list public data def test_does_list_public(self): request = self.client.get('/v1/data/list/') self.assertEqual(request.data, {"public_data_ids":{1:"cyl_400_40.txt"}}) + # Test list a user's private data def test_does_list_user(self): request = self.client.get('/v1/data/list/testUser/', user = self.user) self.assertEqual(request.data, {"user_data_ids":{3:"cyl_400_20.txt"}}) + # Test loading a public data file def test_does_load_data_info_public(self): request = self.client.get('/v1/data/load/1/') print(request.data) self.assertEqual(request.status_code, status.HTTP_200_OK) + # Test loading private data with authorization def test_does_load_data_info_private(self): request = self.client.get('/v1/data/load/3/') print(request.data) @@ -53,6 +56,7 @@ def setUp(self): self.client.force_authenticate(user=self.user) self.client2 = APIClient() + # Test data upload creates data in database def test_is_data_being_created(self): file = open(find("cyl_400_40.txt"), 'rb') data = { @@ -64,6 +68,7 @@ def test_is_data_being_created(self): self.assertEqual(request.data, {"current_user":'testUser', "authenticated" : True, "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) Data.objects.get(id = 3).delete() + # Test data upload w/out authenticated user def test_is_data_being_created_no_user(self): file = open(find("cyl_400_40.txt"), 'rb') data = { @@ -75,6 +80,7 @@ def test_is_data_being_created_no_user(self): self.assertEqual(request.data, {"current_user":'', "authenticated" : False, "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) Data.objects.get(id = 3).delete() + # Test updating file def test_does_file_upload_update(self): file = open(find("cyl_400_40.txt")) data = { @@ -87,8 +93,7 @@ def test_does_file_upload_update(self): self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) Data.objects.get(id = 2).delete() - #TODO write tests for download - + # Test file download def test_does_download(self): request = self.client.get('/v1/data/2/download/') request2 = self.client2.get('/v1/data/2/download/') From 93ad87ed4f46c5a135052b8ee6547969751a4902 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Feb 2025 13:45:33 -0500 Subject: [PATCH 217/675] Add auth packages to requirements.txt --- sasdata/fair_database/requirements.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/requirements.txt b/sasdata/fair_database/requirements.txt index d80bd1382..5eb1b6b95 100644 --- a/sasdata/fair_database/requirements.txt +++ b/sasdata/fair_database/requirements.txt @@ -1,2 +1,7 @@ +#this requirements extends the base sasview requirements files +#to get both you will need to run this after base requirements files django -djangorestframework \ No newline at end of file +djangorestframework +dj-rest-auth +django-allauth +django-rest-knox From 148f80ed14527148b2dec024119040f90d52dbf0 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Feb 2025 13:53:48 -0500 Subject: [PATCH 218/675] Test login/logout multiple clients same account --- sasdata/fair_database/user_app/tests.py | 26 +++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index cd8163803..f019c29a6 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -23,10 +23,6 @@ def setUp(self): "password": "sasview!" } - ''' - def tearDown(self): - self.client.post('/auth/logout/') ''' - # Test if registration successfully creates a new user and logs in def test_register(self): response = self.client.post('/auth/register/',data=self.register_data) @@ -44,6 +40,16 @@ def test_login(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) + # Test simultaneous login by multiple clients + def test_multiple_login(self): + User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + client2 = APIClient() + response = self.client.post('/auth/login/', data=self.login_data) + response2 = client2.post('/auth/login/', data=self.login_data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response2.status_code, status.HTTP_200_OK) + self.assertNotEqual(response.content, response2.content) + # Test get user information def test_user_get(self): user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") @@ -105,6 +111,18 @@ def test_register_logout(self): self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) + def test_multiple_logout(self): + User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + client2 = APIClient() + self.client.post('/auth/login/', data=self.login_data) + client2.post('/auth/login/', data=self.login_data) + response = self.client.post('/auth/logout/') + response2 = client2.get('/auth/user/') + response3 = client2.post('/auth/logout/') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response2.status_code, status.HTTP_200_OK) + self.assertEqual(response3.status_code, status.HTTP_200_OK) + # Test login is successful after registering then logging out def test_register_login(self): register_response = self.client.post('/auth/register/', data=self.register_data) From 1c3077529d94c75699ede7c226874ab6adb3df0b Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Feb 2025 14:09:07 -0500 Subject: [PATCH 219/675] Break up data tests --- sasdata/fair_database/data/tests.py | 41 ++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index a415308e7..67a24ce34 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -14,10 +14,12 @@ def find(filename): class TestLists(TestCase): def setUp(self): - public_test_data = Data.objects.create(id = 1, file_name = "cyl_400_40.txt", is_public = True) + public_test_data = Data.objects.create(id = 1, file_name = "cyl_400_40.txt", + is_public = True) public_test_data.file.save("cyl_400_40.txt", open(find("cyl_400_40.txt"), 'rb')) self.user = User.objects.create_user(username="testUser", password="secret", id = 2) - private_test_data = Data.objects.create(id = 3, current_user = self.user, file_name = "cyl_400_20.txt", is_public = False) + private_test_data = Data.objects.create(id = 3, current_user = self.user, + file_name = "cyl_400_20.txt", is_public = False) private_test_data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), 'rb')) self.client = APIClient() self.client.force_authenticate(user=self.user) @@ -35,13 +37,11 @@ def test_does_list_user(self): # Test loading a public data file def test_does_load_data_info_public(self): request = self.client.get('/v1/data/load/1/') - print(request.data) self.assertEqual(request.status_code, status.HTTP_200_OK) # Test loading private data with authorization def test_does_load_data_info_private(self): request = self.client.get('/v1/data/load/3/') - print(request.data) self.assertEqual(request.status_code, status.HTTP_200_OK) def tearDown(self): @@ -50,7 +50,8 @@ def tearDown(self): class TestingDatabase(APITestCase): def setUp(self): self.user = User.objects.create_user(username="testUser", password="secret", id = 1) - self.data = Data.objects.create(id = 2, current_user = self.user, file_name = "cyl_400_20.txt", is_public = False) + self.data = Data.objects.create(id = 2, current_user = self.user, + file_name = "cyl_400_20.txt", is_public = False) self.data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), 'rb')) self.client = APIClient() self.client.force_authenticate(user=self.user) @@ -65,7 +66,8 @@ def test_is_data_being_created(self): } request = self.client.post('/v1/data/upload/', data=data) self.assertEqual(request.status_code, status.HTTP_200_OK) - self.assertEqual(request.data, {"current_user":'testUser', "authenticated" : True, "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) + self.assertEqual(request.data, {"current_user":'testUser', "authenticated" : True, + "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) Data.objects.get(id = 3).delete() # Test data upload w/out authenticated user @@ -77,7 +79,8 @@ def test_is_data_being_created_no_user(self): } request = self.client2.post('/v1/data/upload/', data=data) self.assertEqual(request.status_code, status.HTTP_200_OK) - self.assertEqual(request.data, {"current_user":'', "authenticated" : False, "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) + self.assertEqual(request.data, {"current_user":'', "authenticated" : False, + "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) Data.objects.get(id = 3).delete() # Test updating file @@ -88,21 +91,33 @@ def test_does_file_upload_update(self): "is_public":False } request = self.client.put('/v1/data/upload/2/', data = data) - request2 = self.client2.put('/v1/data/upload/2/', data = data) - self.assertEqual(request.data, {"current_user":'testUser', "authenticated" : True, "file_id" : 2, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) - self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request.data, {"current_user":'testUser', "authenticated" : True, + "file_id" : 2, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) Data.objects.get(id = 2).delete() + # Test file upload update fails when unauthorized + def test_unauthorized_file_upload_update(self): + file = open(find("cyl_400_40.txt")) + data = { + "file": file, + "is_public": False + } + request = self.client2.put('/v1/data/upload/2/', data=data) + self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + Data.objects.get(id=2).delete() + # Test file download def test_does_download(self): request = self.client.get('/v1/data/2/download/') - request2 = self.client2.get('/v1/data/2/download/') - self.assertEqual(request.status_code, status.HTTP_200_OK) - self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) file_contents = b''.join(request.streaming_content) test_file = open(find('cyl_400_20.txt'), 'rb') + self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(file_contents, test_file.read()) + # Test file download fails when unauthorized + def test_unauthorized_download(self): + request2 = self.client2.get('/v1/data/2/download/') + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT) \ No newline at end of file From 469a2cbe51a06a152ef2e12b6c5406d345f4ec9e Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Feb 2025 14:43:06 -0500 Subject: [PATCH 220/675] Create class for auth/data permissions tests --- .../fair_database/tests/test_permissions.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 sasdata/fair_database/fair_database/tests/test_permissions.py diff --git a/sasdata/fair_database/fair_database/tests/test_permissions.py b/sasdata/fair_database/fair_database/tests/test_permissions.py new file mode 100644 index 000000000..26f01e4d4 --- /dev/null +++ b/sasdata/fair_database/fair_database/tests/test_permissions.py @@ -0,0 +1,60 @@ +import os + +from django.contrib.auth.models import User +from rest_framework.test import APIClient, APITestCase + +from data.models import Data + +def find(filename): + return os.path.join(os.path.dirname(__file__), "../../example_data/1d_data", filename) + +class DataListPermissionsTests(APITestCase): + ''' Test permissions of data views using user_app for authentication. ''' + + def setUp(self): + self.user = User.objects.create_user(username="testUser", password="secret", id=1) + self.user2 = User.objects.create_user(username="testUser2", password="secret", id=2) + public_test_data = Data.objects.create(id=1, file_name="cyl_400_40.txt", + is_public=True) + public_test_data.file.save("cyl_400_40.txt", open(find("cyl_400_40.txt"), 'rb')) + private_test_data = Data.objects.create(id=2, current_user=self.user, + file_name="cyl_400_20.txt", is_public=False) + private_test_data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), 'rb')) + + # Authenticated user can view list of data + + # Unauthenticated user can view list of public data + + # Authenticated user cannot view other users' private data on list + + # Authenticated user can load public data + + # Authenticated user can load own private data + + # Authenticated user cannot load others' private data + + # Unauthenticated user can load public data + + # Unauthenticated user cannot load others' private data + + # Authenticated user can upload data + + # ***Unauthenticated user can upload public data + + # Unauthenticated user cannot upload private data + + # Authenticated user can update own public data + + # Authenticated user can update own private data + + # Authenticated user cannot update unowned public data + + # Unauthenticated user cannot update data + + # Anyone can download public data + + # Authenticated user can download own data + + # Authenticated user cannot download others' data + + # Unauthenticated user cannot download private data From 5cbe23f409cf35b9f62b52fceb99c995a657d1a8 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Feb 2025 15:14:11 -0500 Subject: [PATCH 221/675] Tests for list data permissions --- sasdata/fair_database/__init__.py | 0 .../fair_database/test_permissions.py | 106 ++++++++++++++++++ .../fair_database/tests/test_permissions.py | 60 ---------- 3 files changed, 106 insertions(+), 60 deletions(-) create mode 100644 sasdata/fair_database/__init__.py create mode 100644 sasdata/fair_database/fair_database/test_permissions.py delete mode 100644 sasdata/fair_database/fair_database/tests/test_permissions.py diff --git a/sasdata/fair_database/__init__.py b/sasdata/fair_database/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py new file mode 100644 index 000000000..247af357b --- /dev/null +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -0,0 +1,106 @@ +import os + +from django.contrib.auth.models import User +from rest_framework import status +from rest_framework.test import APIClient, APITestCase + +from data.models import Data + +def find(filename): + return os.path.join(os.path.dirname(__file__), "../../example_data/1d_data", filename) + +class DataListPermissionsTests(APITestCase): + ''' Test permissions of data views using user_app for authentication. ''' + + def setUp(self): + self.user = User.objects.create_user(username="testUser", password="secret", id=1, + email="email@domain.com") + self.user2 = User.objects.create_user(username="testUser2", password="secret", id=2, + email="email2@domain.com") + unowned_test_data = Data.objects.create(id=1, file_name="cyl_400_40.txt", + is_public=True) + unowned_test_data.file.save("cyl_400_40.txt", open(find("cyl_400_40.txt"), 'rb')) + private_test_data = Data.objects.create(id=2, current_user=self.user, + file_name="cyl_400_20.txt", is_public=False) + private_test_data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), 'rb')) + public_test_data = Data.objects.create(id=3, current_user=self.user, + file_name="cyl_testdata.txt", is_public=True) + public_test_data.file.save("cyl_testdata.txt", open(find("cyl_testdata.txt"), 'rb')) + self.login_data_1 = { + 'username': 'testUser', + 'password': 'secret', + 'email': 'email@domain.com' + } + self.login_data_2 = { + 'username': 'testUser2', + 'password': 'secret', + 'email': 'email2@domain.com' + } + + # Authenticated user can view list of data + # TODO: change to reflect inclusion of owned private data + def test_list_authenticated(self): + self.client.post('/auth/login/', data=self.login_data_1) + response = self.client.get('/v1/data/list/') + response2 = self.client.get('/v1/data/list/testUser/') + self.assertEqual(response.data, + {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}) + self.assertEqual(response2.data, + {"user_data_ids": {2: "cyl_400_20.txt", 3: "cyl_testdata.txt"}}) + + # Authenticated user cannot view other users' private data on list + # TODO: Change response codes + def test_list_authenticated_2(self): + self.client.post('/auth/login/', data=self.login_data_2) + response = self.client.get('/v1/data/list/') + response2 = self.client.get('/v1/data/list/testUser/') + response3 = self.client.get('/v1/data/list/testUser2/') + self.assertEqual(response.data, + {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}) + self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response3.data, {"user_data_ids": {}}) + + # Unauthenticated user can view list of public data + def test_list_unauthenticated(self): + response = self.client.get('/v1/data/list/') + response2 = self.client.get('/v1/data/list/testUser/') + self.assertEqual(response.data, + {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}) + self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) + + + # Authenticated user can load public data + def test_load_authenticated_public(self): + self.client.post('/auth/login/', data=self.login_data_1) + response = self.client.get('/v1/data/load/1/') + self.assertEqual(response.status_code, status.HTTP_200_OK) + + # Authenticated user can load own private data + + # Authenticated user cannot load others' private data + + # Unauthenticated user can load public data + + # Unauthenticated user cannot load others' private data + + # Authenticated user can upload data + + # ***Unauthenticated user can upload public data + + # Unauthenticated user cannot upload private data + + # Authenticated user can update own public data + + # Authenticated user can update own private data + + # Authenticated user cannot update unowned public data + + # Unauthenticated user cannot update data + + # Anyone can download public data + + # Authenticated user can download own data + + # Authenticated user cannot download others' data + + # Unauthenticated user cannot download private data diff --git a/sasdata/fair_database/fair_database/tests/test_permissions.py b/sasdata/fair_database/fair_database/tests/test_permissions.py deleted file mode 100644 index 26f01e4d4..000000000 --- a/sasdata/fair_database/fair_database/tests/test_permissions.py +++ /dev/null @@ -1,60 +0,0 @@ -import os - -from django.contrib.auth.models import User -from rest_framework.test import APIClient, APITestCase - -from data.models import Data - -def find(filename): - return os.path.join(os.path.dirname(__file__), "../../example_data/1d_data", filename) - -class DataListPermissionsTests(APITestCase): - ''' Test permissions of data views using user_app for authentication. ''' - - def setUp(self): - self.user = User.objects.create_user(username="testUser", password="secret", id=1) - self.user2 = User.objects.create_user(username="testUser2", password="secret", id=2) - public_test_data = Data.objects.create(id=1, file_name="cyl_400_40.txt", - is_public=True) - public_test_data.file.save("cyl_400_40.txt", open(find("cyl_400_40.txt"), 'rb')) - private_test_data = Data.objects.create(id=2, current_user=self.user, - file_name="cyl_400_20.txt", is_public=False) - private_test_data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), 'rb')) - - # Authenticated user can view list of data - - # Unauthenticated user can view list of public data - - # Authenticated user cannot view other users' private data on list - - # Authenticated user can load public data - - # Authenticated user can load own private data - - # Authenticated user cannot load others' private data - - # Unauthenticated user can load public data - - # Unauthenticated user cannot load others' private data - - # Authenticated user can upload data - - # ***Unauthenticated user can upload public data - - # Unauthenticated user cannot upload private data - - # Authenticated user can update own public data - - # Authenticated user can update own private data - - # Authenticated user cannot update unowned public data - - # Unauthenticated user cannot update data - - # Anyone can download public data - - # Authenticated user can download own data - - # Authenticated user cannot download others' data - - # Unauthenticated user cannot download private data From 4ad99f44c3a08104c3ae5026ef91061c4e8dbbce Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Feb 2025 15:30:05 -0500 Subject: [PATCH 222/675] Tests for data load permissions --- .../fair_database/test_permissions.py | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index 247af357b..a8f13cc25 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -68,20 +68,30 @@ def test_list_unauthenticated(self): {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}) self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) - - # Authenticated user can load public data - def test_load_authenticated_public(self): + # Authenticated user can load public data and owned private data + def test_load_authenticated(self): self.client.post('/auth/login/', data=self.login_data_1) response = self.client.get('/v1/data/load/1/') + response2 = self.client.get('/v1/data/load/2/') self.assertEqual(response.status_code, status.HTTP_200_OK) - - # Authenticated user can load own private data + self.assertEqual(response2.status_code, status.HTTP_200_OK) # Authenticated user cannot load others' private data + def test_load_unauthorized(self): + self.client.post('/auth/login/', data=self.login_data_2) + response = self.client.get('/v1/data/load/2/') + response2 = self.client.get('/v1/data/load/3/') + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response2.status_code, status.HTTP_200_OK) - # Unauthenticated user can load public data - - # Unauthenticated user cannot load others' private data + # Unauthenticated user can load public data only + def test_load_unauthenticated(self): + response = self.client.get('/v1/data/load/1/') + response2 = self.client.get('/v1/data/load/2/') + response3 = self.client.get('/v1/data/load/3/') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response3.status_code, status.HTTP_200_OK) # Authenticated user can upload data From a85b8e5882127b2494b22414527bccf0f1f8c78a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Feb 2025 15:50:25 -0500 Subject: [PATCH 223/675] Tests for data update --- .../fair_database/test_permissions.py | 50 +++++++++++++++++-- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index a8f13cc25..5942e7e9e 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -1,5 +1,7 @@ import os +import shutil +from django.conf import settings from django.contrib.auth.models import User from rest_framework import status from rest_framework.test import APIClient, APITestCase @@ -99,13 +101,50 @@ def test_load_unauthenticated(self): # Unauthenticated user cannot upload private data - # Authenticated user can update own public data - - # Authenticated user can update own private data + # Authenticated user can update own data + def test_upload_put_authenticated(self): + self.client.post('/auth/login/', data=self.login_data_1) + data = { + "is_public": False + } + response = self.client.put('/v1/data/upload/2/', data=data) + response2 = self.client.put('/v1/data/upload/3/', data=data) + self.assertEqual(response.data, + {"current_user": 'testUser', "authenticated": True, "file_id": 2, + "file_alternative_name": "cyl_400_20.txt", "is_public": False}) + self.assertEqual(response2.data, + {"current_user": 'testUser', "authenticated": True, "file_id": 3, + "file_alternative_name": "cyl_testdata.txt", "is_public": False}) + Data.objects.get(id=3).is_public = True - # Authenticated user cannot update unowned public data + # Authenticated user cannot update unowned data + def test_upload_put_unauthorized(self): + self.client.post('/auth/login/', data=self.login_data_2) + file = open(find("cyl_400_40.txt")) + data = { + "file": file, + "is_public": False + } + response = self.client.put('/v1/data/upload/1/', data=data) + response2 = self.client.put('/v1/data/upload/2/', data=data) + response3 = self.client.put('/v1/data/upload/3/', data=data) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(response2.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(response3.status_code, status.HTTP_404_NOT_FOUND) # Unauthenticated user cannot update data + def test_upload_put_unauthenticated(self): + file = open(find("cyl_400_40.txt")) + data = { + "file": file, + "is_public": False + } + response = self.client.put('/v1/data/upload/1/', data=data) + response2 = self.client.put('/v1/data/upload/2/', data=data) + response3 = self.client.put('/v1/data/upload/3/', data=data) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response3.status_code, status.HTTP_403_FORBIDDEN) # Anyone can download public data @@ -114,3 +153,6 @@ def test_load_unauthenticated(self): # Authenticated user cannot download others' data # Unauthenticated user cannot download private data + + def tearDown(self): + shutil.rmtree(settings.MEDIA_ROOT) \ No newline at end of file From 1050d50c2b8f9c491e4070866886e6be7bbb02cf Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Feb 2025 16:02:33 -0500 Subject: [PATCH 224/675] Tests for download permissions --- .../fair_database/test_permissions.py | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index 5942e7e9e..8fbba1cb7 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -146,13 +146,32 @@ def test_upload_put_unauthenticated(self): self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response3.status_code, status.HTTP_403_FORBIDDEN) - # Anyone can download public data - - # Authenticated user can download own data + # Authenticated user can download public and own data + def test_download_authenticated(self): + self.client.post('/auth/login/', data=self.login_data_1) + response = self.client.get('/v1/data/1/download/') + response2 = self.client.get('/v1/data/2/download/') + response3 = self.client.get('/v1/data/3/download/') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response2.status_code, status.HTTP_200_OK) + self.assertEqual(response3.status_code, status.HTTP_200_OK) # Authenticated user cannot download others' data + def test_download_unauthorized(self): + self.client.post('/auth/login/', data=self.login_data_2) + response = self.client.get('/v1/data/2/download/') + response2 = self.client.get('/v1/data/3/download/') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response2.status_code, status.HTTP_200_OK) # Unauthenticated user cannot download private data + def test_download_unauthenticated(self): + response = self.client.get('/v1/data/1/download/') + response2 = self.client.get('/v1/data/2/download/') + response3 = self.client.get('/v1/data/3/download/') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response3.status_code, status.HTTP_200_OK) def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT) \ No newline at end of file From 614b04b5e90cda639dc107bb57292bca6bad407a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 7 Feb 2025 15:55:30 -0500 Subject: [PATCH 225/675] Tests for uploading files --- .../fair_database/test_permissions.py | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index 8fbba1cb7..ff62e5e0a 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -96,10 +96,38 @@ def test_load_unauthenticated(self): self.assertEqual(response3.status_code, status.HTTP_200_OK) # Authenticated user can upload data - - # ***Unauthenticated user can upload public data - - # Unauthenticated user cannot upload private data + def test_upload_authenticated(self): + self.client.post('/auth/login/', data=self.login_data_1) + file = open(find('cyl_testdata1.txt'), 'rb') + data = { + 'file': file, + 'is_public': False + } + response = self.client.post('/v1/data/upload/', data=data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, {"current_user": 'testUser', "authenticated": True, + "file_id": 4, "file_alternative_name": "cyl_testdata1.txt", "is_public": False}) + Data.objects.get(id=4).delete() + + # Unauthenticated user can upload public data only + def test_upload_unauthenticated(self): + file = open(find('cyl_testdata2.txt'), 'rb') + file2 = open(find('cyl_testdata2.txt'), 'rb') + data = { + 'file': file, + 'is_public': True + } + data2 = { + 'file': file2, + 'is_public': False + } + response = self.client.post('/v1/data/upload/', data=data) + response2 = self.client.post('/v1/data/upload/', data=data2) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, {"current_user": '', "authenticated": False, + "file_id": 4, "file_alternative_name": "cyl_testdata2.txt", + "is_public": True}) + self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) # Authenticated user can update own data def test_upload_put_authenticated(self): From e03fa85578201d521fa79ea5cbbeafbd7020a022 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 7 Feb 2025 16:24:25 -0500 Subject: [PATCH 226/675] Test for updating one's own public data --- sasdata/fair_database/data/tests.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 67a24ce34..9800dde54 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -7,7 +7,7 @@ from rest_framework.test import APIClient, APITestCase from rest_framework import status -from .models import Data +from data.models import Data def find(filename): return os.path.join(os.path.dirname(__file__), "../../example_data/1d_data", filename) @@ -95,6 +95,20 @@ def test_does_file_upload_update(self): "file_id" : 2, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) Data.objects.get(id = 2).delete() + def test_public_file_upload_update(self): + data_object = Data.objects.create(id=3, current_user=self.user, + file_name="cyl_testdata.txt", is_public=True) + data_object.file.save("cyl_testdata.txt", open(find("cyl_testdata.txt"), 'rb')) + file = open(find("cyl_testdata1.txt")) + data = { + "file": file, + "is_public": True + } + request = self.client.put('/v1/data/upload/3/', data=data) + self.assertEqual(request.data, {"current_user": 'testUser', "authenticated": True, + "file_id": 3, "file_alternative_name": "cyl_testdata1.txt", "is_public": True}) + Data.objects.get(id=3).delete() + # Test file upload update fails when unauthorized def test_unauthorized_file_upload_update(self): file = open(find("cyl_400_40.txt")) From 50e323ff8977a339a2d9e434eb5e18f219bc1092 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 7 Feb 2025 16:26:14 -0500 Subject: [PATCH 227/675] Allow saving a new file with put --- sasdata/fair_database/data/views.py | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 958724ab4..96d6e4e60 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -50,8 +50,8 @@ def data_info(request, db_id, version = None): @api_view(['POST', 'PUT']) def upload(request, data_id = None, version = None): - #saves file - if request.method == 'POST': + # saves file + if request.method in ['POST', 'PUT'] and data_id == None: form = DataForm(request.data, request.FILES) if form.is_valid(): form.save() @@ -62,21 +62,16 @@ def upload(request, data_id = None, version = None): else: serializer = DataSerializer(db, data={"file_name":os.path.basename(form.instance.file.path)}) - - #saves or updates file + # updates file elif request.method == 'PUT': - #require data_id - if data_id != None and request.user: - if request.user.is_authenticated: - db = get_object_or_404(Data, current_user = request.user.id, id = data_id) - form = DataForm(request.data, request.FILES, instance=db) - if form.is_valid(): - form.save() - serializer = DataSerializer(db, data={"file_name":os.path.basename(form.instance.file.path)}, partial = True) - else: - return HttpResponseForbidden("user is not logged in") + if request.user.is_authenticated: + db = get_object_or_404(Data, current_user = request.user.id, id = data_id) + form = DataForm(request.data, request.FILES, instance=db) + if form.is_valid(): + form.save() + serializer = DataSerializer(db, data={"file_name":os.path.basename(form.instance.file.path)}, partial = True) else: - return HttpResponseBadRequest() + return HttpResponseForbidden("user is not logged in") if serializer.is_valid(): serializer.save() From 67f96d28937a1385a3f85d144cdec457caed2c55 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 7 Feb 2025 16:40:24 -0500 Subject: [PATCH 228/675] Require private data have an owner --- sasdata/fair_database/data/serializers.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index c90249c36..99030aa51 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -5,4 +5,10 @@ class DataSerializer(serializers.ModelSerializer): class Meta: model = Data - fields = "__all__" \ No newline at end of file + fields = "__all__" + + def validate(self, data): + print(data) + if not data['is_public'] and not data['current_user']: + raise serializers.ValidationError('private data must have an owner') + return data \ No newline at end of file From b930ccb7aabe3a55ad560048644169a5e5be7a4f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Feb 2025 14:23:35 -0500 Subject: [PATCH 229/675] Disallow uploading private unowned data --- sasdata/fair_database/data/serializers.py | 5 +++-- sasdata/fair_database/data/tests.py | 4 ++-- sasdata/fair_database/data/views.py | 12 ++++++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 99030aa51..eed3c587e 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -1,3 +1,5 @@ +import os + from rest_framework import serializers from .models import Data @@ -8,7 +10,6 @@ class Meta: fields = "__all__" def validate(self, data): - print(data) - if not data['is_public'] and not data['current_user']: + if not self.context['is_public'] and not data['current_user']: raise serializers.ValidationError('private data must have an owner') return data \ No newline at end of file diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 9800dde54..42652ebbc 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -74,13 +74,13 @@ def test_is_data_being_created(self): def test_is_data_being_created_no_user(self): file = open(find("cyl_400_40.txt"), 'rb') data = { - "is_public":False, + "is_public":True, "file":file } request = self.client2.post('/v1/data/upload/', data=data) self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, {"current_user":'', "authenticated" : False, - "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) + "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : True}) Data.objects.get(id = 3).delete() # Test updating file diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 96d6e4e60..d807649a4 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -58,9 +58,13 @@ def upload(request, data_id = None, version = None): db = Data.objects.get(pk = form.instance.pk) if request.user.is_authenticated: - serializer = DataSerializer(db, data={"file_name":os.path.basename(form.instance.file.path), "current_user" : request.user.id}) + serializer = DataSerializer(db, + data={"file_name":os.path.basename(form.instance.file.path), "current_user" : request.user.id}, + context={"is_public": db.is_public}) else: - serializer = DataSerializer(db, data={"file_name":os.path.basename(form.instance.file.path)}) + serializer = DataSerializer(db, + data={"file_name":os.path.basename(form.instance.file.path), "current_user": None}, + context={"is_public": db.is_public}) # updates file elif request.method == 'PUT': @@ -69,11 +73,11 @@ def upload(request, data_id = None, version = None): form = DataForm(request.data, request.FILES, instance=db) if form.is_valid(): form.save() - serializer = DataSerializer(db, data={"file_name":os.path.basename(form.instance.file.path)}, partial = True) + serializer = DataSerializer(db, data={"file_name":os.path.basename(form.instance.file.path), "current_user": request.user.id}, context={"is_public": db.is_public}, partial = True) else: return HttpResponseForbidden("user is not logged in") - if serializer.is_valid(): + if serializer.is_valid(raise_exception=True): serializer.save() #TODO get warnings/errors later return_data = {"current_user":request.user.username, "authenticated" : request.user.is_authenticated, "file_id" : db.id, "file_alternative_name":serializer.data["file_name"],"is_public" : serializer.data["is_public"]} From 03f41dae5d088530b3347839d6963c0c1098c256 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Feb 2025 14:31:50 -0500 Subject: [PATCH 230/675] Rename Data model to DataFile --- sasdata/fair_database/data/admin.py | 4 +-- sasdata/fair_database/data/forms.py | 6 ++-- .../migrations/0002_rename_data_datafile.py | 19 +++++++++++++ sasdata/fair_database/data/models.py | 2 +- sasdata/fair_database/data/serializers.py | 8 ++---- sasdata/fair_database/data/tests.py | 20 ++++++------- sasdata/fair_database/data/views.py | 28 +++++++++---------- .../fair_database/test_permissions.py | 12 ++++---- 8 files changed, 58 insertions(+), 41 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0002_rename_data_datafile.py diff --git a/sasdata/fair_database/data/admin.py b/sasdata/fair_database/data/admin.py index bfe8c7d93..7e4b76182 100644 --- a/sasdata/fair_database/data/admin.py +++ b/sasdata/fair_database/data/admin.py @@ -1,4 +1,4 @@ from django.contrib import admin -from .models import Data +from data.models import DataFile -admin.site.register(Data) \ No newline at end of file +admin.site.register(DataFile) \ No newline at end of file diff --git a/sasdata/fair_database/data/forms.py b/sasdata/fair_database/data/forms.py index e336efabd..f49ffca14 100644 --- a/sasdata/fair_database/data/forms.py +++ b/sasdata/fair_database/data/forms.py @@ -1,8 +1,8 @@ from django import forms -from .models import Data +from data.models import DataFile # Create the form class. -class DataForm(forms.ModelForm): +class DataFileForm(forms.ModelForm): class Meta: - model = Data + model = DataFile fields = ["file", "is_public"] \ No newline at end of file diff --git a/sasdata/fair_database/data/migrations/0002_rename_data_datafile.py b/sasdata/fair_database/data/migrations/0002_rename_data_datafile.py new file mode 100644 index 000000000..33d9079bd --- /dev/null +++ b/sasdata/fair_database/data/migrations/0002_rename_data_datafile.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.5 on 2025-02-10 19:30 + +from django.conf import settings +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('data', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.RenameModel( + old_name='Data', + new_name='DataFile', + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index e902193c3..821f822da 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -3,7 +3,7 @@ from django.core.files.storage import FileSystemStorage # Create your models here. -class Data(models.Model): +class DataFile(models.Model): #username current_user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index eed3c587e..70fed9d18 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -1,12 +1,10 @@ -import os - from rest_framework import serializers -from .models import Data +from data.models import DataFile -class DataSerializer(serializers.ModelSerializer): +class DataFileSerializer(serializers.ModelSerializer): class Meta: - model = Data + model = DataFile fields = "__all__" def validate(self, data): diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 42652ebbc..8e1535b48 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -7,18 +7,18 @@ from rest_framework.test import APIClient, APITestCase from rest_framework import status -from data.models import Data +from data.models import DataFile def find(filename): return os.path.join(os.path.dirname(__file__), "../../example_data/1d_data", filename) class TestLists(TestCase): def setUp(self): - public_test_data = Data.objects.create(id = 1, file_name = "cyl_400_40.txt", + public_test_data = DataFile.objects.create(id = 1, file_name = "cyl_400_40.txt", is_public = True) public_test_data.file.save("cyl_400_40.txt", open(find("cyl_400_40.txt"), 'rb')) self.user = User.objects.create_user(username="testUser", password="secret", id = 2) - private_test_data = Data.objects.create(id = 3, current_user = self.user, + private_test_data = DataFile.objects.create(id = 3, current_user = self.user, file_name = "cyl_400_20.txt", is_public = False) private_test_data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), 'rb')) self.client = APIClient() @@ -50,7 +50,7 @@ def tearDown(self): class TestingDatabase(APITestCase): def setUp(self): self.user = User.objects.create_user(username="testUser", password="secret", id = 1) - self.data = Data.objects.create(id = 2, current_user = self.user, + self.data = DataFile.objects.create(id = 2, current_user = self.user, file_name = "cyl_400_20.txt", is_public = False) self.data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), 'rb')) self.client = APIClient() @@ -68,7 +68,7 @@ def test_is_data_being_created(self): self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, {"current_user":'testUser', "authenticated" : True, "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) - Data.objects.get(id = 3).delete() + DataFile.objects.get(id = 3).delete() # Test data upload w/out authenticated user def test_is_data_being_created_no_user(self): @@ -81,7 +81,7 @@ def test_is_data_being_created_no_user(self): self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, {"current_user":'', "authenticated" : False, "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : True}) - Data.objects.get(id = 3).delete() + DataFile.objects.get(id = 3).delete() # Test updating file def test_does_file_upload_update(self): @@ -93,10 +93,10 @@ def test_does_file_upload_update(self): request = self.client.put('/v1/data/upload/2/', data = data) self.assertEqual(request.data, {"current_user":'testUser', "authenticated" : True, "file_id" : 2, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) - Data.objects.get(id = 2).delete() + DataFile.objects.get(id = 2).delete() def test_public_file_upload_update(self): - data_object = Data.objects.create(id=3, current_user=self.user, + data_object = DataFile.objects.create(id=3, current_user=self.user, file_name="cyl_testdata.txt", is_public=True) data_object.file.save("cyl_testdata.txt", open(find("cyl_testdata.txt"), 'rb')) file = open(find("cyl_testdata1.txt")) @@ -107,7 +107,7 @@ def test_public_file_upload_update(self): request = self.client.put('/v1/data/upload/3/', data=data) self.assertEqual(request.data, {"current_user": 'testUser', "authenticated": True, "file_id": 3, "file_alternative_name": "cyl_testdata1.txt", "is_public": True}) - Data.objects.get(id=3).delete() + DataFile.objects.get(id=3).delete() # Test file upload update fails when unauthorized def test_unauthorized_file_upload_update(self): @@ -118,7 +118,7 @@ def test_unauthorized_file_upload_update(self): } request = self.client2.put('/v1/data/upload/2/', data=data) self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) - Data.objects.get(id=2).delete() + DataFile.objects.get(id=2).delete() # Test file download def test_does_download(self): diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index d807649a4..a9d01b1e7 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -6,9 +6,9 @@ from rest_framework.response import Response from sasdata.dataloader.loader import Loader -from .serializers import DataSerializer -from .models import Data -from .forms import DataForm +from data.serializers import DataFileSerializer +from data.models import DataFile +from data.forms import DataFileForm @api_view(['GET']) def list_data(request, username = None, version = None): @@ -16,13 +16,13 @@ def list_data(request, username = None, version = None): if username: data_list = {"user_data_ids": {}} if username == request.user.username and request.user.is_authenticated: - private_data = Data.objects.filter(current_user=request.user.id) + private_data = DataFile.objects.filter(current_user=request.user.id) for x in private_data: data_list["user_data_ids"][x.id] = x.file_name else: return HttpResponseBadRequest("user is not logged in, or username is not same as current user") else: - public_data = Data.objects.filter(is_public=True) + public_data = DataFile.objects.filter(is_public=True) data_list = {"public_data_ids": {}} for x in public_data: data_list["public_data_ids"][x.id] = x.file_name @@ -33,7 +33,7 @@ def list_data(request, username = None, version = None): def data_info(request, db_id, version = None): if request.method == 'GET': loader = Loader() - data_db = get_object_or_404(Data, id=db_id) + data_db = get_object_or_404(DataFile, id=db_id) if data_db.is_public: data_list = loader.load(data_db.file.path) contents = [str(data) for data in data_list] @@ -52,28 +52,28 @@ def data_info(request, db_id, version = None): def upload(request, data_id = None, version = None): # saves file if request.method in ['POST', 'PUT'] and data_id == None: - form = DataForm(request.data, request.FILES) + form = DataFileForm(request.data, request.FILES) if form.is_valid(): form.save() - db = Data.objects.get(pk = form.instance.pk) + db = DataFile.objects.get(pk = form.instance.pk) if request.user.is_authenticated: - serializer = DataSerializer(db, + serializer = DataFileSerializer(db, data={"file_name":os.path.basename(form.instance.file.path), "current_user" : request.user.id}, context={"is_public": db.is_public}) else: - serializer = DataSerializer(db, + serializer = DataFileSerializer(db, data={"file_name":os.path.basename(form.instance.file.path), "current_user": None}, context={"is_public": db.is_public}) # updates file elif request.method == 'PUT': if request.user.is_authenticated: - db = get_object_or_404(Data, current_user = request.user.id, id = data_id) - form = DataForm(request.data, request.FILES, instance=db) + db = get_object_or_404(DataFile, current_user = request.user.id, id = data_id) + form = DataFileForm(request.data, request.FILES, instance=db) if form.is_valid(): form.save() - serializer = DataSerializer(db, data={"file_name":os.path.basename(form.instance.file.path), "current_user": request.user.id}, context={"is_public": db.is_public}, partial = True) + serializer = DataFileSerializer(db, data={"file_name":os.path.basename(form.instance.file.path), "current_user": request.user.id}, context={"is_public": db.is_public}, partial = True) else: return HttpResponseForbidden("user is not logged in") @@ -87,7 +87,7 @@ def upload(request, data_id = None, version = None): @api_view(['GET']) def download(request, data_id, version = None): if request.method == 'GET': - data = get_object_or_404(Data, id=data_id) + data = get_object_or_404(DataFile, id=data_id) if not data.is_public: # add session key later if not request.user.is_authenticated: diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index ff62e5e0a..1c37cd547 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -6,7 +6,7 @@ from rest_framework import status from rest_framework.test import APIClient, APITestCase -from data.models import Data +from data.models import DataFile def find(filename): return os.path.join(os.path.dirname(__file__), "../../example_data/1d_data", filename) @@ -19,13 +19,13 @@ def setUp(self): email="email@domain.com") self.user2 = User.objects.create_user(username="testUser2", password="secret", id=2, email="email2@domain.com") - unowned_test_data = Data.objects.create(id=1, file_name="cyl_400_40.txt", + unowned_test_data = DataFile.objects.create(id=1, file_name="cyl_400_40.txt", is_public=True) unowned_test_data.file.save("cyl_400_40.txt", open(find("cyl_400_40.txt"), 'rb')) - private_test_data = Data.objects.create(id=2, current_user=self.user, + private_test_data = DataFile.objects.create(id=2, current_user=self.user, file_name="cyl_400_20.txt", is_public=False) private_test_data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), 'rb')) - public_test_data = Data.objects.create(id=3, current_user=self.user, + public_test_data = DataFile.objects.create(id=3, current_user=self.user, file_name="cyl_testdata.txt", is_public=True) public_test_data.file.save("cyl_testdata.txt", open(find("cyl_testdata.txt"), 'rb')) self.login_data_1 = { @@ -107,7 +107,7 @@ def test_upload_authenticated(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data, {"current_user": 'testUser', "authenticated": True, "file_id": 4, "file_alternative_name": "cyl_testdata1.txt", "is_public": False}) - Data.objects.get(id=4).delete() + DataFile.objects.get(id=4).delete() # Unauthenticated user can upload public data only def test_upload_unauthenticated(self): @@ -143,7 +143,7 @@ def test_upload_put_authenticated(self): self.assertEqual(response2.data, {"current_user": 'testUser', "authenticated": True, "file_id": 3, "file_alternative_name": "cyl_testdata.txt", "is_public": False}) - Data.objects.get(id=3).is_public = True + DataFile.objects.get(id=3).is_public = True # Authenticated user cannot update unowned data def test_upload_put_unauthorized(self): From 66d08c6befe200f9d69d9ed516a922f6c495137a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Feb 2025 14:55:35 -0500 Subject: [PATCH 231/675] Permission class for data --- sasdata/fair_database/data/permissions.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 sasdata/fair_database/data/permissions.py diff --git a/sasdata/fair_database/data/permissions.py b/sasdata/fair_database/data/permissions.py new file mode 100644 index 000000000..d0d32c2d4 --- /dev/null +++ b/sasdata/fair_database/data/permissions.py @@ -0,0 +1,15 @@ +from rest_framework import permissions + +def is_owner(request, obj): + return request.user.is_authenticated and request.user.id == obj.current_user + +class DataPermission(permissions.BasicPermission): + def has_object_permission(self, request, view, obj): + if request.method == 'GET': + if obj.is_public or is_owner(request, obj): + return True + elif request.method == 'DELETE': + if obj.is_private and is_owner(request, obj): + return True + else: + return is_owner(request, obj) \ No newline at end of file From 46a56b8d92d34453766de85a8011d2fc9270e62b Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Feb 2025 16:33:13 -0500 Subject: [PATCH 232/675] Create permissions class --- sasdata/fair_database/data/permissions.py | 15 ---------- .../fair_database/permissions.py | 28 +++++++++++++++++++ 2 files changed, 28 insertions(+), 15 deletions(-) delete mode 100644 sasdata/fair_database/data/permissions.py create mode 100644 sasdata/fair_database/fair_database/permissions.py diff --git a/sasdata/fair_database/data/permissions.py b/sasdata/fair_database/data/permissions.py deleted file mode 100644 index d0d32c2d4..000000000 --- a/sasdata/fair_database/data/permissions.py +++ /dev/null @@ -1,15 +0,0 @@ -from rest_framework import permissions - -def is_owner(request, obj): - return request.user.is_authenticated and request.user.id == obj.current_user - -class DataPermission(permissions.BasicPermission): - def has_object_permission(self, request, view, obj): - if request.method == 'GET': - if obj.is_public or is_owner(request, obj): - return True - elif request.method == 'DELETE': - if obj.is_private and is_owner(request, obj): - return True - else: - return is_owner(request, obj) \ No newline at end of file diff --git a/sasdata/fair_database/fair_database/permissions.py b/sasdata/fair_database/fair_database/permissions.py new file mode 100644 index 000000000..51379b939 --- /dev/null +++ b/sasdata/fair_database/fair_database/permissions.py @@ -0,0 +1,28 @@ +from rest_framework.permissions import BasePermission + + +def is_owner(request, obj): + return request.user.is_authenticated and request.user.id == obj.current_user + +class DataPermission(BasePermission): + + def has_object_permission(self, request, view, obj): + if request.method == 'GET': + if obj.is_public or is_owner(request, obj): + return True + elif request.method == 'DELETE': + if obj.is_private and is_owner(request, obj): + return True + else: + return is_owner(request, obj) + +def check_permissions(request, obj): + if request.method == 'GET': + if obj.is_public or is_owner(request, obj): + return True + elif request.method == 'DELETE': + if obj.is_private and is_owner(request, obj): + return True + else: + return is_owner(request, obj) + From 4bc996c6e0b82725cd1484c3ed0d67de3c4ef918 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Feb 2025 16:34:21 -0500 Subject: [PATCH 233/675] Change permissions class --- sasdata/fair_database/fair_database/settings.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index c96d7b934..22112af4d 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -92,7 +92,13 @@ ) REST_FRAMEWORK = { - 'DEFAULT ATHENTICATION CLASSES': ('knox.auth.TokenAuthentication'), + 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'knox.auth.TokenAuthentication', + 'rest_framework.authentication.SessionAuthentication', + ], + #'DEFAULT_PERMISSION_CLASSES': [ + # 'fair_database.permissions.DataPermission', + #], } REST_AUTH = { From f0bb9ce64148b6f3e619abdda4b8d9c725ed933c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Feb 2025 16:36:45 -0500 Subject: [PATCH 234/675] Change error codes --- sasdata/fair_database/user_app/tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index f019c29a6..28ad0ab6c 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -88,7 +88,7 @@ def test_user_put_name(self): # Test user info inaccessible when unauthenticated def test_user_unauthenticated(self): response = self.client.get('/auth/user/') - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(response.content, b'{"detail":"Authentication credentials were not provided."}') @@ -100,7 +100,7 @@ def test_login_logout(self): response2 = self.client.get('/auth/user/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') - self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) # Test logout is successful after registration def test_register_logout(self): @@ -109,7 +109,7 @@ def test_register_logout(self): response2 = self.client.get('/auth/user/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') - self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) def test_multiple_logout(self): User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") From a791f8eb57c5535927b4b3b753bef82d0d0494e0 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Feb 2025 16:38:21 -0500 Subject: [PATCH 235/675] Remove relative imports --- sasdata/fair_database/user_app/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/user_app/views.py b/sasdata/fair_database/user_app/views.py index 88eb2eece..4a55fdc7e 100644 --- a/sasdata/fair_database/user_app/views.py +++ b/sasdata/fair_database/user_app/views.py @@ -7,8 +7,8 @@ from allauth.account import app_settings as allauth_settings from allauth.socialaccount.providers.orcid.views import OrcidOAuth2Adapter -from .serializers import KnoxSerializer -from .util import create_knox_token +from user_app.serializers import KnoxSerializer +from user_app.util import create_knox_token #Login using knox tokens rather than django-rest-framework tokens. From 582797cedb7c665764b882820b945d3578610744 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Feb 2025 16:39:06 -0500 Subject: [PATCH 236/675] Start changing permissions checking --- sasdata/fair_database/data/views.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index a9d01b1e7..7d80570bd 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -9,22 +9,27 @@ from data.serializers import DataFileSerializer from data.models import DataFile from data.forms import DataFileForm +from fair_database import permissions @api_view(['GET']) def list_data(request, username = None, version = None): if request.method == 'GET': if username: data_list = {"user_data_ids": {}} - if username == request.user.username and request.user.is_authenticated: - private_data = DataFile.objects.filter(current_user=request.user.id) - for x in private_data: - data_list["user_data_ids"][x.id] = x.file_name - else: - return HttpResponseBadRequest("user is not logged in, or username is not same as current user") + #if username == request.user.username and request.user.is_authenticated: + private_data = DataFile.objects.filter(current_user=request.user.id) + for x in private_data: + if not permissions.check_permissions(request, x): + return HttpResponseForbidden() + data_list["user_data_ids"][x.id] = x.file_name + #else: + # return HttpResponseBadRequest("user is not logged in, or username is not same as current user") else: public_data = DataFile.objects.filter(is_public=True) data_list = {"public_data_ids": {}} for x in public_data: + if not permissions.check_permissions(request, x): + return HttpResponseForbidden() data_list["public_data_ids"][x.id] = x.file_name return Response(data_list) return HttpResponseBadRequest("not get method") From a4279cd3522f538f72292b245c2e894233d1c5de Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Feb 2025 13:12:05 -0500 Subject: [PATCH 237/675] Change authentication serializer to used token directly --- sasdata/fair_database/user_app/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/user_app/serializers.py b/sasdata/fair_database/user_app/serializers.py index 5181a7354..7d315377d 100644 --- a/sasdata/fair_database/user_app/serializers.py +++ b/sasdata/fair_database/user_app/serializers.py @@ -7,8 +7,8 @@ class KnoxSerializer(serializers.Serializer): """ Serializer for Knox authentication. """ - token = serializers.CharField() + token = serializers.SerializerMethodField() user = UserDetailsSerializer() def get_token(self, obj): - return obj["token"][1] \ No newline at end of file + return obj["token"][1] \ No newline at end of file From 1783cad395288927671c5e6141e566928edb27ca Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Feb 2025 13:13:44 -0500 Subject: [PATCH 238/675] Change tests to use token authentication properly --- .../fair_database/test_permissions.py | 61 ++++++++++--------- sasdata/fair_database/user_app/tests.py | 17 +++--- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index 1c37cd547..c9c2e991b 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -11,6 +11,9 @@ def find(filename): return os.path.join(os.path.dirname(__file__), "../../example_data/1d_data", filename) +def auth_header(response): + return {'Authorization': 'Token ' + response.data['token']} + class DataListPermissionsTests(APITestCase): ''' Test permissions of data views using user_app for authentication. ''' @@ -42,9 +45,9 @@ def setUp(self): # Authenticated user can view list of data # TODO: change to reflect inclusion of owned private data def test_list_authenticated(self): - self.client.post('/auth/login/', data=self.login_data_1) - response = self.client.get('/v1/data/list/') - response2 = self.client.get('/v1/data/list/testUser/') + token = self.client.post('/auth/login/', data=self.login_data_1) + response = self.client.get('/v1/data/list/', headers=auth_header(token)) + response2 = self.client.get('/v1/data/list/testUser/', headers=auth_header(token)) self.assertEqual(response.data, {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}) self.assertEqual(response2.data, @@ -53,10 +56,10 @@ def test_list_authenticated(self): # Authenticated user cannot view other users' private data on list # TODO: Change response codes def test_list_authenticated_2(self): - self.client.post('/auth/login/', data=self.login_data_2) - response = self.client.get('/v1/data/list/') - response2 = self.client.get('/v1/data/list/testUser/') - response3 = self.client.get('/v1/data/list/testUser2/') + token = self.client.post('/auth/login/', data=self.login_data_2) + response = self.client.get('/v1/data/list/', headers=auth_header(token)) + response2 = self.client.get('/v1/data/list/testUser/', headers=auth_header(token)) + response3 = self.client.get('/v1/data/list/testUser2/', headers=auth_header(token)) self.assertEqual(response.data, {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}) self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) @@ -72,17 +75,17 @@ def test_list_unauthenticated(self): # Authenticated user can load public data and owned private data def test_load_authenticated(self): - self.client.post('/auth/login/', data=self.login_data_1) - response = self.client.get('/v1/data/load/1/') - response2 = self.client.get('/v1/data/load/2/') + token = self.client.post('/auth/login/', data=self.login_data_1) + response = self.client.get('/v1/data/load/1/', headers=auth_header(token)) + response2 = self.client.get('/v1/data/load/2/', headers=auth_header(token)) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) # Authenticated user cannot load others' private data def test_load_unauthorized(self): - self.client.post('/auth/login/', data=self.login_data_2) - response = self.client.get('/v1/data/load/2/') - response2 = self.client.get('/v1/data/load/3/') + token = self.client.post('/auth/login/', data=self.login_data_2) + response = self.client.get('/v1/data/load/2/', headers=auth_header(token)) + response2 = self.client.get('/v1/data/load/3/', headers=auth_header(token)) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response2.status_code, status.HTTP_200_OK) @@ -97,13 +100,13 @@ def test_load_unauthenticated(self): # Authenticated user can upload data def test_upload_authenticated(self): - self.client.post('/auth/login/', data=self.login_data_1) + token = self.client.post('/auth/login/', data=self.login_data_1) file = open(find('cyl_testdata1.txt'), 'rb') data = { 'file': file, 'is_public': False } - response = self.client.post('/v1/data/upload/', data=data) + response = self.client.post('/v1/data/upload/', data=data, headers=auth_header(token)) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data, {"current_user": 'testUser', "authenticated": True, "file_id": 4, "file_alternative_name": "cyl_testdata1.txt", "is_public": False}) @@ -131,12 +134,12 @@ def test_upload_unauthenticated(self): # Authenticated user can update own data def test_upload_put_authenticated(self): - self.client.post('/auth/login/', data=self.login_data_1) + token = self.client.post('/auth/login/', data=self.login_data_1) data = { "is_public": False } - response = self.client.put('/v1/data/upload/2/', data=data) - response2 = self.client.put('/v1/data/upload/3/', data=data) + response = self.client.put('/v1/data/upload/2/', data=data, headers=auth_header(token)) + response2 = self.client.put('/v1/data/upload/3/', data=data, headers=auth_header(token)) self.assertEqual(response.data, {"current_user": 'testUser', "authenticated": True, "file_id": 2, "file_alternative_name": "cyl_400_20.txt", "is_public": False}) @@ -147,15 +150,15 @@ def test_upload_put_authenticated(self): # Authenticated user cannot update unowned data def test_upload_put_unauthorized(self): - self.client.post('/auth/login/', data=self.login_data_2) + token = self.client.post('/auth/login/', data=self.login_data_2) file = open(find("cyl_400_40.txt")) data = { "file": file, "is_public": False } - response = self.client.put('/v1/data/upload/1/', data=data) - response2 = self.client.put('/v1/data/upload/2/', data=data) - response3 = self.client.put('/v1/data/upload/3/', data=data) + response = self.client.put('/v1/data/upload/1/', data=data, headers=auth_header(token)) + response2 = self.client.put('/v1/data/upload/2/', data=data, headers=auth_header(token)) + response3 = self.client.put('/v1/data/upload/3/', data=data, headers=auth_header(token)) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response2.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response3.status_code, status.HTTP_404_NOT_FOUND) @@ -176,19 +179,19 @@ def test_upload_put_unauthenticated(self): # Authenticated user can download public and own data def test_download_authenticated(self): - self.client.post('/auth/login/', data=self.login_data_1) - response = self.client.get('/v1/data/1/download/') - response2 = self.client.get('/v1/data/2/download/') - response3 = self.client.get('/v1/data/3/download/') + token = self.client.post('/auth/login/', data=self.login_data_1) + response = self.client.get('/v1/data/1/download/', headers=auth_header(token)) + response2 = self.client.get('/v1/data/2/download/', headers=auth_header(token)) + response3 = self.client.get('/v1/data/3/download/', headers=auth_header(token)) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) self.assertEqual(response3.status_code, status.HTTP_200_OK) # Authenticated user cannot download others' data def test_download_unauthorized(self): - self.client.post('/auth/login/', data=self.login_data_2) - response = self.client.get('/v1/data/2/download/') - response2 = self.client.get('/v1/data/3/download/') + token = self.client.post('/auth/login/', data=self.login_data_2) + response = self.client.get('/v1/data/2/download/', headers=auth_header(token)) + response2 = self.client.get('/v1/data/3/download/', headers=auth_header(token)) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response2.status_code, status.HTTP_200_OK) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index 28ad0ab6c..b3f203a35 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -1,5 +1,3 @@ -import requests - from django.test import TestCase from rest_framework import status from rest_framework.test import APIClient @@ -23,11 +21,14 @@ def setUp(self): "password": "sasview!" } + def auth_header(self, response): + return {'Authorization': 'Token ' + response.data['token']} + # Test if registration successfully creates a new user and logs in def test_register(self): response = self.client.post('/auth/register/',data=self.register_data) user = User.objects.get(username="testUser") - response2 = self.client.get('/auth/user/') + response2 = self.client.get('/auth/user/', headers=self.auth_header(response)) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(user.email, self.register_data["email"]) self.assertEqual(response2.status_code, status.HTTP_200_OK) @@ -36,7 +37,7 @@ def test_register(self): def test_login(self): User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") response = self.client.post('/auth/login/', data=self.login_data) - response2 = self.client.get('/auth/user/') + response2 = self.client.get('/auth/user/', headers=self.auth_header(response)) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) @@ -115,9 +116,9 @@ def test_multiple_logout(self): User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") client2 = APIClient() self.client.post('/auth/login/', data=self.login_data) - client2.post('/auth/login/', data=self.login_data) + token = client2.post('/auth/login/', data=self.login_data) response = self.client.post('/auth/logout/') - response2 = client2.get('/auth/user/') + response2 = client2.get('/auth/user/', headers=self.auth_header(token)) response3 = client2.post('/auth/logout/') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) @@ -134,7 +135,7 @@ def test_register_login(self): # Test password is successfully changed def test_password_change(self): - self.client.post('/auth/register/', data=self.register_data) + token = self.client.post('/auth/register/', data=self.register_data) data = { "new_password1": "sasview?", "new_password2": "sasview?", @@ -142,7 +143,7 @@ def test_password_change(self): } l_data = self.login_data l_data["password"] = "sasview?" - response = self.client.post('/auth/password/change/', data=data) + response = self.client.post('/auth/password/change/', data=data, headers=self.auth_header(token)) logout_response = self.client.post('/auth/logout/') login_response = self.client.post('/auth/login/', data=l_data) self.assertEqual(response.status_code, status.HTTP_200_OK) From 946b8fe4521e0fc52474020d01a20b9daa626053 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Feb 2025 13:14:51 -0500 Subject: [PATCH 239/675] Turn off session authentication option --- sasdata/fair_database/fair_database/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index 22112af4d..dd448c8e4 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -94,7 +94,7 @@ REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'knox.auth.TokenAuthentication', - 'rest_framework.authentication.SessionAuthentication', + #'rest_framework.authentication.SessionAuthentication', ], #'DEFAULT_PERMISSION_CLASSES': [ # 'fair_database.permissions.DataPermission', From 020704d6e9177657951b8e83297cec51f5221609 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Feb 2025 14:25:13 -0500 Subject: [PATCH 240/675] Add django tests to github actions and formating config --- .github/workflows/test-fair-database.yml | 56 ++++++++++++++++++++++++ .pre-commit-config.yaml | 27 ++++++++---- 2 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/test-fair-database.yml diff --git a/.github/workflows/test-fair-database.yml b/.github/workflows/test-fair-database.yml new file mode 100644 index 000000000..531c93cf1 --- /dev/null +++ b/.github/workflows/test-fair-database.yml @@ -0,0 +1,56 @@ +name: Tests + +on: + [push, pull_request] + +defaults: + run: + shell: bash + +jobs: + unit-test: + + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-latest, ubuntu-latest, windows-latest] + python-version: ['3.12'] + fail-fast: false + + steps: + + - name: Obtain SasData source from git + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + cache-dependency-path: | + **/test.yml + **/requirements*.txt + + ### Installation of build-dependencies + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + python -m pip install wheel setuptools + python -m pip install -r requirements.txt + python -m pip install -r sasdata/fair_database/requirements.txt + + ### Build and test sasdata + + - name: Build sasdata + run: | + # BUILD SASDATA + python setup.py clean + python setup.py build + python -m pip install . + + ### Build documentation (if enabled) + + - name: Test with Django tests + run: | + python sasdata/fair_database/manage.py test diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b92d96596..f1958b339 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,19 @@ -default_install_hook_types: [pre-commit, pre-push] - repos: - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.9 - hooks: - # Run the linter, applying any available fixes - - id: ruff-check - stages: [ pre-commit, pre-push ] - args: [ --fix-only ] +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: trailing-whitespace + files: "sasdata/fair_database/.*" +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.9.2 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix] + files: "sasdata/fair_database/.*" + - id: ruff-format + files: "sasdata/fair_database/.*" +- repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + files: "sasdata/fair_database/.*" From 35bb34b3f524f097ba92c5dd818fa7e8b0ad14fd Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Feb 2025 14:49:06 -0500 Subject: [PATCH 241/675] Restore checks for user-specific list data --- sasdata/fair_database/data/views.py | 109 ++++++++++++++++++---------- 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 7d80570bd..1a3f0d2c5 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -1,7 +1,12 @@ import os from django.shortcuts import get_object_or_404 -from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, Http404, FileResponse +from django.http import ( + HttpResponseBadRequest, + HttpResponseForbidden, + Http404, + FileResponse, +) from rest_framework.decorators import api_view from rest_framework.response import Response @@ -11,19 +16,20 @@ from data.forms import DataFileForm from fair_database import permissions -@api_view(['GET']) -def list_data(request, username = None, version = None): - if request.method == 'GET': + +@api_view(["GET"]) +def list_data(request, username=None, version=None): + if request.method == "GET": if username: data_list = {"user_data_ids": {}} - #if username == request.user.username and request.user.is_authenticated: - private_data = DataFile.objects.filter(current_user=request.user.id) - for x in private_data: - if not permissions.check_permissions(request, x): - return HttpResponseForbidden() - data_list["user_data_ids"][x.id] = x.file_name - #else: - # return HttpResponseBadRequest("user is not logged in, or username is not same as current user") + if username == request.user.username and request.user.is_authenticated: + private_data = DataFile.objects.filter(current_user=request.user.id) + for x in private_data: + data_list["user_data_ids"][x.id] = x.file_name + else: + return HttpResponseBadRequest( + "user is not logged in, or username is not same as current user" + ) else: public_data = DataFile.objects.filter(is_public=True) data_list = {"public_data_ids": {}} @@ -34,9 +40,10 @@ def list_data(request, username = None, version = None): return Response(data_list) return HttpResponseBadRequest("not get method") -@api_view(['GET']) -def data_info(request, db_id, version = None): - if request.method == 'GET': + +@api_view(["GET"]) +def data_info(request, db_id, version=None): + if request.method == "GET": loader = Loader() data_db = get_object_or_404(DataFile, id=db_id) if data_db.is_public: @@ -49,49 +56,77 @@ def data_info(request, db_id, version = None): contents = [str(data) for data in data_list] return_data = {data_db.file_name: contents} else: - return HttpResponseBadRequest("Database is either not public or wrong auth token") + return HttpResponseBadRequest( + "Database is either not public or wrong auth token" + ) return Response(return_data) return HttpResponseBadRequest() -@api_view(['POST', 'PUT']) -def upload(request, data_id = None, version = None): + +@api_view(["POST", "PUT"]) +def upload(request, data_id=None, version=None): # saves file - if request.method in ['POST', 'PUT'] and data_id == None: + if request.method in ["POST", "PUT"] and data_id is None: form = DataFileForm(request.data, request.FILES) if form.is_valid(): form.save() - db = DataFile.objects.get(pk = form.instance.pk) + db = DataFile.objects.get(pk=form.instance.pk) if request.user.is_authenticated: - serializer = DataFileSerializer(db, - data={"file_name":os.path.basename(form.instance.file.path), "current_user" : request.user.id}, - context={"is_public": db.is_public}) + serializer = DataFileSerializer( + db, + data={ + "file_name": os.path.basename(form.instance.file.path), + "current_user": request.user.id, + }, + context={"is_public": db.is_public}, + ) else: - serializer = DataFileSerializer(db, - data={"file_name":os.path.basename(form.instance.file.path), "current_user": None}, - context={"is_public": db.is_public}) + serializer = DataFileSerializer( + db, + data={ + "file_name": os.path.basename(form.instance.file.path), + "current_user": None, + }, + context={"is_public": db.is_public}, + ) # updates file - elif request.method == 'PUT': + elif request.method == "PUT": if request.user.is_authenticated: - db = get_object_or_404(DataFile, current_user = request.user.id, id = data_id) + db = get_object_or_404(DataFile, current_user=request.user.id, id=data_id) form = DataFileForm(request.data, request.FILES, instance=db) if form.is_valid(): form.save() - serializer = DataFileSerializer(db, data={"file_name":os.path.basename(form.instance.file.path), "current_user": request.user.id}, context={"is_public": db.is_public}, partial = True) + serializer = DataFileSerializer( + db, + data={ + "file_name": os.path.basename(form.instance.file.path), + "current_user": request.user.id, + }, + context={"is_public": db.is_public}, + partial=True, + ) else: return HttpResponseForbidden("user is not logged in") if serializer.is_valid(raise_exception=True): serializer.save() - #TODO get warnings/errors later - return_data = {"current_user":request.user.username, "authenticated" : request.user.is_authenticated, "file_id" : db.id, "file_alternative_name":serializer.data["file_name"],"is_public" : serializer.data["is_public"]} + # TODO get warnings/errors later + return_data = { + "current_user": request.user.username, + "authenticated": request.user.is_authenticated, + "file_id": db.id, + "file_alternative_name": serializer.data["file_name"], + "is_public": serializer.data["is_public"], + } return Response(return_data) -#downloads a file -@api_view(['GET']) -def download(request, data_id, version = None): - if request.method == 'GET': + +# downloads a file +@api_view(["GET"]) +def download(request, data_id, version=None): + if request.method == "GET": data = get_object_or_404(DataFile, id=data_id) if not data.is_public: # add session key later @@ -101,10 +136,10 @@ def download(request, data_id, version = None): return HttpResponseForbidden("data is private") # TODO add issues later try: - file = open(data.file.path, 'rb') + file = open(data.file.path, "rb") except Exception as e: return HttpResponseBadRequest(str(e)) if file is None: raise Http404("File not found.") return FileResponse(file, as_attachment=True) - return HttpResponseBadRequest() \ No newline at end of file + return HttpResponseBadRequest() From 2068ad6fe567204001fa0ddfba6ce3caaa76d686 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Feb 2025 14:50:18 -0500 Subject: [PATCH 242/675] ruff formatting --- sasdata/fair_database/data/admin.py | 2 +- sasdata/fair_database/data/apps.py | 4 +- sasdata/fair_database/data/forms.py | 3 +- .../data/migrations/0001_initial.py | 52 +++- .../migrations/0002_rename_data_datafile.py | 7 +- sasdata/fair_database/data/models.py | 35 ++- sasdata/fair_database/data/serializers.py | 7 +- sasdata/fair_database/data/tests.py | 160 ++++++---- sasdata/fair_database/data/urls.py | 15 +- sasdata/fair_database/fair_database/asgi.py | 2 +- .../fair_database/permissions.py | 12 +- .../fair_database/fair_database/settings.py | 148 +++++----- .../fair_database/test_permissions.py | 276 +++++++++++------- .../fair_database/upload_example_data.py | 15 +- sasdata/fair_database/fair_database/urls.py | 5 +- sasdata/fair_database/fair_database/wsgi.py | 2 +- sasdata/fair_database/manage.py | 5 +- sasdata/fair_database/user_app/admin.py | 2 - sasdata/fair_database/user_app/apps.py | 4 +- sasdata/fair_database/user_app/models.py | 2 - sasdata/fair_database/user_app/serializers.py | 3 +- sasdata/fair_database/user_app/tests.py | 131 +++++---- sasdata/fair_database/user_app/urls.py | 19 +- sasdata/fair_database/user_app/util.py | 2 +- sasdata/fair_database/user_app/views.py | 26 +- 25 files changed, 544 insertions(+), 395 deletions(-) diff --git a/sasdata/fair_database/data/admin.py b/sasdata/fair_database/data/admin.py index 7e4b76182..e000e5320 100644 --- a/sasdata/fair_database/data/admin.py +++ b/sasdata/fair_database/data/admin.py @@ -1,4 +1,4 @@ from django.contrib import admin from data.models import DataFile -admin.site.register(DataFile) \ No newline at end of file +admin.site.register(DataFile) diff --git a/sasdata/fair_database/data/apps.py b/sasdata/fair_database/data/apps.py index f6b7ef7fa..b882be950 100644 --- a/sasdata/fair_database/data/apps.py +++ b/sasdata/fair_database/data/apps.py @@ -2,5 +2,5 @@ class DataConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'data' + default_auto_field = "django.db.models.BigAutoField" + name = "data" diff --git a/sasdata/fair_database/data/forms.py b/sasdata/fair_database/data/forms.py index f49ffca14..fde1813df 100644 --- a/sasdata/fair_database/data/forms.py +++ b/sasdata/fair_database/data/forms.py @@ -1,8 +1,9 @@ from django import forms from data.models import DataFile + # Create the form class. class DataFileForm(forms.ModelForm): class Meta: model = DataFile - fields = ["file", "is_public"] \ No newline at end of file + fields = ["file", "is_public"] diff --git a/sasdata/fair_database/data/migrations/0001_initial.py b/sasdata/fair_database/data/migrations/0001_initial.py index 1c7c9df3c..ce7cee7d6 100644 --- a/sasdata/fair_database/data/migrations/0001_initial.py +++ b/sasdata/fair_database/data/migrations/0001_initial.py @@ -7,7 +7,6 @@ class Migration(migrations.Migration): - initial = True dependencies = [ @@ -16,13 +15,52 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Data', + name="Data", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('file_name', models.CharField(blank=True, default=None, help_text='File name', max_length=200, null=True)), - ('file', models.FileField(default=None, help_text='This is a file', storage=django.core.files.storage.FileSystemStorage(), upload_to='uploaded_files')), - ('is_public', models.BooleanField(default=False, help_text='opt in to submit your data into example pool')), - ('current_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "file_name", + models.CharField( + blank=True, + default=None, + help_text="File name", + max_length=200, + null=True, + ), + ), + ( + "file", + models.FileField( + default=None, + help_text="This is a file", + storage=django.core.files.storage.FileSystemStorage(), + upload_to="uploaded_files", + ), + ), + ( + "is_public", + models.BooleanField( + default=False, + help_text="opt in to submit your data into example pool", + ), + ), + ( + "current_user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), ] diff --git a/sasdata/fair_database/data/migrations/0002_rename_data_datafile.py b/sasdata/fair_database/data/migrations/0002_rename_data_datafile.py index 33d9079bd..80a255488 100644 --- a/sasdata/fair_database/data/migrations/0002_rename_data_datafile.py +++ b/sasdata/fair_database/data/migrations/0002_rename_data_datafile.py @@ -5,15 +5,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('data', '0001_initial'), + ("data", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.RenameModel( - old_name='Data', - new_name='DataFile', + old_name="Data", + new_name="DataFile", ), ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 821f822da..013ef0a98 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -2,21 +2,30 @@ from django.contrib.auth.models import User from django.core.files.storage import FileSystemStorage + # Create your models here. class DataFile(models.Model): - #username - current_user = models.ForeignKey(User, blank=True, - null=True, on_delete=models.CASCADE) + # username + current_user = models.ForeignKey( + User, blank=True, null=True, on_delete=models.CASCADE + ) - #file name - file_name = models.CharField(max_length=200, default=None, - blank=True, null=True, help_text="File name") + # file name + file_name = models.CharField( + max_length=200, default=None, blank=True, null=True, help_text="File name" + ) - #imported data - #user can either import a file path or actual file - file = models.FileField(blank=False, default=None, help_text="This is a file", - upload_to="uploaded_files", storage=FileSystemStorage()) + # imported data + # user can either import a file path or actual file + file = models.FileField( + blank=False, + default=None, + help_text="This is a file", + upload_to="uploaded_files", + storage=FileSystemStorage(), + ) - #is the data public? - is_public = models.BooleanField(default=False, - help_text= "opt in to submit your data into example pool") \ No newline at end of file + # is the data public? + is_public = models.BooleanField( + default=False, help_text="opt in to submit your data into example pool" + ) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 70fed9d18..94c1f4e4c 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -2,12 +2,13 @@ from data.models import DataFile + class DataFileSerializer(serializers.ModelSerializer): class Meta: model = DataFile fields = "__all__" def validate(self, data): - if not self.context['is_public'] and not data['current_user']: - raise serializers.ValidationError('private data must have an owner') - return data \ No newline at end of file + if not self.context["is_public"] and not data["current_user"]: + raise serializers.ValidationError("private data must have an owner") + return data diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 8e1535b48..28243173e 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -9,129 +9,161 @@ from data.models import DataFile + def find(filename): - return os.path.join(os.path.dirname(__file__), "../../example_data/1d_data", filename) + return os.path.join( + os.path.dirname(__file__), "../../example_data/1d_data", filename + ) + class TestLists(TestCase): def setUp(self): - public_test_data = DataFile.objects.create(id = 1, file_name = "cyl_400_40.txt", - is_public = True) - public_test_data.file.save("cyl_400_40.txt", open(find("cyl_400_40.txt"), 'rb')) - self.user = User.objects.create_user(username="testUser", password="secret", id = 2) - private_test_data = DataFile.objects.create(id = 3, current_user = self.user, - file_name = "cyl_400_20.txt", is_public = False) - private_test_data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), 'rb')) + public_test_data = DataFile.objects.create( + id=1, file_name="cyl_400_40.txt", is_public=True + ) + public_test_data.file.save("cyl_400_40.txt", open(find("cyl_400_40.txt"), "rb")) + self.user = User.objects.create_user( + username="testUser", password="secret", id=2 + ) + private_test_data = DataFile.objects.create( + id=3, current_user=self.user, file_name="cyl_400_20.txt", is_public=False + ) + private_test_data.file.save( + "cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb") + ) self.client = APIClient() self.client.force_authenticate(user=self.user) # Test list public data def test_does_list_public(self): - request = self.client.get('/v1/data/list/') - self.assertEqual(request.data, {"public_data_ids":{1:"cyl_400_40.txt"}}) + request = self.client.get("/v1/data/list/") + self.assertEqual(request.data, {"public_data_ids": {1: "cyl_400_40.txt"}}) # Test list a user's private data def test_does_list_user(self): - request = self.client.get('/v1/data/list/testUser/', user = self.user) - self.assertEqual(request.data, {"user_data_ids":{3:"cyl_400_20.txt"}}) + request = self.client.get("/v1/data/list/testUser/", user=self.user) + self.assertEqual(request.data, {"user_data_ids": {3: "cyl_400_20.txt"}}) # Test loading a public data file def test_does_load_data_info_public(self): - request = self.client.get('/v1/data/load/1/') + request = self.client.get("/v1/data/load/1/") self.assertEqual(request.status_code, status.HTTP_200_OK) # Test loading private data with authorization def test_does_load_data_info_private(self): - request = self.client.get('/v1/data/load/3/') + request = self.client.get("/v1/data/load/3/") self.assertEqual(request.status_code, status.HTTP_200_OK) def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT) + class TestingDatabase(APITestCase): def setUp(self): - self.user = User.objects.create_user(username="testUser", password="secret", id = 1) - self.data = DataFile.objects.create(id = 2, current_user = self.user, - file_name = "cyl_400_20.txt", is_public = False) - self.data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), 'rb')) + self.user = User.objects.create_user( + username="testUser", password="secret", id=1 + ) + self.data = DataFile.objects.create( + id=2, current_user=self.user, file_name="cyl_400_20.txt", is_public=False + ) + self.data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb")) self.client = APIClient() self.client.force_authenticate(user=self.user) self.client2 = APIClient() # Test data upload creates data in database def test_is_data_being_created(self): - file = open(find("cyl_400_40.txt"), 'rb') - data = { - "is_public":False, - "file":file - } - request = self.client.post('/v1/data/upload/', data=data) + file = open(find("cyl_400_40.txt"), "rb") + data = {"is_public": False, "file": file} + request = self.client.post("/v1/data/upload/", data=data) self.assertEqual(request.status_code, status.HTTP_200_OK) - self.assertEqual(request.data, {"current_user":'testUser', "authenticated" : True, - "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) - DataFile.objects.get(id = 3).delete() + self.assertEqual( + request.data, + { + "current_user": "testUser", + "authenticated": True, + "file_id": 3, + "file_alternative_name": "cyl_400_40.txt", + "is_public": False, + }, + ) + DataFile.objects.get(id=3).delete() # Test data upload w/out authenticated user def test_is_data_being_created_no_user(self): - file = open(find("cyl_400_40.txt"), 'rb') - data = { - "is_public":True, - "file":file - } - request = self.client2.post('/v1/data/upload/', data=data) + file = open(find("cyl_400_40.txt"), "rb") + data = {"is_public": True, "file": file} + request = self.client2.post("/v1/data/upload/", data=data) self.assertEqual(request.status_code, status.HTTP_200_OK) - self.assertEqual(request.data, {"current_user":'', "authenticated" : False, - "file_id" : 3, "file_alternative_name":"cyl_400_40.txt","is_public" : True}) - DataFile.objects.get(id = 3).delete() + self.assertEqual( + request.data, + { + "current_user": "", + "authenticated": False, + "file_id": 3, + "file_alternative_name": "cyl_400_40.txt", + "is_public": True, + }, + ) + DataFile.objects.get(id=3).delete() # Test updating file def test_does_file_upload_update(self): file = open(find("cyl_400_40.txt")) - data = { - "file":file, - "is_public":False - } - request = self.client.put('/v1/data/upload/2/', data = data) - self.assertEqual(request.data, {"current_user":'testUser', "authenticated" : True, - "file_id" : 2, "file_alternative_name":"cyl_400_40.txt","is_public" : False}) - DataFile.objects.get(id = 2).delete() + data = {"file": file, "is_public": False} + request = self.client.put("/v1/data/upload/2/", data=data) + self.assertEqual( + request.data, + { + "current_user": "testUser", + "authenticated": True, + "file_id": 2, + "file_alternative_name": "cyl_400_40.txt", + "is_public": False, + }, + ) + DataFile.objects.get(id=2).delete() def test_public_file_upload_update(self): - data_object = DataFile.objects.create(id=3, current_user=self.user, - file_name="cyl_testdata.txt", is_public=True) - data_object.file.save("cyl_testdata.txt", open(find("cyl_testdata.txt"), 'rb')) + data_object = DataFile.objects.create( + id=3, current_user=self.user, file_name="cyl_testdata.txt", is_public=True + ) + data_object.file.save("cyl_testdata.txt", open(find("cyl_testdata.txt"), "rb")) file = open(find("cyl_testdata1.txt")) - data = { - "file": file, - "is_public": True - } - request = self.client.put('/v1/data/upload/3/', data=data) - self.assertEqual(request.data, {"current_user": 'testUser', "authenticated": True, - "file_id": 3, "file_alternative_name": "cyl_testdata1.txt", "is_public": True}) + data = {"file": file, "is_public": True} + request = self.client.put("/v1/data/upload/3/", data=data) + self.assertEqual( + request.data, + { + "current_user": "testUser", + "authenticated": True, + "file_id": 3, + "file_alternative_name": "cyl_testdata1.txt", + "is_public": True, + }, + ) DataFile.objects.get(id=3).delete() # Test file upload update fails when unauthorized def test_unauthorized_file_upload_update(self): file = open(find("cyl_400_40.txt")) - data = { - "file": file, - "is_public": False - } - request = self.client2.put('/v1/data/upload/2/', data=data) + data = {"file": file, "is_public": False} + request = self.client2.put("/v1/data/upload/2/", data=data) self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) DataFile.objects.get(id=2).delete() # Test file download def test_does_download(self): - request = self.client.get('/v1/data/2/download/') - file_contents = b''.join(request.streaming_content) - test_file = open(find('cyl_400_20.txt'), 'rb') + request = self.client.get("/v1/data/2/download/") + file_contents = b"".join(request.streaming_content) + test_file = open(find("cyl_400_20.txt"), "rb") self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(file_contents, test_file.read()) # Test file download fails when unauthorized def test_unauthorized_download(self): - request2 = self.client2.get('/v1/data/2/download/') + request2 = self.client2.get("/v1/data/2/download/") self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) def tearDown(self): - shutil.rmtree(settings.MEDIA_ROOT) \ No newline at end of file + shutil.rmtree(settings.MEDIA_ROOT) diff --git a/sasdata/fair_database/data/urls.py b/sasdata/fair_database/data/urls.py index abe4ffdb3..17fa2adfd 100644 --- a/sasdata/fair_database/data/urls.py +++ b/sasdata/fair_database/data/urls.py @@ -3,11 +3,10 @@ from . import views urlpatterns = [ - path("list/", views.list_data, name = "list public file_ids"), - path("list//", views.list_data, name = "view users file_ids"), - path("load//", views.data_info, name = "views data using file id"), - - path("upload/", views.upload, name = "upload data into db"), - path("upload//", views.upload, name = "update file in data"), - path("/download/", views.download, name = "download data from db"), -] \ No newline at end of file + path("list/", views.list_data, name="list public file_ids"), + path("list//", views.list_data, name="view users file_ids"), + path("load//", views.data_info, name="views data using file id"), + path("upload/", views.upload, name="upload data into db"), + path("upload//", views.upload, name="update file in data"), + path("/download/", views.download, name="download data from db"), +] diff --git a/sasdata/fair_database/fair_database/asgi.py b/sasdata/fair_database/fair_database/asgi.py index f47618a3e..a10c9b212 100644 --- a/sasdata/fair_database/fair_database/asgi.py +++ b/sasdata/fair_database/fair_database/asgi.py @@ -11,6 +11,6 @@ from django.core.asgi import get_asgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fair_database.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "fair_database.settings") application = get_asgi_application() diff --git a/sasdata/fair_database/fair_database/permissions.py b/sasdata/fair_database/fair_database/permissions.py index 51379b939..fb1949383 100644 --- a/sasdata/fair_database/fair_database/permissions.py +++ b/sasdata/fair_database/fair_database/permissions.py @@ -4,25 +4,25 @@ def is_owner(request, obj): return request.user.is_authenticated and request.user.id == obj.current_user -class DataPermission(BasePermission): +class DataPermission(BasePermission): def has_object_permission(self, request, view, obj): - if request.method == 'GET': + if request.method == "GET": if obj.is_public or is_owner(request, obj): return True - elif request.method == 'DELETE': + elif request.method == "DELETE": if obj.is_private and is_owner(request, obj): return True else: return is_owner(request, obj) + def check_permissions(request, obj): - if request.method == 'GET': + if request.method == "GET": if obj.is_public or is_owner(request, obj): return True - elif request.method == 'DELETE': + elif request.method == "DELETE": if obj.is_private and is_owner(request, obj): return True else: return is_owner(request, obj) - diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index dd448c8e4..885e3c903 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -21,7 +21,7 @@ # See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure--f-t5!pdhq&4)^&xenr^k0e8n%-h06jx9d0&2kft(!+1$xzig)' +SECRET_KEY = "django-insecure--f-t5!pdhq&4)^&xenr^k0e8n%-h06jx9d0&2kft(!+1$xzig)" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -32,107 +32,105 @@ # Application definition INSTALLED_APPS = [ - 'data.apps.DataConfig', - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django.contrib.sites', - 'rest_framework', - 'rest_framework.authtoken', - 'allauth', - 'allauth.account', - 'allauth.socialaccount', - 'allauth.socialaccount.providers.orcid', - 'dj_rest_auth', - 'dj_rest_auth.registration', - 'knox', - 'user_app.apps.UserAppConfig', + "data.apps.DataConfig", + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django.contrib.sites", + "rest_framework", + "rest_framework.authtoken", + "allauth", + "allauth.account", + "allauth.socialaccount", + "allauth.socialaccount.providers.orcid", + "dj_rest_auth", + "dj_rest_auth.registration", + "knox", + "user_app.apps.UserAppConfig", ] SITE_ID = 1 MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'allauth.account.middleware.AccountMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "allauth.account.middleware.AccountMiddleware", ] -ROOT_URLCONF = 'fair_database.urls' +ROOT_URLCONF = "fair_database.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] -WSGI_APPLICATION = 'fair_database.wsgi.application' +WSGI_APPLICATION = "fair_database.wsgi.application" # Authentication AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', - 'allauth.account.auth_backends.AuthenticationBackend', + "django.contrib.auth.backends.ModelBackend", + "allauth.account.auth_backends.AuthenticationBackend", ) REST_FRAMEWORK = { - 'DEFAULT_AUTHENTICATION_CLASSES': [ - 'knox.auth.TokenAuthentication', + "DEFAULT_AUTHENTICATION_CLASSES": [ + "knox.auth.TokenAuthentication", #'rest_framework.authentication.SessionAuthentication', ], #'DEFAULT_PERMISSION_CLASSES': [ # 'fair_database.permissions.DataPermission', - #], + # ], } REST_AUTH = { - 'TOKEN_SERIALIZER': 'user_app.serializers.KnoxSerializer', - 'USER_DETAILS_SERIALIZER': 'dj_rest_auth.serializers.UserDetailsSerializer', - 'TOKEN_MODEL': 'knox.models.AuthToken', - 'TOKEN_CREATOR': 'user_app.util.create_knox_token', + "TOKEN_SERIALIZER": "user_app.serializers.KnoxSerializer", + "USER_DETAILS_SERIALIZER": "dj_rest_auth.serializers.UserDetailsSerializer", + "TOKEN_MODEL": "knox.models.AuthToken", + "TOKEN_CREATOR": "user_app.util.create_knox_token", } # allauth settings HEADLESS_ONLY = True -ACCOUNT_EMAIL_VERIFICATION = 'none' +ACCOUNT_EMAIL_VERIFICATION = "none" # to enable ORCID, register for credentials through ORCID and fill out client_id and secret SOCIALACCOUNT_PROVIDERS = { - 'orcid': { - 'APPS': [ + "orcid": { + "APPS": [ { - 'client_id': '', - 'secret': '', - 'key': '', + "client_id": "", + "secret": "", + "key": "", } - ], - 'SCOPE': [ - 'profile', 'email', + "SCOPE": [ + "profile", + "email", ], - 'AUTH_PARAMETERS': { - 'access_type': 'online' - }, + "AUTH_PARAMETERS": {"access_type": "online"}, # Base domain of the API. Default value: 'orcid.org', for the production API - 'BASE_DOMAIN':'sandbox.orcid.org', # for the sandbox API + "BASE_DOMAIN": "sandbox.orcid.org", # for the sandbox API # Member API or Public API? Default: False (for the public API) - 'MEMBER_API': False, + "MEMBER_API": False, } } @@ -140,9 +138,9 @@ # https://docs.djangoproject.com/en/5.1/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", } } @@ -152,16 +150,16 @@ AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] @@ -169,9 +167,9 @@ # Internationalization # https://docs.djangoproject.com/en/5.1/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -182,14 +180,14 @@ # https://docs.djangoproject.com/en/4.2/howto/static-files/ -STATIC_ROOT = os.path.join(BASE_DIR, 'static') -STATIC_URL = '/static/' +STATIC_ROOT = os.path.join(BASE_DIR, "static") +STATIC_URL = "/static/" -#instead of doing this, create a create a new media_root +# instead of doing this, create a create a new media_root MEDIA_ROOT = os.path.join(BASE_DIR, "media") -MEDIA_URL = '/media/' +MEDIA_URL = "/media/" # Default primary key field type # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index c9c2e991b..cf6632c4f 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -4,161 +4,218 @@ from django.conf import settings from django.contrib.auth.models import User from rest_framework import status -from rest_framework.test import APIClient, APITestCase +from rest_framework.test import APITestCase from data.models import DataFile + def find(filename): - return os.path.join(os.path.dirname(__file__), "../../example_data/1d_data", filename) + return os.path.join( + os.path.dirname(__file__), "../../example_data/1d_data", filename + ) + def auth_header(response): - return {'Authorization': 'Token ' + response.data['token']} + return {"Authorization": "Token " + response.data["token"]} + class DataListPermissionsTests(APITestCase): - ''' Test permissions of data views using user_app for authentication. ''' + """Test permissions of data views using user_app for authentication.""" def setUp(self): - self.user = User.objects.create_user(username="testUser", password="secret", id=1, - email="email@domain.com") - self.user2 = User.objects.create_user(username="testUser2", password="secret", id=2, - email="email2@domain.com") - unowned_test_data = DataFile.objects.create(id=1, file_name="cyl_400_40.txt", - is_public=True) - unowned_test_data.file.save("cyl_400_40.txt", open(find("cyl_400_40.txt"), 'rb')) - private_test_data = DataFile.objects.create(id=2, current_user=self.user, - file_name="cyl_400_20.txt", is_public=False) - private_test_data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), 'rb')) - public_test_data = DataFile.objects.create(id=3, current_user=self.user, - file_name="cyl_testdata.txt", is_public=True) - public_test_data.file.save("cyl_testdata.txt", open(find("cyl_testdata.txt"), 'rb')) + self.user = User.objects.create_user( + username="testUser", password="secret", id=1, email="email@domain.com" + ) + self.user2 = User.objects.create_user( + username="testUser2", password="secret", id=2, email="email2@domain.com" + ) + unowned_test_data = DataFile.objects.create( + id=1, file_name="cyl_400_40.txt", is_public=True + ) + unowned_test_data.file.save( + "cyl_400_40.txt", open(find("cyl_400_40.txt"), "rb") + ) + private_test_data = DataFile.objects.create( + id=2, current_user=self.user, file_name="cyl_400_20.txt", is_public=False + ) + private_test_data.file.save( + "cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb") + ) + public_test_data = DataFile.objects.create( + id=3, current_user=self.user, file_name="cyl_testdata.txt", is_public=True + ) + public_test_data.file.save( + "cyl_testdata.txt", open(find("cyl_testdata.txt"), "rb") + ) self.login_data_1 = { - 'username': 'testUser', - 'password': 'secret', - 'email': 'email@domain.com' + "username": "testUser", + "password": "secret", + "email": "email@domain.com", } self.login_data_2 = { - 'username': 'testUser2', - 'password': 'secret', - 'email': 'email2@domain.com' + "username": "testUser2", + "password": "secret", + "email": "email2@domain.com", } # Authenticated user can view list of data # TODO: change to reflect inclusion of owned private data def test_list_authenticated(self): - token = self.client.post('/auth/login/', data=self.login_data_1) - response = self.client.get('/v1/data/list/', headers=auth_header(token)) - response2 = self.client.get('/v1/data/list/testUser/', headers=auth_header(token)) - self.assertEqual(response.data, - {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}) - self.assertEqual(response2.data, - {"user_data_ids": {2: "cyl_400_20.txt", 3: "cyl_testdata.txt"}}) + token = self.client.post("/auth/login/", data=self.login_data_1) + response = self.client.get("/v1/data/list/", headers=auth_header(token)) + response2 = self.client.get( + "/v1/data/list/testUser/", headers=auth_header(token) + ) + self.assertEqual( + response.data, + {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}, + ) + self.assertEqual( + response2.data, + {"user_data_ids": {2: "cyl_400_20.txt", 3: "cyl_testdata.txt"}}, + ) # Authenticated user cannot view other users' private data on list # TODO: Change response codes def test_list_authenticated_2(self): - token = self.client.post('/auth/login/', data=self.login_data_2) - response = self.client.get('/v1/data/list/', headers=auth_header(token)) - response2 = self.client.get('/v1/data/list/testUser/', headers=auth_header(token)) - response3 = self.client.get('/v1/data/list/testUser2/', headers=auth_header(token)) - self.assertEqual(response.data, - {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}) + token = self.client.post("/auth/login/", data=self.login_data_2) + response = self.client.get("/v1/data/list/", headers=auth_header(token)) + response2 = self.client.get( + "/v1/data/list/testUser/", headers=auth_header(token) + ) + response3 = self.client.get( + "/v1/data/list/testUser2/", headers=auth_header(token) + ) + self.assertEqual( + response.data, + {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}, + ) self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response3.data, {"user_data_ids": {}}) # Unauthenticated user can view list of public data def test_list_unauthenticated(self): - response = self.client.get('/v1/data/list/') - response2 = self.client.get('/v1/data/list/testUser/') - self.assertEqual(response.data, - {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}) + response = self.client.get("/v1/data/list/") + response2 = self.client.get("/v1/data/list/testUser/") + self.assertEqual( + response.data, + {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}, + ) self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) # Authenticated user can load public data and owned private data def test_load_authenticated(self): - token = self.client.post('/auth/login/', data=self.login_data_1) - response = self.client.get('/v1/data/load/1/', headers=auth_header(token)) - response2 = self.client.get('/v1/data/load/2/', headers=auth_header(token)) + token = self.client.post("/auth/login/", data=self.login_data_1) + response = self.client.get("/v1/data/load/1/", headers=auth_header(token)) + response2 = self.client.get("/v1/data/load/2/", headers=auth_header(token)) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) # Authenticated user cannot load others' private data def test_load_unauthorized(self): - token = self.client.post('/auth/login/', data=self.login_data_2) - response = self.client.get('/v1/data/load/2/', headers=auth_header(token)) - response2 = self.client.get('/v1/data/load/3/', headers=auth_header(token)) + token = self.client.post("/auth/login/", data=self.login_data_2) + response = self.client.get("/v1/data/load/2/", headers=auth_header(token)) + response2 = self.client.get("/v1/data/load/3/", headers=auth_header(token)) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response2.status_code, status.HTTP_200_OK) # Unauthenticated user can load public data only def test_load_unauthenticated(self): - response = self.client.get('/v1/data/load/1/') - response2 = self.client.get('/v1/data/load/2/') - response3 = self.client.get('/v1/data/load/3/') + response = self.client.get("/v1/data/load/1/") + response2 = self.client.get("/v1/data/load/2/") + response3 = self.client.get("/v1/data/load/3/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response3.status_code, status.HTTP_200_OK) # Authenticated user can upload data def test_upload_authenticated(self): - token = self.client.post('/auth/login/', data=self.login_data_1) - file = open(find('cyl_testdata1.txt'), 'rb') - data = { - 'file': file, - 'is_public': False - } - response = self.client.post('/v1/data/upload/', data=data, headers=auth_header(token)) + token = self.client.post("/auth/login/", data=self.login_data_1) + file = open(find("cyl_testdata1.txt"), "rb") + data = {"file": file, "is_public": False} + response = self.client.post( + "/v1/data/upload/", data=data, headers=auth_header(token) + ) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, {"current_user": 'testUser', "authenticated": True, - "file_id": 4, "file_alternative_name": "cyl_testdata1.txt", "is_public": False}) + self.assertEqual( + response.data, + { + "current_user": "testUser", + "authenticated": True, + "file_id": 4, + "file_alternative_name": "cyl_testdata1.txt", + "is_public": False, + }, + ) DataFile.objects.get(id=4).delete() # Unauthenticated user can upload public data only def test_upload_unauthenticated(self): - file = open(find('cyl_testdata2.txt'), 'rb') - file2 = open(find('cyl_testdata2.txt'), 'rb') - data = { - 'file': file, - 'is_public': True - } - data2 = { - 'file': file2, - 'is_public': False - } - response = self.client.post('/v1/data/upload/', data=data) - response2 = self.client.post('/v1/data/upload/', data=data2) + file = open(find("cyl_testdata2.txt"), "rb") + file2 = open(find("cyl_testdata2.txt"), "rb") + data = {"file": file, "is_public": True} + data2 = {"file": file2, "is_public": False} + response = self.client.post("/v1/data/upload/", data=data) + response2 = self.client.post("/v1/data/upload/", data=data2) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, {"current_user": '', "authenticated": False, - "file_id": 4, "file_alternative_name": "cyl_testdata2.txt", - "is_public": True}) + self.assertEqual( + response.data, + { + "current_user": "", + "authenticated": False, + "file_id": 4, + "file_alternative_name": "cyl_testdata2.txt", + "is_public": True, + }, + ) self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) # Authenticated user can update own data def test_upload_put_authenticated(self): - token = self.client.post('/auth/login/', data=self.login_data_1) - data = { - "is_public": False - } - response = self.client.put('/v1/data/upload/2/', data=data, headers=auth_header(token)) - response2 = self.client.put('/v1/data/upload/3/', data=data, headers=auth_header(token)) - self.assertEqual(response.data, - {"current_user": 'testUser', "authenticated": True, "file_id": 2, - "file_alternative_name": "cyl_400_20.txt", "is_public": False}) - self.assertEqual(response2.data, - {"current_user": 'testUser', "authenticated": True, "file_id": 3, - "file_alternative_name": "cyl_testdata.txt", "is_public": False}) + token = self.client.post("/auth/login/", data=self.login_data_1) + data = {"is_public": False} + response = self.client.put( + "/v1/data/upload/2/", data=data, headers=auth_header(token) + ) + response2 = self.client.put( + "/v1/data/upload/3/", data=data, headers=auth_header(token) + ) + self.assertEqual( + response.data, + { + "current_user": "testUser", + "authenticated": True, + "file_id": 2, + "file_alternative_name": "cyl_400_20.txt", + "is_public": False, + }, + ) + self.assertEqual( + response2.data, + { + "current_user": "testUser", + "authenticated": True, + "file_id": 3, + "file_alternative_name": "cyl_testdata.txt", + "is_public": False, + }, + ) DataFile.objects.get(id=3).is_public = True # Authenticated user cannot update unowned data def test_upload_put_unauthorized(self): - token = self.client.post('/auth/login/', data=self.login_data_2) + token = self.client.post("/auth/login/", data=self.login_data_2) file = open(find("cyl_400_40.txt")) - data = { - "file": file, - "is_public": False - } - response = self.client.put('/v1/data/upload/1/', data=data, headers=auth_header(token)) - response2 = self.client.put('/v1/data/upload/2/', data=data, headers=auth_header(token)) - response3 = self.client.put('/v1/data/upload/3/', data=data, headers=auth_header(token)) + data = {"file": file, "is_public": False} + response = self.client.put( + "/v1/data/upload/1/", data=data, headers=auth_header(token) + ) + response2 = self.client.put( + "/v1/data/upload/2/", data=data, headers=auth_header(token) + ) + response3 = self.client.put( + "/v1/data/upload/3/", data=data, headers=auth_header(token) + ) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response2.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response3.status_code, status.HTTP_404_NOT_FOUND) @@ -166,43 +223,40 @@ def test_upload_put_unauthorized(self): # Unauthenticated user cannot update data def test_upload_put_unauthenticated(self): file = open(find("cyl_400_40.txt")) - data = { - "file": file, - "is_public": False - } - response = self.client.put('/v1/data/upload/1/', data=data) - response2 = self.client.put('/v1/data/upload/2/', data=data) - response3 = self.client.put('/v1/data/upload/3/', data=data) + data = {"file": file, "is_public": False} + response = self.client.put("/v1/data/upload/1/", data=data) + response2 = self.client.put("/v1/data/upload/2/", data=data) + response3 = self.client.put("/v1/data/upload/3/", data=data) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response3.status_code, status.HTTP_403_FORBIDDEN) # Authenticated user can download public and own data def test_download_authenticated(self): - token = self.client.post('/auth/login/', data=self.login_data_1) - response = self.client.get('/v1/data/1/download/', headers=auth_header(token)) - response2 = self.client.get('/v1/data/2/download/', headers=auth_header(token)) - response3 = self.client.get('/v1/data/3/download/', headers=auth_header(token)) + token = self.client.post("/auth/login/", data=self.login_data_1) + response = self.client.get("/v1/data/1/download/", headers=auth_header(token)) + response2 = self.client.get("/v1/data/2/download/", headers=auth_header(token)) + response3 = self.client.get("/v1/data/3/download/", headers=auth_header(token)) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) self.assertEqual(response3.status_code, status.HTTP_200_OK) # Authenticated user cannot download others' data def test_download_unauthorized(self): - token = self.client.post('/auth/login/', data=self.login_data_2) - response = self.client.get('/v1/data/2/download/', headers=auth_header(token)) - response2 = self.client.get('/v1/data/3/download/', headers=auth_header(token)) + token = self.client.post("/auth/login/", data=self.login_data_2) + response = self.client.get("/v1/data/2/download/", headers=auth_header(token)) + response2 = self.client.get("/v1/data/3/download/", headers=auth_header(token)) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response2.status_code, status.HTTP_200_OK) # Unauthenticated user cannot download private data def test_download_unauthenticated(self): - response = self.client.get('/v1/data/1/download/') - response2 = self.client.get('/v1/data/2/download/') - response3 = self.client.get('/v1/data/3/download/') + response = self.client.get("/v1/data/1/download/") + response2 = self.client.get("/v1/data/2/download/") + response3 = self.client.get("/v1/data/3/download/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response3.status_code, status.HTTP_200_OK) def tearDown(self): - shutil.rmtree(settings.MEDIA_ROOT) \ No newline at end of file + shutil.rmtree(settings.MEDIA_ROOT) diff --git a/sasdata/fair_database/fair_database/upload_example_data.py b/sasdata/fair_database/fair_database/upload_example_data.py index bf014cf20..1b16fdca4 100644 --- a/sasdata/fair_database/fair_database/upload_example_data.py +++ b/sasdata/fair_database/fair_database/upload_example_data.py @@ -4,7 +4,8 @@ from glob import glob -EXAMPLE_DATA_DIR = os.environ.get("EXAMPLE_DATA_DIR", '../../example_data') +EXAMPLE_DATA_DIR = os.environ.get("EXAMPLE_DATA_DIR", "../../example_data") + def parse_1D(): dir_1d = os.path.join(EXAMPLE_DATA_DIR, "1d_data") @@ -14,6 +15,7 @@ def parse_1D(): for file_path in glob(os.path.join(dir_1d, "*")): upload_file(file_path) + def parse_2D(): dir_2d = os.path.join(EXAMPLE_DATA_DIR, "2d_data") if not os.path.isdir(dir_2d): @@ -22,6 +24,7 @@ def parse_2D(): for file_path in glob(os.path.join(dir_2d, "*")): upload_file(file_path) + def parse_sesans(): sesans_dir = os.path.join(EXAMPLE_DATA_DIR, "sesans_data") if not os.path.isdir(sesans_dir): @@ -30,12 +33,14 @@ def parse_sesans(): for file_path in glob(os.path.join(sesans_dir, "*")): upload_file(file_path) + def upload_file(file_path): - url = 'http://localhost:8000/v1/data/upload/' - file = open(file_path, 'rb') - requests.request('POST', url, data={'is_public': True}, files={'file':file}) + url = "http://localhost:8000/v1/data/upload/" + file = open(file_path, "rb") + requests.request("POST", url, data={"is_public": True}, files={"file": file}) + -if __name__ == '__main__': +if __name__ == "__main__": parse_1D() parse_2D() parse_sesans() diff --git a/sasdata/fair_database/fair_database/urls.py b/sasdata/fair_database/fair_database/urls.py index 89bac77cf..0cfeb0aca 100644 --- a/sasdata/fair_database/fair_database/urls.py +++ b/sasdata/fair_database/fair_database/urls.py @@ -14,12 +14,13 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.contrib import admin from django.urls import include, path, re_path urlpatterns = [ re_path(r"^(?P(v1))/data/", include("data.urls")), path("admin/", admin.site.urls), - path("accounts/", include("allauth.urls")), #needed for social auth - path('auth/', include('user_app.urls')), + path("accounts/", include("allauth.urls")), # needed for social auth + path("auth/", include("user_app.urls")), ] diff --git a/sasdata/fair_database/fair_database/wsgi.py b/sasdata/fair_database/fair_database/wsgi.py index cb0870868..5dfc4819c 100644 --- a/sasdata/fair_database/fair_database/wsgi.py +++ b/sasdata/fair_database/fair_database/wsgi.py @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fair_database.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "fair_database.settings") application = get_wsgi_application() diff --git a/sasdata/fair_database/manage.py b/sasdata/fair_database/manage.py index c74d5f9c3..7d7e97246 100755 --- a/sasdata/fair_database/manage.py +++ b/sasdata/fair_database/manage.py @@ -1,12 +1,13 @@ #!/usr/bin/env python """Django's command-line utility for administrative tasks.""" + import os import sys def main(): """Run administrative tasks.""" - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fair_database.settings') + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "fair_database.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: @@ -18,5 +19,5 @@ def main(): execute_from_command_line(sys.argv) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/sasdata/fair_database/user_app/admin.py b/sasdata/fair_database/user_app/admin.py index 8c38f3f3d..846f6b406 100644 --- a/sasdata/fair_database/user_app/admin.py +++ b/sasdata/fair_database/user_app/admin.py @@ -1,3 +1 @@ -from django.contrib import admin - # Register your models here. diff --git a/sasdata/fair_database/user_app/apps.py b/sasdata/fair_database/user_app/apps.py index f2d1d4178..83a29decf 100644 --- a/sasdata/fair_database/user_app/apps.py +++ b/sasdata/fair_database/user_app/apps.py @@ -2,5 +2,5 @@ class UserAppConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'user_app' + default_auto_field = "django.db.models.BigAutoField" + name = "user_app" diff --git a/sasdata/fair_database/user_app/models.py b/sasdata/fair_database/user_app/models.py index 71a836239..6b2021999 100644 --- a/sasdata/fair_database/user_app/models.py +++ b/sasdata/fair_database/user_app/models.py @@ -1,3 +1 @@ -from django.db import models - # Create your models here. diff --git a/sasdata/fair_database/user_app/serializers.py b/sasdata/fair_database/user_app/serializers.py index 7d315377d..c739fea2a 100644 --- a/sasdata/fair_database/user_app/serializers.py +++ b/sasdata/fair_database/user_app/serializers.py @@ -7,8 +7,9 @@ class KnoxSerializer(serializers.Serializer): """ Serializer for Knox authentication. """ + token = serializers.SerializerMethodField() user = UserDetailsSerializer() def get_token(self, obj): - return obj["token"][1] \ No newline at end of file + return obj["token"][1] diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index b3f203a35..664fd963a 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -4,148 +4,166 @@ from django.contrib.auth.models import User + # Create your tests here. class AuthTests(TestCase): - def setUp(self): self.client = APIClient() self.register_data = { "email": "email@domain.org", "username": "testUser", "password1": "sasview!", - "password2": "sasview!" + "password2": "sasview!", } self.login_data = { "username": "testUser", "email": "email@domain.org", - "password": "sasview!" + "password": "sasview!", } def auth_header(self, response): - return {'Authorization': 'Token ' + response.data['token']} + return {"Authorization": "Token " + response.data["token"]} # Test if registration successfully creates a new user and logs in def test_register(self): - response = self.client.post('/auth/register/',data=self.register_data) + response = self.client.post("/auth/register/", data=self.register_data) user = User.objects.get(username="testUser") - response2 = self.client.get('/auth/user/', headers=self.auth_header(response)) + response2 = self.client.get("/auth/user/", headers=self.auth_header(response)) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(user.email, self.register_data["email"]) self.assertEqual(response2.status_code, status.HTTP_200_OK) # Test if login successful def test_login(self): - User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") - response = self.client.post('/auth/login/', data=self.login_data) - response2 = self.client.get('/auth/user/', headers=self.auth_header(response)) + User.objects.create_user( + username="testUser", password="sasview!", email="email@domain.org" + ) + response = self.client.post("/auth/login/", data=self.login_data) + response2 = self.client.get("/auth/user/", headers=self.auth_header(response)) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) # Test simultaneous login by multiple clients def test_multiple_login(self): - User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + User.objects.create_user( + username="testUser", password="sasview!", email="email@domain.org" + ) client2 = APIClient() - response = self.client.post('/auth/login/', data=self.login_data) - response2 = client2.post('/auth/login/', data=self.login_data) + response = self.client.post("/auth/login/", data=self.login_data) + response2 = client2.post("/auth/login/", data=self.login_data) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) self.assertNotEqual(response.content, response2.content) # Test get user information def test_user_get(self): - user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + user = User.objects.create_user( + username="testUser", password="sasview!", email="email@domain.org" + ) self.client.force_authenticate(user=user) - response = self.client.get('/auth/user/') + response = self.client.get("/auth/user/") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.content, - b'{"pk":1,"username":"testUser","email":"email@domain.org","first_name":"","last_name":""}') + self.assertEqual( + response.content, + b'{"pk":1,"username":"testUser","email":"email@domain.org","first_name":"","last_name":""}', + ) # Test changing username def test_user_put_username(self): - user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + user = User.objects.create_user( + username="testUser", password="sasview!", email="email@domain.org" + ) self.client.force_authenticate(user=user) - data = { - "username": "newName" - } - response = self.client.put('/auth/user/', data=data) + data = {"username": "newName"} + response = self.client.put("/auth/user/", data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.content, - b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"","last_name":""}') + self.assertEqual( + response.content, + b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"","last_name":""}', + ) # Test changing username and first and last name def test_user_put_name(self): - user = User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + user = User.objects.create_user( + username="testUser", password="sasview!", email="email@domain.org" + ) self.client.force_authenticate(user=user) - data = { - "username": "newName", - "first_name": "Clark", - "last_name": "Kent" - } - response = self.client.put('/auth/user/', data=data) + data = {"username": "newName", "first_name": "Clark", "last_name": "Kent"} + response = self.client.put("/auth/user/", data=data) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.content, - b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"Clark","last_name":"Kent"}') + self.assertEqual( + response.content, + b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"Clark","last_name":"Kent"}', + ) # Test user info inaccessible when unauthenticated def test_user_unauthenticated(self): - response = self.client.get('/auth/user/') + response = self.client.get("/auth/user/") self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - self.assertEqual(response.content, - b'{"detail":"Authentication credentials were not provided."}') + self.assertEqual( + response.content, + b'{"detail":"Authentication credentials were not provided."}', + ) # Test logout is successful after login def test_login_logout(self): - User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") - self.client.post('/auth/login/', data=self.login_data) - response = self.client.post('/auth/logout/') - response2 = self.client.get('/auth/user/') + User.objects.create_user( + username="testUser", password="sasview!", email="email@domain.org" + ) + self.client.post("/auth/login/", data=self.login_data) + response = self.client.post("/auth/logout/") + response2 = self.client.get("/auth/user/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) # Test logout is successful after registration def test_register_logout(self): - self.client.post('/auth/register/', data=self.register_data) - response = self.client.post('/auth/logout/') - response2 = self.client.get('/auth/user/') + self.client.post("/auth/register/", data=self.register_data) + response = self.client.post("/auth/logout/") + response2 = self.client.get("/auth/user/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) def test_multiple_logout(self): - User.objects.create_user(username="testUser", password="sasview!", email="email@domain.org") + User.objects.create_user( + username="testUser", password="sasview!", email="email@domain.org" + ) client2 = APIClient() - self.client.post('/auth/login/', data=self.login_data) - token = client2.post('/auth/login/', data=self.login_data) - response = self.client.post('/auth/logout/') - response2 = client2.get('/auth/user/', headers=self.auth_header(token)) - response3 = client2.post('/auth/logout/') + self.client.post("/auth/login/", data=self.login_data) + token = client2.post("/auth/login/", data=self.login_data) + response = self.client.post("/auth/logout/") + response2 = client2.get("/auth/user/", headers=self.auth_header(token)) + response3 = client2.post("/auth/logout/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) self.assertEqual(response3.status_code, status.HTTP_200_OK) # Test login is successful after registering then logging out def test_register_login(self): - register_response = self.client.post('/auth/register/', data=self.register_data) - logout_response = self.client.post('/auth/logout/') - login_response = self.client.post('/auth/login/', data=self.login_data) + register_response = self.client.post("/auth/register/", data=self.register_data) + logout_response = self.client.post("/auth/logout/") + login_response = self.client.post("/auth/login/", data=self.login_data) self.assertEqual(register_response.status_code, status.HTTP_201_CREATED) self.assertEqual(logout_response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) # Test password is successfully changed def test_password_change(self): - token = self.client.post('/auth/register/', data=self.register_data) + token = self.client.post("/auth/register/", data=self.register_data) data = { "new_password1": "sasview?", "new_password2": "sasview?", - "old_password": "sasview!" + "old_password": "sasview!", } l_data = self.login_data l_data["password"] = "sasview?" - response = self.client.post('/auth/password/change/', data=data, headers=self.auth_header(token)) - logout_response = self.client.post('/auth/logout/') - login_response = self.client.post('/auth/login/', data=l_data) + response = self.client.post( + "/auth/password/change/", data=data, headers=self.auth_header(token) + ) + logout_response = self.client.post("/auth/logout/") + login_response = self.client.post("/auth/login/", data=l_data) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(logout_response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) @@ -161,4 +179,3 @@ def test_password_change(self): # unauthenticated user cannot modify data # logged-in user cannot modify data other than their own # logged-in user cannot access the private data of others - diff --git a/sasdata/fair_database/user_app/urls.py b/sasdata/fair_database/user_app/urls.py index 339d7d8b8..808cbfcee 100644 --- a/sasdata/fair_database/user_app/urls.py +++ b/sasdata/fair_database/user_app/urls.py @@ -1,15 +1,14 @@ from django.urls import path -from dj_rest_auth.views import (LogoutView, - UserDetailsView, PasswordChangeView) +from dj_rest_auth.views import LogoutView, UserDetailsView, PasswordChangeView from .views import KnoxLoginView, KnoxRegisterView, OrcidLoginView -'''Urls for authentication. Orcid login not functional.''' +"""Urls for authentication. Orcid login not functional.""" urlpatterns = [ - path('register/', KnoxRegisterView.as_view(), name='register'), - path('login/', KnoxLoginView.as_view(), name='login'), - path('logout/', LogoutView.as_view(), name='logout'), - path('user/', UserDetailsView.as_view(), name='view user information'), - path('password/change/', PasswordChangeView.as_view(), name='change password'), - path('login/orcid/', OrcidLoginView.as_view(), name='orcid login') -] \ No newline at end of file + path("register/", KnoxRegisterView.as_view(), name="register"), + path("login/", KnoxLoginView.as_view(), name="login"), + path("logout/", LogoutView.as_view(), name="logout"), + path("user/", UserDetailsView.as_view(), name="view user information"), + path("password/change/", PasswordChangeView.as_view(), name="change password"), + path("login/orcid/", OrcidLoginView.as_view(), name="orcid login"), +] diff --git a/sasdata/fair_database/user_app/util.py b/sasdata/fair_database/user_app/util.py index ab9bcd0d9..c6b43cc63 100644 --- a/sasdata/fair_database/user_app/util.py +++ b/sasdata/fair_database/user_app/util.py @@ -3,4 +3,4 @@ def create_knox_token(token_model, user, serializer): token = AuthToken.objects.create(user=user) - return token \ No newline at end of file + return token diff --git a/sasdata/fair_database/user_app/views.py b/sasdata/fair_database/user_app/views.py index 4a55fdc7e..32113a749 100644 --- a/sasdata/fair_database/user_app/views.py +++ b/sasdata/fair_database/user_app/views.py @@ -1,5 +1,3 @@ -from django.conf import settings - from rest_framework.response import Response from dj_rest_auth.views import LoginView from dj_rest_auth.registration.views import RegisterView, SocialLoginView @@ -10,33 +8,33 @@ from user_app.serializers import KnoxSerializer from user_app.util import create_knox_token -#Login using knox tokens rather than django-rest-framework tokens. +# Login using knox tokens rather than django-rest-framework tokens. -class KnoxLoginView(LoginView): +class KnoxLoginView(LoginView): def get_response(self): serializer_class = self.get_response_serializer() - data = { - 'user': self.user, - 'token': self.token - } - serializer = serializer_class(instance=data, context={'request': self.request}) + data = {"user": self.user, "token": self.token} + serializer = serializer_class(instance=data, context={"request": self.request}) return Response(serializer.data, status=200) + # Registration using knox tokens rather than django-rest-framework tokens. class KnoxRegisterView(RegisterView): - def get_response_data(self, user): - return KnoxSerializer({'user': user, 'token': self.token}).data + return KnoxSerializer({"user": user, "token": self.token}).data def perform_create(self, serializer): user = serializer.save(self.request) - self.token = create_knox_token(None,user,None) - complete_signup(self.request._request, user, allauth_settings.EMAIL_VERIFICATION, None) + self.token = create_knox_token(None, user, None) + complete_signup( + self.request._request, user, allauth_settings.EMAIL_VERIFICATION, None + ) return user + # For ORCID login class OrcidLoginView(SocialLoginView): - adapter_class = OrcidOAuth2Adapter \ No newline at end of file + adapter_class = OrcidOAuth2Adapter From 1cc21a846ed19fa63f83ecc6f5419e2bb36bd5af Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Feb 2025 15:13:34 -0500 Subject: [PATCH 243/675] Fix permissions bug that prevented access to a user's own private data --- sasdata/fair_database/fair_database/permissions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/fair_database/fair_database/permissions.py b/sasdata/fair_database/fair_database/permissions.py index fb1949383..e62e72572 100644 --- a/sasdata/fair_database/fair_database/permissions.py +++ b/sasdata/fair_database/fair_database/permissions.py @@ -2,7 +2,7 @@ def is_owner(request, obj): - return request.user.is_authenticated and request.user.id == obj.current_user + return request.user.is_authenticated and request.user == obj.current_user class DataPermission(BasePermission): From 79993c5ba47f176f5f1986c60ae916932d3cbda2 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Feb 2025 15:26:10 -0500 Subject: [PATCH 244/675] Change permissions handling for load and upload --- sasdata/fair_database/data/views.py | 49 ++++++++----------- .../fair_database/test_permissions.py | 12 +++-- 2 files changed, 28 insertions(+), 33 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 1a3f0d2c5..789220d89 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -46,19 +46,13 @@ def data_info(request, db_id, version=None): if request.method == "GET": loader = Loader() data_db = get_object_or_404(DataFile, id=db_id) - if data_db.is_public: - data_list = loader.load(data_db.file.path) - contents = [str(data) for data in data_list] - return_data = {data_db.file_name: contents} - # rewrite with "user.is_authenticated" - elif (data_db.current_user == request.user) and request.user.is_authenticated: - data_list = loader.load(data_db.file.path) - contents = [str(data) for data in data_list] - return_data = {data_db.file_name: contents} - else: - return HttpResponseBadRequest( - "Database is either not public or wrong auth token" + if not permissions.check_permissions(request, data_db): + return HttpResponseForbidden( + "Data is either not public or wrong auth token" ) + data_list = loader.load(data_db.file.path) + contents = [str(data) for data in data_list] + return_data = {data_db.file_name: contents} return Response(return_data) return HttpResponseBadRequest() @@ -93,22 +87,21 @@ def upload(request, data_id=None, version=None): # updates file elif request.method == "PUT": - if request.user.is_authenticated: - db = get_object_or_404(DataFile, current_user=request.user.id, id=data_id) - form = DataFileForm(request.data, request.FILES, instance=db) - if form.is_valid(): - form.save() - serializer = DataFileSerializer( - db, - data={ - "file_name": os.path.basename(form.instance.file.path), - "current_user": request.user.id, - }, - context={"is_public": db.is_public}, - partial=True, - ) - else: - return HttpResponseForbidden("user is not logged in") + db = get_object_or_404(DataFile, id=data_id) + if not permissions.check_permissions(request, db): + return HttpResponseForbidden("must be the data owner to modify") + form = DataFileForm(request.data, request.FILES, instance=db) + if form.is_valid(): + form.save() + serializer = DataFileSerializer( + db, + data={ + "file_name": os.path.basename(form.instance.file.path), + "current_user": request.user.id, + }, + context={"is_public": db.is_public}, + partial=True, + ) if serializer.is_valid(raise_exception=True): serializer.save() diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index cf6632c4f..f7e32a5c5 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -108,15 +108,17 @@ def test_load_authenticated(self): token = self.client.post("/auth/login/", data=self.login_data_1) response = self.client.get("/v1/data/load/1/", headers=auth_header(token)) response2 = self.client.get("/v1/data/load/2/", headers=auth_header(token)) + response3 = self.client.get("/v1/data/load/3/", headers=auth_header(token)) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) + self.assertEqual(response3.status_code, status.HTTP_200_OK) # Authenticated user cannot load others' private data def test_load_unauthorized(self): token = self.client.post("/auth/login/", data=self.login_data_2) response = self.client.get("/v1/data/load/2/", headers=auth_header(token)) response2 = self.client.get("/v1/data/load/3/", headers=auth_header(token)) - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response2.status_code, status.HTTP_200_OK) # Unauthenticated user can load public data only @@ -125,7 +127,7 @@ def test_load_unauthenticated(self): response2 = self.client.get("/v1/data/load/2/") response3 = self.client.get("/v1/data/load/3/") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response3.status_code, status.HTTP_200_OK) # Authenticated user can upload data @@ -216,9 +218,9 @@ def test_upload_put_unauthorized(self): response3 = self.client.put( "/v1/data/upload/3/", data=data, headers=auth_header(token) ) - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual(response2.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual(response3.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response3.status_code, status.HTTP_403_FORBIDDEN) # Unauthenticated user cannot update data def test_upload_put_unauthenticated(self): From 101dd34e7f168af59cec438d0bfdf3ab0ba9c735 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Feb 2025 15:30:20 -0500 Subject: [PATCH 245/675] Change permissions handling for download --- sasdata/fair_database/data/views.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 789220d89..a17bca641 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -121,12 +121,8 @@ def upload(request, data_id=None, version=None): def download(request, data_id, version=None): if request.method == "GET": data = get_object_or_404(DataFile, id=data_id) - if not data.is_public: - # add session key later - if not request.user.is_authenticated: - return HttpResponseForbidden("data is private, must log in") - if not request.user == data.current_user: - return HttpResponseForbidden("data is private") + if not permissions.check_permissions(request, data): + return HttpResponseForbidden("data is private") # TODO add issues later try: file = open(data.file.path, "rb") From b7a49bf085bad3635605f8b02b2cb733a87e9c16 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Feb 2025 15:32:28 -0500 Subject: [PATCH 246/675] Return bad request for non put or push upload --- sasdata/fair_database/data/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index a17bca641..09088589a 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -102,6 +102,8 @@ def upload(request, data_id=None, version=None): context={"is_public": db.is_public}, partial=True, ) + else: + return HttpResponseBadRequest() if serializer.is_valid(raise_exception=True): serializer.save() From 08ef969e6e4c937226a2e1ccf3993b07b26e257f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Feb 2025 16:26:07 -0500 Subject: [PATCH 247/675] Add tests for accessing nonexistent data --- sasdata/fair_database/data/tests.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 28243173e..330c7ff49 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -44,6 +44,11 @@ def test_does_list_user(self): request = self.client.get("/v1/data/list/testUser/", user=self.user) self.assertEqual(request.data, {"user_data_ids": {3: "cyl_400_20.txt"}}) + # Test list a nonexistent user's data + def test_list_other_user(self): + request = self.client.get("/v1/data/list/fakeUser/") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + # Test loading a public data file def test_does_load_data_info_public(self): request = self.client.get("/v1/data/load/1/") @@ -54,6 +59,11 @@ def test_does_load_data_info_private(self): request = self.client.get("/v1/data/load/3/") self.assertEqual(request.status_code, status.HTTP_200_OK) + # Test loading data that does not exist + def test_load_data_info_nonexistent(self): + request = self.client.get("/v1/data/load/5/") + self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) + def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT) @@ -124,6 +134,7 @@ def test_does_file_upload_update(self): ) DataFile.objects.get(id=2).delete() + # Test updating a public file def test_public_file_upload_update(self): data_object = DataFile.objects.create( id=3, current_user=self.user, file_name="cyl_testdata.txt", is_public=True @@ -152,6 +163,13 @@ def test_unauthorized_file_upload_update(self): self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) DataFile.objects.get(id=2).delete() + # Test update nonexistent file fails + def test_file_upload_update_not_found(self): + file = open(find("cyl_400_40.txt")) + data = {"file": file, "is_public": False} + request = self.client2.put("/v1/data/upload/5/", data=data) + self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) + # Test file download def test_does_download(self): request = self.client.get("/v1/data/2/download/") @@ -165,5 +183,10 @@ def test_unauthorized_download(self): request2 = self.client2.get("/v1/data/2/download/") self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + # Test download nonexistent file + def test_download_nonexistent(self): + request = self.client.get("/v1/data/5/download/") + self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) + def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT) From 86fe7e58afc5c82c350e82ed783c3992323b8aa6 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Feb 2025 16:51:48 -0500 Subject: [PATCH 248/675] Comment out unfinished test line --- sasdata/quantities/test_numerical_encoding.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/test_numerical_encoding.py b/sasdata/quantities/test_numerical_encoding.py index 80cfbad9a..93642a3f6 100644 --- a/sasdata/quantities/test_numerical_encoding.py +++ b/sasdata/quantities/test_numerical_encoding.py @@ -63,6 +63,6 @@ def test_numpy_dtypes_encode_decode(dtype): ]) def test_coo_matrix_encode_decode(shape, n, m, dtype): - i_indices = + #i_indices = values = np.arange(10) \ No newline at end of file From 99cf4a3e3a08319d734986de7849f3630911300c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 12 Feb 2025 10:45:38 -0500 Subject: [PATCH 249/675] Read download files to ensure they close - attempt to fix unit test failures on windows --- sasdata/fair_database/fair_database/test_permissions.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index f7e32a5c5..1ed5edd23 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -239,6 +239,9 @@ def test_download_authenticated(self): response = self.client.get("/v1/data/1/download/", headers=auth_header(token)) response2 = self.client.get("/v1/data/2/download/", headers=auth_header(token)) response3 = self.client.get("/v1/data/3/download/", headers=auth_header(token)) + b"".join(response.streaming_content) + b"".join(response2.streaming_content) + b"".join(response3.streaming_content) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) self.assertEqual(response3.status_code, status.HTTP_200_OK) @@ -248,6 +251,7 @@ def test_download_unauthorized(self): token = self.client.post("/auth/login/", data=self.login_data_2) response = self.client.get("/v1/data/2/download/", headers=auth_header(token)) response2 = self.client.get("/v1/data/3/download/", headers=auth_header(token)) + b"".join(response2.streaming_content) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response2.status_code, status.HTTP_200_OK) @@ -256,6 +260,8 @@ def test_download_unauthenticated(self): response = self.client.get("/v1/data/1/download/") response2 = self.client.get("/v1/data/2/download/") response3 = self.client.get("/v1/data/3/download/") + b"".join(response.streaming_content) + b"".join(response2.streaming_content) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response3.status_code, status.HTTP_200_OK) From 2b09137d0938f0b967d4fab2b86671470037a168 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 12 Feb 2025 10:45:38 -0500 Subject: [PATCH 250/675] Fix typo --- sasdata/fair_database/fair_database/test_permissions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index 1ed5edd23..f13c6745b 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -261,7 +261,7 @@ def test_download_unauthenticated(self): response2 = self.client.get("/v1/data/2/download/") response3 = self.client.get("/v1/data/3/download/") b"".join(response.streaming_content) - b"".join(response2.streaming_content) + b"".join(response3.streaming_content) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response3.status_code, status.HTTP_200_OK) From 1b14074adb8bd5fc66b9cc30ab022cae33708ea3 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 13 Feb 2025 11:47:16 -0500 Subject: [PATCH 251/675] Preliminary documentation for models --- sasdata/fair_database/data/models.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 013ef0a98..cfffd940e 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -3,8 +3,9 @@ from django.core.files.storage import FileSystemStorage -# Create your models here. class DataFile(models.Model): + """Database model for file contents.""" + # username current_user = models.ForeignKey( User, blank=True, null=True, on_delete=models.CASCADE @@ -29,3 +30,12 @@ class DataFile(models.Model): is_public = models.BooleanField( default=False, help_text="opt in to submit your data into example pool" ) + + +"""Database model for a set of data and associated metadata.""" + +"""Database model for group of DataSets associated by a varying parameter.""" + +"""Database model for tree of operations performed on a DataSet.""" + +"""Database model for a project save state.""" From ed0ae490085c76d2fbd2e16607fd9ee2e5929a91 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 13 Feb 2025 11:58:15 -0500 Subject: [PATCH 252/675] Base abstract model for user and is_public --- .../0003_alter_datafile_is_public.py | 19 +++++++++++++++++ sasdata/fair_database/data/models.py | 21 ++++++++++++------- 2 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0003_alter_datafile_is_public.py diff --git a/sasdata/fair_database/data/migrations/0003_alter_datafile_is_public.py b/sasdata/fair_database/data/migrations/0003_alter_datafile_is_public.py new file mode 100644 index 000000000..e6415eb80 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0003_alter_datafile_is_public.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.5 on 2025-02-13 16:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0002_rename_data_datafile"), + ] + + operations = [ + migrations.AlterField( + model_name="datafile", + name="is_public", + field=models.BooleanField( + default=False, help_text="opt in to make your data public" + ), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index cfffd940e..7a563108e 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -3,14 +3,26 @@ from django.core.files.storage import FileSystemStorage -class DataFile(models.Model): - """Database model for file contents.""" +class Data(models.Model): + """Base model for data.""" # username current_user = models.ForeignKey( User, blank=True, null=True, on_delete=models.CASCADE ) + # is the data public? + is_public = models.BooleanField( + default=False, help_text="opt in to make your data public" + ) + + class Meta: + abstract = True + + +class DataFile(Data): + """Database model for file contents.""" + # file name file_name = models.CharField( max_length=200, default=None, blank=True, null=True, help_text="File name" @@ -26,11 +38,6 @@ class DataFile(models.Model): storage=FileSystemStorage(), ) - # is the data public? - is_public = models.BooleanField( - default=False, help_text="opt in to submit your data into example pool" - ) - """Database model for a set of data and associated metadata.""" From becf1b55f24596b6deb58c698ef3173809ccccd2 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 14 Feb 2025 14:24:37 -0500 Subject: [PATCH 253/675] Enable list other users' public data --- sasdata/fair_database/data/views.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 09088589a..be9ce9757 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -22,14 +22,10 @@ def list_data(request, username=None, version=None): if request.method == "GET": if username: data_list = {"user_data_ids": {}} - if username == request.user.username and request.user.is_authenticated: - private_data = DataFile.objects.filter(current_user=request.user.id) - for x in private_data: + private_data = DataFile.objects.filter(current_user=request.user.id) + for x in private_data: + if permissions.check_permissions(request, x): data_list["user_data_ids"][x.id] = x.file_name - else: - return HttpResponseBadRequest( - "user is not logged in, or username is not same as current user" - ) else: public_data = DataFile.objects.filter(is_public=True) data_list = {"public_data_ids": {}} From cfac0fc188acdbfb162798b16c34a973c820427d Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 14 Feb 2025 14:27:15 -0500 Subject: [PATCH 254/675] 404 for list by nonexistent username --- sasdata/fair_database/data/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index be9ce9757..472623772 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -1,5 +1,6 @@ import os +from django.contrib.auth.models import User from django.shortcuts import get_object_or_404 from django.http import ( HttpResponseBadRequest, @@ -21,6 +22,7 @@ def list_data(request, username=None, version=None): if request.method == "GET": if username: + get_object_or_404(User, username=username) data_list = {"user_data_ids": {}} private_data = DataFile.objects.filter(current_user=request.user.id) for x in private_data: From 28df2397db19b64fef2e5a1585e624be9d4626c8 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 14 Feb 2025 14:37:05 -0500 Subject: [PATCH 255/675] Switch to filter by username param not request user --- sasdata/fair_database/data/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 472623772..fcc782c5a 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -22,9 +22,9 @@ def list_data(request, username=None, version=None): if request.method == "GET": if username: - get_object_or_404(User, username=username) + search_user = get_object_or_404(User, username=username) data_list = {"user_data_ids": {}} - private_data = DataFile.objects.filter(current_user=request.user.id) + private_data = DataFile.objects.filter(current_user=search_user) for x in private_data: if permissions.check_permissions(request, x): data_list["user_data_ids"][x.id] = x.file_name From fe03cbb5f47d54a6c3b6ea693ff3e46eb762496c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 14 Feb 2025 14:37:53 -0500 Subject: [PATCH 256/675] Change permissions tests to expected user list behavior --- sasdata/fair_database/fair_database/test_permissions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index f13c6745b..9bf91d04d 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -90,7 +90,8 @@ def test_list_authenticated_2(self): response.data, {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}, ) - self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response2.status_code, status.HTTP_200_OK) + self.assertEqual(response2.data, {"user_data_ids": {3: "cyl_testdata.txt"}}) self.assertEqual(response3.data, {"user_data_ids": {}}) # Unauthenticated user can view list of public data @@ -101,7 +102,8 @@ def test_list_unauthenticated(self): response.data, {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}, ) - self.assertEqual(response2.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response2.status_code, status.HTTP_200_OK) + self.assertEqual(response2.data, {"user_data_ids": {3: "cyl_testdata.txt"}}) # Authenticated user can load public data and owned private data def test_load_authenticated(self): From fbd01691df23e908e0ff90edfc0f1c42ace81594 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 14 Feb 2025 14:44:24 -0500 Subject: [PATCH 257/675] Change data tests to expected user list behavior --- sasdata/fair_database/data/tests.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 330c7ff49..10ebede87 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -44,10 +44,16 @@ def test_does_list_user(self): request = self.client.get("/v1/data/list/testUser/", user=self.user) self.assertEqual(request.data, {"user_data_ids": {3: "cyl_400_20.txt"}}) - # Test list a nonexistent user's data + # Test list another user's public data def test_list_other_user(self): + client2 = APIClient() + request = client2.get("/v1/data/list/testUser/", user=self.user) + self.assertEqual(request.data, {"user_data_ids": {}}) + + # Test list a nonexistent user's data + def test_list_nonexistent_user(self): request = self.client.get("/v1/data/list/fakeUser/") - self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) # Test loading a public data file def test_does_load_data_info_public(self): From ea076c4dabef1be8e1aa5408be81a7d432aef98c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 17 Feb 2025 14:15:13 -0500 Subject: [PATCH 258/675] Create initial version of dataset model --- sasdata/fair_database/data/models.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 7a563108e..97a63acb2 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -39,7 +39,27 @@ class DataFile(Data): ) -"""Database model for a set of data and associated metadata.""" +class DataSet(Data): + """Database model for a set of data and associated metadata.""" + + # dataset name + name = models.CharField() + + # associated files + files = models.ManyToManyField(DataFile) + + # ordinate + ordinate = models.JSONField() + + # abscissae + abscissae = models.JSONField() + + # data contents + data_contents = models.JSONField() + + # metadata + raw_metadata = models.JSONField() + """Database model for group of DataSets associated by a varying parameter.""" From 56229c7f7c64ee4207c3e315728a61e430ba87a7 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 17 Feb 2025 15:11:51 -0500 Subject: [PATCH 259/675] Start OperationTree model --- sasdata/fair_database/data/models.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 97a63acb2..0dac9170a 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -63,6 +63,16 @@ class DataSet(Data): """Database model for group of DataSets associated by a varying parameter.""" -"""Database model for tree of operations performed on a DataSet.""" + +class OperationTree(Data): + """Database model for tree of operations performed on a DataSet.""" + + # Dataset the operation tree is performed on + dataset = models.ForeignKey(DataSet) + + # operation + + # previous operation + """Database model for a project save state.""" From cb26e84ad414b1b0752aee70f545ac716460d9fa Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 17 Feb 2025 15:13:55 -0500 Subject: [PATCH 260/675] Start Session model --- sasdata/fair_database/data/models.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 0dac9170a..bfabb8c47 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -75,4 +75,11 @@ class OperationTree(Data): # previous operation -"""Database model for a project save state.""" +class Session(Data): + """Database model for a project save state.""" + + # dataset + dataset = models.ForeignKey(DataSet) + + # operation tree + operations = models.ForeignKey(OperationTree) From ba66ee508e04b06989313da3c904571ef9ef546e Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 19 Feb 2025 13:42:29 -0500 Subject: [PATCH 261/675] Begin SasData serializers --- sasdata/data.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sasdata/data.py b/sasdata/data.py index 9a05276d2..1df5d6d04 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,3 +1,4 @@ +import json from enum import Enum from typing import TypeVar, Any, Self from dataclasses import dataclass @@ -68,3 +69,19 @@ def summary(self, indent = " ", include_raw=False): s += key_tree(self._raw_metadata) return s + + def serialise(self) -> str: + return json.dumps(self._serialise_json()) + + def _serialise_json(self) -> dict[str, Any]: + return { + "name": self.name, + "data_contents": [], + "raw_metadata": {}, + "verbose": self._verbose, + "metadata": {}, + "ordinate": {}, + "abscissae": [], + "mask": {}, + "model_requirements": {} + } \ No newline at end of file From 0c7489921fe82ca8322370f5edff41895f6ff630 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 19 Feb 2025 15:54:48 -0500 Subject: [PATCH 262/675] Start of Quantity serializer --- sasdata/quantities/quantity.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 4701df6d5..c0175aa13 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1189,6 +1189,16 @@ def in_si_with_standard_error(self): else: return self.in_si(), None + # TODO: fill out actual values + def _serialise_json(self): + return { + "value": "", # figure out QuantityType serialisation + "units": "", # Unit serialisation + "standard_error": "", # also QuantityType serialisation + "hash_seed": self._hash_seed, # is this just a string? + "history": {} # QuantityHistory serializer + } + def __mul__(self: Self, other: ArrayLike | Self ) -> Self: if isinstance(other, Quantity): return DerivedQuantity( From 37be69349a904de0f4e5a4c15f0d644b019431a3 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 19 Feb 2025 16:07:53 -0500 Subject: [PATCH 263/675] Serializer for NamedQuantity --- sasdata/quantities/quantity.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index c0175aa13..f2a724b52 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1437,6 +1437,10 @@ def with_standard_error(self, standard_error: Quantity): raise UnitError(f"Standard error units ({standard_error.units}) " f"are not compatible with value units ({self.units})") + def _serialise_json(self): + quantity = super()._serialise_json() + quantity["name"] = self.name + return quantity @property def string_repr(self): From 5dbc62733b7d3493de9d72cd5803f676d87669b2 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 19 Feb 2025 16:09:28 -0500 Subject: [PATCH 264/675] Continue SasData serializer and add notes --- sasdata/data.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 1df5d6d04..46045fa9f 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -73,15 +73,16 @@ def summary(self, indent = " ", include_raw=False): def serialise(self) -> str: return json.dumps(self._serialise_json()) + # TODO: replace with serialization methods when written def _serialise_json(self) -> dict[str, Any]: return { "name": self.name, - "data_contents": [], - "raw_metadata": {}, + "data_contents": [q._serialise_json() for q in self._data_contents], + "raw_metadata": {}, # serialization for Groups and DataSets "verbose": self._verbose, - "metadata": {}, - "ordinate": {}, - "abscissae": [], + "metadata": {}, # serialization for MetaData + "ordinate": self.ordinate._serialise_json(), + "abscissae": [q._serialise_json() for q in self.abscissae], "mask": {}, "model_requirements": {} } \ No newline at end of file From cf873d14e1a2343876dcb06f7673dfc4e2cda4ba Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 19 Feb 2025 16:13:27 -0500 Subject: [PATCH 265/675] Add class for MetaData model --- sasdata/fair_database/data/models.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index bfabb8c47..41d500f85 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -48,6 +48,9 @@ class DataSet(Data): # associated files files = models.ManyToManyField(DataFile) + # metadata + metadata = models.ForeignKey("MetaData") + # ordinate ordinate = models.JSONField() @@ -61,6 +64,13 @@ class DataSet(Data): raw_metadata = models.JSONField() +class MetaData: + """Database model for scattering metadata""" + + # Associated data set + dataset = models.ForeignKey(DataSet) + + """Database model for group of DataSets associated by a varying parameter.""" From 758026c738f7956d5480b9344846cbcdf982fc2b Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Feb 2025 10:20:23 -0500 Subject: [PATCH 266/675] Temporarily comment out unmigrated models --- sasdata/fair_database/data/models.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 41d500f85..61ac7ed2b 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -39,17 +39,18 @@ class DataFile(Data): ) +''' class DataSet(Data): """Database model for a set of data and associated metadata.""" # dataset name - name = models.CharField() + name = models.CharField(max_length=200) # associated files files = models.ManyToManyField(DataFile) # metadata - metadata = models.ForeignKey("MetaData") + # metadata = models.ForeignKey("MetaData", on_delete=models.CASCADE) # ordinate ordinate = models.JSONField() @@ -68,7 +69,7 @@ class MetaData: """Database model for scattering metadata""" # Associated data set - dataset = models.ForeignKey(DataSet) + # dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) """Database model for group of DataSets associated by a varying parameter.""" @@ -78,7 +79,7 @@ class OperationTree(Data): """Database model for tree of operations performed on a DataSet.""" # Dataset the operation tree is performed on - dataset = models.ForeignKey(DataSet) + # dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) # operation @@ -89,7 +90,8 @@ class Session(Data): """Database model for a project save state.""" # dataset - dataset = models.ForeignKey(DataSet) + # dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) # operation tree - operations = models.ForeignKey(OperationTree) + # operations = models.ForeignKey(OperationTree, on_delete=models.CASCADE) +''' From b9511244d1af27db34de264f1ca780e7ab4d7ff9 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Feb 2025 14:36:02 -0500 Subject: [PATCH 267/675] Serializers for units --- sasdata/quantities/_units_base.py | 21 +++++++++++++++++++-- sasdata/quantities/quantity.py | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index a3b68b892..ed8188602 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -12,11 +12,11 @@ class DimensionError(Exception): class Dimensions: """ - Note that some SI Base units are not useful from the perspecive of the sasview project, and make things + Note that some SI Base units are not useful from the perspective of the sasview project, and make things behave badly. In particular: moles and angular measures are dimensionless, and candelas are really a weighted measure of power. - We do however track angle and amount, because its really useful for formatting units + We do however track angle and amount, because it's really useful for formatting units """ def __init__(self, @@ -200,6 +200,17 @@ def si_repr(self): return ''.join(tokens) + def _serialise_json(self): + return { + "length": self.length, + "time": self.time, + "mass": self.mass, + "current": self.current, + "temperature": self.temperature, + "amount": self.moles_hint, + "angle": self.angle_hint + } + class Unit: def __init__(self, @@ -264,6 +275,12 @@ def __repr__(self): @staticmethod def parse(unit_string: str) -> "Unit": pass + + def _serialise_json(self): + return { + "scale": self.scale, + "dimensions": self.dimensions._serialise_json() + } class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): """ This processor minimises the dimensionality of the unit by multiplying by as many units of the specified type as needed """ diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index f2a724b52..a6d4906d2 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1193,7 +1193,7 @@ def in_si_with_standard_error(self): def _serialise_json(self): return { "value": "", # figure out QuantityType serialisation - "units": "", # Unit serialisation + "units": self.units._serialise_json(), # Unit serialisation "standard_error": "", # also QuantityType serialisation "hash_seed": self._hash_seed, # is this just a string? "history": {} # QuantityHistory serializer From a541bf900823c89925e21ef8a60cebfa3559016a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Feb 2025 14:45:55 -0500 Subject: [PATCH 268/675] QuantityHistory serializer --- sasdata/quantities/quantity.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index a6d4906d2..69e9a101e 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1087,6 +1087,15 @@ def summary(self): return s + def _serialise_json(self): + return { + "operation_tree": self.operation_tree.serialise(), + "references": { + key: self.references[key]._serialise_json() for key in self.references + } + + } + class Quantity[QuantityType]: @@ -1196,7 +1205,7 @@ def _serialise_json(self): "units": self.units._serialise_json(), # Unit serialisation "standard_error": "", # also QuantityType serialisation "hash_seed": self._hash_seed, # is this just a string? - "history": {} # QuantityHistory serializer + "history": self.history._serialise_json() } def __mul__(self: Self, other: ArrayLike | Self ) -> Self: From 7b67ec9bfe140063d3d5b80608bd6b01d2a20ad9 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Feb 2025 14:57:28 -0500 Subject: [PATCH 269/675] Serializers for Dataset and Group --- sasdata/data.py | 2 +- sasdata/data_backing.py | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/sasdata/data.py b/sasdata/data.py index 46045fa9f..00f0cc446 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -78,7 +78,7 @@ def _serialise_json(self) -> dict[str, Any]: return { "name": self.name, "data_contents": [q._serialise_json() for q in self._data_contents], - "raw_metadata": {}, # serialization for Groups and DataSets + "raw_metadata": self._raw_metadata._serialise_json(), "verbose": self._verbose, "metadata": {}, # serialization for MetaData "ordinate": self.ordinate._serialise_json(), diff --git a/sasdata/data_backing.py b/sasdata/data_backing.py index 564f466a6..c880c97d8 100644 --- a/sasdata/data_backing.py +++ b/sasdata/data_backing.py @@ -36,6 +36,22 @@ def summary(self, indent_amount: int = 0, indent: str = " ") -> str: return s + def _serialise_json(self): + content = { + { + "name": self.name, + "data": "", # TODO: figure out QuantityType serialisation + "attributes": {} + } + } + for key in self.attributes: + value = self.attributes[key] + if isinstance(value, (Group, Dataset)): + content["attributes"]["key"] = value._serialise_json() + else: + content["attributes"]["key"] = value + return content + @dataclass class Group: name: str @@ -48,6 +64,16 @@ def summary(self, indent_amount: int=0, indent=" "): return s + def _serialise_json(self): + return { + { + "name": self.name, + "children": { + key: self.children[key]._serialise_json() for key in self.children + } + } + } + class Function: """ Representation of a (data driven) function, such as I vs Q """ From 5351d3d11d5b5fa850d40b8c3f851dc581ab8203 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Feb 2025 15:15:09 -0500 Subject: [PATCH 270/675] Framework for metadata serializers --- sasdata/data.py | 2 +- sasdata/metadata.py | 59 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/sasdata/data.py b/sasdata/data.py index 00f0cc446..17f2aac6f 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -80,7 +80,7 @@ def _serialise_json(self) -> dict[str, Any]: "data_contents": [q._serialise_json() for q in self._data_contents], "raw_metadata": self._raw_metadata._serialise_json(), "verbose": self._verbose, - "metadata": {}, # serialization for MetaData + "metadata": self.metadata._serialise_json(), "ordinate": self.ordinate._serialise_json(), "abscissae": [q._serialise_json() for q in self.abscissae], "mask": {}, diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 1597535ee..82ad3e6c8 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -223,6 +223,18 @@ def summary(self) -> str: # # return _str + def _serialise_json(self): + return { + "name": "", + "sample_id": "", + "thickness": "", + "transmission": "", + "temperature": "", + "position": "", + "orientation": "", + "details": "" + } + class Process: """ @@ -254,6 +266,42 @@ def summary(self): f" Notes: {self.notes.value}\n" ) + def _serialise_json(self): + return { + "name": "", + "date": "", + "description": "", + "term": "", + "notes": "" + } + +class TransmissionSpectrum: + """ + Class that holds information about transmission spectrum + for white beams and spallation sources. + """ + def __init__(self, target_object: AccessorTarget): + # TODO: Needs to be multiple instances + self.name = StringAccessor(target_object, "name") + self.timestamp = StringAccessor(target_object, "timestamp") + + # Wavelength (float) [A] + self.wavelength = LengthAccessor[ArrayLike](target_object, + "wavelength", + "wavelength.units") + + # Transmission (float) [unit less] + self.transmission = DimensionlessAccessor[ArrayLike](target_object, + "transmission", + "units", + default_unit=units.none) + + # Transmission Deviation (float) [unit less] + self.transmission_deviation = DimensionlessAccessor[ArrayLike](target_object, + "transmission_deviation", + "transmission_deviation.units", + default_unit=units.none) + @dataclass class Instrument: @@ -326,3 +374,14 @@ def summary(self): self.process.summary() + self.sample.summary() + (self.instrument.summary() if self.instrument else "")) + + def _serialise_json(self): + return { + "instrument": self.instrument._serialise_json(), + "process": self.process._serialise_json(), + "sample": self.sample._serialise_json(), + "transmission_spectrum": self.transmission_spectrum._serialise_json(), + "title": self.title, + "run": self.run, + "definition": self.definition + } \ No newline at end of file From 1b2f9fa771aa81eec5f6b8f4935dd81dd1f7ae99 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Feb 2025 15:32:07 -0500 Subject: [PATCH 271/675] Instrument metadata serializers --- sasdata/metadata.py | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 82ad3e6c8..f9e1aee01 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -64,6 +64,17 @@ def summary(self): f" Pixel size: {self.pixel_size.value}\n" f" Slit length: {self.slit_length.value}\n") + def _serialise_json(self): + return { + "name": "", + "distance": "", + "offset": "", + "orientation": "", + "beam_center": "", + "pixel_size": "", + "slit_length": "" + } + class Aperture: @@ -273,34 +284,8 @@ def _serialise_json(self): "description": "", "term": "", "notes": "" - } + } default_unit=units.none) -class TransmissionSpectrum: - """ - Class that holds information about transmission spectrum - for white beams and spallation sources. - """ - def __init__(self, target_object: AccessorTarget): - # TODO: Needs to be multiple instances - self.name = StringAccessor(target_object, "name") - self.timestamp = StringAccessor(target_object, "timestamp") - - # Wavelength (float) [A] - self.wavelength = LengthAccessor[ArrayLike](target_object, - "wavelength", - "wavelength.units") - - # Transmission (float) [unit less] - self.transmission = DimensionlessAccessor[ArrayLike](target_object, - "transmission", - "units", - default_unit=units.none) - - # Transmission Deviation (float) [unit less] - self.transmission_deviation = DimensionlessAccessor[ArrayLike](target_object, - "transmission_deviation", - "transmission_deviation.units", - default_unit=units.none) @dataclass From 815f8e48e19d610e7a337fe4b49462c3630cd5f1 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Feb 2025 15:53:26 -0500 Subject: [PATCH 272/675] Finish metadata serializers --- sasdata/metadata.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index f9e1aee01..987f498b2 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -66,13 +66,13 @@ def summary(self): def _serialise_json(self): return { - "name": "", - "distance": "", - "offset": "", - "orientation": "", - "beam_center": "", - "pixel_size": "", - "slit_length": "" + "name": self.name.value, + "distance": self.distance.value._serialise_json(), + "offset": self.offset.value._serialise_json(), + "orientation": self.orientation.value._serialise_json(), + "beam_center": self.beam_center.value._serialise_json(), + "pixel_size": self.pixel_size.value._serialise_json(), + "slit_length": self.slit_length.value._serialise_json() } @@ -236,14 +236,14 @@ def summary(self) -> str: def _serialise_json(self): return { - "name": "", - "sample_id": "", - "thickness": "", - "transmission": "", - "temperature": "", - "position": "", - "orientation": "", - "details": "" + "name": self.name.value, + "sample_id": self.sample_id.value, + "thickness": self.thickness.value._serialise_json(), + "transmission": self.transmission.value, + "temperature": self.temperature.value._serialise_json(), + "position": self.position.value._serialise_json(), + "orientation": self.orientation.value._serialise_json(), + "details": self.details.value } From dff6b89035bb2cf7a649fa033b3801695827b5cd Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 25 Feb 2025 16:30:00 -0500 Subject: [PATCH 273/675] Updates to in-progess data models --- sasdata/fair_database/data/models.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 61ac7ed2b..5c007975c 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -64,6 +64,20 @@ class DataSet(Data): # metadata raw_metadata = models.JSONField() +class Quantity(): + + # data value + value = models.JSONField() + + # variance of the data + variance = models.JSONField() + + # units + units = models.CharField(max_length=200) + + hash = IntegerField() # this might change + + class MetaData: """Database model for scattering metadata""" @@ -84,8 +98,10 @@ class OperationTree(Data): # operation # previous operation + parent_operation = models.ForeignKey("self", blank=True, null=True) +''' - +''' class Session(Data): """Database model for a project save state.""" From 12ebd981a1ba733a0c2b8ee812c6841bd0ba675f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 26 Feb 2025 11:11:02 -0500 Subject: [PATCH 274/675] Initial versions of phase 1 models --- sasdata/fair_database/data/models.py | 29 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 5c007975c..f2c54b336 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -6,7 +6,7 @@ class Data(models.Model): """Base model for data.""" - # username + # owner of the data current_user = models.ForeignKey( User, blank=True, null=True, on_delete=models.CASCADE ) @@ -39,7 +39,6 @@ class DataFile(Data): ) -''' class DataSet(Data): """Database model for a set of data and associated metadata.""" @@ -50,21 +49,23 @@ class DataSet(Data): files = models.ManyToManyField(DataFile) # metadata - # metadata = models.ForeignKey("MetaData", on_delete=models.CASCADE) + metadata = models.ForeignKey("MetaData", on_delete=models.CASCADE) # ordinate - ordinate = models.JSONField() + # ordinate = models.JSONField() # abscissae - abscissae = models.JSONField() + # abscissae = models.JSONField() # data contents - data_contents = models.JSONField() + # data_contents = models.JSONField() # metadata - raw_metadata = models.JSONField() + # raw_metadata = models.JSONField() + -class Quantity(): +class Quantity: + """Database model for data quantities such as the ordinate and abscissae.""" # data value value = models.JSONField() @@ -75,31 +76,31 @@ class Quantity(): # units units = models.CharField(max_length=200) - hash = IntegerField() # this might change - + # hash value + hash = models.IntegerField() class MetaData: """Database model for scattering metadata""" # Associated data set - # dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) + dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) """Database model for group of DataSets associated by a varying parameter.""" -class OperationTree(Data): +class OperationTree: """Database model for tree of operations performed on a DataSet.""" # Dataset the operation tree is performed on - # dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) + dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) # operation # previous operation parent_operation = models.ForeignKey("self", blank=True, null=True) -''' + ''' class Session(Data): From fa517583de92725f171b7bf94accfc66b794ac62 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 26 Feb 2025 11:36:29 -0500 Subject: [PATCH 275/675] model migrations --- ...aset_metadata_dataset_metadata_and_more.py | 127 ++++++++++++++++++ sasdata/fair_database/data/models.py | 18 ++- 2 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0004_quantity_dataset_metadata_dataset_metadata_and_more.py diff --git a/sasdata/fair_database/data/migrations/0004_quantity_dataset_metadata_dataset_metadata_and_more.py b/sasdata/fair_database/data/migrations/0004_quantity_dataset_metadata_dataset_metadata_and_more.py new file mode 100644 index 000000000..e56dc51dc --- /dev/null +++ b/sasdata/fair_database/data/migrations/0004_quantity_dataset_metadata_dataset_metadata_and_more.py @@ -0,0 +1,127 @@ +# Generated by Django 5.1.6 on 2025-02-26 16:34 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0003_alter_datafile_is_public"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Quantity", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("value", models.JSONField()), + ("variance", models.JSONField()), + ("units", models.CharField(max_length=200)), + ("hash", models.IntegerField()), + ], + ), + migrations.CreateModel( + name="DataSet", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "is_public", + models.BooleanField( + default=False, help_text="opt in to make your data public" + ), + ), + ("name", models.CharField(max_length=200)), + ( + "current_user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ("files", models.ManyToManyField(to="data.datafile")), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="MetaData", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "dataset", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="associated_data", + to="data.dataset", + ), + ), + ], + ), + migrations.AddField( + model_name="dataset", + name="metadata", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="associated_metadata", + to="data.metadata", + ), + ), + migrations.CreateModel( + name="OperationTree", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "dataset", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="data.dataset" + ), + ), + ( + "parent_operation", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="data.operationtree", + ), + ), + ], + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index f2c54b336..69066fb12 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -49,7 +49,9 @@ class DataSet(Data): files = models.ManyToManyField(DataFile) # metadata - metadata = models.ForeignKey("MetaData", on_delete=models.CASCADE) + metadata = models.OneToOneField( + "MetaData", on_delete=models.CASCADE, related_name="associated_metadata" + ) # ordinate # ordinate = models.JSONField() @@ -64,7 +66,7 @@ class DataSet(Data): # raw_metadata = models.JSONField() -class Quantity: +class Quantity(models.Model): """Database model for data quantities such as the ordinate and abscissae.""" # data value @@ -80,17 +82,19 @@ class Quantity: hash = models.IntegerField() -class MetaData: +class MetaData(models.Model): """Database model for scattering metadata""" # Associated data set - dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) + dataset = models.OneToOneField( + "DataSet", on_delete=models.CASCADE, related_name="associated_data" + ) """Database model for group of DataSets associated by a varying parameter.""" -class OperationTree: +class OperationTree(models.Model): """Database model for tree of operations performed on a DataSet.""" # Dataset the operation tree is performed on @@ -99,7 +103,9 @@ class OperationTree: # operation # previous operation - parent_operation = models.ForeignKey("self", blank=True, null=True) + parent_operation = models.ForeignKey( + "self", blank=True, null=True, on_delete=models.CASCADE + ) ''' From d3396b3456fbdc419613aabb0329882ddfd74113 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 26 Feb 2025 11:55:16 -0500 Subject: [PATCH 276/675] Add field for authorized non-owners for data --- ...t_datafile_users_dataset_users_and_more.py | 58 +++++++++++++++++++ sasdata/fair_database/data/models.py | 14 ++--- 2 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0005_remove_metadata_dataset_datafile_users_dataset_users_and_more.py diff --git a/sasdata/fair_database/data/migrations/0005_remove_metadata_dataset_datafile_users_dataset_users_and_more.py b/sasdata/fair_database/data/migrations/0005_remove_metadata_dataset_datafile_users_dataset_users_and_more.py new file mode 100644 index 000000000..3a333c2ff --- /dev/null +++ b/sasdata/fair_database/data/migrations/0005_remove_metadata_dataset_datafile_users_dataset_users_and_more.py @@ -0,0 +1,58 @@ +# Generated by Django 5.1.6 on 2025-02-26 16:54 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0004_quantity_dataset_metadata_dataset_metadata_and_more"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.RemoveField( + model_name="metadata", + name="dataset", + ), + migrations.AddField( + model_name="datafile", + name="users", + field=models.ManyToManyField(related_name="+", to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name="dataset", + name="users", + field=models.ManyToManyField(related_name="+", to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name="datafile", + name="current_user", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="dataset", + name="current_user", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="dataset", + name="metadata", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, to="data.metadata" + ), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 69066fb12..1393d05a0 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -8,9 +8,11 @@ class Data(models.Model): # owner of the data current_user = models.ForeignKey( - User, blank=True, null=True, on_delete=models.CASCADE + User, blank=True, null=True, on_delete=models.CASCADE, related_name="+" ) + users = models.ManyToManyField(User, related_name="+") + # is the data public? is_public = models.BooleanField( default=False, help_text="opt in to make your data public" @@ -49,9 +51,7 @@ class DataSet(Data): files = models.ManyToManyField(DataFile) # metadata - metadata = models.OneToOneField( - "MetaData", on_delete=models.CASCADE, related_name="associated_metadata" - ) + metadata = models.OneToOneField("MetaData", on_delete=models.CASCADE) # ordinate # ordinate = models.JSONField() @@ -86,9 +86,9 @@ class MetaData(models.Model): """Database model for scattering metadata""" # Associated data set - dataset = models.OneToOneField( - "DataSet", on_delete=models.CASCADE, related_name="associated_data" - ) + # dataset = models.OneToOneField( + # "DataSet", on_delete=models.CASCADE, related_name="associated_data" + # ) """Database model for group of DataSets associated by a varying parameter.""" From ebe4fc18e70356e26e8c0ba604d002ee453e6f56 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 26 Feb 2025 12:50:27 -0500 Subject: [PATCH 277/675] Change permissions to check users with access instead of just ownership --- sasdata/fair_database/fair_database/permissions.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/fair_database/permissions.py b/sasdata/fair_database/fair_database/permissions.py index e62e72572..3ce03f0cd 100644 --- a/sasdata/fair_database/fair_database/permissions.py +++ b/sasdata/fair_database/fair_database/permissions.py @@ -5,10 +5,14 @@ def is_owner(request, obj): return request.user.is_authenticated and request.user == obj.current_user +def has_access(request, obj): + return request.user.is_authenticated and request.user in obj.users.all() + + class DataPermission(BasePermission): def has_object_permission(self, request, view, obj): if request.method == "GET": - if obj.is_public or is_owner(request, obj): + if obj.is_public or has_access(request, obj): return True elif request.method == "DELETE": if obj.is_private and is_owner(request, obj): @@ -19,7 +23,7 @@ def has_object_permission(self, request, view, obj): def check_permissions(request, obj): if request.method == "GET": - if obj.is_public or is_owner(request, obj): + if obj.is_public or has_access(request, obj): return True elif request.method == "DELETE": if obj.is_private and is_owner(request, obj): From 748658bd722341003eb4debc55b50b70a3fa9d2e Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 26 Feb 2025 16:08:32 -0500 Subject: [PATCH 278/675] Ensure owner can access data --- ...lter_datafile_users_alter_dataset_users.py | 28 +++++++++++++++++++ ...lter_datafile_users_alter_dataset_users.py | 28 +++++++++++++++++++ ...lter_datafile_users_alter_dataset_users.py | 28 +++++++++++++++++++ sasdata/fair_database/data/models.py | 2 +- sasdata/fair_database/data/views.py | 2 ++ .../fair_database/permissions.py | 6 +++- 6 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0006_alter_datafile_users_alter_dataset_users.py create mode 100644 sasdata/fair_database/data/migrations/0007_alter_datafile_users_alter_dataset_users.py create mode 100644 sasdata/fair_database/data/migrations/0008_alter_datafile_users_alter_dataset_users.py diff --git a/sasdata/fair_database/data/migrations/0006_alter_datafile_users_alter_dataset_users.py b/sasdata/fair_database/data/migrations/0006_alter_datafile_users_alter_dataset_users.py new file mode 100644 index 000000000..4d2ee4de3 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0006_alter_datafile_users_alter_dataset_users.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.6 on 2025-02-26 20:53 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0005_remove_metadata_dataset_datafile_users_dataset_users_and_more"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name="datafile", + name="users", + field=models.ManyToManyField( + default=[], related_name="+", to=settings.AUTH_USER_MODEL + ), + ), + migrations.AlterField( + model_name="dataset", + name="users", + field=models.ManyToManyField( + default=[], related_name="+", to=settings.AUTH_USER_MODEL + ), + ), + ] diff --git a/sasdata/fair_database/data/migrations/0007_alter_datafile_users_alter_dataset_users.py b/sasdata/fair_database/data/migrations/0007_alter_datafile_users_alter_dataset_users.py new file mode 100644 index 000000000..6540d4e77 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0007_alter_datafile_users_alter_dataset_users.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.6 on 2025-02-26 21:03 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0006_alter_datafile_users_alter_dataset_users"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name="datafile", + name="users", + field=models.ManyToManyField( + blank=True, default=[], related_name="+", to=settings.AUTH_USER_MODEL + ), + ), + migrations.AlterField( + model_name="dataset", + name="users", + field=models.ManyToManyField( + blank=True, default=[], related_name="+", to=settings.AUTH_USER_MODEL + ), + ), + ] diff --git a/sasdata/fair_database/data/migrations/0008_alter_datafile_users_alter_dataset_users.py b/sasdata/fair_database/data/migrations/0008_alter_datafile_users_alter_dataset_users.py new file mode 100644 index 000000000..56dbb1778 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0008_alter_datafile_users_alter_dataset_users.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.6 on 2025-02-26 21:04 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0007_alter_datafile_users_alter_dataset_users"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name="datafile", + name="users", + field=models.ManyToManyField( + blank=True, related_name="+", to=settings.AUTH_USER_MODEL + ), + ), + migrations.AlterField( + model_name="dataset", + name="users", + field=models.ManyToManyField( + blank=True, related_name="+", to=settings.AUTH_USER_MODEL + ), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 1393d05a0..2ad7b6641 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -11,7 +11,7 @@ class Data(models.Model): User, blank=True, null=True, on_delete=models.CASCADE, related_name="+" ) - users = models.ManyToManyField(User, related_name="+") + users = models.ManyToManyField(User, blank=True, related_name="+") # is the data public? is_public = models.BooleanField( diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index fcc782c5a..ce2785895 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -70,6 +70,7 @@ def upload(request, data_id=None, version=None): data={ "file_name": os.path.basename(form.instance.file.path), "current_user": request.user.id, + "users": [request.user.id], }, context={"is_public": db.is_public}, ) @@ -79,6 +80,7 @@ def upload(request, data_id=None, version=None): data={ "file_name": os.path.basename(form.instance.file.path), "current_user": None, + "users": [], }, context={"is_public": db.is_public}, ) diff --git a/sasdata/fair_database/fair_database/permissions.py b/sasdata/fair_database/fair_database/permissions.py index 3ce03f0cd..224e4c00b 100644 --- a/sasdata/fair_database/fair_database/permissions.py +++ b/sasdata/fair_database/fair_database/permissions.py @@ -6,7 +6,11 @@ def is_owner(request, obj): def has_access(request, obj): - return request.user.is_authenticated and request.user in obj.users.all() + return ( + is_owner(request, obj) + or request.user.is_authenticated + and request.user in obj.users.all() + ) class DataPermission(BasePermission): From 6c817ad1615cb23ad7d4ff35fc3ce34176c6a46f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 26 Feb 2025 16:14:50 -0500 Subject: [PATCH 279/675] Bare bones serializers for database models --- sasdata/fair_database/data/serializers.py | 26 ++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 94c1f4e4c..8e9c29743 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from data.models import DataFile +from data.models import DataFile, DataSet, MetaData, OperationTree, Quantity class DataFileSerializer(serializers.ModelSerializer): @@ -12,3 +12,27 @@ def validate(self, data): if not self.context["is_public"] and not data["current_user"]: raise serializers.ValidationError("private data must have an owner") return data + + +class DataSetSerializer(serializers.ModelSerializer): + class Meta: + model = DataSet + fields = "__all__" + + +class QuantitySerializer(serializers.ModelSerializer): + class Meta: + model = Quantity + fields = "__all__" + + +class MetaDataSerializer(serializers.ModelSerializer): + class Meta: + model = MetaData + fields = "__all__" + + +class OperationTreeSerializer(serializers.ModelSerializer): + class Meta: + model = OperationTree + fields = "__all__" From 6be263ceef4c6cd4eeb88198775caed5bc9da2d2 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 27 Feb 2025 14:32:21 -0500 Subject: [PATCH 280/675] Switch serialization methods to non-protected so pycharm stops nagging me --- sasdata/data_backing.py | 8 +++--- sasdata/metadata.py | 45 +++++++++++++++++-------------- sasdata/quantities/_units_base.py | 6 ++--- sasdata/quantities/quantity.py | 12 ++++----- 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/sasdata/data_backing.py b/sasdata/data_backing.py index c880c97d8..0504963ed 100644 --- a/sasdata/data_backing.py +++ b/sasdata/data_backing.py @@ -36,7 +36,7 @@ def summary(self, indent_amount: int = 0, indent: str = " ") -> str: return s - def _serialise_json(self): + def serialise_json(self): content = { { "name": self.name, @@ -47,7 +47,7 @@ def _serialise_json(self): for key in self.attributes: value = self.attributes[key] if isinstance(value, (Group, Dataset)): - content["attributes"]["key"] = value._serialise_json() + content["attributes"]["key"] = value.serialise_json() else: content["attributes"]["key"] = value return content @@ -64,12 +64,12 @@ def summary(self, indent_amount: int=0, indent=" "): return s - def _serialise_json(self): + def serialise_json(self): return { { "name": self.name, "children": { - key: self.children[key]._serialise_json() for key in self.children + key: self.children[key].serialise_json() for key in self.children } } } diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 987f498b2..2bd0f9b21 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -64,15 +64,15 @@ def summary(self): f" Pixel size: {self.pixel_size.value}\n" f" Slit length: {self.slit_length.value}\n") - def _serialise_json(self): + def serialise_json(self): return { "name": self.name.value, - "distance": self.distance.value._serialise_json(), - "offset": self.offset.value._serialise_json(), - "orientation": self.orientation.value._serialise_json(), - "beam_center": self.beam_center.value._serialise_json(), - "pixel_size": self.pixel_size.value._serialise_json(), - "slit_length": self.slit_length.value._serialise_json() + "distance": self.distance.value.serialise_json(), + "offset": self.offset.value.serialise_json(), + "orientation": self.orientation.value.serialise_json(), + "beam_center": self.beam_center.value.serialise_json(), + "pixel_size": self.pixel_size.value.serialise_json(), + "slit_length": self.slit_length.value.serialise_json() } @@ -128,6 +128,12 @@ def summary(self): f"Collimation:\n" f" Length: {self.length}\n") + def serialise_json(self): + return { + "name": self.name.value, + "length": self.length.value.serialise_json() + } + @dataclass class BeamSize: name: Optional[str] @@ -234,15 +240,15 @@ def summary(self) -> str: # # return _str - def _serialise_json(self): + def serialise_json(self): return { "name": self.name.value, "sample_id": self.sample_id.value, - "thickness": self.thickness.value._serialise_json(), + "thickness": self.thickness.value.serialise_json(), "transmission": self.transmission.value, - "temperature": self.temperature.value._serialise_json(), - "position": self.position.value._serialise_json(), - "orientation": self.orientation.value._serialise_json(), + "temperature": self.temperature.value.serialise_json(), + "position": self.position.value.serialise_json(), + "orientation": self.orientation.value.serialise_json(), "details": self.details.value } @@ -277,15 +283,14 @@ def summary(self): f" Notes: {self.notes.value}\n" ) - def _serialise_json(self): + def serialise_json(self): return { "name": "", "date": "", "description": "", "term": "", "notes": "" - } default_unit=units.none) - + } @dataclass @@ -360,12 +365,12 @@ def summary(self): self.sample.summary() + (self.instrument.summary() if self.instrument else "")) - def _serialise_json(self): + def serialise_json(self): return { - "instrument": self.instrument._serialise_json(), - "process": self.process._serialise_json(), - "sample": self.sample._serialise_json(), - "transmission_spectrum": self.transmission_spectrum._serialise_json(), + "instrument": self.instrument.serialise_json(), + "process": self.process.serialise_json(), + "sample": self.sample.serialise_json(), + "transmission_spectrum": self.transmission_spectrum.serialise_json(), "title": self.title, "run": self.run, "definition": self.definition diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index ed8188602..cfe878c90 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -200,7 +200,7 @@ def si_repr(self): return ''.join(tokens) - def _serialise_json(self): + def serialise_json(self): return { "length": self.length, "time": self.time, @@ -276,10 +276,10 @@ def __repr__(self): def parse(unit_string: str) -> "Unit": pass - def _serialise_json(self): + def serialise_json(self): return { "scale": self.scale, - "dimensions": self.dimensions._serialise_json() + "dimensions": self.dimensions.serialise_json() } class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): """ This processor minimises the dimensionality of the unit by multiplying by as many diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 69e9a101e..21717a59e 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1087,11 +1087,11 @@ def summary(self): return s - def _serialise_json(self): + def serialise_json(self): return { "operation_tree": self.operation_tree.serialise(), "references": { - key: self.references[key]._serialise_json() for key in self.references + key: self.references[key].serialise_json() for key in self.references } } @@ -1199,13 +1199,13 @@ def in_si_with_standard_error(self): return self.in_si(), None # TODO: fill out actual values - def _serialise_json(self): + def serialise_json(self): return { "value": "", # figure out QuantityType serialisation - "units": self.units._serialise_json(), # Unit serialisation + "units": self.units.serialise_json(), # Unit serialisation "standard_error": "", # also QuantityType serialisation "hash_seed": self._hash_seed, # is this just a string? - "history": self.history._serialise_json() + "history": self.history.serialise_json() } def __mul__(self: Self, other: ArrayLike | Self ) -> Self: @@ -1446,7 +1446,7 @@ def with_standard_error(self, standard_error: Quantity): raise UnitError(f"Standard error units ({standard_error.units}) " f"are not compatible with value units ({self.units})") - def _serialise_json(self): + def serialise_json(self): quantity = super()._serialise_json() quantity["name"] = self.name return quantity From 05462a4951e6c67f2124651ae123561634900858 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 27 Feb 2025 14:32:52 -0500 Subject: [PATCH 281/675] Deserialization for SasData class --- sasdata/data.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 17f2aac6f..c71132260 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -70,6 +70,17 @@ def summary(self, indent = " ", include_raw=False): return s + def deserialise(self, data: str) -> "SasData": + json_data = json.loads(data) + return self.deserialise_json(json_data) + + def deserialise_json(self, json_data: dict) -> "SasData": + name = json_data["name"] + data_contents = [] # deserialise Quantity + raw_metadata = None # deserialise Group + verbose = json_data["verbose"] + return SasData(name, data_contents, raw_metadata, verbose) + def serialise(self) -> str: return json.dumps(self._serialise_json()) @@ -77,12 +88,12 @@ def serialise(self) -> str: def _serialise_json(self) -> dict[str, Any]: return { "name": self.name, - "data_contents": [q._serialise_json() for q in self._data_contents], - "raw_metadata": self._raw_metadata._serialise_json(), + "data_contents": [q.serialise_json() for q in self._data_contents], + "raw_metadata": self._raw_metadata.serialise_json(), "verbose": self._verbose, - "metadata": self.metadata._serialise_json(), - "ordinate": self.ordinate._serialise_json(), - "abscissae": [q._serialise_json() for q in self.abscissae], + "metadata": self.metadata.serialise_json(), + "ordinate": self.ordinate.serialise_json(), + "abscissae": [q.serialise_json() for q in self.abscissae], "mask": {}, "model_requirements": {} } \ No newline at end of file From 1a77cb5097db953a2ed1d3fe055ce04b4fd36ed1 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 27 Feb 2025 15:00:03 -0500 Subject: [PATCH 282/675] Deserialization for Group and Dataset classes --- sasdata/data.py | 12 ++++++----- sasdata/data_backing.py | 45 +++++++++++++++++++++++++++++++---------- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index c71132260..3ff477ab0 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -70,14 +70,16 @@ def summary(self, indent = " ", include_raw=False): return s - def deserialise(self, data: str) -> "SasData": + @staticmethod + def deserialise(data: str) -> "SasData": json_data = json.loads(data) - return self.deserialise_json(json_data) + return SasData.deserialise_json(json_data) - def deserialise_json(self, json_data: dict) -> "SasData": + @staticmethod + def deserialise_json(json_data: dict) -> "SasData": name = json_data["name"] - data_contents = [] # deserialise Quantity - raw_metadata = None # deserialise Group + data_contents = [] # deserialize Quantity + raw_metadata = Group.deserialise_json(json_data["raw_metadata"]) verbose = json_data["verbose"] return SasData(name, data_contents, raw_metadata, verbose) diff --git a/sasdata/data_backing.py b/sasdata/data_backing.py index 0504963ed..315212628 100644 --- a/sasdata/data_backing.py +++ b/sasdata/data_backing.py @@ -36,13 +36,25 @@ def summary(self, indent_amount: int = 0, indent: str = " ") -> str: return s + @staticmethod + def deserialise_json(json_data: dict) -> "Dataset": + name = json_data["name"] + data = "" # TODO: figure out QuantityType serialisation + attributes = {} + for key in json_data["attributes"]: + value = json_data["attributes"][key] + if isinstance(value, dict): + attributes[key] = Dataset.deserialise_json(value) + else: + attributes[key] = value + return Dataset(name, data, attributes) + def serialise_json(self): content = { - { - "name": self.name, - "data": "", # TODO: figure out QuantityType serialisation - "attributes": {} - } + "name": self.name, + "data": "", # TODO: figure out QuantityType serialisation + "attributes": {}, + "type": "dataset" } for key in self.attributes: value = self.attributes[key] @@ -64,14 +76,25 @@ def summary(self, indent_amount: int=0, indent=" "): return s + @staticmethod + def deserialise_json(json_data: dict) -> "Group": + name = json_data["name"] + children = {} + for key in json_data["children"]: + value = json_data["children"][key] + if value["type"] == "group": + children[key] = Group.deserialise_json(value) + else: + children[key] = Dataset.deserialise_json(value) + return Group(name, children) + def serialise_json(self): return { - { - "name": self.name, - "children": { - key: self.children[key].serialise_json() for key in self.children - } - } + "name": self.name, + "children": { + key: self.children[key].serialise_json() for key in self.children + }, + "type": "group" } class Function: From 5246352fd6820ed926336b05709ea82e4b82f55f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 27 Feb 2025 15:24:42 -0500 Subject: [PATCH 283/675] Deserialization for Quantity classes --- sasdata/quantities/quantity.py | 40 +++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 21717a59e..6b3b045b1 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1087,6 +1087,13 @@ def summary(self): return s + @staticmethod + def deserialise_json(json_data: dict) -> "QuantityHistory": + # TODO: figure out if this should be deserialise_json + operation_tree = Operation.deserialise(json_data["operation_tree"]) + references = {} # TODO: figure out QuantityType + return QuantityHistory(operation_tree, references) + def serialise_json(self): return { "operation_tree": self.operation_tree.serialise(), @@ -1198,6 +1205,17 @@ def in_si_with_standard_error(self): else: return self.in_si(), None + @staticmethod + def deserialise_json(json_data: dict) -> "Quantity": + value = None + units = Unit.deserialise_json(json_data["units"]) + standard_error = None + hash_seed = json_data["hash_seed"] + history = QuantityHistory.deserialise_json(json_data["history"]) + quantity = Quantity(value, units, standard_error, hash_seed) + quantity.history = history + return quantity + # TODO: fill out actual values def serialise_json(self): return { @@ -1446,8 +1464,19 @@ def with_standard_error(self, standard_error: Quantity): raise UnitError(f"Standard error units ({standard_error.units}) " f"are not compatible with value units ({self.units})") + @staticmethod + def deserialise_json(json_data: dict) -> "NamedQuantity": + name = json_data["name"] + value = None + units = Unit.deserialise_json(json_data["units"]) + standard_error = None + history = QuantityHistory.deserialise_json(json_data["history"]) + quantity = NamedQuantity(name, value, units, standard_error) + quantity.history = history + return quantity + def serialise_json(self): - quantity = super()._serialise_json() + quantity = super().serialise_json() quantity["name"] = self.name return quantity @@ -1481,3 +1510,12 @@ def variance(self) -> Quantity: self._variance_cache = self.history.variance_propagate(self.units) return self._variance_cache + + + @staticmethod + def deserialise_json(json_data: dict) -> "DerivedQuantity": + value = None # TODO: figure out QuantityType + units = Unit.deserialise_json(json_data["units"]) + history = QuantityHistory.deserialise_json(json_data["history"]) + quantity = DerivedQuantity(value, units, history) + return quantity From 074c8d6b089481fa85a98a55731ca7a730a7d91f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 27 Feb 2025 16:14:35 -0500 Subject: [PATCH 284/675] Add functionality to give and remove access to a file --- sasdata/fair_database/data/serializers.py | 5 +++++ sasdata/fair_database/data/urls.py | 1 + sasdata/fair_database/data/views.py | 14 +++++++++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 8e9c29743..820561a76 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -14,6 +14,11 @@ def validate(self, data): return data +class AccessManagementSerializer(serializers.Serializer): + username = serializers.CharField(max_length=200, required=False) + access = serializers.BooleanField() + + class DataSetSerializer(serializers.ModelSerializer): class Meta: model = DataSet diff --git a/sasdata/fair_database/data/urls.py b/sasdata/fair_database/data/urls.py index 17fa2adfd..615a38414 100644 --- a/sasdata/fair_database/data/urls.py +++ b/sasdata/fair_database/data/urls.py @@ -9,4 +9,5 @@ path("upload/", views.upload, name="upload data into db"), path("upload//", views.upload, name="update file in data"), path("/download/", views.download, name="download data from db"), + path("manage/", views.manage_access, name="manage access to files"), ] diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index ce2785895..d96998a5f 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -12,7 +12,7 @@ from rest_framework.response import Response from sasdata.dataloader.loader import Loader -from data.serializers import DataFileSerializer +from data.serializers import DataFileSerializer, AccessManagementSerializer from data.models import DataFile from data.forms import DataFileForm from fair_database import permissions @@ -118,6 +118,18 @@ def upload(request, data_id=None, version=None): return Response(return_data) +@api_view(["PUT"]) +def manage_access(request, data_id, version=None): + serializer = AccessManagementSerializer(request) + serializer.is_valid() + db = get_object_or_404(DataFile, id=data_id) + user = User.get_object_or_404(username=serializer.data["username"]) + if serializer.data["access"]: + db.users.add(user) + else: + db.users.remove(user) + + # downloads a file @api_view(["GET"]) def download(request, data_id, version=None): From 6e5f883eb01daf5210c335486d75f104d764c6a6 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 27 Feb 2025 16:22:32 -0500 Subject: [PATCH 285/675] Add response to access management view --- sasdata/fair_database/data/views.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index d96998a5f..e4dfc416f 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -128,6 +128,12 @@ def manage_access(request, data_id, version=None): db.users.add(user) else: db.users.remove(user) + response_data = { + "user": user.username, + "file": db.pk, + "access": serializer.data["access"], + } + return Response(response_data) # downloads a file From 25e83fa757c88671abe9379a50f54d2b5c201b2a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 27 Feb 2025 16:38:21 -0500 Subject: [PATCH 286/675] Tests for access granting/removal --- sasdata/fair_database/data/tests.py | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 10ebede87..007f2b545 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -196,3 +196,42 @@ def test_download_nonexistent(self): def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT) + + +class TestAccessManagement(TestCase): + def setUp(self): + self.user1 = User.objects.create_user(username="testUser", password="secret") + self.user2 = User.objects.create_user(username="testUser2", password="secret2") + private_test_data = DataFile.objects.create( + id=1, current_user=self.user1, file_name="cyl_400_40.txt", is_public=False + ) + private_test_data.file.save( + "cyl_400_40.txt", open(find("cyl_400_40.txt"), "rb") + ) + shared_test_data = DataFile.objects.create( + id=2, current_user=self.user1, file_name="cyl_400_20.txt", is_public=False + ) + shared_test_data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb")) + shared_test_data.users.add(self.user2) + self.client1 = APIClient() + self.client1.force_authenticate(self.user1) + self.client2 = APIClient() + self.client2.force_authenticate(self.user2) + + # test granting another user access to private data + def test_grant_access(self): + data = {"username": "testUser2", "access": True} + request1 = self.client1.put("/v1/data/manage/1/", data=data) + request2 = self.client2.get("/v1/data/load/1/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_200_OK) + + # test removing another user's access to private data + def test_remove_access(self): + data = {"username": "testUser2", "access": False} + request1 = self.client2.get("/v1/data/load/2/") + request2 = self.client1.put("/v1/data/manage/2/", data=data) + request3 = self.client2.get("/v1/data/load/2/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) From 0774b52504b2cba927a3e786ed2e6df181ffa243 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 27 Feb 2025 16:38:54 -0500 Subject: [PATCH 287/675] Fix errors in access management view --- sasdata/fair_database/data/urls.py | 2 +- sasdata/fair_database/data/views.py | 4 +- .../media/uploaded_files/cyl_400_20.txt | 22 ++++++++ .../uploaded_files/cyl_400_20_Ig5qu3J.txt | 22 ++++++++ .../uploaded_files/cyl_400_20_Viyk7c7.txt | 22 ++++++++ .../uploaded_files/cyl_400_20_q7R0RfJ.txt | 22 ++++++++ .../media/uploaded_files/cyl_400_40.txt | 56 +++++++++++++++++++ .../uploaded_files/cyl_400_40_DmgpxJO.txt | 56 +++++++++++++++++++ .../uploaded_files/cyl_400_40_JvFpGUR.txt | 56 +++++++++++++++++++ .../uploaded_files/cyl_400_40_pF4ocC6.txt | 56 +++++++++++++++++++ .../uploaded_files/cyl_400_40_w6ynvDd.txt | 56 +++++++++++++++++++ 11 files changed, 371 insertions(+), 3 deletions(-) create mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_20.txt create mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_20_Ig5qu3J.txt create mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_20_Viyk7c7.txt create mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_20_q7R0RfJ.txt create mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_40.txt create mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_40_DmgpxJO.txt create mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_40_JvFpGUR.txt create mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_40_pF4ocC6.txt create mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_40_w6ynvDd.txt diff --git a/sasdata/fair_database/data/urls.py b/sasdata/fair_database/data/urls.py index 615a38414..8721927f1 100644 --- a/sasdata/fair_database/data/urls.py +++ b/sasdata/fair_database/data/urls.py @@ -9,5 +9,5 @@ path("upload/", views.upload, name="upload data into db"), path("upload//", views.upload, name="update file in data"), path("/download/", views.download, name="download data from db"), - path("manage/", views.manage_access, name="manage access to files"), + path("manage//", views.manage_access, name="manage access to files"), ] diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index e4dfc416f..629cc124d 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -120,10 +120,10 @@ def upload(request, data_id=None, version=None): @api_view(["PUT"]) def manage_access(request, data_id, version=None): - serializer = AccessManagementSerializer(request) + serializer = AccessManagementSerializer(data=request.data) serializer.is_valid() db = get_object_or_404(DataFile, id=data_id) - user = User.get_object_or_404(username=serializer.data["username"]) + user = get_object_or_404(User, username=serializer.data["username"]) if serializer.data["access"]: db.users.add(user) else: diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_20.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_20.txt new file mode 100644 index 000000000..de7144653 --- /dev/null +++ b/sasdata/fair_database/media/uploaded_files/cyl_400_20.txt @@ -0,0 +1,22 @@ + +0 -1.#IND +0.025 125.852 +0.05 53.6662 +0.075 26.0733 +0.1 11.8935 +0.125 4.61714 +0.15 1.29983 +0.175 0.171347 +0.2 0.0417614 +0.225 0.172719 +0.25 0.247876 +0.275 0.20301 +0.3 0.104599 +0.325 0.0285595 +0.35 0.00213344 +0.375 0.0137511 +0.4 0.0312374 +0.425 0.0350328 +0.45 0.0243172 +0.475 0.00923067 +0.5 0.00121297 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_20_Ig5qu3J.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_20_Ig5qu3J.txt new file mode 100644 index 000000000..de7144653 --- /dev/null +++ b/sasdata/fair_database/media/uploaded_files/cyl_400_20_Ig5qu3J.txt @@ -0,0 +1,22 @@ + +0 -1.#IND +0.025 125.852 +0.05 53.6662 +0.075 26.0733 +0.1 11.8935 +0.125 4.61714 +0.15 1.29983 +0.175 0.171347 +0.2 0.0417614 +0.225 0.172719 +0.25 0.247876 +0.275 0.20301 +0.3 0.104599 +0.325 0.0285595 +0.35 0.00213344 +0.375 0.0137511 +0.4 0.0312374 +0.425 0.0350328 +0.45 0.0243172 +0.475 0.00923067 +0.5 0.00121297 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_20_Viyk7c7.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_20_Viyk7c7.txt new file mode 100644 index 000000000..de7144653 --- /dev/null +++ b/sasdata/fair_database/media/uploaded_files/cyl_400_20_Viyk7c7.txt @@ -0,0 +1,22 @@ + +0 -1.#IND +0.025 125.852 +0.05 53.6662 +0.075 26.0733 +0.1 11.8935 +0.125 4.61714 +0.15 1.29983 +0.175 0.171347 +0.2 0.0417614 +0.225 0.172719 +0.25 0.247876 +0.275 0.20301 +0.3 0.104599 +0.325 0.0285595 +0.35 0.00213344 +0.375 0.0137511 +0.4 0.0312374 +0.425 0.0350328 +0.45 0.0243172 +0.475 0.00923067 +0.5 0.00121297 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_20_q7R0RfJ.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_20_q7R0RfJ.txt new file mode 100644 index 000000000..de7144653 --- /dev/null +++ b/sasdata/fair_database/media/uploaded_files/cyl_400_20_q7R0RfJ.txt @@ -0,0 +1,22 @@ + +0 -1.#IND +0.025 125.852 +0.05 53.6662 +0.075 26.0733 +0.1 11.8935 +0.125 4.61714 +0.15 1.29983 +0.175 0.171347 +0.2 0.0417614 +0.225 0.172719 +0.25 0.247876 +0.275 0.20301 +0.3 0.104599 +0.325 0.0285595 +0.35 0.00213344 +0.375 0.0137511 +0.4 0.0312374 +0.425 0.0350328 +0.45 0.0243172 +0.475 0.00923067 +0.5 0.00121297 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_40.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_40.txt new file mode 100644 index 000000000..b533fa18e --- /dev/null +++ b/sasdata/fair_database/media/uploaded_files/cyl_400_40.txt @@ -0,0 +1,56 @@ + +0 -1.#IND +0.00925926 1246.59 +0.0185185 612.143 +0.0277778 361.142 +0.037037 211.601 +0.0462963 122.127 +0.0555556 65.2385 +0.0648148 30.8914 +0.0740741 12.4737 +0.0833333 3.51371 +0.0925926 0.721835 +0.101852 0.583607 +0.111111 1.31084 +0.12037 1.9432 +0.12963 1.94286 +0.138889 1.58912 +0.148148 0.987076 +0.157407 0.456678 +0.166667 0.147595 +0.175926 0.027441 +0.185185 0.0999575 +0.194444 0.198717 +0.203704 0.277667 +0.212963 0.288172 +0.222222 0.220056 +0.231481 0.139378 +0.240741 0.0541106 +0.25 0.0140158 +0.259259 0.0132187 +0.268519 0.0336301 +0.277778 0.0672911 +0.287037 0.0788983 +0.296296 0.0764438 +0.305556 0.0555445 +0.314815 0.0280548 +0.324074 0.0111798 +0.333333 0.00156156 +0.342593 0.00830883 +0.351852 0.0186266 +0.361111 0.0275426 +0.37037 0.03192 +0.37963 0.0255329 +0.388889 0.0175216 +0.398148 0.0073075 +0.407407 0.0016631 +0.416667 0.00224153 +0.425926 0.0051335 +0.435185 0.0112914 +0.444444 0.0138209 +0.453704 0.0137453 +0.462963 0.0106682 +0.472222 0.00532472 +0.481481 0.00230646 +0.490741 0.000335344 +0.5 0.00177224 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_40_DmgpxJO.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_40_DmgpxJO.txt new file mode 100644 index 000000000..b533fa18e --- /dev/null +++ b/sasdata/fair_database/media/uploaded_files/cyl_400_40_DmgpxJO.txt @@ -0,0 +1,56 @@ + +0 -1.#IND +0.00925926 1246.59 +0.0185185 612.143 +0.0277778 361.142 +0.037037 211.601 +0.0462963 122.127 +0.0555556 65.2385 +0.0648148 30.8914 +0.0740741 12.4737 +0.0833333 3.51371 +0.0925926 0.721835 +0.101852 0.583607 +0.111111 1.31084 +0.12037 1.9432 +0.12963 1.94286 +0.138889 1.58912 +0.148148 0.987076 +0.157407 0.456678 +0.166667 0.147595 +0.175926 0.027441 +0.185185 0.0999575 +0.194444 0.198717 +0.203704 0.277667 +0.212963 0.288172 +0.222222 0.220056 +0.231481 0.139378 +0.240741 0.0541106 +0.25 0.0140158 +0.259259 0.0132187 +0.268519 0.0336301 +0.277778 0.0672911 +0.287037 0.0788983 +0.296296 0.0764438 +0.305556 0.0555445 +0.314815 0.0280548 +0.324074 0.0111798 +0.333333 0.00156156 +0.342593 0.00830883 +0.351852 0.0186266 +0.361111 0.0275426 +0.37037 0.03192 +0.37963 0.0255329 +0.388889 0.0175216 +0.398148 0.0073075 +0.407407 0.0016631 +0.416667 0.00224153 +0.425926 0.0051335 +0.435185 0.0112914 +0.444444 0.0138209 +0.453704 0.0137453 +0.462963 0.0106682 +0.472222 0.00532472 +0.481481 0.00230646 +0.490741 0.000335344 +0.5 0.00177224 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_40_JvFpGUR.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_40_JvFpGUR.txt new file mode 100644 index 000000000..b533fa18e --- /dev/null +++ b/sasdata/fair_database/media/uploaded_files/cyl_400_40_JvFpGUR.txt @@ -0,0 +1,56 @@ + +0 -1.#IND +0.00925926 1246.59 +0.0185185 612.143 +0.0277778 361.142 +0.037037 211.601 +0.0462963 122.127 +0.0555556 65.2385 +0.0648148 30.8914 +0.0740741 12.4737 +0.0833333 3.51371 +0.0925926 0.721835 +0.101852 0.583607 +0.111111 1.31084 +0.12037 1.9432 +0.12963 1.94286 +0.138889 1.58912 +0.148148 0.987076 +0.157407 0.456678 +0.166667 0.147595 +0.175926 0.027441 +0.185185 0.0999575 +0.194444 0.198717 +0.203704 0.277667 +0.212963 0.288172 +0.222222 0.220056 +0.231481 0.139378 +0.240741 0.0541106 +0.25 0.0140158 +0.259259 0.0132187 +0.268519 0.0336301 +0.277778 0.0672911 +0.287037 0.0788983 +0.296296 0.0764438 +0.305556 0.0555445 +0.314815 0.0280548 +0.324074 0.0111798 +0.333333 0.00156156 +0.342593 0.00830883 +0.351852 0.0186266 +0.361111 0.0275426 +0.37037 0.03192 +0.37963 0.0255329 +0.388889 0.0175216 +0.398148 0.0073075 +0.407407 0.0016631 +0.416667 0.00224153 +0.425926 0.0051335 +0.435185 0.0112914 +0.444444 0.0138209 +0.453704 0.0137453 +0.462963 0.0106682 +0.472222 0.00532472 +0.481481 0.00230646 +0.490741 0.000335344 +0.5 0.00177224 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_40_pF4ocC6.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_40_pF4ocC6.txt new file mode 100644 index 000000000..b533fa18e --- /dev/null +++ b/sasdata/fair_database/media/uploaded_files/cyl_400_40_pF4ocC6.txt @@ -0,0 +1,56 @@ + +0 -1.#IND +0.00925926 1246.59 +0.0185185 612.143 +0.0277778 361.142 +0.037037 211.601 +0.0462963 122.127 +0.0555556 65.2385 +0.0648148 30.8914 +0.0740741 12.4737 +0.0833333 3.51371 +0.0925926 0.721835 +0.101852 0.583607 +0.111111 1.31084 +0.12037 1.9432 +0.12963 1.94286 +0.138889 1.58912 +0.148148 0.987076 +0.157407 0.456678 +0.166667 0.147595 +0.175926 0.027441 +0.185185 0.0999575 +0.194444 0.198717 +0.203704 0.277667 +0.212963 0.288172 +0.222222 0.220056 +0.231481 0.139378 +0.240741 0.0541106 +0.25 0.0140158 +0.259259 0.0132187 +0.268519 0.0336301 +0.277778 0.0672911 +0.287037 0.0788983 +0.296296 0.0764438 +0.305556 0.0555445 +0.314815 0.0280548 +0.324074 0.0111798 +0.333333 0.00156156 +0.342593 0.00830883 +0.351852 0.0186266 +0.361111 0.0275426 +0.37037 0.03192 +0.37963 0.0255329 +0.388889 0.0175216 +0.398148 0.0073075 +0.407407 0.0016631 +0.416667 0.00224153 +0.425926 0.0051335 +0.435185 0.0112914 +0.444444 0.0138209 +0.453704 0.0137453 +0.462963 0.0106682 +0.472222 0.00532472 +0.481481 0.00230646 +0.490741 0.000335344 +0.5 0.00177224 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_40_w6ynvDd.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_40_w6ynvDd.txt new file mode 100644 index 000000000..b533fa18e --- /dev/null +++ b/sasdata/fair_database/media/uploaded_files/cyl_400_40_w6ynvDd.txt @@ -0,0 +1,56 @@ + +0 -1.#IND +0.00925926 1246.59 +0.0185185 612.143 +0.0277778 361.142 +0.037037 211.601 +0.0462963 122.127 +0.0555556 65.2385 +0.0648148 30.8914 +0.0740741 12.4737 +0.0833333 3.51371 +0.0925926 0.721835 +0.101852 0.583607 +0.111111 1.31084 +0.12037 1.9432 +0.12963 1.94286 +0.138889 1.58912 +0.148148 0.987076 +0.157407 0.456678 +0.166667 0.147595 +0.175926 0.027441 +0.185185 0.0999575 +0.194444 0.198717 +0.203704 0.277667 +0.212963 0.288172 +0.222222 0.220056 +0.231481 0.139378 +0.240741 0.0541106 +0.25 0.0140158 +0.259259 0.0132187 +0.268519 0.0336301 +0.277778 0.0672911 +0.287037 0.0788983 +0.296296 0.0764438 +0.305556 0.0555445 +0.314815 0.0280548 +0.324074 0.0111798 +0.333333 0.00156156 +0.342593 0.00830883 +0.351852 0.0186266 +0.361111 0.0275426 +0.37037 0.03192 +0.37963 0.0255329 +0.388889 0.0175216 +0.398148 0.0073075 +0.407407 0.0016631 +0.416667 0.00224153 +0.425926 0.0051335 +0.435185 0.0112914 +0.444444 0.0138209 +0.453704 0.0137453 +0.462963 0.0106682 +0.472222 0.00532472 +0.481481 0.00230646 +0.490741 0.000335344 +0.5 0.00177224 From db3aa23d182daf3208d991bf17bb7cb87a1a9344 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 28 Feb 2025 12:00:09 -0500 Subject: [PATCH 288/675] More tests for access management --- sasdata/fair_database/data/tests.py | 46 +++++++++++++-- .../media/uploaded_files/cyl_400_20.txt | 22 -------- .../uploaded_files/cyl_400_20_Ig5qu3J.txt | 22 -------- .../uploaded_files/cyl_400_20_Viyk7c7.txt | 22 -------- .../uploaded_files/cyl_400_20_q7R0RfJ.txt | 22 -------- .../media/uploaded_files/cyl_400_40.txt | 56 ------------------- .../uploaded_files/cyl_400_40_DmgpxJO.txt | 56 ------------------- .../uploaded_files/cyl_400_40_JvFpGUR.txt | 56 ------------------- .../uploaded_files/cyl_400_40_pF4ocC6.txt | 56 ------------------- .../uploaded_files/cyl_400_40_w6ynvDd.txt | 56 ------------------- 10 files changed, 41 insertions(+), 373 deletions(-) delete mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_20.txt delete mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_20_Ig5qu3J.txt delete mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_20_Viyk7c7.txt delete mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_20_q7R0RfJ.txt delete mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_40.txt delete mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_40_DmgpxJO.txt delete mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_40_JvFpGUR.txt delete mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_40_pF4ocC6.txt delete mode 100644 sasdata/fair_database/media/uploaded_files/cyl_400_40_w6ynvDd.txt diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 007f2b545..368eb55ee 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -202,17 +202,19 @@ class TestAccessManagement(TestCase): def setUp(self): self.user1 = User.objects.create_user(username="testUser", password="secret") self.user2 = User.objects.create_user(username="testUser2", password="secret2") - private_test_data = DataFile.objects.create( + self.private_test_data = DataFile.objects.create( id=1, current_user=self.user1, file_name="cyl_400_40.txt", is_public=False ) - private_test_data.file.save( + self.private_test_data.file.save( "cyl_400_40.txt", open(find("cyl_400_40.txt"), "rb") ) - shared_test_data = DataFile.objects.create( + self.shared_test_data = DataFile.objects.create( id=2, current_user=self.user1, file_name="cyl_400_20.txt", is_public=False ) - shared_test_data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb")) - shared_test_data.users.add(self.user2) + self.shared_test_data.file.save( + "cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb") + ) + self.shared_test_data.users.add(self.user2) self.client1 = APIClient() self.client1.force_authenticate(self.user1) self.client2 = APIClient() @@ -235,3 +237,37 @@ def test_remove_access(self): self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) + + def test_remove_no_access(self): + data = {"username": "testUser2", "access": False} + request1 = self.client2.get("/v1/data/load/1/") + request2 = self.client1.put("/v1/data/manage/1/", data=data) + request3 = self.client2.get("/v1/data/load/1/") + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) + + def test_cant_revoke_own_access(self): + data = {"username": "testUser", "access": False} + request1 = self.client1.put("/v1/data/manage/1/", data=data) + request2 = self.client1.get("/v1/data/load/1/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_200_OK) + + def test_grant_existing_access(self): + data = {"username": "testUser2", "access": True} + request1 = self.client2.get("/v1/data/load/2/") + request2 = self.client1.put("/v1/data/manage/2/", data=data) + request3 = self.client2.get("/v1/data/load/2/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertEqual(request3.status_code, status.HTTP_200_OK) + + def test_no_edit_access(self): + data = {"is_public": True} + request = self.client2.put("/v1/data/upload/2/", data=data) + self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + self.assertFalse(self.shared_test_data.is_public) + + def tearDown(self): + shutil.rmtree(settings.MEDIA_ROOT) diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_20.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_20.txt deleted file mode 100644 index de7144653..000000000 --- a/sasdata/fair_database/media/uploaded_files/cyl_400_20.txt +++ /dev/null @@ -1,22 +0,0 @@ - -0 -1.#IND -0.025 125.852 -0.05 53.6662 -0.075 26.0733 -0.1 11.8935 -0.125 4.61714 -0.15 1.29983 -0.175 0.171347 -0.2 0.0417614 -0.225 0.172719 -0.25 0.247876 -0.275 0.20301 -0.3 0.104599 -0.325 0.0285595 -0.35 0.00213344 -0.375 0.0137511 -0.4 0.0312374 -0.425 0.0350328 -0.45 0.0243172 -0.475 0.00923067 -0.5 0.00121297 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_20_Ig5qu3J.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_20_Ig5qu3J.txt deleted file mode 100644 index de7144653..000000000 --- a/sasdata/fair_database/media/uploaded_files/cyl_400_20_Ig5qu3J.txt +++ /dev/null @@ -1,22 +0,0 @@ - -0 -1.#IND -0.025 125.852 -0.05 53.6662 -0.075 26.0733 -0.1 11.8935 -0.125 4.61714 -0.15 1.29983 -0.175 0.171347 -0.2 0.0417614 -0.225 0.172719 -0.25 0.247876 -0.275 0.20301 -0.3 0.104599 -0.325 0.0285595 -0.35 0.00213344 -0.375 0.0137511 -0.4 0.0312374 -0.425 0.0350328 -0.45 0.0243172 -0.475 0.00923067 -0.5 0.00121297 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_20_Viyk7c7.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_20_Viyk7c7.txt deleted file mode 100644 index de7144653..000000000 --- a/sasdata/fair_database/media/uploaded_files/cyl_400_20_Viyk7c7.txt +++ /dev/null @@ -1,22 +0,0 @@ - -0 -1.#IND -0.025 125.852 -0.05 53.6662 -0.075 26.0733 -0.1 11.8935 -0.125 4.61714 -0.15 1.29983 -0.175 0.171347 -0.2 0.0417614 -0.225 0.172719 -0.25 0.247876 -0.275 0.20301 -0.3 0.104599 -0.325 0.0285595 -0.35 0.00213344 -0.375 0.0137511 -0.4 0.0312374 -0.425 0.0350328 -0.45 0.0243172 -0.475 0.00923067 -0.5 0.00121297 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_20_q7R0RfJ.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_20_q7R0RfJ.txt deleted file mode 100644 index de7144653..000000000 --- a/sasdata/fair_database/media/uploaded_files/cyl_400_20_q7R0RfJ.txt +++ /dev/null @@ -1,22 +0,0 @@ - -0 -1.#IND -0.025 125.852 -0.05 53.6662 -0.075 26.0733 -0.1 11.8935 -0.125 4.61714 -0.15 1.29983 -0.175 0.171347 -0.2 0.0417614 -0.225 0.172719 -0.25 0.247876 -0.275 0.20301 -0.3 0.104599 -0.325 0.0285595 -0.35 0.00213344 -0.375 0.0137511 -0.4 0.0312374 -0.425 0.0350328 -0.45 0.0243172 -0.475 0.00923067 -0.5 0.00121297 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_40.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_40.txt deleted file mode 100644 index b533fa18e..000000000 --- a/sasdata/fair_database/media/uploaded_files/cyl_400_40.txt +++ /dev/null @@ -1,56 +0,0 @@ - -0 -1.#IND -0.00925926 1246.59 -0.0185185 612.143 -0.0277778 361.142 -0.037037 211.601 -0.0462963 122.127 -0.0555556 65.2385 -0.0648148 30.8914 -0.0740741 12.4737 -0.0833333 3.51371 -0.0925926 0.721835 -0.101852 0.583607 -0.111111 1.31084 -0.12037 1.9432 -0.12963 1.94286 -0.138889 1.58912 -0.148148 0.987076 -0.157407 0.456678 -0.166667 0.147595 -0.175926 0.027441 -0.185185 0.0999575 -0.194444 0.198717 -0.203704 0.277667 -0.212963 0.288172 -0.222222 0.220056 -0.231481 0.139378 -0.240741 0.0541106 -0.25 0.0140158 -0.259259 0.0132187 -0.268519 0.0336301 -0.277778 0.0672911 -0.287037 0.0788983 -0.296296 0.0764438 -0.305556 0.0555445 -0.314815 0.0280548 -0.324074 0.0111798 -0.333333 0.00156156 -0.342593 0.00830883 -0.351852 0.0186266 -0.361111 0.0275426 -0.37037 0.03192 -0.37963 0.0255329 -0.388889 0.0175216 -0.398148 0.0073075 -0.407407 0.0016631 -0.416667 0.00224153 -0.425926 0.0051335 -0.435185 0.0112914 -0.444444 0.0138209 -0.453704 0.0137453 -0.462963 0.0106682 -0.472222 0.00532472 -0.481481 0.00230646 -0.490741 0.000335344 -0.5 0.00177224 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_40_DmgpxJO.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_40_DmgpxJO.txt deleted file mode 100644 index b533fa18e..000000000 --- a/sasdata/fair_database/media/uploaded_files/cyl_400_40_DmgpxJO.txt +++ /dev/null @@ -1,56 +0,0 @@ - -0 -1.#IND -0.00925926 1246.59 -0.0185185 612.143 -0.0277778 361.142 -0.037037 211.601 -0.0462963 122.127 -0.0555556 65.2385 -0.0648148 30.8914 -0.0740741 12.4737 -0.0833333 3.51371 -0.0925926 0.721835 -0.101852 0.583607 -0.111111 1.31084 -0.12037 1.9432 -0.12963 1.94286 -0.138889 1.58912 -0.148148 0.987076 -0.157407 0.456678 -0.166667 0.147595 -0.175926 0.027441 -0.185185 0.0999575 -0.194444 0.198717 -0.203704 0.277667 -0.212963 0.288172 -0.222222 0.220056 -0.231481 0.139378 -0.240741 0.0541106 -0.25 0.0140158 -0.259259 0.0132187 -0.268519 0.0336301 -0.277778 0.0672911 -0.287037 0.0788983 -0.296296 0.0764438 -0.305556 0.0555445 -0.314815 0.0280548 -0.324074 0.0111798 -0.333333 0.00156156 -0.342593 0.00830883 -0.351852 0.0186266 -0.361111 0.0275426 -0.37037 0.03192 -0.37963 0.0255329 -0.388889 0.0175216 -0.398148 0.0073075 -0.407407 0.0016631 -0.416667 0.00224153 -0.425926 0.0051335 -0.435185 0.0112914 -0.444444 0.0138209 -0.453704 0.0137453 -0.462963 0.0106682 -0.472222 0.00532472 -0.481481 0.00230646 -0.490741 0.000335344 -0.5 0.00177224 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_40_JvFpGUR.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_40_JvFpGUR.txt deleted file mode 100644 index b533fa18e..000000000 --- a/sasdata/fair_database/media/uploaded_files/cyl_400_40_JvFpGUR.txt +++ /dev/null @@ -1,56 +0,0 @@ - -0 -1.#IND -0.00925926 1246.59 -0.0185185 612.143 -0.0277778 361.142 -0.037037 211.601 -0.0462963 122.127 -0.0555556 65.2385 -0.0648148 30.8914 -0.0740741 12.4737 -0.0833333 3.51371 -0.0925926 0.721835 -0.101852 0.583607 -0.111111 1.31084 -0.12037 1.9432 -0.12963 1.94286 -0.138889 1.58912 -0.148148 0.987076 -0.157407 0.456678 -0.166667 0.147595 -0.175926 0.027441 -0.185185 0.0999575 -0.194444 0.198717 -0.203704 0.277667 -0.212963 0.288172 -0.222222 0.220056 -0.231481 0.139378 -0.240741 0.0541106 -0.25 0.0140158 -0.259259 0.0132187 -0.268519 0.0336301 -0.277778 0.0672911 -0.287037 0.0788983 -0.296296 0.0764438 -0.305556 0.0555445 -0.314815 0.0280548 -0.324074 0.0111798 -0.333333 0.00156156 -0.342593 0.00830883 -0.351852 0.0186266 -0.361111 0.0275426 -0.37037 0.03192 -0.37963 0.0255329 -0.388889 0.0175216 -0.398148 0.0073075 -0.407407 0.0016631 -0.416667 0.00224153 -0.425926 0.0051335 -0.435185 0.0112914 -0.444444 0.0138209 -0.453704 0.0137453 -0.462963 0.0106682 -0.472222 0.00532472 -0.481481 0.00230646 -0.490741 0.000335344 -0.5 0.00177224 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_40_pF4ocC6.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_40_pF4ocC6.txt deleted file mode 100644 index b533fa18e..000000000 --- a/sasdata/fair_database/media/uploaded_files/cyl_400_40_pF4ocC6.txt +++ /dev/null @@ -1,56 +0,0 @@ - -0 -1.#IND -0.00925926 1246.59 -0.0185185 612.143 -0.0277778 361.142 -0.037037 211.601 -0.0462963 122.127 -0.0555556 65.2385 -0.0648148 30.8914 -0.0740741 12.4737 -0.0833333 3.51371 -0.0925926 0.721835 -0.101852 0.583607 -0.111111 1.31084 -0.12037 1.9432 -0.12963 1.94286 -0.138889 1.58912 -0.148148 0.987076 -0.157407 0.456678 -0.166667 0.147595 -0.175926 0.027441 -0.185185 0.0999575 -0.194444 0.198717 -0.203704 0.277667 -0.212963 0.288172 -0.222222 0.220056 -0.231481 0.139378 -0.240741 0.0541106 -0.25 0.0140158 -0.259259 0.0132187 -0.268519 0.0336301 -0.277778 0.0672911 -0.287037 0.0788983 -0.296296 0.0764438 -0.305556 0.0555445 -0.314815 0.0280548 -0.324074 0.0111798 -0.333333 0.00156156 -0.342593 0.00830883 -0.351852 0.0186266 -0.361111 0.0275426 -0.37037 0.03192 -0.37963 0.0255329 -0.388889 0.0175216 -0.398148 0.0073075 -0.407407 0.0016631 -0.416667 0.00224153 -0.425926 0.0051335 -0.435185 0.0112914 -0.444444 0.0138209 -0.453704 0.0137453 -0.462963 0.0106682 -0.472222 0.00532472 -0.481481 0.00230646 -0.490741 0.000335344 -0.5 0.00177224 diff --git a/sasdata/fair_database/media/uploaded_files/cyl_400_40_w6ynvDd.txt b/sasdata/fair_database/media/uploaded_files/cyl_400_40_w6ynvDd.txt deleted file mode 100644 index b533fa18e..000000000 --- a/sasdata/fair_database/media/uploaded_files/cyl_400_40_w6ynvDd.txt +++ /dev/null @@ -1,56 +0,0 @@ - -0 -1.#IND -0.00925926 1246.59 -0.0185185 612.143 -0.0277778 361.142 -0.037037 211.601 -0.0462963 122.127 -0.0555556 65.2385 -0.0648148 30.8914 -0.0740741 12.4737 -0.0833333 3.51371 -0.0925926 0.721835 -0.101852 0.583607 -0.111111 1.31084 -0.12037 1.9432 -0.12963 1.94286 -0.138889 1.58912 -0.148148 0.987076 -0.157407 0.456678 -0.166667 0.147595 -0.175926 0.027441 -0.185185 0.0999575 -0.194444 0.198717 -0.203704 0.277667 -0.212963 0.288172 -0.222222 0.220056 -0.231481 0.139378 -0.240741 0.0541106 -0.25 0.0140158 -0.259259 0.0132187 -0.268519 0.0336301 -0.277778 0.0672911 -0.287037 0.0788983 -0.296296 0.0764438 -0.305556 0.0555445 -0.314815 0.0280548 -0.324074 0.0111798 -0.333333 0.00156156 -0.342593 0.00830883 -0.351852 0.0186266 -0.361111 0.0275426 -0.37037 0.03192 -0.37963 0.0255329 -0.388889 0.0175216 -0.398148 0.0073075 -0.407407 0.0016631 -0.416667 0.00224153 -0.425926 0.0051335 -0.435185 0.0112914 -0.444444 0.0138209 -0.453704 0.0137453 -0.462963 0.0106682 -0.472222 0.00532472 -0.481481 0.00230646 -0.490741 0.000335344 -0.5 0.00177224 From 4d8d43aa72d728154f06f6c77f9675d44871bbb3 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 3 Mar 2025 11:15:39 -0500 Subject: [PATCH 289/675] Remove redundant code in permissions --- sasdata/fair_database/fair_database/permissions.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/sasdata/fair_database/fair_database/permissions.py b/sasdata/fair_database/fair_database/permissions.py index 224e4c00b..89e677be3 100644 --- a/sasdata/fair_database/fair_database/permissions.py +++ b/sasdata/fair_database/fair_database/permissions.py @@ -26,11 +26,4 @@ def has_object_permission(self, request, view, obj): def check_permissions(request, obj): - if request.method == "GET": - if obj.is_public or has_access(request, obj): - return True - elif request.method == "DELETE": - if obj.is_private and is_owner(request, obj): - return True - else: - return is_owner(request, obj) + return DataPermission().has_object_permission(request, None, obj) From 10a1662ee3be81e95d5f347bae46e95312b527ab Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 3 Mar 2025 15:37:32 -0500 Subject: [PATCH 290/675] QuantityType serialisation for some types --- sasdata/data.py | 1 - sasdata/quantities/quantity.py | 25 +++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 3ff477ab0..d4cb03321 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -86,7 +86,6 @@ def deserialise_json(json_data: dict) -> "SasData": def serialise(self) -> str: return json.dumps(self._serialise_json()) - # TODO: replace with serialization methods when written def _serialise_json(self) -> dict[str, Any]: return { "name": self.name, diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 6b3b045b1..e507820b2 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -3,6 +3,7 @@ from typing import Self import numpy as np +from docutils.frontend import validate_ternary from numpy._typing import ArrayLike from sasdata.quantities import units @@ -997,6 +998,12 @@ def hash_data_via_numpy(*data: ArrayLike): QuantityType = TypeVar("QuantityType") +# TODO: figure out how to handle np.ndarray serialization (save as file or otherwise) +def quantity_type_serialisation(var): + if isinstance(var, (str, int, float)): + return var + return None + class QuantityHistory: """ Class that holds the information for keeping track of operations done on quantities """ @@ -1089,9 +1096,11 @@ def summary(self): @staticmethod def deserialise_json(json_data: dict) -> "QuantityHistory": - # TODO: figure out if this should be deserialise_json operation_tree = Operation.deserialise(json_data["operation_tree"]) - references = {} # TODO: figure out QuantityType + references = { + key: Quantity.deserialise_json(json_data["references"][key]) + for key in json_data["references"] + } return QuantityHistory(operation_tree, references) def serialise_json(self): @@ -1207,9 +1216,9 @@ def in_si_with_standard_error(self): @staticmethod def deserialise_json(json_data: dict) -> "Quantity": - value = None + value = None # TODO QuantityType deserialisation units = Unit.deserialise_json(json_data["units"]) - standard_error = None + standard_error = None #TODO QuantityType deserialisation hash_seed = json_data["hash_seed"] history = QuantityHistory.deserialise_json(json_data["history"]) quantity = Quantity(value, units, standard_error, hash_seed) @@ -1219,9 +1228,9 @@ def deserialise_json(json_data: dict) -> "Quantity": # TODO: fill out actual values def serialise_json(self): return { - "value": "", # figure out QuantityType serialisation + "value": quantity_type_serialisation(self.value), "units": self.units.serialise_json(), # Unit serialisation - "standard_error": "", # also QuantityType serialisation + "standard_error": quantity_type_serialisation(self._variance ** 0.5), "hash_seed": self._hash_seed, # is this just a string? "history": self.history.serialise_json() } @@ -1467,9 +1476,9 @@ def with_standard_error(self, standard_error: Quantity): @staticmethod def deserialise_json(json_data: dict) -> "NamedQuantity": name = json_data["name"] - value = None + value = None # TODO: figure out QuantityType deserialization units = Unit.deserialise_json(json_data["units"]) - standard_error = None + standard_error = None # TODO: QuantityType deserialization history = QuantityHistory.deserialise_json(json_data["history"]) quantity = NamedQuantity(name, value, units, standard_error) quantity.history = history From ae9abfb2337ce7e07c84d00faa9ad5cb97fa8824 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 3 Mar 2025 15:39:09 -0500 Subject: [PATCH 291/675] Allow viewing who has access to a file --- sasdata/fair_database/data/views.py | 41 +++++++++++++++++++---------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 629cc124d..8069b0a31 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -118,22 +118,35 @@ def upload(request, data_id=None, version=None): return Response(return_data) -@api_view(["PUT"]) +# view or control who has access to a file +@api_view(["GET", "PUT"]) def manage_access(request, data_id, version=None): - serializer = AccessManagementSerializer(data=request.data) - serializer.is_valid() db = get_object_or_404(DataFile, id=data_id) - user = get_object_or_404(User, username=serializer.data["username"]) - if serializer.data["access"]: - db.users.add(user) - else: - db.users.remove(user) - response_data = { - "user": user.username, - "file": db.pk, - "access": serializer.data["access"], - } - return Response(response_data) + if not permissions.is_owner(request, db): + return HttpResponseForbidden("Must be the data owner to manage access") + if request.method == "GET": + response_data = { + "file": db.pk, + "file_name": db.file_name, + "users": [user.username for user in db.users], + } + return Response(response_data) + elif request.method == "PUT": + serializer = AccessManagementSerializer(data=request.data) + serializer.is_valid() + user = get_object_or_404(User, username=serializer.data["username"]) + if serializer.data["access"]: + db.users.add(user) + else: + db.users.remove(user) + response_data = { + "user": user.username, + "file": db.pk, + "file_name": db.file_name, + "access": serializer.data["access"], + } + return Response(response_data) + return HttpResponseBadRequest() # downloads a file From 5a7dbd61930bde9fff6f588f81c563ed79cbd2dc Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 3 Mar 2025 15:51:29 -0500 Subject: [PATCH 292/675] Test listing users with access to a file --- sasdata/fair_database/data/tests.py | 14 ++++++++++++++ sasdata/fair_database/data/views.py | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 368eb55ee..2df0b35e3 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -220,6 +220,20 @@ def setUp(self): self.client2 = APIClient() self.client2.force_authenticate(self.user2) + # test viewing no one with access + def test_view_no_access(self): + request = self.client1.get("/v1/data/manage/1/") + data = {"file": 1, "file_name": "cyl_400_40.txt", "users": []} + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(request.data, data) + + # test viewing list of users with access + def test_view_access(self): + request = self.client1.get("/v1/data/manage/2/") + data = {"file": 2, "file_name": "cyl_400_20.txt", "users": ["testUser2"]} + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(request.data, data) + # test granting another user access to private data def test_grant_access(self): data = {"username": "testUser2", "access": True} diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 8069b0a31..8bf488825 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -128,7 +128,7 @@ def manage_access(request, data_id, version=None): response_data = { "file": db.pk, "file_name": db.file_name, - "users": [user.username for user in db.users], + "users": [user.username for user in db.users.all()], } return Response(response_data) elif request.method == "PUT": From 409bd1cc232059fe67a9bca642ee864fa187f37f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 3 Mar 2025 16:00:47 -0500 Subject: [PATCH 293/675] Test permissions on file access management views --- sasdata/fair_database/data/tests.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 2df0b35e3..df15deaa3 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -283,5 +283,23 @@ def test_no_edit_access(self): self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) self.assertFalse(self.shared_test_data.is_public) + def test_only_view_access_to_owned_file(self): + request1 = self.client2.get("/v1/data/manage/1/") + request2 = self.client2.get("/v1/data/manage/2/") + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + + def test_only_edit_access_to_owned_file(self): + data1 = {"username": "testUser2", "access": True} + data2 = {"username": "testUser1", "access": False} + request1 = self.client2.put("/v1/data/manage/1/", data=data1) + request2 = self.client2.put("/v1/data/manage/2/", data=data2) + request3 = self.client2.get("/v1/data/load/1/") + request4 = self.client1.get("/v1/data/load/2/") + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request4.status_code, status.HTTP_200_OK) + def tearDown(self): shutil.rmtree(settings.MEDIA_ROOT) From 9e530005a4004ad4a510bbc8bc2f9916ac07b47a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 5 Mar 2025 10:35:48 -0500 Subject: [PATCH 294/675] Serialize variance not standard deviation --- sasdata/quantities/quantity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index e507820b2..8ddf15b3d 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1230,7 +1230,7 @@ def serialise_json(self): return { "value": quantity_type_serialisation(self.value), "units": self.units.serialise_json(), # Unit serialisation - "standard_error": quantity_type_serialisation(self._variance ** 0.5), + "variance": quantity_type_serialisation(self._variance), "hash_seed": self._hash_seed, # is this just a string? "history": self.history.serialise_json() } From 6290459728c968b9613233f1c6888ba9e82789a9 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 5 Mar 2025 11:01:58 -0500 Subject: [PATCH 295/675] Finish models pending later changes --- ...definition_metadata_instrument_and_more.py | 52 +++++++++++++++++++ sasdata/fair_database/data/models.py | 51 +++++++++++++----- 2 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0009_metadata_definition_metadata_instrument_and_more.py diff --git a/sasdata/fair_database/data/migrations/0009_metadata_definition_metadata_instrument_and_more.py b/sasdata/fair_database/data/migrations/0009_metadata_definition_metadata_instrument_and_more.py new file mode 100644 index 000000000..e6ae8a4ac --- /dev/null +++ b/sasdata/fair_database/data/migrations/0009_metadata_definition_metadata_instrument_and_more.py @@ -0,0 +1,52 @@ +# Generated by Django 5.1.6 on 2025-03-05 15:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0008_alter_datafile_users_alter_dataset_users"), + ] + + operations = [ + migrations.AddField( + model_name="metadata", + name="definition", + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name="metadata", + name="instrument", + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name="metadata", + name="process", + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name="metadata", + name="raw_metadata", + field=models.JSONField(default=dict), + ), + migrations.AddField( + model_name="metadata", + name="run", + field=models.CharField(blank=True, max_length=500, null=True), + ), + migrations.AddField( + model_name="metadata", + name="sample", + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name="metadata", + name="title", + field=models.CharField(default="Title", max_length=500), + ), + migrations.AddField( + model_name="metadata", + name="transmission_spectrum", + field=models.JSONField(blank=True, null=True), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 2ad7b6641..797ba7563 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -44,27 +44,26 @@ class DataFile(Data): class DataSet(Data): """Database model for a set of data and associated metadata.""" + # TODO: Update when plan for this is finished. + # dataset name name = models.CharField(max_length=200) # associated files files = models.ManyToManyField(DataFile) - # metadata + # metadata - maybe a foreign key? metadata = models.OneToOneField("MetaData", on_delete=models.CASCADE) # ordinate - # ordinate = models.JSONField() + # ordinate = models.ForeignKey("Quantity", on_delete=models.CASCADE) # abscissae - # abscissae = models.JSONField() + # abscissae = models.ManyToManyField("Quantity", on_delete=models.CASCADE) - # data contents + # data contents - maybe ManyToManyField # data_contents = models.JSONField() - # metadata - # raw_metadata = models.JSONField() - class Quantity(models.Model): """Database model for data quantities such as the ordinate and abscissae.""" @@ -85,13 +84,30 @@ class Quantity(models.Model): class MetaData(models.Model): """Database model for scattering metadata""" - # Associated data set - # dataset = models.OneToOneField( - # "DataSet", on_delete=models.CASCADE, related_name="associated_data" - # ) + # title + title = models.CharField(max_length=500, default="Title") + + # run + # TODO: find out if this is expected to be long + run = models.CharField(max_length=500, blank=True, null=True) + + # definition + definition = models.TextField(blank=True, null=True) + + # instrument + instrument = models.JSONField(blank=True, null=True) + + # process + process = models.JSONField(blank=True, null=True) + # sample + sample = models.JSONField(blank=True, null=True) -"""Database model for group of DataSets associated by a varying parameter.""" + # transmission spectrum + transmission_spectrum = models.JSONField(blank=True, null=True) + + # raw metadata (for recreating in SasView only) + raw_metadata = models.JSONField(default=dict) class OperationTree(models.Model): @@ -117,4 +133,15 @@ class Session(Data): # operation tree # operations = models.ForeignKey(OperationTree, on_delete=models.CASCADE) + +class PublishedState(): + """Database model for a project published state.""" + + # published + published = models.BooleanField(default=False) + + # doi + doi = models.URLField() + + ''' From cca121b38f99c93439bca543c4431deae7261602 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 5 Mar 2025 11:20:51 -0500 Subject: [PATCH 296/675] Add to documentation on future ORCID integration --- sasdata/fair_database/fair_database/settings.py | 2 ++ sasdata/fair_database/user_app/urls.py | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index 885e3c903..6918a4de2 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -113,6 +113,8 @@ ACCOUNT_EMAIL_VERIFICATION = "none" # to enable ORCID, register for credentials through ORCID and fill out client_id and secret +# https://info.orcid.org/documentation/integration-guide/ +# https://docs.allauth.org/en/latest/socialaccount/index.html SOCIALACCOUNT_PROVIDERS = { "orcid": { "APPS": [ diff --git a/sasdata/fair_database/user_app/urls.py b/sasdata/fair_database/user_app/urls.py index 808cbfcee..cf44e8d6c 100644 --- a/sasdata/fair_database/user_app/urls.py +++ b/sasdata/fair_database/user_app/urls.py @@ -1,8 +1,8 @@ from django.urls import path from dj_rest_auth.views import LogoutView, UserDetailsView, PasswordChangeView -from .views import KnoxLoginView, KnoxRegisterView, OrcidLoginView +from .views import KnoxLoginView, KnoxRegisterView -"""Urls for authentication. Orcid login not functional.""" +"""Urls for authentication. Orcid login not functional. See settings.py for ORCID activation.""" urlpatterns = [ path("register/", KnoxRegisterView.as_view(), name="register"), @@ -10,5 +10,5 @@ path("logout/", LogoutView.as_view(), name="logout"), path("user/", UserDetailsView.as_view(), name="view user information"), path("password/change/", PasswordChangeView.as_view(), name="change password"), - path("login/orcid/", OrcidLoginView.as_view(), name="orcid login"), + # path("login/orcid/", OrcidLoginView.as_view(), name="orcid login"), ] From 1c7bfd95df550771d113e9fbcc28fab01dd2c49f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 5 Mar 2025 13:21:30 -0500 Subject: [PATCH 297/675] Change data creation status code to 201 --- sasdata/fair_database/data/tests.py | 4 ++-- sasdata/fair_database/data/views.py | 5 ++++- sasdata/fair_database/fair_database/test_permissions.py | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index df15deaa3..32b78b2b1 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -92,7 +92,7 @@ def test_is_data_being_created(self): file = open(find("cyl_400_40.txt"), "rb") data = {"is_public": False, "file": file} request = self.client.post("/v1/data/upload/", data=data) - self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual( request.data, { @@ -110,7 +110,7 @@ def test_is_data_being_created_no_user(self): file = open(find("cyl_400_40.txt"), "rb") data = {"is_public": True, "file": file} request = self.client2.post("/v1/data/upload/", data=data) - self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual( request.data, { diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 8bf488825..f28bc412a 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -10,6 +10,7 @@ ) from rest_framework.decorators import api_view from rest_framework.response import Response +from rest_framework import status from sasdata.dataloader.loader import Loader from data.serializers import DataFileSerializer, AccessManagementSerializer @@ -58,7 +59,9 @@ def data_info(request, db_id, version=None): @api_view(["POST", "PUT"]) def upload(request, data_id=None, version=None): # saves file + response_status = status.HTTP_200_OK if request.method in ["POST", "PUT"] and data_id is None: + response_status = status.HTTP_201_CREATED form = DataFileForm(request.data, request.FILES) if form.is_valid(): form.save() @@ -115,7 +118,7 @@ def upload(request, data_id=None, version=None): "file_alternative_name": serializer.data["file_name"], "is_public": serializer.data["is_public"], } - return Response(return_data) + return Response(return_data, status=response_status) # view or control who has access to a file diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index 9bf91d04d..09aef3268 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -140,7 +140,7 @@ def test_upload_authenticated(self): response = self.client.post( "/v1/data/upload/", data=data, headers=auth_header(token) ) - self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual( response.data, { @@ -161,7 +161,7 @@ def test_upload_unauthenticated(self): data2 = {"file": file2, "is_public": False} response = self.client.post("/v1/data/upload/", data=data) response2 = self.client.post("/v1/data/upload/", data=data2) - self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual( response.data, { From 3493ee6d9b94b3adc865dfec71a163f28bcf41b8 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Mar 2025 11:25:35 -0500 Subject: [PATCH 298/675] Modify tests to run faster --- .../fair_database/test_permissions.py | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index 09aef3268..c02a5e782 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -22,37 +22,38 @@ def auth_header(response): class DataListPermissionsTests(APITestCase): """Test permissions of data views using user_app for authentication.""" - def setUp(self): - self.user = User.objects.create_user( + @classmethod + def setUpTestData(cls): + cls.user = User.objects.create_user( username="testUser", password="secret", id=1, email="email@domain.com" ) - self.user2 = User.objects.create_user( + cls.user2 = User.objects.create_user( username="testUser2", password="secret", id=2, email="email2@domain.com" ) - unowned_test_data = DataFile.objects.create( + cls.unowned_test_data = DataFile.objects.create( id=1, file_name="cyl_400_40.txt", is_public=True ) - unowned_test_data.file.save( + cls.unowned_test_data.file.save( "cyl_400_40.txt", open(find("cyl_400_40.txt"), "rb") ) - private_test_data = DataFile.objects.create( - id=2, current_user=self.user, file_name="cyl_400_20.txt", is_public=False + cls.private_test_data = DataFile.objects.create( + id=2, current_user=cls.user, file_name="cyl_400_20.txt", is_public=False ) - private_test_data.file.save( + cls.private_test_data.file.save( "cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb") ) - public_test_data = DataFile.objects.create( - id=3, current_user=self.user, file_name="cyl_testdata.txt", is_public=True + cls.public_test_data = DataFile.objects.create( + id=3, current_user=cls.user, file_name="cyl_testdata.txt", is_public=True ) - public_test_data.file.save( + cls.public_test_data.file.save( "cyl_testdata.txt", open(find("cyl_testdata.txt"), "rb") ) - self.login_data_1 = { + cls.login_data_1 = { "username": "testUser", "password": "secret", "email": "email@domain.com", } - self.login_data_2 = { + cls.login_data_2 = { "username": "testUser2", "password": "secret", "email": "email2@domain.com", @@ -268,5 +269,11 @@ def test_download_unauthenticated(self): self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response3.status_code, status.HTTP_200_OK) - def tearDown(self): + @classmethod + def tearDownClass(cls): + cls.user.delete() + cls.user2.delete() + cls.public_test_data.delete() + cls.private_test_data.delete() + cls.unowned_test_data.delete() shutil.rmtree(settings.MEDIA_ROOT) From f7ec3bb89d2dc8613923b3e4893140368b6bee92 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Mar 2025 11:40:59 -0500 Subject: [PATCH 299/675] Change authentication tests to run faster --- sasdata/fair_database/user_app/tests.py | 112 ++++++++++++------------ 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index 664fd963a..f5dd6b042 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -7,98 +7,96 @@ # Create your tests here. class AuthTests(TestCase): - def setUp(self): - self.client = APIClient() - self.register_data = { + @classmethod + def setUpTestData(cls): + cls.client1 = APIClient() + cls.register_data = { "email": "email@domain.org", "username": "testUser", "password1": "sasview!", "password2": "sasview!", } - self.login_data = { + cls.login_data = { "username": "testUser", "email": "email@domain.org", "password": "sasview!", } + cls.login_data_2 = { + "username": "testUser2", + "email": "email2@domain.org", + "password": "sasview!", + } + cls.user = User.objects.create_user( + id=1, username="testUser2", password="sasview!", email="email2@domain.org" + ) def auth_header(self, response): return {"Authorization": "Token " + response.data["token"]} # Test if registration successfully creates a new user and logs in def test_register(self): - response = self.client.post("/auth/register/", data=self.register_data) + response = self.client1.post("/auth/register/", data=self.register_data) user = User.objects.get(username="testUser") - response2 = self.client.get("/auth/user/", headers=self.auth_header(response)) + response2 = self.client1.get("/auth/user/", headers=self.auth_header(response)) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(user.email, self.register_data["email"]) self.assertEqual(response2.status_code, status.HTTP_200_OK) + user.delete() # Test if login successful def test_login(self): - User.objects.create_user( - username="testUser", password="sasview!", email="email@domain.org" - ) - response = self.client.post("/auth/login/", data=self.login_data) - response2 = self.client.get("/auth/user/", headers=self.auth_header(response)) + response = self.client1.post("/auth/login/", data=self.login_data_2) + response2 = self.client1.get("/auth/user/", headers=self.auth_header(response)) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) # Test simultaneous login by multiple clients def test_multiple_login(self): - User.objects.create_user( - username="testUser", password="sasview!", email="email@domain.org" - ) client2 = APIClient() - response = self.client.post("/auth/login/", data=self.login_data) - response2 = client2.post("/auth/login/", data=self.login_data) + response = self.client1.post("/auth/login/", data=self.login_data_2) + response2 = client2.post("/auth/login/", data=self.login_data_2) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) self.assertNotEqual(response.content, response2.content) # Test get user information def test_user_get(self): - user = User.objects.create_user( - username="testUser", password="sasview!", email="email@domain.org" - ) - self.client.force_authenticate(user=user) - response = self.client.get("/auth/user/") + self.client1.force_authenticate(user=self.user) + response = self.client1.get("/auth/user/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( response.content, - b'{"pk":1,"username":"testUser","email":"email@domain.org","first_name":"","last_name":""}', + b'{"pk":1,"username":"testUser2","email":"email2@domain.org","first_name":"","last_name":""}', ) # Test changing username def test_user_put_username(self): - user = User.objects.create_user( - username="testUser", password="sasview!", email="email@domain.org" - ) - self.client.force_authenticate(user=user) + self.client1.force_authenticate(user=self.user) data = {"username": "newName"} - response = self.client.put("/auth/user/", data=data) + response = self.client1.put("/auth/user/", data=data) + self.user.username = "testUser2" self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( response.content, - b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"","last_name":""}', + b'{"pk":1,"username":"newName","email":"email2@domain.org","first_name":"","last_name":""}', ) # Test changing username and first and last name def test_user_put_name(self): - user = User.objects.create_user( - username="testUser", password="sasview!", email="email@domain.org" - ) - self.client.force_authenticate(user=user) + self.client1.force_authenticate(user=self.user) data = {"username": "newName", "first_name": "Clark", "last_name": "Kent"} - response = self.client.put("/auth/user/", data=data) + response = self.client1.put("/auth/user/", data=data) + self.user.first_name = "" + self.user.last_name = "" self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( response.content, - b'{"pk":1,"username":"newName","email":"email@domain.org","first_name":"Clark","last_name":"Kent"}', + b'{"pk":1,"username":"newName","email":"email2@domain.org","first_name":"Clark","last_name":"Kent"}', ) # Test user info inaccessible when unauthenticated def test_user_unauthenticated(self): - response = self.client.get("/auth/user/") + response = self.client1.get("/auth/user/") self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual( response.content, @@ -107,33 +105,28 @@ def test_user_unauthenticated(self): # Test logout is successful after login def test_login_logout(self): - User.objects.create_user( - username="testUser", password="sasview!", email="email@domain.org" - ) - self.client.post("/auth/login/", data=self.login_data) - response = self.client.post("/auth/logout/") - response2 = self.client.get("/auth/user/") + self.client1.post("/auth/login/", data=self.login_data_2) + response = self.client1.post("/auth/logout/") + response2 = self.client1.get("/auth/user/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) # Test logout is successful after registration def test_register_logout(self): - self.client.post("/auth/register/", data=self.register_data) - response = self.client.post("/auth/logout/") - response2 = self.client.get("/auth/user/") + self.client1.post("/auth/register/", data=self.register_data) + response = self.client1.post("/auth/logout/") + response2 = self.client1.get("/auth/user/") + User.objects.get(username="testUser").delete() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) def test_multiple_logout(self): - User.objects.create_user( - username="testUser", password="sasview!", email="email@domain.org" - ) client2 = APIClient() - self.client.post("/auth/login/", data=self.login_data) - token = client2.post("/auth/login/", data=self.login_data) - response = self.client.post("/auth/logout/") + self.client1.post("/auth/login/", data=self.login_data_2) + token = client2.post("/auth/login/", data=self.login_data_2) + response = self.client1.post("/auth/logout/") response2 = client2.get("/auth/user/", headers=self.auth_header(token)) response3 = client2.post("/auth/logout/") self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -142,16 +135,19 @@ def test_multiple_logout(self): # Test login is successful after registering then logging out def test_register_login(self): - register_response = self.client.post("/auth/register/", data=self.register_data) - logout_response = self.client.post("/auth/logout/") - login_response = self.client.post("/auth/login/", data=self.login_data) + register_response = self.client1.post( + "/auth/register/", data=self.register_data + ) + logout_response = self.client1.post("/auth/logout/") + login_response = self.client1.post("/auth/login/", data=self.login_data) + User.objects.get(username="testUser").delete() self.assertEqual(register_response.status_code, status.HTTP_201_CREATED) self.assertEqual(logout_response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) # Test password is successfully changed def test_password_change(self): - token = self.client.post("/auth/register/", data=self.register_data) + token = self.client1.post("/auth/register/", data=self.register_data) data = { "new_password1": "sasview?", "new_password2": "sasview?", @@ -159,11 +155,13 @@ def test_password_change(self): } l_data = self.login_data l_data["password"] = "sasview?" - response = self.client.post( + response = self.client1.post( "/auth/password/change/", data=data, headers=self.auth_header(token) ) - logout_response = self.client.post("/auth/logout/") - login_response = self.client.post("/auth/login/", data=l_data) + logout_response = self.client1.post("/auth/logout/") + login_response = self.client1.post("/auth/login/", data=l_data) + l_data["password"] = "sasview!" + User.objects.get(username="testUser").delete() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(logout_response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) From 0b0318a1d87fdc0dffc9e474e5184052148c80fe Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Mar 2025 13:51:21 -0500 Subject: [PATCH 300/675] Speed up data tests --- sasdata/fair_database/data/tests.py | 148 ++++++++++++++++------------ 1 file changed, 85 insertions(+), 63 deletions(-) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 32b78b2b1..009592183 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -3,6 +3,7 @@ from django.conf import settings from django.test import TestCase +from django.db.models import Max from django.contrib.auth.models import User from rest_framework.test import APIClient, APITestCase from rest_framework import status @@ -17,31 +18,34 @@ def find(filename): class TestLists(TestCase): - def setUp(self): - public_test_data = DataFile.objects.create( + @classmethod + def setUpTestData(cls): + cls.public_test_data = DataFile.objects.create( id=1, file_name="cyl_400_40.txt", is_public=True ) - public_test_data.file.save("cyl_400_40.txt", open(find("cyl_400_40.txt"), "rb")) - self.user = User.objects.create_user( + cls.public_test_data.file.save( + "cyl_400_40.txt", open(find("cyl_400_40.txt"), "rb") + ) + cls.user = User.objects.create_user( username="testUser", password="secret", id=2 ) - private_test_data = DataFile.objects.create( - id=3, current_user=self.user, file_name="cyl_400_20.txt", is_public=False + cls.private_test_data = DataFile.objects.create( + id=3, current_user=cls.user, file_name="cyl_400_20.txt", is_public=False ) - private_test_data.file.save( + cls.private_test_data.file.save( "cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb") ) - self.client = APIClient() - self.client.force_authenticate(user=self.user) + cls.client1 = APIClient() + cls.client1.force_authenticate(user=cls.user) # Test list public data def test_does_list_public(self): - request = self.client.get("/v1/data/list/") + request = self.client1.get("/v1/data/list/") self.assertEqual(request.data, {"public_data_ids": {1: "cyl_400_40.txt"}}) # Test list a user's private data def test_does_list_user(self): - request = self.client.get("/v1/data/list/testUser/", user=self.user) + request = self.client1.get("/v1/data/list/testUser/", user=self.user) self.assertEqual(request.data, {"user_data_ids": {3: "cyl_400_20.txt"}}) # Test list another user's public data @@ -52,122 +56,131 @@ def test_list_other_user(self): # Test list a nonexistent user's data def test_list_nonexistent_user(self): - request = self.client.get("/v1/data/list/fakeUser/") + request = self.client1.get("/v1/data/list/fakeUser/") self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) # Test loading a public data file def test_does_load_data_info_public(self): - request = self.client.get("/v1/data/load/1/") + request = self.client1.get("/v1/data/load/1/") self.assertEqual(request.status_code, status.HTTP_200_OK) # Test loading private data with authorization def test_does_load_data_info_private(self): - request = self.client.get("/v1/data/load/3/") + request = self.client1.get("/v1/data/load/3/") self.assertEqual(request.status_code, status.HTTP_200_OK) # Test loading data that does not exist def test_load_data_info_nonexistent(self): - request = self.client.get("/v1/data/load/5/") + request = self.client1.get("/v1/data/load/5/") self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) - def tearDown(self): + @classmethod + def tearDownClass(cls): + cls.public_test_data.delete() + cls.private_test_data.delete() + cls.user.delete() shutil.rmtree(settings.MEDIA_ROOT) class TestingDatabase(APITestCase): - def setUp(self): - self.user = User.objects.create_user( + @classmethod + def setUpTestData(cls): + cls.user = User.objects.create_user( username="testUser", password="secret", id=1 ) - self.data = DataFile.objects.create( - id=2, current_user=self.user, file_name="cyl_400_20.txt", is_public=False + cls.data = DataFile.objects.create( + id=1, current_user=cls.user, file_name="cyl_400_20.txt", is_public=False ) - self.data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb")) - self.client = APIClient() - self.client.force_authenticate(user=self.user) - self.client2 = APIClient() + cls.data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb")) + cls.client1 = APIClient() + cls.client1.force_authenticate(user=cls.user) + cls.client2 = APIClient() # Test data upload creates data in database def test_is_data_being_created(self): file = open(find("cyl_400_40.txt"), "rb") data = {"is_public": False, "file": file} - request = self.client.post("/v1/data/upload/", data=data) + request = self.client1.post("/v1/data/upload/", data=data) + max_id = DataFile.objects.aggregate(Max("id"))["id__max"] self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual( request.data, { "current_user": "testUser", "authenticated": True, - "file_id": 3, + "file_id": max_id, "file_alternative_name": "cyl_400_40.txt", "is_public": False, }, ) - DataFile.objects.get(id=3).delete() + DataFile.objects.get(id=max_id).delete() # Test data upload w/out authenticated user def test_is_data_being_created_no_user(self): - file = open(find("cyl_400_40.txt"), "rb") + file = open(find("cyl_testdata.txt"), "rb") data = {"is_public": True, "file": file} request = self.client2.post("/v1/data/upload/", data=data) + max_id = DataFile.objects.aggregate(Max("id"))["id__max"] self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual( request.data, { "current_user": "", "authenticated": False, - "file_id": 3, - "file_alternative_name": "cyl_400_40.txt", + "file_id": max_id, + "file_alternative_name": "cyl_testdata.txt", "is_public": True, }, ) - DataFile.objects.get(id=3).delete() + DataFile.objects.get(id=max_id).delete() # Test updating file def test_does_file_upload_update(self): - file = open(find("cyl_400_40.txt")) + file = open(find("cyl_testdata1.txt")) data = {"file": file, "is_public": False} - request = self.client.put("/v1/data/upload/2/", data=data) + request = self.client1.put("/v1/data/upload/1/", data=data) self.assertEqual( request.data, { "current_user": "testUser", "authenticated": True, - "file_id": 2, - "file_alternative_name": "cyl_400_40.txt", + "file_id": 1, + "file_alternative_name": "cyl_testdata1.txt", "is_public": False, }, ) - DataFile.objects.get(id=2).delete() + self.data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb")) + self.data.file_name = "cyl_400_20.txt" # Test updating a public file def test_public_file_upload_update(self): data_object = DataFile.objects.create( - id=3, current_user=self.user, file_name="cyl_testdata.txt", is_public=True + id=3, current_user=self.user, file_name="cyl_testdata2.txt", is_public=True ) - data_object.file.save("cyl_testdata.txt", open(find("cyl_testdata.txt"), "rb")) - file = open(find("cyl_testdata1.txt")) + data_object.file.save( + "cyl_testdata2.txt", open(find("cyl_testdata2.txt"), "rb") + ) + file = open(find("conalbumin.txt")) data = {"file": file, "is_public": True} - request = self.client.put("/v1/data/upload/3/", data=data) + request = self.client1.put("/v1/data/upload/3/", data=data) self.assertEqual( request.data, { "current_user": "testUser", "authenticated": True, "file_id": 3, - "file_alternative_name": "cyl_testdata1.txt", + "file_alternative_name": "conalbumin.txt", "is_public": True, }, ) - DataFile.objects.get(id=3).delete() + data_object.delete() # Test file upload update fails when unauthorized def test_unauthorized_file_upload_update(self): file = open(find("cyl_400_40.txt")) data = {"file": file, "is_public": False} - request = self.client2.put("/v1/data/upload/2/", data=data) + request = self.client2.put("/v1/data/upload/1/", data=data) self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) - DataFile.objects.get(id=2).delete() # Test update nonexistent file fails def test_file_upload_update_not_found(self): @@ -178,7 +191,7 @@ def test_file_upload_update_not_found(self): # Test file download def test_does_download(self): - request = self.client.get("/v1/data/2/download/") + request = self.client1.get("/v1/data/1/download/") file_contents = b"".join(request.streaming_content) test_file = open(find("cyl_400_20.txt"), "rb") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -186,39 +199,43 @@ def test_does_download(self): # Test file download fails when unauthorized def test_unauthorized_download(self): - request2 = self.client2.get("/v1/data/2/download/") + request2 = self.client2.get("/v1/data/1/download/") self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) # Test download nonexistent file def test_download_nonexistent(self): - request = self.client.get("/v1/data/5/download/") + request = self.client1.get("/v1/data/5/download/") self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) - def tearDown(self): + @classmethod + def tearDownClass(cls): + cls.user.delete() + cls.data.delete() shutil.rmtree(settings.MEDIA_ROOT) class TestAccessManagement(TestCase): - def setUp(self): - self.user1 = User.objects.create_user(username="testUser", password="secret") - self.user2 = User.objects.create_user(username="testUser2", password="secret2") - self.private_test_data = DataFile.objects.create( - id=1, current_user=self.user1, file_name="cyl_400_40.txt", is_public=False + @classmethod + def setUpTestData(cls): + cls.user1 = User.objects.create_user(username="testUser", password="secret") + cls.user2 = User.objects.create_user(username="testUser2", password="secret2") + cls.private_test_data = DataFile.objects.create( + id=1, current_user=cls.user1, file_name="cyl_400_40.txt", is_public=False ) - self.private_test_data.file.save( + cls.private_test_data.file.save( "cyl_400_40.txt", open(find("cyl_400_40.txt"), "rb") ) - self.shared_test_data = DataFile.objects.create( - id=2, current_user=self.user1, file_name="cyl_400_20.txt", is_public=False + cls.shared_test_data = DataFile.objects.create( + id=2, current_user=cls.user1, file_name="cyl_400_20.txt", is_public=False ) - self.shared_test_data.file.save( + cls.shared_test_data.file.save( "cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb") ) - self.shared_test_data.users.add(self.user2) - self.client1 = APIClient() - self.client1.force_authenticate(self.user1) - self.client2 = APIClient() - self.client2.force_authenticate(self.user2) + cls.shared_test_data.users.add(cls.user2) + cls.client1 = APIClient() + cls.client1.force_authenticate(cls.user1) + cls.client2 = APIClient() + cls.client2.force_authenticate(cls.user2) # test viewing no one with access def test_view_no_access(self): @@ -301,5 +318,10 @@ def test_only_edit_access_to_owned_file(self): self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request4.status_code, status.HTTP_200_OK) - def tearDown(self): + @classmethod + def tearDownClass(cls): + cls.user1.delete() + cls.user2.delete() + cls.private_test_data.delete() + cls.shared_test_data.delete() shutil.rmtree(settings.MEDIA_ROOT) From a243430f0636c3460a4b736d6fdcd45782cde6c7 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Mar 2025 15:28:35 -0500 Subject: [PATCH 301/675] One more attempt to speed up user_app tests --- sasdata/fair_database/user_app/tests.py | 37 ++++++++++--------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index f5dd6b042..f53e922cf 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -10,6 +10,7 @@ class AuthTests(TestCase): @classmethod def setUpTestData(cls): cls.client1 = APIClient() + cls.client2 = APIClient() cls.register_data = { "email": "email@domain.org", "username": "testUser", @@ -29,6 +30,8 @@ def setUpTestData(cls): cls.user = User.objects.create_user( id=1, username="testUser2", password="sasview!", email="email2@domain.org" ) + cls.client3 = APIClient() + cls.client3.force_authenticate(user=cls.user) def auth_header(self, response): return {"Authorization": "Token " + response.data["token"]} @@ -52,17 +55,15 @@ def test_login(self): # Test simultaneous login by multiple clients def test_multiple_login(self): - client2 = APIClient() response = self.client1.post("/auth/login/", data=self.login_data_2) - response2 = client2.post("/auth/login/", data=self.login_data_2) + response2 = self.client2.post("/auth/login/", data=self.login_data_2) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) self.assertNotEqual(response.content, response2.content) # Test get user information def test_user_get(self): - self.client1.force_authenticate(user=self.user) - response = self.client1.get("/auth/user/") + response = self.client3.get("/auth/user/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( response.content, @@ -71,9 +72,8 @@ def test_user_get(self): # Test changing username def test_user_put_username(self): - self.client1.force_authenticate(user=self.user) data = {"username": "newName"} - response = self.client1.put("/auth/user/", data=data) + response = self.client3.put("/auth/user/", data=data) self.user.username = "testUser2" self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( @@ -83,9 +83,8 @@ def test_user_put_username(self): # Test changing username and first and last name def test_user_put_name(self): - self.client1.force_authenticate(user=self.user) data = {"username": "newName", "first_name": "Clark", "last_name": "Kent"} - response = self.client1.put("/auth/user/", data=data) + response = self.client3.put("/auth/user/", data=data) self.user.first_name = "" self.user.last_name = "" self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -123,12 +122,11 @@ def test_register_logout(self): self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) def test_multiple_logout(self): - client2 = APIClient() self.client1.post("/auth/login/", data=self.login_data_2) - token = client2.post("/auth/login/", data=self.login_data_2) + token = self.client2.post("/auth/login/", data=self.login_data_2) response = self.client1.post("/auth/logout/") - response2 = client2.get("/auth/user/", headers=self.auth_header(token)) - response3 = client2.post("/auth/logout/") + response2 = self.client2.get("/auth/user/", headers=self.auth_header(token)) + response3 = self.client2.post("/auth/logout/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) self.assertEqual(response3.status_code, status.HTTP_200_OK) @@ -147,23 +145,16 @@ def test_register_login(self): # Test password is successfully changed def test_password_change(self): - token = self.client1.post("/auth/register/", data=self.register_data) data = { "new_password1": "sasview?", "new_password2": "sasview?", "old_password": "sasview!", } - l_data = self.login_data - l_data["password"] = "sasview?" - response = self.client1.post( - "/auth/password/change/", data=data, headers=self.auth_header(token) - ) - logout_response = self.client1.post("/auth/logout/") - login_response = self.client1.post("/auth/login/", data=l_data) - l_data["password"] = "sasview!" - User.objects.get(username="testUser").delete() + self.login_data_2["password"] = "sasview?" + response = self.client3.post("/auth/password/change/", data=data) + login_response = self.client1.post("/auth/login/", data=self.login_data_2) + self.login_data_2["password"] = "sasview!" self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(logout_response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) From afe105f81a05a1076f5ea4cfb7ca1472e874ff21 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Mar 2025 15:37:39 -0500 Subject: [PATCH 302/675] Create view to delete a file --- sasdata/fair_database/data/urls.py | 1 + sasdata/fair_database/data/views.py | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/sasdata/fair_database/data/urls.py b/sasdata/fair_database/data/urls.py index 8721927f1..a61fff553 100644 --- a/sasdata/fair_database/data/urls.py +++ b/sasdata/fair_database/data/urls.py @@ -10,4 +10,5 @@ path("upload//", views.upload, name="update file in data"), path("/download/", views.download, name="download data from db"), path("manage//", views.manage_access, name="manage access to files"), + path("delete//", views.delete, name="delete file"), ] diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index f28bc412a..73aba65c0 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -152,6 +152,16 @@ def manage_access(request, data_id, version=None): return HttpResponseBadRequest() +# delete a file +@api_view(["DELETE"]) +def delete(request, data_id, version=None): + db = get_object_or_404(DataFile, id=data_id) + if not permissions.is_owner(request, db): + return HttpResponseForbidden("Must be the data owner to delete") + db.delete() + return Response(data={"success": True}) + + # downloads a file @api_view(["GET"]) def download(request, data_id, version=None): From 2de24dc90a8cb45675aad5fe04c04022c69e1e5a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 6 Mar 2025 15:55:31 -0500 Subject: [PATCH 303/675] Tests for file delete --- sasdata/fair_database/data/tests.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/tests.py index 009592183..b53089679 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/tests.py @@ -207,6 +207,20 @@ def test_download_nonexistent(self): request = self.client1.get("/v1/data/5/download/") self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) + # Test deleting a file + def test_delete(self): + DataFile.objects.create( + id=6, current_user=self.user, file_name="test.txt", is_public=False + ) + request = self.client1.delete("/v1/data/delete/6/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertFalse(DataFile.objects.filter(pk=6).exists()) + + # Test deleting a file fails when unauthorized + def test_delete_unauthorized(self): + request = self.client2.delete("/v1/data/delete/1/") + self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + @classmethod def tearDownClass(cls): cls.user.delete() From 916eb986413453a6bfaf715839d015a2b9f63be5 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Mar 2025 10:13:43 -0400 Subject: [PATCH 304/675] Reorganize data tests --- sasdata/fair_database/data/test/__init__.py | 0 sasdata/fair_database/data/{tests.py => test/test_datafile.py} | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 sasdata/fair_database/data/test/__init__.py rename sasdata/fair_database/data/{tests.py => test/test_datafile.py} (99%) diff --git a/sasdata/fair_database/data/test/__init__.py b/sasdata/fair_database/data/test/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/fair_database/data/tests.py b/sasdata/fair_database/data/test/test_datafile.py similarity index 99% rename from sasdata/fair_database/data/tests.py rename to sasdata/fair_database/data/test/test_datafile.py index b53089679..45dc0d98f 100644 --- a/sasdata/fair_database/data/tests.py +++ b/sasdata/fair_database/data/test/test_datafile.py @@ -13,7 +13,7 @@ def find(filename): return os.path.join( - os.path.dirname(__file__), "../../example_data/1d_data", filename + os.path.dirname(__file__), "../../../example_data/1d_data", filename ) From eb31ea78da16999c41359334d5109a48743b605e Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Mar 2025 10:37:45 -0400 Subject: [PATCH 305/675] Start planning tests for dataset --- .../fair_database/data/test/test_dataset.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 sasdata/fair_database/data/test/test_dataset.py diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py new file mode 100644 index 000000000..ee6a68ae6 --- /dev/null +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -0,0 +1,20 @@ +# test GET +# list datasets - include public and owned, disinclude private unowned +# get one dataset - succeeds if public or owned, fails if private and unowned +# get list of people with access to dataset (owner) +# fail to get list of people with access to dataset (not owner) + +# test POST +# create a public dataset +# create a private dataset +# can't create an unowned private dataset + +# test PUT +# edit an owned dataset +# can't edit an unowned dataset +# change access to a dataset + +# test DELETE +# delete an owned private dataset +# can't delete an unowned dataset +# can't delete a public dataset From a36af758ef3c7c7fe484be94d86bf1fb3ae3c2de Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Mar 2025 12:12:02 -0400 Subject: [PATCH 306/675] Initial version of DataSetView --- sasdata/fair_database/data/urls.py | 2 ++ sasdata/fair_database/data/views.py | 32 +++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/urls.py b/sasdata/fair_database/data/urls.py index a61fff553..89f900788 100644 --- a/sasdata/fair_database/data/urls.py +++ b/sasdata/fair_database/data/urls.py @@ -11,4 +11,6 @@ path("/download/", views.download, name="download data from db"), path("manage//", views.manage_access, name="manage access to files"), path("delete//", views.delete, name="delete file"), + path("set/", views.DataSetView.as_view(), name="view, create, modify datasets"), + # path("set//", views.DataSetView.as_view()), ] diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 73aba65c0..018b47751 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -11,12 +11,18 @@ from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status +from rest_framework.views import APIView from sasdata.dataloader.loader import Loader -from data.serializers import DataFileSerializer, AccessManagementSerializer -from data.models import DataFile +from data.serializers import ( + DataFileSerializer, + DataSetSerializer, + AccessManagementSerializer, +) +from data.models import DataFile, DataSet from data.forms import DataFileForm from fair_database import permissions +from fair_database.permissions import DataPermission @api_view(["GET"]) @@ -178,3 +184,25 @@ def download(request, data_id, version=None): raise Http404("File not found.") return FileResponse(file, as_attachment=True) return HttpResponseBadRequest() + + +class DataSetView(APIView): + permission_classes = [DataPermission] + + def get(self, request, version=None): + # TODO: filter based on access + data = DataSet.objects.all() + data_list = {"dataset_ids": {}} + for set in data: + data_list["dataset_ids"][set.id] = set.name + return Response(data=data_list) + + def post(self, request): + # TODO: JSON deserialization probably + serializer = DataSetSerializer(data=request.data) + db = None + None + if serializer.is_valid(): + db = serializer.save() + response = {"dataset_id": db.id, "name": db.name} + return Response(data=response) From be7bdc47913b76ef8d123a941173ba3ed2733c27 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Mar 2025 13:09:11 -0400 Subject: [PATCH 307/675] General structure of dataset views --- sasdata/fair_database/data/urls.py | 13 +++++++++++-- sasdata/fair_database/data/views.py | 25 ++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/data/urls.py b/sasdata/fair_database/data/urls.py index 89f900788..64c081288 100644 --- a/sasdata/fair_database/data/urls.py +++ b/sasdata/fair_database/data/urls.py @@ -11,6 +11,15 @@ path("/download/", views.download, name="download data from db"), path("manage//", views.manage_access, name="manage access to files"), path("delete//", views.delete, name="delete file"), - path("set/", views.DataSetView.as_view(), name="view, create, modify datasets"), - # path("set//", views.DataSetView.as_view()), + path("set/", views.DataSetView.as_view(), name="view and create datasets"), + path( + "set//", + views.SingleDataSetView.as_view(), + "load, modify, delete datasets", + ), + path( + "set//users/", + views.DataSetUsersView.as_view(), + "manage access to datasets", + ), ] diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 018b47751..c4719618a 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -197,7 +197,7 @@ def get(self, request, version=None): data_list["dataset_ids"][set.id] = set.name return Response(data=data_list) - def post(self, request): + def post(self, request, version=None): # TODO: JSON deserialization probably serializer = DataSetSerializer(data=request.data) db = None @@ -206,3 +206,26 @@ def post(self, request): db = serializer.save() response = {"dataset_id": db.id, "name": db.name} return Response(data=response) + + +class SingleDataSetView(APIView): + permission_classes = [DataPermission] + + def get(self, request, data_id, version=None): + pass + + def put(self, request, data_id, version=None): + pass + + def delete(self, request, data_id, version=None): + pass + + +class DataSetUsersView(APIView): + permission_classes = [DataPermission] + + def get(self, request, data_id, version=None): + pass + + def put(self, request, data_id, version=None): + pass From 13a7717bbdf204af224dcf6bfbfa411dabaf1dd0 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Mar 2025 15:29:08 -0400 Subject: [PATCH 308/675] Updated version of DataSetView pending serialization changes --- sasdata/fair_database/data/serializers.py | 2 ++ sasdata/fair_database/data/views.py | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 820561a76..7e56da786 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -20,6 +20,8 @@ class AccessManagementSerializer(serializers.Serializer): class DataSetSerializer(serializers.ModelSerializer): + # TODO: custom validation, maybe custom serialization handling of current_user + # TODO: account for nested serialization class Meta: model = DataSet fields = "__all__" diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index c4719618a..746c2a0ba 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -187,25 +187,32 @@ def download(request, data_id, version=None): class DataSetView(APIView): + """View associated with the DataSet model. Functionality for viewing a list of datasets and creating a dataset.""" + permission_classes = [DataPermission] + # get a list of accessible datasets def get(self, request, version=None): - # TODO: filter based on access - data = DataSet.objects.all() data_list = {"dataset_ids": {}} - for set in data: - data_list["dataset_ids"][set.id] = set.name + for dataset in DataSet.objects.all(): + if permissions.check_permissions(request, dataset): + data_list["dataset_ids"][dataset.id] = dataset.name return Response(data=data_list) + # create a dataset def post(self, request, version=None): # TODO: JSON deserialization probably + # TODO: revisit request data format serializer = DataSetSerializer(data=request.data) db = None - None if serializer.is_valid(): db = serializer.save() response = {"dataset_id": db.id, "name": db.name} - return Response(data=response) + return Response(data=response, status=status.HTTP_201_CREATED) + + # create a dataset + def put(self, request, version=None): + return self.post(request, version) class SingleDataSetView(APIView): From ec7a9a7e3a93b48eb74ff7dc8d40dd00279c2ddd Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Mar 2025 16:05:02 -0400 Subject: [PATCH 309/675] First draft of SingleDataSetView --- sasdata/fair_database/data/views.py | 40 ++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 746c2a0ba..4980b1c29 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -187,12 +187,17 @@ def download(request, data_id, version=None): class DataSetView(APIView): - """View associated with the DataSet model. Functionality for viewing a list of datasets and creating a dataset.""" + """ + View associated with the DataSet model. + + Functionality for viewing a list of datasets and creating a dataset. + """ permission_classes = [DataPermission] # get a list of accessible datasets def get(self, request, version=None): + # TODO: add filtering by at minimum the user data_list = {"dataset_ids": {}} for dataset in DataSet.objects.all(): if permissions.check_permissions(request, dataset): @@ -216,16 +221,43 @@ def put(self, request, version=None): class SingleDataSetView(APIView): + """ + View associated with single datasets. + + Functionality for accessing a dataset in a format intended to be loaded + into SasView, modifying a dataset, or deleting a dataset. + """ + permission_classes = [DataPermission] + # get a specific dataset def get(self, request, data_id, version=None): - pass + db = get_object_or_404(DataSet, id=data_id) + if not permissions.check_permissions(request, db): + return HttpResponseForbidden( + "You do not have permission to view this dataset." + ) + serializer = DataSetSerializer(db) + return Response(serializer.data) + # edit a specific dataset def put(self, request, data_id, version=None): - pass + db = get_object_or_404(DataSet, id=data_id) + if not permissions.check_permissions(request, db): + return HttpResponseForbidden("Cannot modify a dataset you do not own") + serializer = DataSetSerializer(db, request.data, partial=True) + if serializer.is_valid(): + serializer.save() + data = {"data_id": db.id, "name": db.name, "is_public": db.is_public} + return Response(data) + # delete a dataset def delete(self, request, data_id, version=None): - pass + db = get_object_or_404(DataSet, id=data_id) + if not permissions.check_permissions(request, db): + return HttpResponseForbidden("Not authorized to delete") + db.delete() + return Response({"success": True}) class DataSetUsersView(APIView): From bf7a9b5a5bc5551ffd578187681ac7185a0aae3e Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Mar 2025 16:31:11 -0400 Subject: [PATCH 310/675] First draft for DataSetUsersView --- sasdata/fair_database/data/views.py | 37 +++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 4980b1c29..9090a7f09 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -261,10 +261,43 @@ def delete(self, request, data_id, version=None): class DataSetUsersView(APIView): + """ + View for the users that have access to a dataset. + + Functionality for accessing a list of users with access and granting or + revoking access. + """ + permission_classes = [DataPermission] + # get a list of users with access to dataset data_id def get(self, request, data_id, version=None): - pass + db = get_object_or_404(data_id) + if not permissions.check_permissions(request, db): + return HttpResponseForbidden("Must be the dataset owner to view access") + response_data = { + "data_id": db.id, + "name": db.name, + "users": [user.username for user in db.users.all()], + } + return Response(response_data) + # grant or revoke a user's access to dataset data_id def put(self, request, data_id, version=None): - pass + db = get_object_or_404(data_id) + if not permissions.check_permissions(request, db): + return HttpResponseForbidden("Must be the dataset owner to manage access") + serializer = AccessManagementSerializer(data=request.data) + serializer.is_valid() + user = get_object_or_404(User, username=serializer.data["username"]) + if serializer.data["access"]: + db.users.add(user) + else: + db.users.remove(user) + response_data = { + "user": user.username, + "data_id": db.id, + "name": db.name, + "access": serializer.data["access"], + } + return Response(response_data) From 456aecce14113a8c9659d5d1b182bdfc7e0530a6 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Mar 2025 16:37:31 -0400 Subject: [PATCH 311/675] Add filter based on username in get request body --- sasdata/fair_database/data/views.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 9090a7f09..b7244bf5e 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -199,7 +199,10 @@ class DataSetView(APIView): def get(self, request, version=None): # TODO: add filtering by at minimum the user data_list = {"dataset_ids": {}} - for dataset in DataSet.objects.all(): + data = DataSet.objects.all() + if "username" in request.data: + data = DataSet.objects.filter(username=request.data["username"]) + for dataset in data: if permissions.check_permissions(request, dataset): data_list["dataset_ids"][dataset.id] = dataset.name return Response(data=data_list) From 1cd68faf1d82a581f6f73e4a6dbb1e3ab819da88 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Mar 2025 17:02:15 -0400 Subject: [PATCH 312/675] Nested metadata serialization --- sasdata/fair_database/data/serializers.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 7e56da786..3c19d3d10 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -19,13 +19,29 @@ class AccessManagementSerializer(serializers.Serializer): access = serializers.BooleanField() +class MetaDataSerializer(serializers.ModelSerializer): + class Meta: + model = MetaData + fields = "__all__" + + class DataSetSerializer(serializers.ModelSerializer): # TODO: custom validation, maybe custom serialization handling of current_user # TODO: account for nested serialization + metadata = MetaDataSerializer() + class Meta: model = DataSet fields = "__all__" + def create(self, validated_data): + metadata_raw = validated_data.pop("metadata") + metadata = MetaDataSerializer.create( + MetaDataSerializer(), validated_data=metadata_raw + ) + dataset = DataSet.objects.update_or_create(**validated_data, metadata=metadata) + return dataset + class QuantitySerializer(serializers.ModelSerializer): class Meta: @@ -33,12 +49,6 @@ class Meta: fields = "__all__" -class MetaDataSerializer(serializers.ModelSerializer): - class Meta: - model = MetaData - fields = "__all__" - - class OperationTreeSerializer(serializers.ModelSerializer): class Meta: model = OperationTree From dabb92fda15b3d356250c0cd900f015f3348aa5c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 10 Mar 2025 17:08:10 -0400 Subject: [PATCH 313/675] Fix error in paths --- sasdata/fair_database/data/urls.py | 4 ++-- sasdata/fair_database/data/views.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/data/urls.py b/sasdata/fair_database/data/urls.py index 64c081288..32ab14c0e 100644 --- a/sasdata/fair_database/data/urls.py +++ b/sasdata/fair_database/data/urls.py @@ -15,11 +15,11 @@ path( "set//", views.SingleDataSetView.as_view(), - "load, modify, delete datasets", + name="load, modify, delete datasets", ), path( "set//users/", views.DataSetUsersView.as_view(), - "manage access to datasets", + name="manage access to datasets", ), ] diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index b7244bf5e..82b782ea9 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -197,7 +197,6 @@ class DataSetView(APIView): # get a list of accessible datasets def get(self, request, version=None): - # TODO: add filtering by at minimum the user data_list = {"dataset_ids": {}} data = DataSet.objects.all() if "username" in request.data: From d72cdb2fa1b7215fd55aebc9202f569512b531e9 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Mar 2025 14:26:54 -0400 Subject: [PATCH 314/675] Structure for dataset tests --- sasdata/fair_database/data/models.py | 5 +- .../fair_database/data/test/test_dataset.py | 123 ++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 797ba7563..78cda5ba9 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -53,7 +53,10 @@ class DataSet(Data): files = models.ManyToManyField(DataFile) # metadata - maybe a foreign key? - metadata = models.OneToOneField("MetaData", on_delete=models.CASCADE) + # TODO: when MetaData is finished, set blank/null false + metadata = models.OneToOneField( + "MetaData", blank=True, null=True, on_delete=models.CASCADE + ) # ordinate # ordinate = models.ForeignKey("Quantity", on_delete=models.CASCADE) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index ee6a68ae6..cafa6e4f0 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -1,3 +1,8 @@ +from django.contrib.auth.models import User +from rest_framework.test import APIClient, APITestCase + +from data.models import DataSet + # test GET # list datasets - include public and owned, disinclude private unowned # get one dataset - succeeds if public or owned, fails if private and unowned @@ -18,3 +23,121 @@ # delete an owned private dataset # can't delete an unowned dataset # can't delete a public dataset + +""" +DataSetView: + get + - requires 3 users, private data, public data, probably unowned data + for authenticated user (can see public data and own private data) + - can't see someone else's private data (may or may not be a separate test) + for unauthenticated user (can see public data) + with username specified + with nonexistent username specified + post + - requires 2 users, no data + for authenticated user + for unauthenticated user, public + for unauthenticated, private (fails) + put + - probably 1 user, no data + should be same as post + +SingleDataSetView: + get + for owned private data + for unowned private data authenticated + for private data unauthenticated + for public data + put + same as get I think + delete""" + + +class TestDataSet(APITestCase): + @classmethod + def setUpTestData(cls): + cls.user1 = User.objects.create_user(username="testUser1", password="secret") + cls.user2 = User.objects.create_user(username="testUser2", password="secret") + cls.public_dataset = DataSet.objects.create( + id=1, + current_user=cls.user1, + is_public=True, + name="Dataset 1", + metadata=None, + ) + cls.private_dataset = DataSet.objects.create( + id=2, current_user=cls.user1, name="Dataset 1", metadata=None + ) + cls.unowned_dataset = DataSet.objects.create( + id=3, is_public=True, name="Dataset 3", metadata=None + ) + cls.auth_client1 = APIClient() + cls.auth_client2 = APIClient() + cls.auth_client1.force_authenticate(cls.user1) + cls.auth_client2.force_authenticate(cls.user2) + + @classmethod + def tearDownClass(cls): + cls.public_dataset.delete() + cls.private_dataset.delete() + cls.unowned_dataset.delete() + cls.user1.delete() + cls.user2.delete() + + +# TODO: decide whether to just combine this w/ above +class TestSingleDataSet(APITestCase): + @classmethod + def setUpTestData(cls): + cls.user1 = User.objects.create_user(username="testUser1", password="secret") + cls.user2 = User.objects.create_user(username="testUser2", password="secret") + cls.public_dataset = DataSet.objects.create( + id=1, + current_user=cls.user1, + is_public=True, + name="Dataset 1", + metadata=None, + ) + cls.private_dataset = DataSet.objects.create( + id=2, current_user=cls.user1, name="Dataset 2", metadata=None + ) + cls.unowned_dataset = DataSet.objects.create( + id=3, is_public=True, name="Dataset 3", metadata=None + ) + cls.auth_client1 = APIClient() + cls.auth_client2 = APIClient() + cls.auth_client1.force_authenticate(cls.user1) + cls.auth_client2.force_authenticate(cls.user2) + + @classmethod + def tearDownClass(cls): + cls.public_dataset.delete() + cls.private_dataset.delete() + cls.unowned_dataset.delete() + cls.user1.delete() + cls.user2.delete() + + +class TestDataSetAccessManagement(APITestCase): + @classmethod + def setUpTestData(cls): + cls.user1 = User.objects.create_user(username="testUser1", password="secret") + cls.user2 = User.objects.create_user(username="testUser2", password="secret") + cls.private_dataset = DataSet.objects.create( + id=1, current_user=cls.user1, name="Dataset 1", metadata=None + ) + cls.shared_dataset = DataSet.objects.create( + id=2, current_user=cls.user1, name="Dataset 2", metadata=None + ) + cls.shared_dataset.users.add(cls.user2) + cls.client1 = APIClient() + cls.client2 = APIClient() + cls.client1.force_authenticate(cls.user1) + cls.client2.force_authenticate(cls.user2) + + @classmethod + def tearDownClass(cls): + cls.private_dataset.delete() + cls.shared_dataset.delete() + cls.user1.delete() + cls.user2.delete() From 81e1ba10aa0ec2fd6cc12406db1a3184d92530e1 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Mar 2025 15:11:41 -0400 Subject: [PATCH 315/675] Fix filtering by username --- sasdata/fair_database/data/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 82b782ea9..b4d3921d2 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -199,8 +199,9 @@ class DataSetView(APIView): def get(self, request, version=None): data_list = {"dataset_ids": {}} data = DataSet.objects.all() - if "username" in request.data: - data = DataSet.objects.filter(username=request.data["username"]) + if "username" in request.GET: + user = get_object_or_404(User, username=request.GET["username"]) + data = DataSet.objects.filter(current_user=user) for dataset in data: if permissions.check_permissions(request, dataset): data_list["dataset_ids"][dataset.id] = dataset.name From 149a186753e3b9576293b1ba3efd915dedbf24d1 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Mar 2025 15:12:19 -0400 Subject: [PATCH 316/675] Tests for listing datasets --- .../migrations/0010_alter_dataset_metadata.py | 23 ++++++++++ .../fair_database/data/test/test_dataset.py | 42 ++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 sasdata/fair_database/data/migrations/0010_alter_dataset_metadata.py diff --git a/sasdata/fair_database/data/migrations/0010_alter_dataset_metadata.py b/sasdata/fair_database/data/migrations/0010_alter_dataset_metadata.py new file mode 100644 index 000000000..a8acab29f --- /dev/null +++ b/sasdata/fair_database/data/migrations/0010_alter_dataset_metadata.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.6 on 2025-03-11 18:46 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0009_metadata_definition_metadata_instrument_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="dataset", + name="metadata", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="data.metadata", + ), + ), + ] diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index cafa6e4f0..452faa8b2 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -1,5 +1,6 @@ from django.contrib.auth.models import User from rest_framework.test import APIClient, APITestCase +from rest_framework import status from data.models import DataSet @@ -66,7 +67,7 @@ def setUpTestData(cls): metadata=None, ) cls.private_dataset = DataSet.objects.create( - id=2, current_user=cls.user1, name="Dataset 1", metadata=None + id=2, current_user=cls.user1, name="Dataset 2", metadata=None ) cls.unowned_dataset = DataSet.objects.create( id=3, is_public=True, name="Dataset 3", metadata=None @@ -76,6 +77,45 @@ def setUpTestData(cls): cls.auth_client1.force_authenticate(cls.user1) cls.auth_client2.force_authenticate(cls.user2) + def test_list_private(self): + request = self.auth_client1.get("/v1/data/set/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + {"dataset_ids": {1: "Dataset 1", 2: "Dataset 2", 3: "Dataset 3"}}, + ) + + def test_list_public(self): + request = self.auth_client2.get("/v1/data/set/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, {"dataset_ids": {1: "Dataset 1", 3: "Dataset 3"}} + ) + + def test_list_unauthenticated(self): + request = self.client.get("/v1/data/set/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, {"dataset_ids": {1: "Dataset 1", 3: "Dataset 3"}} + ) + + def test_list_username(self): + request = self.auth_client1.get("/v1/data/set/", data={"username": "testUser1"}) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, {"dataset_ids": {1: "Dataset 1", 2: "Dataset 2"}} + ) + + def test_list_username_2(self): + request = self.auth_client1.get("/v1/data/set/", {"username": "testUser2"}) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(request.data, {"dataset_ids": {}}) + + def test_list_username_unauthenticated(self): + request = self.client.get("/v1/data/set/", {"username": "testUser1"}) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(request.data, {"dataset_ids": {1: "Dataset 1"}}) + @classmethod def tearDownClass(cls): cls.public_dataset.delete() From 1b13e3eee50390f09a432afc17c17b4a02e022a8 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Mar 2025 15:39:16 -0400 Subject: [PATCH 317/675] Tests for loading a dataset --- .../fair_database/data/test/test_dataset.py | 90 ++++++++++++++++++- 1 file changed, 86 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 452faa8b2..20ceafbfa 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -57,8 +57,12 @@ class TestDataSet(APITestCase): @classmethod def setUpTestData(cls): - cls.user1 = User.objects.create_user(username="testUser1", password="secret") - cls.user2 = User.objects.create_user(username="testUser2", password="secret") + cls.user1 = User.objects.create_user( + id=1, username="testUser1", password="secret" + ) + cls.user2 = User.objects.create_user( + id=2, username="testUser2", password="secret" + ) cls.public_dataset = DataSet.objects.create( id=1, current_user=cls.user1, @@ -99,6 +103,8 @@ def test_list_unauthenticated(self): request.data, {"dataset_ids": {1: "Dataset 1", 3: "Dataset 3"}} ) + # TODO: test unauthorized gets + def test_list_username(self): request = self.auth_client1.get("/v1/data/set/", data={"username": "testUser1"}) self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -116,6 +122,19 @@ def test_list_username_unauthenticated(self): self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, {"dataset_ids": {1: "Dataset 1"}}) + # TODO: test listing by user that doesn't exist + # TODO: test listing by other parameters if functionality is added for that + + # TODO: write test for post - probably will need to change post method to account for owner + def test_dataset_created(self): + pass + + def test_dataset_created_unauthenticated(self): + pass + + def test_no_private_unowned_dataset(self): + pass + @classmethod def tearDownClass(cls): cls.public_dataset.delete() @@ -129,8 +148,12 @@ def tearDownClass(cls): class TestSingleDataSet(APITestCase): @classmethod def setUpTestData(cls): - cls.user1 = User.objects.create_user(username="testUser1", password="secret") - cls.user2 = User.objects.create_user(username="testUser2", password="secret") + cls.user1 = User.objects.create_user( + id=1, username="testUser1", password="secret" + ) + cls.user2 = User.objects.create_user( + id=2, username="testUser2", password="secret" + ) cls.public_dataset = DataSet.objects.create( id=1, current_user=cls.user1, @@ -149,6 +172,65 @@ def setUpTestData(cls): cls.auth_client1.force_authenticate(cls.user1) cls.auth_client2.force_authenticate(cls.user2) + # TODO: change load return data + def test_load_private_dataset(self): + request = self.auth_client1.get("/v1/data/set/2/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertDictEqual( + request.data, + { + "id": 2, + "current_user": 1, + "users": [], + "is_public": False, + "name": "Dataset 2", + "files": [], + "metadata": None, + }, + ) + + def test_load_public_dataset(self): + request1 = self.client.get("/v1/data/set/1/") + request2 = self.auth_client2.get("/v1/data/set/1/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertDictEqual( + request1.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Dataset 1", + "files": [], + "metadata": None, + }, + ) + + def test_load_unowned_dataset(self): + request1 = self.auth_client1.get("/v1/data/set/3/") + request2 = self.client.get("/v1/data/set/3/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertDictEqual( + request1.data, + { + "id": 3, + "current_user": None, + "users": [], + "is_public": True, + "name": "Dataset 3", + "files": [], + "metadata": None, + }, + ) + + def test_load_private_dataset_unauthorized(self): + request1 = self.auth_client2.get("/v1/data/set/2/") + request2 = self.client.get("/v1/data/set/2/") + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + @classmethod def tearDownClass(cls): cls.public_dataset.delete() From 61e243e10a1d7805586b19fd458bb20a8ee16574 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Mar 2025 15:49:58 -0400 Subject: [PATCH 318/675] Test for deleting a dataset --- sasdata/fair_database/data/test/test_dataset.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 20ceafbfa..25135d6c7 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -231,6 +231,14 @@ def test_load_private_dataset_unauthorized(self): self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + def test_delete_dataset(self): + request = self.auth_client1.delete("/v1/data/set/2/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertRaises(DataSet.DoesNotExist, DataSet.objects.get, id=2) + self.private_dataset = DataSet.objects.create( + id=2, current_user=self.user1, name="Dataset 2", metadata=None + ) + @classmethod def tearDownClass(cls): cls.public_dataset.delete() From 854031f702ce7b68e7d9d8ca74f2d239d4801299 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Mar 2025 15:50:13 -0400 Subject: [PATCH 319/675] Fix error in permissions check --- sasdata/fair_database/fair_database/permissions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/fair_database/fair_database/permissions.py b/sasdata/fair_database/fair_database/permissions.py index 89e677be3..4b5c98047 100644 --- a/sasdata/fair_database/fair_database/permissions.py +++ b/sasdata/fair_database/fair_database/permissions.py @@ -19,7 +19,7 @@ def has_object_permission(self, request, view, obj): if obj.is_public or has_access(request, obj): return True elif request.method == "DELETE": - if obj.is_private and is_owner(request, obj): + if not obj.is_public and is_owner(request, obj): return True else: return is_owner(request, obj) From e76a73bdfd3f89b9017f3bf865dc62e41b3e6910 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Mar 2025 16:08:49 -0400 Subject: [PATCH 320/675] Tests for updating a dataset --- sasdata/fair_database/data/test/test_dataset.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 25135d6c7..ee2d72c7d 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -144,7 +144,6 @@ def tearDownClass(cls): cls.user2.delete() -# TODO: decide whether to just combine this w/ above class TestSingleDataSet(APITestCase): @classmethod def setUpTestData(cls): @@ -231,6 +230,22 @@ def test_load_private_dataset_unauthorized(self): self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + # TODO: check put return data + def test_update_private_dataset(self): + request = self.auth_client1.put("/v1/data/set/2/", data={"is_public": True}) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertTrue(DataSet.objects.get(id=2).is_public) + self.private_dataset.save() + self.assertFalse(DataSet.objects.get(id=2).is_public) + + def test_update_public_dataset(self): + request = self.auth_client1.put( + "/v1/data/set/1/", data={"name": "Different name"} + ) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(DataSet.objects.get(id=1).name, "Different name") + self.public_dataset.save() + def test_delete_dataset(self): request = self.auth_client1.delete("/v1/data/set/2/") self.assertEqual(request.status_code, status.HTTP_200_OK) From 5bad89316c4e4fc140cc03136ae45289f79330be Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Mar 2025 16:25:30 -0400 Subject: [PATCH 321/675] Only allow data owner to view/manage access --- sasdata/fair_database/data/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index b4d3921d2..c133e254a 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -275,8 +275,8 @@ class DataSetUsersView(APIView): # get a list of users with access to dataset data_id def get(self, request, data_id, version=None): - db = get_object_or_404(data_id) - if not permissions.check_permissions(request, db): + db = get_object_or_404(DataSet, id=data_id) + if not permissions.is_owner(request, db): return HttpResponseForbidden("Must be the dataset owner to view access") response_data = { "data_id": db.id, @@ -287,8 +287,8 @@ def get(self, request, data_id, version=None): # grant or revoke a user's access to dataset data_id def put(self, request, data_id, version=None): - db = get_object_or_404(data_id) - if not permissions.check_permissions(request, db): + db = get_object_or_404(DataSet, id=data_id) + if not permissions.is_owner(request, db): return HttpResponseForbidden("Must be the dataset owner to manage access") serializer = AccessManagementSerializer(data=request.data) serializer.is_valid() From 870c1b928588e3b310ab8d25f56d47cc8650c203 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Mar 2025 16:26:12 -0400 Subject: [PATCH 322/675] Tests for listing users with access to a dataset --- .../fair_database/data/test/test_dataset.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index ee2d72c7d..236de665d 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -53,6 +53,9 @@ same as get I think delete""" +# TODO: test unauthorized requests +# TODO: test permissions for users w/ access granted + class TestDataSet(APITestCase): @classmethod @@ -103,8 +106,6 @@ def test_list_unauthenticated(self): request.data, {"dataset_ids": {1: "Dataset 1", 3: "Dataset 3"}} ) - # TODO: test unauthorized gets - def test_list_username(self): request = self.auth_client1.get("/v1/data/set/", data={"username": "testUser1"}) self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -280,6 +281,22 @@ def setUpTestData(cls): cls.client1.force_authenticate(cls.user1) cls.client2.force_authenticate(cls.user2) + def test_list_access_private(self): + request = self.client1.get("/v1/data/set/1/users/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(request.data, {"data_id": 1, "name": "Dataset 1", "users": []}) + + def test_list_access_shared(self): + request = self.client1.get("/v1/data/set/2/users/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, {"data_id": 2, "name": "Dataset 2", "users": ["testUser2"]} + ) + + def test_list_access_unauthorized(self): + request = self.client2.get("/v1/data/set/2/users/") + self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + @classmethod def tearDownClass(cls): cls.private_dataset.delete() From 3232681508c83cd7a6f7d2f14cee86731ba346af Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 11 Mar 2025 16:36:52 -0400 Subject: [PATCH 323/675] Test granting access to dataset --- sasdata/fair_database/data/test/test_dataset.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 236de665d..ccdf9a6fd 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -297,6 +297,17 @@ def test_list_access_unauthorized(self): request = self.client2.get("/v1/data/set/2/users/") self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + def test_grant_access(self): + request1 = self.client1.put( + "/v1/data/set/1/users/", data={"username": "testUser2", "access": True} + ) + request2 = self.client2.get("/v1/data/set/1/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertIn( # codespell:ignore + self.user2, DataSet.objects.get(id=1).users.all() + ) + @classmethod def tearDownClass(cls): cls.private_dataset.delete() From b1910eb3107fe27f5b38dc515fc7b90abd787b9a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 12 Mar 2025 14:36:09 -0400 Subject: [PATCH 324/675] Fix dataset creation --- sasdata/fair_database/data/serializers.py | 22 ++++++++++++++++++++-- sasdata/fair_database/data/views.py | 9 +++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 3c19d3d10..88ac8e9e7 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -28,7 +28,10 @@ class Meta: class DataSetSerializer(serializers.ModelSerializer): # TODO: custom validation, maybe custom serialization handling of current_user # TODO: account for nested serialization - metadata = MetaDataSerializer() + metadata = MetaDataSerializer(read_only=False) + files = serializers.PrimaryKeyRelatedField( + required=False, many=True, allow_null=True, queryset=DataFile + ) class Meta: model = DataSet @@ -39,9 +42,24 @@ def create(self, validated_data): metadata = MetaDataSerializer.create( MetaDataSerializer(), validated_data=metadata_raw ) - dataset = DataSet.objects.update_or_create(**validated_data, metadata=metadata) + dataset = DataSet.objects.create(metadata=metadata, **validated_data) return dataset + # TODO: account for updating other attributes + # TODO: account for metadata potentially being null + def update(self, instance, validated_data): + if "metadata" in validated_data: + metadata_raw = validated_data.pop("metadata") + new_metadata = MetaDataSerializer.update( + MetaDataSerializer(), instance.metadata, validated_data=metadata_raw + ) + instance.metadata = new_metadata + instance.save() + instance.is_public = validated_data.get("is_public", instance.is_public) + instance.name = validated_data.get("name", instance.name) + instance.save() + return instance + class QuantitySerializer(serializers.ModelSerializer): class Meta: diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index c133e254a..5ea1e8018 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -212,11 +212,12 @@ def post(self, request, version=None): # TODO: JSON deserialization probably # TODO: revisit request data format serializer = DataSetSerializer(data=request.data) - db = None if serializer.is_valid(): - db = serializer.save() - response = {"dataset_id": db.id, "name": db.name} - return Response(data=response, status=status.HTTP_201_CREATED) + serializer.save() + db = serializer.instance + response = {"dataset_id": db.id, "name": db.name, "is_public": db.is_public} + return Response(data=response, status=status.HTTP_201_CREATED) + return HttpResponseBadRequest() # create a dataset def put(self, request, version=None): From 0f08cc7b69a9e26073e5b1a296b1e107f06a6c4c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 12 Mar 2025 14:49:37 -0400 Subject: [PATCH 325/675] Set owner when data is created --- sasdata/fair_database/data/serializers.py | 2 ++ sasdata/fair_database/data/views.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 88ac8e9e7..96eb50ffd 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -38,6 +38,8 @@ class Meta: fields = "__all__" def create(self, validated_data): + if self.context.user.is_authenticated: + validated_data["current_user"] = self.context.user metadata_raw = validated_data.pop("metadata") metadata = MetaDataSerializer.create( MetaDataSerializer(), validated_data=metadata_raw diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 5ea1e8018..e6ea0d9e6 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -211,7 +211,7 @@ def get(self, request, version=None): def post(self, request, version=None): # TODO: JSON deserialization probably # TODO: revisit request data format - serializer = DataSetSerializer(data=request.data) + serializer = DataSetSerializer(data=request.data, context=request) if serializer.is_valid(): serializer.save() db = serializer.instance From 7693dd25dd7cc19df5263ba72a5c0aa8f9179ecc Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 12 Mar 2025 15:15:18 -0400 Subject: [PATCH 326/675] Disallow creating private data without an owner --- sasdata/fair_database/data/serializers.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 96eb50ffd..bae71e198 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -26,8 +26,6 @@ class Meta: class DataSetSerializer(serializers.ModelSerializer): - # TODO: custom validation, maybe custom serialization handling of current_user - # TODO: account for nested serialization metadata = MetaDataSerializer(read_only=False) files = serializers.PrimaryKeyRelatedField( required=False, many=True, allow_null=True, queryset=DataFile @@ -37,6 +35,22 @@ class Meta: model = DataSet fields = "__all__" + def validate(self, data): + if ( + not self.context.user.is_authenticated + and "is_public" in data + and not data["is_public"] + ): + raise serializers.ValidationError("private data must have an owner") + if "current_user" in data: + if "is_public" in data: + if not "is_public": + raise serializers.ValidationError("private data must have an owner") + else: + if not self.instance.is_public: + raise serializers.ValidationError("private data must have an owner") + return data + def create(self, validated_data): if self.context.user.is_authenticated: validated_data["current_user"] = self.context.user From ae79da45eee83e9b19ba85309172b4670236deab Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 12 Mar 2025 15:15:59 -0400 Subject: [PATCH 327/675] Finish tests for DataSetView --- .../fair_database/data/test/test_dataset.py | 77 +++++++++++++++++-- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index ccdf9a6fd..bbe9dc9fa 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -1,4 +1,5 @@ from django.contrib.auth.models import User +from django.db.models import Max from rest_framework.test import APIClient, APITestCase from rest_framework import status @@ -60,12 +61,25 @@ class TestDataSet(APITestCase): @classmethod def setUpTestData(cls): + cls.empty_metadata = { + "title": "New Metadata", + "run": "X", + "description": "test", + "instrument": {}, + "process": {}, + "sample": {}, + "transmission_spectrum": {}, + "raw_metadata": {}, + } cls.user1 = User.objects.create_user( id=1, username="testUser1", password="secret" ) cls.user2 = User.objects.create_user( id=2, username="testUser2", password="secret" ) + cls.user3 = User.objects.create_user( + id=3, username="testUser3", password="secret" + ) cls.public_dataset = DataSet.objects.create( id=1, current_user=cls.user1, @@ -79,10 +93,13 @@ def setUpTestData(cls): cls.unowned_dataset = DataSet.objects.create( id=3, is_public=True, name="Dataset 3", metadata=None ) + cls.private_dataset.users.add(cls.user3) cls.auth_client1 = APIClient() cls.auth_client2 = APIClient() + cls.auth_client3 = APIClient() cls.auth_client1.force_authenticate(cls.user1) cls.auth_client2.force_authenticate(cls.user2) + cls.auth_client3.force_authenticate(cls.user3) def test_list_private(self): request = self.auth_client1.get("/v1/data/set/") @@ -99,6 +116,14 @@ def test_list_public(self): request.data, {"dataset_ids": {1: "Dataset 1", 3: "Dataset 3"}} ) + def test_list_granted_access(self): + request = self.auth_client3.get("/v1/data/set/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + {"dataset_ids": {1: "Dataset 1", 2: "Dataset 2", 3: "Dataset 3"}}, + ) + def test_list_unauthenticated(self): request = self.client.get("/v1/data/set/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -123,18 +148,57 @@ def test_list_username_unauthenticated(self): self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, {"dataset_ids": {1: "Dataset 1"}}) - # TODO: test listing by user that doesn't exist + def test_list_wrong_username(self): + request = self.auth_client1.get("/v1/data/set/", {"username": "fakeUser1"}) + self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) + # TODO: test listing by other parameters if functionality is added for that - # TODO: write test for post - probably will need to change post method to account for owner def test_dataset_created(self): - pass + dataset = {"name": "New Dataset", "metadata": self.empty_metadata} + request = self.auth_client1.post("/v1/data/set/", data=dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_metadata = new_dataset.metadata + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual( + request.data, + {"dataset_id": max_id, "name": "New Dataset", "is_public": False}, + ) + self.assertEqual(new_dataset.name, "New Dataset") + self.assertEqual(new_metadata.title, "New Metadata") + self.assertEqual(new_dataset.current_user.username, "testUser1") + new_dataset.delete() + new_metadata.delete() def test_dataset_created_unauthenticated(self): - pass + dataset = { + "name": "New Dataset", + "metadata": self.empty_metadata, + "is_public": True, + } + request = self.client.post("/v1/data/set/", data=dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_metadata = new_dataset.metadata + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual( + request.data, + {"dataset_id": max_id, "name": "New Dataset", "is_public": True}, + ) + self.assertEqual(new_dataset.name, "New Dataset") + self.assertIsNone(new_dataset.current_user) + new_dataset.delete() + new_metadata.delete() def test_no_private_unowned_dataset(self): - pass + dataset = { + "name": "Disallowed Dataset", + "metadata": self.empty_metadata, + "is_public": False, + } + request = self.client.post("/v1/data/set/", data=dataset, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) @classmethod def tearDownClass(cls): @@ -143,6 +207,7 @@ def tearDownClass(cls): cls.unowned_dataset.delete() cls.user1.delete() cls.user2.delete() + cls.user3.delete() class TestSingleDataSet(APITestCase): @@ -247,6 +312,8 @@ def test_update_public_dataset(self): self.assertEqual(DataSet.objects.get(id=1).name, "Different name") self.public_dataset.save() + # TODO: test updating metadata + def test_delete_dataset(self): request = self.auth_client1.delete("/v1/data/set/2/") self.assertEqual(request.status_code, status.HTTP_200_OK) From 76fb0b514a6fcc7cbb469c683245125cd2ab76dd Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 12 Mar 2025 15:37:07 -0400 Subject: [PATCH 328/675] Fix error in accessing request user from serializer --- sasdata/fair_database/data/serializers.py | 6 +++--- sasdata/fair_database/data/views.py | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index bae71e198..ba381df5a 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -37,7 +37,7 @@ class Meta: def validate(self, data): if ( - not self.context.user.is_authenticated + not self.context["request"].user.is_authenticated and "is_public" in data and not data["is_public"] ): @@ -52,8 +52,8 @@ def validate(self, data): return data def create(self, validated_data): - if self.context.user.is_authenticated: - validated_data["current_user"] = self.context.user + if self.context["request"].user.is_authenticated: + validated_data["current_user"] = self.context["request"].user metadata_raw = validated_data.pop("metadata") metadata = MetaDataSerializer.create( MetaDataSerializer(), validated_data=metadata_raw diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index e6ea0d9e6..25cf428a8 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -211,7 +211,7 @@ def get(self, request, version=None): def post(self, request, version=None): # TODO: JSON deserialization probably # TODO: revisit request data format - serializer = DataSetSerializer(data=request.data, context=request) + serializer = DataSetSerializer(data=request.data, context={"request": request}) if serializer.is_valid(): serializer.save() db = serializer.instance @@ -249,7 +249,9 @@ def put(self, request, data_id, version=None): db = get_object_or_404(DataSet, id=data_id) if not permissions.check_permissions(request, db): return HttpResponseForbidden("Cannot modify a dataset you do not own") - serializer = DataSetSerializer(db, request.data, partial=True) + serializer = DataSetSerializer( + db, request.data, context={"request": request}, partial=True + ) if serializer.is_valid(): serializer.save() data = {"data_id": db.id, "name": db.name, "is_public": db.is_public} From 56b35f4ea568e2ba80ea0ab2493498b655a625e6 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 12 Mar 2025 15:42:04 -0400 Subject: [PATCH 329/675] Modify existing SingleDataSet tests --- .../fair_database/data/test/test_dataset.py | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index bbe9dc9fa..fa75de8bf 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -219,6 +219,9 @@ def setUpTestData(cls): cls.user2 = User.objects.create_user( id=2, username="testUser2", password="secret" ) + cls.user3 = User.objects.create_user( + id=3, username="testUser3", password="secret" + ) cls.public_dataset = DataSet.objects.create( id=1, current_user=cls.user1, @@ -232,21 +235,26 @@ def setUpTestData(cls): cls.unowned_dataset = DataSet.objects.create( id=3, is_public=True, name="Dataset 3", metadata=None ) + cls.private_dataset.users.add(cls.user3) cls.auth_client1 = APIClient() cls.auth_client2 = APIClient() + cls.auth_client3 = APIClient() cls.auth_client1.force_authenticate(cls.user1) cls.auth_client2.force_authenticate(cls.user2) + cls.auth_client3.force_authenticate(cls.user3) # TODO: change load return data def test_load_private_dataset(self): - request = self.auth_client1.get("/v1/data/set/2/") - self.assertEqual(request.status_code, status.HTTP_200_OK) + request1 = self.auth_client1.get("/v1/data/set/2/") + request2 = self.auth_client3.get("/v1/data/set/2/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertDictEqual( - request.data, + request1.data, { "id": 2, "current_user": 1, - "users": [], + "users": [3], "is_public": False, "name": "Dataset 2", "files": [], @@ -296,10 +304,14 @@ def test_load_private_dataset_unauthorized(self): self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) - # TODO: check put return data def test_update_private_dataset(self): - request = self.auth_client1.put("/v1/data/set/2/", data={"is_public": True}) - self.assertEqual(request.status_code, status.HTTP_200_OK) + request1 = self.auth_client1.put("/v1/data/set/2/", data={"is_public": True}) + request2 = self.auth_client3.put("/v1/data/set/2/", data={"is_public": False}) + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual( + request1.data, {"data_id": 2, "name": "Dataset 2", "is_public": True} + ) self.assertTrue(DataSet.objects.get(id=2).is_public) self.private_dataset.save() self.assertFalse(DataSet.objects.get(id=2).is_public) @@ -309,14 +321,18 @@ def test_update_public_dataset(self): "/v1/data/set/1/", data={"name": "Different name"} ) self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, {"data_id": 1, "name": "Different name", "is_public": True} + ) self.assertEqual(DataSet.objects.get(id=1).name, "Different name") self.public_dataset.save() - # TODO: test updating metadata + # TODO: test updating metadata once metadata is figured out def test_delete_dataset(self): request = self.auth_client1.delete("/v1/data/set/2/") self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(request.data, {"success": True}) self.assertRaises(DataSet.DoesNotExist, DataSet.objects.get, id=2) self.private_dataset = DataSet.objects.create( id=2, current_user=self.user1, name="Dataset 2", metadata=None @@ -329,6 +345,7 @@ def tearDownClass(cls): cls.unowned_dataset.delete() cls.user1.delete() cls.user2.delete() + cls.user3.delete() class TestDataSetAccessManagement(APITestCase): From 3c0eb2cc0c29bba4b5295f78b38e0d8c16dab278 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 12 Mar 2025 15:45:27 -0400 Subject: [PATCH 330/675] Tests for unauthorized deletes --- sasdata/fair_database/data/test/test_dataset.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index fa75de8bf..34e30b77b 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -338,6 +338,20 @@ def test_delete_dataset(self): id=2, current_user=self.user1, name="Dataset 2", metadata=None ) + def test_delete_public_dataset(self): + request = self.auth_client1.delete("/v1/data/set/1/") + self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + + def test_delete_unowned_dataset(self): + request = self.auth_client1.delete("/v1/data/set/3/") + self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + + def test_delete_dataset_unauthorized(self): + request1 = self.auth_client2.delete("/v1/data/set/1/") + request2 = self.auth_client3.delete("/v1/data/set/2/") + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + @classmethod def tearDownClass(cls): cls.public_dataset.delete() From 5a573bf7898dcf2125a540800291fe1e045b5d45 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 12 Mar 2025 15:58:34 -0400 Subject: [PATCH 331/675] Finish DataSetAccessManagement tests --- .../fair_database/data/test/test_dataset.py | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 34e30b77b..71d497464 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -380,15 +380,17 @@ def setUpTestData(cls): cls.client2.force_authenticate(cls.user2) def test_list_access_private(self): - request = self.client1.get("/v1/data/set/1/users/") - self.assertEqual(request.status_code, status.HTTP_200_OK) - self.assertEqual(request.data, {"data_id": 1, "name": "Dataset 1", "users": []}) + request1 = self.client1.get("/v1/data/set/1/users/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual( + request1.data, {"data_id": 1, "name": "Dataset 1", "users": []} + ) def test_list_access_shared(self): - request = self.client1.get("/v1/data/set/2/users/") - self.assertEqual(request.status_code, status.HTTP_200_OK) + request1 = self.client1.get("/v1/data/set/2/users/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual( - request.data, {"data_id": 2, "name": "Dataset 2", "users": ["testUser2"]} + request1.data, {"data_id": 2, "name": "Dataset 2", "users": ["testUser2"]} ) def test_list_access_unauthorized(self): @@ -405,6 +407,23 @@ def test_grant_access(self): self.assertIn( # codespell:ignore self.user2, DataSet.objects.get(id=1).users.all() ) + self.private_dataset.users.remove(self.user2) + + def test_revoke_access(self): + request1 = self.client1.put( + "/v1/data/set/2/users/", data={"username": "testUser2", "access": False} + ) + request2 = self.client2.get("/v1/data/set/2/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + self.assertNotIn(self.user2, DataSet.objects.get(id=2).users.all()) + self.shared_dataset.users.add(self.user2) + + def test_revoke_access_unauthorized(self): + request1 = self.client2.put( + "/v1/data/set/2/users/", data={"username": "testUser2", "access": False} + ) + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) @classmethod def tearDownClass(cls): From be019949b7b388e7c444aed36ac14cb3c5b831fb Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 13 Mar 2025 11:40:45 -0400 Subject: [PATCH 332/675] Allow private data owner to change, not just public --- sasdata/fair_database/data/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index ba381df5a..3f1912bf4 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -42,7 +42,7 @@ def validate(self, data): and not data["is_public"] ): raise serializers.ValidationError("private data must have an owner") - if "current_user" in data: + if "current_user" in data and data["current_user"] is None: if "is_public" in data: if not "is_public": raise serializers.ValidationError("private data must have an owner") From 43a001004c3e7829e706cfa0f835377a4ffced55 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 13 Mar 2025 11:57:25 -0400 Subject: [PATCH 333/675] Document dataset tests + one more test --- sasdata/fair_database/data/models.py | 1 + .../fair_database/data/test/test_dataset.py | 101 ++++++++---------- 2 files changed, 47 insertions(+), 55 deletions(-) diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 78cda5ba9..ad6f0e8cd 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -84,6 +84,7 @@ class Quantity(models.Model): hash = models.IntegerField() +# TODO: revisit metadata when sasdata metadata is rewritten class MetaData(models.Model): """Database model for scattering metadata""" diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 71d497464..3400e23e2 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -5,60 +5,10 @@ from data.models import DataSet -# test GET -# list datasets - include public and owned, disinclude private unowned -# get one dataset - succeeds if public or owned, fails if private and unowned -# get list of people with access to dataset (owner) -# fail to get list of people with access to dataset (not owner) - -# test POST -# create a public dataset -# create a private dataset -# can't create an unowned private dataset - -# test PUT -# edit an owned dataset -# can't edit an unowned dataset -# change access to a dataset - -# test DELETE -# delete an owned private dataset -# can't delete an unowned dataset -# can't delete a public dataset - -""" -DataSetView: - get - - requires 3 users, private data, public data, probably unowned data - for authenticated user (can see public data and own private data) - - can't see someone else's private data (may or may not be a separate test) - for unauthenticated user (can see public data) - with username specified - with nonexistent username specified - post - - requires 2 users, no data - for authenticated user - for unauthenticated user, public - for unauthenticated, private (fails) - put - - probably 1 user, no data - should be same as post - -SingleDataSetView: - get - for owned private data - for unowned private data authenticated - for private data unauthenticated - for public data - put - same as get I think - delete""" - -# TODO: test unauthorized requests -# TODO: test permissions for users w/ access granted - class TestDataSet(APITestCase): + """Test HTTP methods of DataSetView.""" + @classmethod def setUpTestData(cls): cls.empty_metadata = { @@ -101,6 +51,7 @@ def setUpTestData(cls): cls.auth_client2.force_authenticate(cls.user2) cls.auth_client3.force_authenticate(cls.user3) + # Test a user can list their own private data def test_list_private(self): request = self.auth_client1.get("/v1/data/set/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -109,6 +60,7 @@ def test_list_private(self): {"dataset_ids": {1: "Dataset 1", 2: "Dataset 2", 3: "Dataset 3"}}, ) + # Test a user can see others' public but not private data in list def test_list_public(self): request = self.auth_client2.get("/v1/data/set/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -116,6 +68,7 @@ def test_list_public(self): request.data, {"dataset_ids": {1: "Dataset 1", 3: "Dataset 3"}} ) + # Test a user can see private data they have been granted access to def test_list_granted_access(self): request = self.auth_client3.get("/v1/data/set/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -124,6 +77,7 @@ def test_list_granted_access(self): {"dataset_ids": {1: "Dataset 1", 2: "Dataset 2", 3: "Dataset 3"}}, ) + # Test an unauthenticated user can list public data def test_list_unauthenticated(self): request = self.client.get("/v1/data/set/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -131,6 +85,7 @@ def test_list_unauthenticated(self): request.data, {"dataset_ids": {1: "Dataset 1", 3: "Dataset 3"}} ) + # Test a user can see all data listed by their username def test_list_username(self): request = self.auth_client1.get("/v1/data/set/", data={"username": "testUser1"}) self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -138,22 +93,26 @@ def test_list_username(self): request.data, {"dataset_ids": {1: "Dataset 1", 2: "Dataset 2"}} ) + # Test a user can list public data by another user's username def test_list_username_2(self): request = self.auth_client1.get("/v1/data/set/", {"username": "testUser2"}) self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, {"dataset_ids": {}}) + # Test an unauthenticated user can list public data by a username def test_list_username_unauthenticated(self): request = self.client.get("/v1/data/set/", {"username": "testUser1"}) self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, {"dataset_ids": {1: "Dataset 1"}}) + # Test listing by a username that doesn't exist def test_list_wrong_username(self): request = self.auth_client1.get("/v1/data/set/", {"username": "fakeUser1"}) self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) # TODO: test listing by other parameters if functionality is added for that + # Test creating a dataset with associated metadata def test_dataset_created(self): dataset = {"name": "New Dataset", "metadata": self.empty_metadata} request = self.auth_client1.post("/v1/data/set/", data=dataset, format="json") @@ -171,6 +130,7 @@ def test_dataset_created(self): new_dataset.delete() new_metadata.delete() + # Test creating a dataset while unauthenticated def test_dataset_created_unauthenticated(self): dataset = { "name": "New Dataset", @@ -191,6 +151,7 @@ def test_dataset_created_unauthenticated(self): new_dataset.delete() new_metadata.delete() + # Test that a private dataset cannot be created without an owner def test_no_private_unowned_dataset(self): dataset = { "name": "Disallowed Dataset", @@ -211,6 +172,8 @@ def tearDownClass(cls): class TestSingleDataSet(APITestCase): + """Tests for HTTP methods of SingleDataSetView.""" + @classmethod def setUpTestData(cls): cls.user1 = User.objects.create_user( @@ -244,6 +207,7 @@ def setUpTestData(cls): cls.auth_client3.force_authenticate(cls.user3) # TODO: change load return data + # Test successfully accessing a private dataset def test_load_private_dataset(self): request1 = self.auth_client1.get("/v1/data/set/2/") request2 = self.auth_client3.get("/v1/data/set/2/") @@ -262,6 +226,7 @@ def test_load_private_dataset(self): }, ) + # Test successfully accessing a public dataset def test_load_public_dataset(self): request1 = self.client.get("/v1/data/set/1/") request2 = self.auth_client2.get("/v1/data/set/1/") @@ -280,6 +245,7 @@ def test_load_public_dataset(self): }, ) + # Test successfully accessing an unowned public dataset def test_load_unowned_dataset(self): request1 = self.auth_client1.get("/v1/data/set/3/") request2 = self.client.get("/v1/data/set/3/") @@ -298,12 +264,14 @@ def test_load_unowned_dataset(self): }, ) + # Test unsuccessfully accessing a private dataset def test_load_private_dataset_unauthorized(self): request1 = self.auth_client2.get("/v1/data/set/2/") request2 = self.client.get("/v1/data/set/2/") self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + # Test only owner can change a private dataset def test_update_private_dataset(self): request1 = self.auth_client1.put("/v1/data/set/2/", data={"is_public": True}) request2 = self.auth_client3.put("/v1/data/set/2/", data={"is_public": False}) @@ -316,19 +284,31 @@ def test_update_private_dataset(self): self.private_dataset.save() self.assertFalse(DataSet.objects.get(id=2).is_public) + # Test changing a public dataset def test_update_public_dataset(self): - request = self.auth_client1.put( + request1 = self.auth_client1.put( "/v1/data/set/1/", data={"name": "Different name"} ) - self.assertEqual(request.status_code, status.HTTP_200_OK) + request2 = self.auth_client2.put("/v1/data/set/1/", data={"is_public": False}) + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual( - request.data, {"data_id": 1, "name": "Different name", "is_public": True} + request1.data, {"data_id": 1, "name": "Different name", "is_public": True} ) self.assertEqual(DataSet.objects.get(id=1).name, "Different name") self.public_dataset.save() # TODO: test updating metadata once metadata is figured out + # TODO: test invalid updates if and when those are figured out + # Test changing an unowned dataset + def test_update_unowned_dataset(self): + request1 = self.auth_client1.put("/v1/data/set/3/", data={"current_user": 1}) + request2 = self.client.put("/v1/data/set/3/", data={"name": "Different name"}) + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + + # Test deleting a dataset def test_delete_dataset(self): request = self.auth_client1.delete("/v1/data/set/2/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -338,14 +318,17 @@ def test_delete_dataset(self): id=2, current_user=self.user1, name="Dataset 2", metadata=None ) + # Test cannot delete a public dataset def test_delete_public_dataset(self): request = self.auth_client1.delete("/v1/data/set/1/") self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + # Test cannot delete an unowned dataset def test_delete_unowned_dataset(self): request = self.auth_client1.delete("/v1/data/set/3/") self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + # Test cannot delete another user's dataset def test_delete_dataset_unauthorized(self): request1 = self.auth_client2.delete("/v1/data/set/1/") request2 = self.auth_client3.delete("/v1/data/set/2/") @@ -363,6 +346,8 @@ def tearDownClass(cls): class TestDataSetAccessManagement(APITestCase): + """Tests for HTTP methods of DataSetUsersView.""" + @classmethod def setUpTestData(cls): cls.user1 = User.objects.create_user(username="testUser1", password="secret") @@ -379,6 +364,7 @@ def setUpTestData(cls): cls.client1.force_authenticate(cls.user1) cls.client2.force_authenticate(cls.user2) + # Test listing no users with access def test_list_access_private(self): request1 = self.client1.get("/v1/data/set/1/users/") self.assertEqual(request1.status_code, status.HTTP_200_OK) @@ -386,6 +372,7 @@ def test_list_access_private(self): request1.data, {"data_id": 1, "name": "Dataset 1", "users": []} ) + # Test listing users with access def test_list_access_shared(self): request1 = self.client1.get("/v1/data/set/2/users/") self.assertEqual(request1.status_code, status.HTTP_200_OK) @@ -393,10 +380,12 @@ def test_list_access_shared(self): request1.data, {"data_id": 2, "name": "Dataset 2", "users": ["testUser2"]} ) + # Test only owner can view access def test_list_access_unauthorized(self): request = self.client2.get("/v1/data/set/2/users/") self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + # Test granting access to a dataset def test_grant_access(self): request1 = self.client1.put( "/v1/data/set/1/users/", data={"username": "testUser2", "access": True} @@ -409,6 +398,7 @@ def test_grant_access(self): ) self.private_dataset.users.remove(self.user2) + # Test revoking access to a dataset def test_revoke_access(self): request1 = self.client1.put( "/v1/data/set/2/users/", data={"username": "testUser2", "access": False} @@ -419,6 +409,7 @@ def test_revoke_access(self): self.assertNotIn(self.user2, DataSet.objects.get(id=2).users.all()) self.shared_dataset.users.add(self.user2) + # Test only the owner can change access def test_revoke_access_unauthorized(self): request1 = self.client2.put( "/v1/data/set/2/users/", data={"username": "testUser2", "access": False} From b7d8e8f0f402a622e06cc2dedb4ea5a70a391035 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 13 Mar 2025 13:13:49 -0400 Subject: [PATCH 334/675] Documentation for datafile tests --- sasdata/fair_database/data/test/test_datafile.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sasdata/fair_database/data/test/test_datafile.py b/sasdata/fair_database/data/test/test_datafile.py index 45dc0d98f..28e3078bc 100644 --- a/sasdata/fair_database/data/test/test_datafile.py +++ b/sasdata/fair_database/data/test/test_datafile.py @@ -18,6 +18,8 @@ def find(filename): class TestLists(TestCase): + """Test get methods for DataFile.""" + @classmethod def setUpTestData(cls): cls.public_test_data = DataFile.objects.create( @@ -83,6 +85,8 @@ def tearDownClass(cls): class TestingDatabase(APITestCase): + """Test non-get methods for DataFile.""" + @classmethod def setUpTestData(cls): cls.user = User.objects.create_user( @@ -229,6 +233,8 @@ def tearDownClass(cls): class TestAccessManagement(TestCase): + """Test viewing and managing access for a file.""" + @classmethod def setUpTestData(cls): cls.user1 = User.objects.create_user(username="testUser", password="secret") @@ -283,6 +289,7 @@ def test_remove_access(self): self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) + # test removing access from a user that already lacks access def test_remove_no_access(self): data = {"username": "testUser2", "access": False} request1 = self.client2.get("/v1/data/load/1/") @@ -292,6 +299,7 @@ def test_remove_no_access(self): self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) + # test owner's access cannot be removed def test_cant_revoke_own_access(self): data = {"username": "testUser", "access": False} request1 = self.client1.put("/v1/data/manage/1/", data=data) @@ -299,6 +307,7 @@ def test_cant_revoke_own_access(self): self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) + # test giving access to a user that already has access def test_grant_existing_access(self): data = {"username": "testUser2", "access": True} request1 = self.client2.get("/v1/data/load/2/") @@ -308,18 +317,21 @@ def test_grant_existing_access(self): self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertEqual(request3.status_code, status.HTTP_200_OK) + # test that access is read-only for the file def test_no_edit_access(self): data = {"is_public": True} request = self.client2.put("/v1/data/upload/2/", data=data) self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) self.assertFalse(self.shared_test_data.is_public) + # test that only the owner can view who has access def test_only_view_access_to_owned_file(self): request1 = self.client2.get("/v1/data/manage/1/") request2 = self.client2.get("/v1/data/manage/2/") self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + # test that only the owner can change access def test_only_edit_access_to_owned_file(self): data1 = {"username": "testUser2", "access": True} data2 = {"username": "testUser1", "access": False} From cd7d581f8c0fcede7fb39409690c58a63d3dd0e5 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 13 Mar 2025 16:20:50 -0400 Subject: [PATCH 335/675] Tests to make sure a user can't specify a data object id --- .../fair_database/data/test/test_datafile.py | 20 +++++++++++++++++++ .../fair_database/data/test/test_dataset.py | 18 +++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/sasdata/fair_database/data/test/test_datafile.py b/sasdata/fair_database/data/test/test_datafile.py index 28e3078bc..5f3394e62 100644 --- a/sasdata/fair_database/data/test/test_datafile.py +++ b/sasdata/fair_database/data/test/test_datafile.py @@ -138,6 +138,26 @@ def test_is_data_being_created_no_user(self): ) DataFile.objects.get(id=max_id).delete() + # Test whether a user can overwrite data by specifying an in-use id + def test_no_data_overwrite(self): + file = open(find("apoferritin.txt")) + data = {"is_public": True, "file": file, id: 1} + request = self.client1.post("/v1/data/upload/", data=data) + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual(DataFile.objects.get(id=1).file_name, "cyl_400_20.txt") + max_id = DataFile.objects.aggregate(Max("id"))["id__max"] + self.assertEqual( + request.data, + { + "current_user": "testUser", + "authenticated": True, + "file_id": max_id, + "file_alternative_name": "apoferritin.txt", + "is_public": True, + }, + ) + DataFile.objects.get(id=max_id).delete() + # Test updating file def test_does_file_upload_update(self): file = open(find("cyl_testdata1.txt")) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 3400e23e2..df1e54cab 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -161,6 +161,24 @@ def test_no_private_unowned_dataset(self): request = self.client.post("/v1/data/set/", data=dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + # Test whether a user can overwrite data by specifying an in-use id + def test_no_data_overwrite(self): + dataset = { + "id": 2, + "name": "Overwrite Dataset", + "metadata": self.empty_metadata, + "is_public": True, + } + request = self.auth_client2.post("/v1/data/set/", data=dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual(DataSet.objects.get(id=2).name, "Dataset 2") + self.assertEqual( + request.data, + {"dataset_id": max_id, "name": "Overwrite Dataset", "is_public": True}, + ) + DataSet.objects.get(id=max_id).delete() + @classmethod def tearDownClass(cls): cls.public_dataset.delete() From b8013964ca175854b818bc596be5c4a98d0e0e7c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 14 Mar 2025 10:17:38 -0400 Subject: [PATCH 336/675] Add operation/parameters fields to OperationTree model --- sasdata/data.py | 1 + ...tree_operation_operationtree_parameters.py | 23 +++++++++++++++++++ sasdata/fair_database/data/models.py | 4 ++++ 3 files changed, 28 insertions(+) create mode 100644 sasdata/fair_database/data/migrations/0011_operationtree_operation_operationtree_parameters.py diff --git a/sasdata/data.py b/sasdata/data.py index d4cb03321..3977200dd 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -86,6 +86,7 @@ def deserialise_json(json_data: dict) -> "SasData": def serialise(self) -> str: return json.dumps(self._serialise_json()) + # TODO: fix serializers eventually def _serialise_json(self) -> dict[str, Any]: return { "name": self.name, diff --git a/sasdata/fair_database/data/migrations/0011_operationtree_operation_operationtree_parameters.py b/sasdata/fair_database/data/migrations/0011_operationtree_operation_operationtree_parameters.py new file mode 100644 index 000000000..f4a943881 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0011_operationtree_operation_operationtree_parameters.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.6 on 2025-03-14 14:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0010_alter_dataset_metadata"), + ] + + operations = [ + migrations.AddField( + model_name="operationtree", + name="operation", + field=models.CharField(default="undefined", max_length=10), + preserve_default=False, + ), + migrations.AddField( + model_name="operationtree", + name="parameters", + field=models.JSONField(default=dict), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index ad6f0e8cd..2d87f16b2 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -121,6 +121,10 @@ class OperationTree(models.Model): dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) # operation + operation = models.CharField(max_length=10) + + # parameters + parameters = models.JSONField(default=dict) # previous operation parent_operation = models.ForeignKey( From f4a41dbb6bc5ad7b420fce6eb5e85f2c861a4ca8 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 14 Mar 2025 11:17:27 -0400 Subject: [PATCH 337/675] Change response status to 401 for unauthenticated --- sasdata/fair_database/data/views.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 25cf428a8..2e5ed93a5 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -5,6 +5,7 @@ from django.http import ( HttpResponseBadRequest, HttpResponseForbidden, + HttpResponse, Http404, FileResponse, ) @@ -40,6 +41,8 @@ def list_data(request, username=None, version=None): data_list = {"public_data_ids": {}} for x in public_data: if not permissions.check_permissions(request, x): + if not request.user.is_authenticated: + return HttpResponse("Unauthorized", status=401) return HttpResponseForbidden() data_list["public_data_ids"][x.id] = x.file_name return Response(data_list) @@ -52,6 +55,8 @@ def data_info(request, db_id, version=None): loader = Loader() data_db = get_object_or_404(DataFile, id=db_id) if not permissions.check_permissions(request, data_db): + if not request.user.is_authenticated: + return HttpResponse("Must be authenticated to view", status=401) return HttpResponseForbidden( "Data is either not public or wrong auth token" ) @@ -98,6 +103,8 @@ def upload(request, data_id=None, version=None): elif request.method == "PUT": db = get_object_or_404(DataFile, id=data_id) if not permissions.check_permissions(request, db): + if not request.user.is_authenticated: + return HttpResponse("must be authenticated to modify", status=401) return HttpResponseForbidden("must be the data owner to modify") form = DataFileForm(request.data, request.FILES, instance=db) if form.is_valid(): @@ -132,6 +139,8 @@ def upload(request, data_id=None, version=None): def manage_access(request, data_id, version=None): db = get_object_or_404(DataFile, id=data_id) if not permissions.is_owner(request, db): + if not request.user.is_authenticated: + return HttpResponse("Must be authenticated to manage access", status=401) return HttpResponseForbidden("Must be the data owner to manage access") if request.method == "GET": response_data = { @@ -163,6 +172,8 @@ def manage_access(request, data_id, version=None): def delete(request, data_id, version=None): db = get_object_or_404(DataFile, id=data_id) if not permissions.is_owner(request, db): + if not request.user.is_authenticated: + return HttpResponse("Must be authenticated to delete", status=401) return HttpResponseForbidden("Must be the data owner to delete") db.delete() return Response(data={"success": True}) @@ -174,6 +185,8 @@ def download(request, data_id, version=None): if request.method == "GET": data = get_object_or_404(DataFile, id=data_id) if not permissions.check_permissions(request, data): + if not request.user.is_authenticated: + return HttpResponse("Must be authenticated to download", status=401) return HttpResponseForbidden("data is private") # TODO add issues later try: @@ -238,6 +251,8 @@ class SingleDataSetView(APIView): def get(self, request, data_id, version=None): db = get_object_or_404(DataSet, id=data_id) if not permissions.check_permissions(request, db): + if not request.user.is_authenticated: + return HttpResponse("Must be authenticated to view dataset", status=401) return HttpResponseForbidden( "You do not have permission to view this dataset." ) @@ -248,6 +263,10 @@ def get(self, request, data_id, version=None): def put(self, request, data_id, version=None): db = get_object_or_404(DataSet, id=data_id) if not permissions.check_permissions(request, db): + if not request.user.is_authenticated: + return HttpResponse( + "Must be authenticated to modify dataset", status=401 + ) return HttpResponseForbidden("Cannot modify a dataset you do not own") serializer = DataSetSerializer( db, request.data, context={"request": request}, partial=True @@ -261,6 +280,10 @@ def put(self, request, data_id, version=None): def delete(self, request, data_id, version=None): db = get_object_or_404(DataSet, id=data_id) if not permissions.check_permissions(request, db): + if not request.user.is_authenticated: + return HttpResponse( + "Must be authenticated to delete a dataset", status=401 + ) return HttpResponseForbidden("Not authorized to delete") db.delete() return Response({"success": True}) @@ -280,6 +303,8 @@ class DataSetUsersView(APIView): def get(self, request, data_id, version=None): db = get_object_or_404(DataSet, id=data_id) if not permissions.is_owner(request, db): + if not request.user.is_authenticated: + return HttpResponse("Must be authenticated to view access", status=401) return HttpResponseForbidden("Must be the dataset owner to view access") response_data = { "data_id": db.id, @@ -292,6 +317,10 @@ def get(self, request, data_id, version=None): def put(self, request, data_id, version=None): db = get_object_or_404(DataSet, id=data_id) if not permissions.is_owner(request, db): + if not request.user.is_authenticated: + return HttpResponse( + "Must be authenticated to manage access", status=401 + ) return HttpResponseForbidden("Must be the dataset owner to manage access") serializer = AccessManagementSerializer(data=request.data) serializer.is_valid() From 9d0e0943416bb62042a9237b31918e9509963e89 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 14 Mar 2025 11:17:54 -0400 Subject: [PATCH 338/675] Change tests to reflect status code change --- sasdata/fair_database/data/test/test_datafile.py | 6 +++--- sasdata/fair_database/data/test/test_dataset.py | 4 ++-- .../fair_database/fair_database/test_permissions.py | 11 +++++------ 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/sasdata/fair_database/data/test/test_datafile.py b/sasdata/fair_database/data/test/test_datafile.py index 5f3394e62..128498d51 100644 --- a/sasdata/fair_database/data/test/test_datafile.py +++ b/sasdata/fair_database/data/test/test_datafile.py @@ -204,7 +204,7 @@ def test_unauthorized_file_upload_update(self): file = open(find("cyl_400_40.txt")) data = {"file": file, "is_public": False} request = self.client2.put("/v1/data/upload/1/", data=data) - self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request.status_code, status.HTTP_401_UNAUTHORIZED) # Test update nonexistent file fails def test_file_upload_update_not_found(self): @@ -224,7 +224,7 @@ def test_does_download(self): # Test file download fails when unauthorized def test_unauthorized_download(self): request2 = self.client2.get("/v1/data/1/download/") - self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) # Test download nonexistent file def test_download_nonexistent(self): @@ -243,7 +243,7 @@ def test_delete(self): # Test deleting a file fails when unauthorized def test_delete_unauthorized(self): request = self.client2.delete("/v1/data/delete/1/") - self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request.status_code, status.HTTP_401_UNAUTHORIZED) @classmethod def tearDownClass(cls): diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index df1e54cab..f3f09711b 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -287,7 +287,7 @@ def test_load_private_dataset_unauthorized(self): request1 = self.auth_client2.get("/v1/data/set/2/") request2 = self.client.get("/v1/data/set/2/") self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) # Test only owner can change a private dataset def test_update_private_dataset(self): @@ -324,7 +324,7 @@ def test_update_unowned_dataset(self): request1 = self.auth_client1.put("/v1/data/set/3/", data={"current_user": 1}) request2 = self.client.put("/v1/data/set/3/", data={"name": "Different name"}) self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) # Test deleting a dataset def test_delete_dataset(self): diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index c02a5e782..403eefd74 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -77,7 +77,6 @@ def test_list_authenticated(self): ) # Authenticated user cannot view other users' private data on list - # TODO: Change response codes def test_list_authenticated_2(self): token = self.client.post("/auth/login/", data=self.login_data_2) response = self.client.get("/v1/data/list/", headers=auth_header(token)) @@ -130,7 +129,7 @@ def test_load_unauthenticated(self): response2 = self.client.get("/v1/data/load/2/") response3 = self.client.get("/v1/data/load/3/") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(response3.status_code, status.HTTP_200_OK) # Authenticated user can upload data @@ -232,9 +231,9 @@ def test_upload_put_unauthenticated(self): response = self.client.put("/v1/data/upload/1/", data=data) response2 = self.client.put("/v1/data/upload/2/", data=data) response3 = self.client.put("/v1/data/upload/3/", data=data) - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual(response3.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertEqual(response3.status_code, status.HTTP_401_UNAUTHORIZED) # Authenticated user can download public and own data def test_download_authenticated(self): @@ -266,7 +265,7 @@ def test_download_unauthenticated(self): b"".join(response.streaming_content) b"".join(response3.streaming_content) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(response3.status_code, status.HTTP_200_OK) @classmethod From 14c3f2f76f01ce363768a9a54f75f5a513b8de52 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 17 Mar 2025 16:40:05 -0400 Subject: [PATCH 339/675] Skeleton of reformatted data file API --- sasdata/fair_database/data/views.py | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 2e5ed93a5..011468eb1 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -26,6 +26,55 @@ from fair_database.permissions import DataPermission +class DataFileView(APIView): + """ + View associated with the DataFile model. + + Functionality for viewing a list of files and uploading a new file. + """ + + def get(self): + pass + + def post(self): + pass + + def put(self): + pass + + +class SingleDataFileView(APIView): + """ + View associated with a single DataFile. + + Functionality for viewing, modifying, or deleting a DataFile. + """ + + def get(self): + pass + + def put(self): + pass + + def delete(self): + pass + + +class DataFileUsersView(APIView): + """ + View for the users that have access to a dataset. + + Functionality for accessing a list of users with access and granting or + revoking access. + """ + + def get(self): + pass + + def put(self): + pass + + @api_view(["GET"]) def list_data(request, username=None, version=None): if request.method == "GET": From b5c6664cf2d409c36d2586c68897e83d75942db3 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 17 Mar 2025 16:51:23 -0400 Subject: [PATCH 340/675] Fill out DataFileView methods --- sasdata/fair_database/data/views.py | 50 +++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 011468eb1..31a41055f 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -33,14 +33,52 @@ class DataFileView(APIView): Functionality for viewing a list of files and uploading a new file. """ - def get(self): - pass + def get(self, request, version=None): + if "username" in request.GET: + search_user = get_object_or_404(User, username=request.GET["username"]) + data_list = {"user_data_ids": {}} + private_data = DataFile.objects.filter(current_user=search_user) + for x in private_data: + if permissions.check_permissions(request, x): + data_list["user_data_ids"][x.id] = x.file_name + else: + public_data = DataFile.objects.all() + data_list = {"public_data_ids": {}} + for x in public_data: + if permissions.check_permissions(request, x): + data_list["public_data_ids"][x.id] = x.file_name + return Response(data_list) - def post(self): - pass + def post(self, request, version=None): + form = DataFileForm(request.data, request.FILES) + if form.is_valid(): + form.save() + db = DataFile.objects.get(pk=form.instance.pk) + serializer = DataFileSerializer( + db, + data={ + "file_name": os.path.basename(form.instance.file.path), + "current_user": None, + "users": [], + }, + context={"is_public": db.is_public}, + ) + if request.user.is_authenticated: + serializer.data["current_user"] = request.user.id - def put(self): - pass + if serializer.is_valid(raise_exception=True): + serializer.save() + return_data = { + "current_user": request.user.username, + "authenticated": request.user.is_authenticated, + "file_id": db.id, + "file_alternative_name": serializer.data["file_name"], + "is_public": serializer.data["is_public"], + } + return Response(return_data, status=status.HTTP_201_CREATED) + + def put(self, request, version=None): + return self.post(request, version) class SingleDataFileView(APIView): From 6d7441c556e4710eaaefd7eedaddbb8de6b70ca4 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 17 Mar 2025 16:55:00 -0400 Subject: [PATCH 341/675] Change serializer validation to raise exception --- sasdata/fair_database/data/views.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 31a41055f..ba830fabf 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -312,12 +312,11 @@ def post(self, request, version=None): # TODO: JSON deserialization probably # TODO: revisit request data format serializer = DataSetSerializer(data=request.data, context={"request": request}) - if serializer.is_valid(): + if serializer.is_valid(raise_exception=True): serializer.save() - db = serializer.instance - response = {"dataset_id": db.id, "name": db.name, "is_public": db.is_public} - return Response(data=response, status=status.HTTP_201_CREATED) - return HttpResponseBadRequest() + db = serializer.instance + response = {"dataset_id": db.id, "name": db.name, "is_public": db.is_public} + return Response(data=response, status=status.HTTP_201_CREATED) # create a dataset def put(self, request, version=None): @@ -358,7 +357,7 @@ def put(self, request, data_id, version=None): serializer = DataSetSerializer( db, request.data, context={"request": request}, partial=True ) - if serializer.is_valid(): + if serializer.is_valid(raise_exception=True): serializer.save() data = {"data_id": db.id, "name": db.name, "is_public": db.is_public} return Response(data) @@ -410,7 +409,7 @@ def put(self, request, data_id, version=None): ) return HttpResponseForbidden("Must be the dataset owner to manage access") serializer = AccessManagementSerializer(data=request.data) - serializer.is_valid() + serializer.is_valid(raise_exception=True) user = get_object_or_404(User, username=serializer.data["username"]) if serializer.data["access"]: db.users.add(user) From 409a89ad5f7ed7336d50be5927fe9ea64b4c5c76 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 17 Mar 2025 17:15:38 -0400 Subject: [PATCH 342/675] SingleDataFileView methods --- sasdata/fair_database/data/views.py | 74 ++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index ba830fabf..8e0e93d6d 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -33,6 +33,7 @@ class DataFileView(APIView): Functionality for viewing a list of files and uploading a new file. """ + # List of datafiles def get(self, request, version=None): if "username" in request.GET: search_user = get_object_or_404(User, username=request.GET["username"]) @@ -49,6 +50,7 @@ def get(self, request, version=None): data_list["public_data_ids"][x.id] = x.file_name return Response(data_list) + # Create a datafile def post(self, request, version=None): form = DataFileForm(request.data, request.FILES) if form.is_valid(): @@ -77,6 +79,7 @@ def post(self, request, version=None): } return Response(return_data, status=status.HTTP_201_CREATED) + # Create a datafile def put(self, request, version=None): return self.post(request, version) @@ -88,14 +91,73 @@ class SingleDataFileView(APIView): Functionality for viewing, modifying, or deleting a DataFile. """ - def get(self): - pass + # Load the contents of a datafile or download the file to a device + def get(self, request, data_id, version=None): + data = get_object_or_404(DataFile, id=data_id) + if "download" in request.data and request.data["download"]: + if not permissions.check_permissions(request, data): + if not request.user.is_authenticated: + return HttpResponse("Must be authenticated to download", status=401) + return HttpResponseForbidden("data is private") + try: + file = open(data.file.path, "rb") + except Exception as e: + return HttpResponseBadRequest(str(e)) + if file is None: + raise Http404("File not found.") + return FileResponse(file, as_attachment=True) + else: + loader = Loader() + if not permissions.check_permissions(request, data): + if not request.user.is_authenticated: + return HttpResponse("Must be authenticated to view", status=401) + return HttpResponseForbidden( + "Data is either not public or wrong auth token" + ) + data_list = loader.load(data.file.path) + contents = [str(data) for data in data_list] + return_data = {data.file_name: contents} + return Response(return_data) - def put(self): - pass + # Modify a datafile + def put(self, request, data_id, version=None): + db = get_object_or_404(DataFile, id=data_id) + if not permissions.check_permissions(request, db): + if not request.user.is_authenticated: + return HttpResponse("must be authenticated to modify", status=401) + return HttpResponseForbidden("must be the data owner to modify") + form = DataFileForm(request.data, request.FILES, instance=db) + if form.is_valid(): + form.save() + serializer = DataFileSerializer( + db, + data={ + "file_name": os.path.basename(form.instance.file.path), + "current_user": request.user.id, + }, + context={"is_public": db.is_public}, + partial=True, + ) + if serializer.is_valid(raise_exception=True): + serializer.save() + return_data = { + "current_user": request.user.username, + "authenticated": request.user.is_authenticated, + "file_id": db.id, + "file_alternative_name": serializer.data["file_name"], + "is_public": serializer.data["is_public"], + } + return Response(return_data) - def delete(self): - pass + # Delete a datafile + def delete(self, request, data_id, version=None): + db = get_object_or_404(DataFile, id=data_id) + if not permissions.is_owner(request, db): + if not request.user.is_authenticated: + return HttpResponse("Must be authenticated to delete", status=401) + return HttpResponseForbidden("Must be the data owner to delete") + db.delete() + return Response(data={"success": True}) class DataFileUsersView(APIView): From 4606bb9513e8fc8298d15dbf1fbfe1b05d8ca018 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 17 Mar 2025 17:22:09 -0400 Subject: [PATCH 343/675] DataFileUsersView methods --- sasdata/fair_database/data/views.py | 42 ++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 8e0e93d6d..c11f65675 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -168,11 +168,45 @@ class DataFileUsersView(APIView): revoking access. """ - def get(self): - pass + # View users with access to a datafile + def get(self, request, data_id, version=None): + db = get_object_or_404(DataFile, id=data_id) + if not permissions.is_owner(request, db): + if not request.user.is_authenticated: + return HttpResponse( + "Must be authenticated to manage access", status=401 + ) + return HttpResponseForbidden("Must be the data owner to manage access") + response_data = { + "file": db.pk, + "file_name": db.file_name, + "users": [user.username for user in db.users.all()], + } + return Response(response_data) - def put(self): - pass + # Grant or revoke access to a datafile + def put(self, request, data_id, version=None): + db = get_object_or_404(DataFile, id=data_id) + if not permissions.is_owner(request, db): + if not request.user.is_authenticated: + return HttpResponse( + "Must be authenticated to manage access", status=401 + ) + return HttpResponseForbidden("Must be the data owner to manage access") + serializer = AccessManagementSerializer(data=request.data) + serializer.is_valid() + user = get_object_or_404(User, username=serializer.data["username"]) + if serializer.data["access"]: + db.users.add(user) + else: + db.users.remove(user) + response_data = { + "user": user.username, + "file": db.pk, + "file_name": db.file_name, + "access": serializer.data["access"], + } + return Response(response_data) @api_view(["GET"]) From 74f1aab6c7d626feaef1da7dffb9207c4f6c8f00 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 17 Mar 2025 17:26:06 -0400 Subject: [PATCH 344/675] Update urls for DataFile --- sasdata/fair_database/data/urls.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/sasdata/fair_database/data/urls.py b/sasdata/fair_database/data/urls.py index 32ab14c0e..e3f2a4191 100644 --- a/sasdata/fair_database/data/urls.py +++ b/sasdata/fair_database/data/urls.py @@ -3,14 +3,17 @@ from . import views urlpatterns = [ - path("list/", views.list_data, name="list public file_ids"), - path("list//", views.list_data, name="view users file_ids"), - path("load//", views.data_info, name="views data using file id"), - path("upload/", views.upload, name="upload data into db"), - path("upload//", views.upload, name="update file in data"), - path("/download/", views.download, name="download data from db"), - path("manage//", views.manage_access, name="manage access to files"), - path("delete//", views.delete, name="delete file"), + path("file/", views.DataFileView.as_view(), name="view and create files"), + path( + "file//", + views.SingleDataFileView.as_view(), + name="view, download, modify, delete files", + ), + path( + "file//users/", + views.DataFileUsersView.as_view(), + name="manage access to files", + ), path("set/", views.DataSetView.as_view(), name="view and create datasets"), path( "set//", From ea9b8ab76595fdef296ee1c4d02651101ef87d0c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 17 Mar 2025 17:47:52 -0400 Subject: [PATCH 345/675] Fix error in get method for file download --- sasdata/fair_database/data/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index c11f65675..1ea90f94c 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -94,7 +94,7 @@ class SingleDataFileView(APIView): # Load the contents of a datafile or download the file to a device def get(self, request, data_id, version=None): data = get_object_or_404(DataFile, id=data_id) - if "download" in request.data and request.data["download"]: + if "download" in request.GET and request.GET["download"]: if not permissions.check_permissions(request, data): if not request.user.is_authenticated: return HttpResponse("Must be authenticated to download", status=401) From 7dcd9d9fa1f3a39d0b08d2edaa08ff50d1409320 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 17 Mar 2025 17:50:57 -0400 Subject: [PATCH 346/675] Fix error in datafile post --- sasdata/fair_database/data/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 1ea90f94c..1b8aefe88 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -66,7 +66,7 @@ def post(self, request, version=None): context={"is_public": db.is_public}, ) if request.user.is_authenticated: - serializer.data["current_user"] = request.user.id + serializer.initial_data["current_user"] = request.user.id if serializer.is_valid(raise_exception=True): serializer.save() From 10874b479de2bb34db9fff385e695f0e6c5071fb Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 17 Mar 2025 17:54:09 -0400 Subject: [PATCH 347/675] Update tests for url changes --- .../fair_database/data/test/test_datafile.py | 91 ++++++++++--------- .../fair_database/test_permissions.py | 87 ++++++++++-------- sasdata/fair_database/user_app/tests.py | 12 --- 3 files changed, 100 insertions(+), 90 deletions(-) diff --git a/sasdata/fair_database/data/test/test_datafile.py b/sasdata/fair_database/data/test/test_datafile.py index 128498d51..7bbd9fb03 100644 --- a/sasdata/fair_database/data/test/test_datafile.py +++ b/sasdata/fair_database/data/test/test_datafile.py @@ -42,38 +42,45 @@ def setUpTestData(cls): # Test list public data def test_does_list_public(self): - request = self.client1.get("/v1/data/list/") - self.assertEqual(request.data, {"public_data_ids": {1: "cyl_400_40.txt"}}) + request = self.client1.get("/v1/data/file/") + self.assertEqual( + request.data, + {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_400_20.txt"}}, + ) # Test list a user's private data def test_does_list_user(self): - request = self.client1.get("/v1/data/list/testUser/", user=self.user) + request = self.client1.get( + "/v1/data/file/", data={"username": "testUser"}, user=self.user + ) self.assertEqual(request.data, {"user_data_ids": {3: "cyl_400_20.txt"}}) # Test list another user's public data def test_list_other_user(self): client2 = APIClient() - request = client2.get("/v1/data/list/testUser/", user=self.user) + request = client2.get( + "/v1/data/file/", data={"username": "testUser"}, user=self.user + ) self.assertEqual(request.data, {"user_data_ids": {}}) # Test list a nonexistent user's data def test_list_nonexistent_user(self): - request = self.client1.get("/v1/data/list/fakeUser/") + request = self.client1.get("/v1/data/file/", data={"username": "fakeUser"}) self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) # Test loading a public data file def test_does_load_data_info_public(self): - request = self.client1.get("/v1/data/load/1/") + request = self.client1.get("/v1/data/file/1/") self.assertEqual(request.status_code, status.HTTP_200_OK) # Test loading private data with authorization def test_does_load_data_info_private(self): - request = self.client1.get("/v1/data/load/3/") + request = self.client1.get("/v1/data/file/3/") self.assertEqual(request.status_code, status.HTTP_200_OK) # Test loading data that does not exist def test_load_data_info_nonexistent(self): - request = self.client1.get("/v1/data/load/5/") + request = self.client1.get("/v1/data/file/5/") self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) @classmethod @@ -104,7 +111,7 @@ def setUpTestData(cls): def test_is_data_being_created(self): file = open(find("cyl_400_40.txt"), "rb") data = {"is_public": False, "file": file} - request = self.client1.post("/v1/data/upload/", data=data) + request = self.client1.post("/v1/data/file/", data=data) max_id = DataFile.objects.aggregate(Max("id"))["id__max"] self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual( @@ -123,7 +130,7 @@ def test_is_data_being_created(self): def test_is_data_being_created_no_user(self): file = open(find("cyl_testdata.txt"), "rb") data = {"is_public": True, "file": file} - request = self.client2.post("/v1/data/upload/", data=data) + request = self.client2.post("/v1/data/file/", data=data) max_id = DataFile.objects.aggregate(Max("id"))["id__max"] self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual( @@ -142,7 +149,7 @@ def test_is_data_being_created_no_user(self): def test_no_data_overwrite(self): file = open(find("apoferritin.txt")) data = {"is_public": True, "file": file, id: 1} - request = self.client1.post("/v1/data/upload/", data=data) + request = self.client1.post("/v1/data/file/", data=data) self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual(DataFile.objects.get(id=1).file_name, "cyl_400_20.txt") max_id = DataFile.objects.aggregate(Max("id"))["id__max"] @@ -162,7 +169,7 @@ def test_no_data_overwrite(self): def test_does_file_upload_update(self): file = open(find("cyl_testdata1.txt")) data = {"file": file, "is_public": False} - request = self.client1.put("/v1/data/upload/1/", data=data) + request = self.client1.put("/v1/data/file/1/", data=data) self.assertEqual( request.data, { @@ -186,7 +193,7 @@ def test_public_file_upload_update(self): ) file = open(find("conalbumin.txt")) data = {"file": file, "is_public": True} - request = self.client1.put("/v1/data/upload/3/", data=data) + request = self.client1.put("/v1/data/file/3/", data=data) self.assertEqual( request.data, { @@ -203,19 +210,19 @@ def test_public_file_upload_update(self): def test_unauthorized_file_upload_update(self): file = open(find("cyl_400_40.txt")) data = {"file": file, "is_public": False} - request = self.client2.put("/v1/data/upload/1/", data=data) + request = self.client2.put("/v1/data/file/1/", data=data) self.assertEqual(request.status_code, status.HTTP_401_UNAUTHORIZED) # Test update nonexistent file fails def test_file_upload_update_not_found(self): file = open(find("cyl_400_40.txt")) data = {"file": file, "is_public": False} - request = self.client2.put("/v1/data/upload/5/", data=data) + request = self.client2.put("/v1/data/file/5/", data=data) self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) # Test file download def test_does_download(self): - request = self.client1.get("/v1/data/1/download/") + request = self.client1.get("/v1/data/file/1/", data={"download": True}) file_contents = b"".join(request.streaming_content) test_file = open(find("cyl_400_20.txt"), "rb") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -223,12 +230,12 @@ def test_does_download(self): # Test file download fails when unauthorized def test_unauthorized_download(self): - request2 = self.client2.get("/v1/data/1/download/") + request2 = self.client2.get("/v1/data/file/1/", data={"download": True}) self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) # Test download nonexistent file def test_download_nonexistent(self): - request = self.client1.get("/v1/data/5/download/") + request = self.client1.get("/v1/data/file/5/", data={"download": True}) self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) # Test deleting a file @@ -236,13 +243,13 @@ def test_delete(self): DataFile.objects.create( id=6, current_user=self.user, file_name="test.txt", is_public=False ) - request = self.client1.delete("/v1/data/delete/6/") + request = self.client1.delete("/v1/data/file/6/") self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertFalse(DataFile.objects.filter(pk=6).exists()) # Test deleting a file fails when unauthorized def test_delete_unauthorized(self): - request = self.client2.delete("/v1/data/delete/1/") + request = self.client2.delete("/v1/data/file/1/") self.assertEqual(request.status_code, status.HTTP_401_UNAUTHORIZED) @classmethod @@ -279,14 +286,14 @@ def setUpTestData(cls): # test viewing no one with access def test_view_no_access(self): - request = self.client1.get("/v1/data/manage/1/") + request = self.client1.get("/v1/data/file/1/users/") data = {"file": 1, "file_name": "cyl_400_40.txt", "users": []} self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, data) # test viewing list of users with access def test_view_access(self): - request = self.client1.get("/v1/data/manage/2/") + request = self.client1.get("/v1/data/file/2/users/") data = {"file": 2, "file_name": "cyl_400_20.txt", "users": ["testUser2"]} self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, data) @@ -294,17 +301,17 @@ def test_view_access(self): # test granting another user access to private data def test_grant_access(self): data = {"username": "testUser2", "access": True} - request1 = self.client1.put("/v1/data/manage/1/", data=data) - request2 = self.client2.get("/v1/data/load/1/") + request1 = self.client1.put("/v1/data/file/1/users/", data=data) + request2 = self.client2.get("/v1/data/file/1/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) # test removing another user's access to private data def test_remove_access(self): data = {"username": "testUser2", "access": False} - request1 = self.client2.get("/v1/data/load/2/") - request2 = self.client1.put("/v1/data/manage/2/", data=data) - request3 = self.client2.get("/v1/data/load/2/") + request1 = self.client2.get("/v1/data/file/2/") + request2 = self.client1.put("/v1/data/file/2/users/", data=data) + request3 = self.client2.get("/v1/data/file/2/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) @@ -312,9 +319,9 @@ def test_remove_access(self): # test removing access from a user that already lacks access def test_remove_no_access(self): data = {"username": "testUser2", "access": False} - request1 = self.client2.get("/v1/data/load/1/") - request2 = self.client1.put("/v1/data/manage/1/", data=data) - request3 = self.client2.get("/v1/data/load/1/") + request1 = self.client2.get("/v1/data/file/1/") + request2 = self.client1.put("/v1/data/file/1/users/", data=data) + request3 = self.client2.get("/v1/data/file/1/") self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) @@ -322,17 +329,17 @@ def test_remove_no_access(self): # test owner's access cannot be removed def test_cant_revoke_own_access(self): data = {"username": "testUser", "access": False} - request1 = self.client1.put("/v1/data/manage/1/", data=data) - request2 = self.client1.get("/v1/data/load/1/") + request1 = self.client1.put("/v1/data/file/1/users/", data=data) + request2 = self.client1.get("/v1/data/file/1/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) # test giving access to a user that already has access def test_grant_existing_access(self): data = {"username": "testUser2", "access": True} - request1 = self.client2.get("/v1/data/load/2/") - request2 = self.client1.put("/v1/data/manage/2/", data=data) - request3 = self.client2.get("/v1/data/load/2/") + request1 = self.client2.get("/v1/data/file/2/") + request2 = self.client1.put("/v1/data/file/2/users/", data=data) + request3 = self.client2.get("/v1/data/file/2/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertEqual(request3.status_code, status.HTTP_200_OK) @@ -340,14 +347,14 @@ def test_grant_existing_access(self): # test that access is read-only for the file def test_no_edit_access(self): data = {"is_public": True} - request = self.client2.put("/v1/data/upload/2/", data=data) + request = self.client2.put("/v1/data/file/2/", data=data) self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) self.assertFalse(self.shared_test_data.is_public) # test that only the owner can view who has access def test_only_view_access_to_owned_file(self): - request1 = self.client2.get("/v1/data/manage/1/") - request2 = self.client2.get("/v1/data/manage/2/") + request1 = self.client2.get("/v1/data/file/1/users/") + request2 = self.client2.get("/v1/data/file/2/users/") self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) @@ -355,10 +362,10 @@ def test_only_view_access_to_owned_file(self): def test_only_edit_access_to_owned_file(self): data1 = {"username": "testUser2", "access": True} data2 = {"username": "testUser1", "access": False} - request1 = self.client2.put("/v1/data/manage/1/", data=data1) - request2 = self.client2.put("/v1/data/manage/2/", data=data2) - request3 = self.client2.get("/v1/data/load/1/") - request4 = self.client1.get("/v1/data/load/2/") + request1 = self.client2.put("/v1/data/file/1/users/", data=data1) + request2 = self.client2.put("/v1/data/file/2/users/", data=data2) + request3 = self.client2.get("/v1/data/file/1/") + request4 = self.client1.get("/v1/data/file/2/") self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index 403eefd74..df76d1a64 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -60,16 +60,21 @@ def setUpTestData(cls): } # Authenticated user can view list of data - # TODO: change to reflect inclusion of owned private data def test_list_authenticated(self): token = self.client.post("/auth/login/", data=self.login_data_1) - response = self.client.get("/v1/data/list/", headers=auth_header(token)) + response = self.client.get("/v1/data/file/", headers=auth_header(token)) response2 = self.client.get( - "/v1/data/list/testUser/", headers=auth_header(token) + "/v1/data/file/", data={"username": "testUser"}, headers=auth_header(token) ) self.assertEqual( response.data, - {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}, + { + "public_data_ids": { + 1: "cyl_400_40.txt", + 2: "cyl_400_20.txt", + 3: "cyl_testdata.txt", + } + }, ) self.assertEqual( response2.data, @@ -79,12 +84,12 @@ def test_list_authenticated(self): # Authenticated user cannot view other users' private data on list def test_list_authenticated_2(self): token = self.client.post("/auth/login/", data=self.login_data_2) - response = self.client.get("/v1/data/list/", headers=auth_header(token)) + response = self.client.get("/v1/data/file/", headers=auth_header(token)) response2 = self.client.get( - "/v1/data/list/testUser/", headers=auth_header(token) + "/v1/data/file/", data={"username": "testUser"}, headers=auth_header(token) ) response3 = self.client.get( - "/v1/data/list/testUser2/", headers=auth_header(token) + "/v1/data/file/", data={"username": "testUser2"}, headers=auth_header(token) ) self.assertEqual( response.data, @@ -96,8 +101,8 @@ def test_list_authenticated_2(self): # Unauthenticated user can view list of public data def test_list_unauthenticated(self): - response = self.client.get("/v1/data/list/") - response2 = self.client.get("/v1/data/list/testUser/") + response = self.client.get("/v1/data/file/") + response2 = self.client.get("/v1/data/file/", data={"username": "testUser"}) self.assertEqual( response.data, {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_testdata.txt"}}, @@ -108,9 +113,9 @@ def test_list_unauthenticated(self): # Authenticated user can load public data and owned private data def test_load_authenticated(self): token = self.client.post("/auth/login/", data=self.login_data_1) - response = self.client.get("/v1/data/load/1/", headers=auth_header(token)) - response2 = self.client.get("/v1/data/load/2/", headers=auth_header(token)) - response3 = self.client.get("/v1/data/load/3/", headers=auth_header(token)) + response = self.client.get("/v1/data/file/1/", headers=auth_header(token)) + response2 = self.client.get("/v1/data/file/2/", headers=auth_header(token)) + response3 = self.client.get("/v1/data/file/3/", headers=auth_header(token)) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) self.assertEqual(response3.status_code, status.HTTP_200_OK) @@ -118,16 +123,16 @@ def test_load_authenticated(self): # Authenticated user cannot load others' private data def test_load_unauthorized(self): token = self.client.post("/auth/login/", data=self.login_data_2) - response = self.client.get("/v1/data/load/2/", headers=auth_header(token)) - response2 = self.client.get("/v1/data/load/3/", headers=auth_header(token)) + response = self.client.get("/v1/data/file/2/", headers=auth_header(token)) + response2 = self.client.get("/v1/data/file/3/", headers=auth_header(token)) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response2.status_code, status.HTTP_200_OK) # Unauthenticated user can load public data only def test_load_unauthenticated(self): - response = self.client.get("/v1/data/load/1/") - response2 = self.client.get("/v1/data/load/2/") - response3 = self.client.get("/v1/data/load/3/") + response = self.client.get("/v1/data/file/1/") + response2 = self.client.get("/v1/data/file/2/") + response3 = self.client.get("/v1/data/file/3/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(response3.status_code, status.HTTP_200_OK) @@ -138,7 +143,7 @@ def test_upload_authenticated(self): file = open(find("cyl_testdata1.txt"), "rb") data = {"file": file, "is_public": False} response = self.client.post( - "/v1/data/upload/", data=data, headers=auth_header(token) + "/v1/data/file/", data=data, headers=auth_header(token) ) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual( @@ -159,8 +164,8 @@ def test_upload_unauthenticated(self): file2 = open(find("cyl_testdata2.txt"), "rb") data = {"file": file, "is_public": True} data2 = {"file": file2, "is_public": False} - response = self.client.post("/v1/data/upload/", data=data) - response2 = self.client.post("/v1/data/upload/", data=data2) + response = self.client.post("/v1/data/file/", data=data) + response2 = self.client.post("/v1/data/file/", data=data2) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual( response.data, @@ -179,10 +184,10 @@ def test_upload_put_authenticated(self): token = self.client.post("/auth/login/", data=self.login_data_1) data = {"is_public": False} response = self.client.put( - "/v1/data/upload/2/", data=data, headers=auth_header(token) + "/v1/data/file/2/", data=data, headers=auth_header(token) ) response2 = self.client.put( - "/v1/data/upload/3/", data=data, headers=auth_header(token) + "/v1/data/file/3/", data=data, headers=auth_header(token) ) self.assertEqual( response.data, @@ -212,13 +217,13 @@ def test_upload_put_unauthorized(self): file = open(find("cyl_400_40.txt")) data = {"file": file, "is_public": False} response = self.client.put( - "/v1/data/upload/1/", data=data, headers=auth_header(token) + "/v1/data/file/1/", data=data, headers=auth_header(token) ) response2 = self.client.put( - "/v1/data/upload/2/", data=data, headers=auth_header(token) + "/v1/data/file/2/", data=data, headers=auth_header(token) ) response3 = self.client.put( - "/v1/data/upload/3/", data=data, headers=auth_header(token) + "/v1/data/file/3/", data=data, headers=auth_header(token) ) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response2.status_code, status.HTTP_403_FORBIDDEN) @@ -228,9 +233,9 @@ def test_upload_put_unauthorized(self): def test_upload_put_unauthenticated(self): file = open(find("cyl_400_40.txt")) data = {"file": file, "is_public": False} - response = self.client.put("/v1/data/upload/1/", data=data) - response2 = self.client.put("/v1/data/upload/2/", data=data) - response3 = self.client.put("/v1/data/upload/3/", data=data) + response = self.client.put("/v1/data/file/1/", data=data) + response2 = self.client.put("/v1/data/file/2/", data=data) + response3 = self.client.put("/v1/data/file/3/", data=data) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(response3.status_code, status.HTTP_401_UNAUTHORIZED) @@ -238,9 +243,15 @@ def test_upload_put_unauthenticated(self): # Authenticated user can download public and own data def test_download_authenticated(self): token = self.client.post("/auth/login/", data=self.login_data_1) - response = self.client.get("/v1/data/1/download/", headers=auth_header(token)) - response2 = self.client.get("/v1/data/2/download/", headers=auth_header(token)) - response3 = self.client.get("/v1/data/3/download/", headers=auth_header(token)) + response = self.client.get( + "/v1/data/file/1/", data={"download": True}, headers=auth_header(token) + ) + response2 = self.client.get( + "/v1/data/file/2/", data={"download": True}, headers=auth_header(token) + ) + response3 = self.client.get( + "/v1/data/file/3/", data={"download": True}, headers=auth_header(token) + ) b"".join(response.streaming_content) b"".join(response2.streaming_content) b"".join(response3.streaming_content) @@ -251,17 +262,21 @@ def test_download_authenticated(self): # Authenticated user cannot download others' data def test_download_unauthorized(self): token = self.client.post("/auth/login/", data=self.login_data_2) - response = self.client.get("/v1/data/2/download/", headers=auth_header(token)) - response2 = self.client.get("/v1/data/3/download/", headers=auth_header(token)) + response = self.client.get( + "/v1/data/file/2/", data={"download": True}, headers=auth_header(token) + ) + response2 = self.client.get( + "/v1/data/file/3/", data={"download": True}, headers=auth_header(token) + ) b"".join(response2.streaming_content) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response2.status_code, status.HTTP_200_OK) # Unauthenticated user cannot download private data def test_download_unauthenticated(self): - response = self.client.get("/v1/data/1/download/") - response2 = self.client.get("/v1/data/2/download/") - response3 = self.client.get("/v1/data/3/download/") + response = self.client.get("/v1/data/file/1/", data={"download": True}) + response2 = self.client.get("/v1/data/file/2/", data={"download": True}) + response3 = self.client.get("/v1/data/file/3/", data={"download": True}) b"".join(response.streaming_content) b"".join(response3.streaming_content) self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index f53e922cf..36a8e8ec0 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -156,15 +156,3 @@ def test_password_change(self): self.login_data_2["password"] = "sasview!" self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) - - -# logged-in user can create Data, is data's current_user - - -# Permissions -# Any user can access public data -# logged-in user can access and modify their own private data -# unauthenticated user cannot access private data -# unauthenticated user cannot modify data -# logged-in user cannot modify data other than their own -# logged-in user cannot access the private data of others From 97f69292fc0015805180b4c87b6efd4eac599dd2 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 18 Mar 2025 14:20:22 -0400 Subject: [PATCH 348/675] Updates for changes to SasData class --- sasdata/data.py | 12 ++++++------ sasdata/fair_database/data/models.py | 19 ++++++++++++------- sasdata/quantities/quantity.py | 6 +++++- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 3977200dd..e0d35f019 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -78,10 +78,11 @@ def deserialise(data: str) -> "SasData": @staticmethod def deserialise_json(json_data: dict) -> "SasData": name = json_data["name"] - data_contents = [] # deserialize Quantity + data_contents = {} # deserialize Quantity + dataset_type = json_data["dataset_type"] raw_metadata = Group.deserialise_json(json_data["raw_metadata"]) verbose = json_data["verbose"] - return SasData(name, data_contents, raw_metadata, verbose) + return SasData(name, data_contents, dataset_type, raw_metadata, verbose) def serialise(self) -> str: return json.dumps(self._serialise_json()) @@ -90,12 +91,11 @@ def serialise(self) -> str: def _serialise_json(self) -> dict[str, Any]: return { "name": self.name, - "data_contents": [q.serialise_json() for q in self._data_contents], + "data_contents": {q: self._data_contents[q].serialise_json() for q in self._data_contents}, + "dataset_type": None, # TODO: update when DatasetType is more finalized "raw_metadata": self._raw_metadata.serialise_json(), "verbose": self._verbose, - "metadata": self.metadata.serialise_json(), - "ordinate": self.ordinate.serialise_json(), - "abscissae": [q.serialise_json() for q in self.abscissae], + "metadata": self.metadata.serialise_json(), # TODO: fix metadata eventually "mask": {}, "model_requirements": {} } \ No newline at end of file diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 2d87f16b2..fb7c6a95d 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -58,14 +58,11 @@ class DataSet(Data): "MetaData", blank=True, null=True, on_delete=models.CASCADE ) - # ordinate - # ordinate = models.ForeignKey("Quantity", on_delete=models.CASCADE) - - # abscissae - # abscissae = models.ManyToManyField("Quantity", on_delete=models.CASCADE) - # data contents - maybe ManyToManyField - # data_contents = models.JSONField() + # data_contents = models.ManyToManyField("Quantity", through="LabeledQuantity") + + # type of dataset + # dataset_type = models.JSONField() class Quantity(models.Model): @@ -84,6 +81,14 @@ class Quantity(models.Model): hash = models.IntegerField() +""" +class LabeledQuantity(models.Model): + dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) + quantity = models.ForeignKey(Quantity, on_delete=models.CASCADE) + label = models.CharField(max_length=20) +""" + + # TODO: revisit metadata when sasdata metadata is rewritten class MetaData(models.Model): """Database model for scattering metadata""" diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 8ddf15b3d..0e778e208 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1022,7 +1022,11 @@ def jacobian(self) -> list[Operation]: return [self.operation_tree.derivative(key) for key in self.reference_key_list] def _recalculate(self): - """ Recalculate the value of this object - primary use case is for testing """ + """ Recalculate the value of this object - primary use case is + + + + for testing """ return self.operation_tree.evaluate(self.references) def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, int]: "Quantity"] = {}): From 95f8e5855cb85de64c55d66061744975f8bd4db4 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 18 Mar 2025 14:29:50 -0400 Subject: [PATCH 349/675] Add published state to session model --- sasdata/fair_database/data/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index fb7c6a95d..08af5b944 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -142,11 +142,13 @@ class Session(Data): """Database model for a project save state.""" # dataset - # dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) + # dataset = models.ManyToManyField(DataSet) # operation tree # operations = models.ForeignKey(OperationTree, on_delete=models.CASCADE) + published_state = models.ForeignKey("PublishedState", blank=True, null=True, on_delete=SET_NULL) + class PublishedState(): """Database model for a project published state.""" From 688e179c14da8c8d4b6215ccc9c328b854bcbe19 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 19 Mar 2025 16:05:18 -0400 Subject: [PATCH 350/675] Update metadata serializers for latest changes --- sasdata/metadata.py | 225 ++++++++++++++++++++++++++++++++------------ 1 file changed, 164 insertions(+), 61 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 2bd0f9b21..6176c2bf9 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -9,6 +9,53 @@ DimensionlessAccessor, FloatAccessor, TemperatureAccessor, AccessorTarget +from dataclasses import dataclass + +from sasdata.quantities.quantity import Quantity + +@dataclass(kw_only=True) +class Vec3: + """A three-vector of measured quantities""" + x : Quantity[float] | None + y : Quantity[float] | None + z : Quantity[float] | None + + def serialise_json(self): + data = { + "x": None, + "y": None, + "z": None + } + if self.x is not None: + data["x"] = self.x.serialise_json() + if self.y is not None: + data["y"] = self.y.serialise_json() + if self.z is not None: + data["z"] = self.z.serialise_json() + return data + +@dataclass(kw_only=True) +class Rot3: + """A measured rotation in 3-space""" + roll : Quantity[float] | None + pitch : Quantity[float] | None + yaw : Quantity[float] | None + + def serialise_json(self): + data = { + "roll": None, + "pitch": None, + "yaw": None + } + if self.roll is not None: + data["roll"] = self.roll.serialise_json() + if self.pitch is not None: + data["pitch"] = self.pitch.serialise_json() + if self.yaw is not None: + data["yaw"] = self.yaw.serialise_json() + return data + +@dataclass(kw_only=True) class Detector: """ Detector information @@ -65,15 +112,28 @@ def summary(self): f" Slit length: {self.slit_length.value}\n") def serialise_json(self): - return { - "name": self.name.value, - "distance": self.distance.value.serialise_json(), - "offset": self.offset.value.serialise_json(), - "orientation": self.orientation.value.serialise_json(), - "beam_center": self.beam_center.value.serialise_json(), - "pixel_size": self.pixel_size.value.serialise_json(), - "slit_length": self.slit_length.value.serialise_json() + data = { + "name": self.name, + "distance": None, + "offset": None, + "orientation": None, + "beam_center": None, + "pixel_size": None, + "slit_length": None } + if self.distance is not None: + data["distance"] = self.distance.serialise_json() + if self.offset is not None: + data["offset"] = self.offset.serialise_json() + if self.orientation is not None: + data["orientation"] = self.orientation.serialise_json() + if self.beam_center is not None: + data["beam_center"] = self.beam_center.serialise_json() + if self.pixel_size is not None: + data["pixel_size"] = self.pixel_size.serialise_json() + if self.slit_length is not None: + data["slit_length"] = self.slit_length.serialise_json() + return data class Aperture: @@ -104,9 +164,22 @@ def __init__(self, target_object: AccessorTarget): def summary(self): return (f"Aperture:\n" - f" Name: {self.name.value}\n" - f" Aperture size: {self.size.value}\n" - f" Aperture distance: {self.distance.value}\n") + f" Name: {self.name}\n" + f" Aperture size: {self.size}\n" + f" Aperture distance: {self.distance}\n") + + def serialise_json(self): + data = { + "distance": None, + "size": None, + "size_name": self.size_name, + "name": self.name, + "type": self.type_ + } + if self.distance is not None: + data["distance"] = self.distance.serialise_json() + if self.size is not None: + data["size"] = self.size.serialise_json() class Collimation: """ @@ -129,17 +202,27 @@ def summary(self): f" Length: {self.length}\n") def serialise_json(self): - return { - "name": self.name.value, - "length": self.length.value.serialise_json() + data = { + "length": None, + "apertures": [a.serialise_json() for a in self.apertures] } + if self.length is not None: + data["length"] = self.length.serialise_json() + return data @dataclass class BeamSize: - name: Optional[str] - x: Optional[Quantity[float]] - y: Optional[Quantity[float]] - z: Optional[Quantity[float]] + name: str | None + size: Vec3 | None + + def serialise_json(self): + data = { + "name": self.name, + "size": None + } + if self.size is not None: + data["size"] = self.size.serialise_json() + return data @dataclass @@ -169,6 +252,28 @@ def summary(self) -> str: f" Beam Size: {self.beam_size}\n" ) + def serialise_json(self): + data = { + "radiation": self.radiation, + "beam_shape": self.beam_shape, + "beam_size": None, + "wavelength": None, + "wavelength_min": None, + "wavelength_max": None, + "wavelength_spread": None + } + if self.beam_size is not None: + data["beam_size"] = self.beam_size.serialise_json() + if self.wavelength is not None: + data["wavelength"] = self.wavelength.serialise_json() + if self.wavelength_min is not None: + data["wavelength_min"] = self.wavelength_min.serialise_json() + if self.wavelength_max is not None: + data["wavelength_max"] = self.wavelength_max.serialise_json() + if self.wavelength_spread is not None: + data["wavelength_spread"] = self.wavelength_spread.serialise_json() + return data + """ Definitions of radiation types @@ -241,16 +346,25 @@ def summary(self) -> str: # return _str def serialise_json(self): - return { - "name": self.name.value, - "sample_id": self.sample_id.value, - "thickness": self.thickness.value.serialise_json(), - "transmission": self.transmission.value, - "temperature": self.temperature.value.serialise_json(), - "position": self.position.value.serialise_json(), - "orientation": self.orientation.value.serialise_json(), - "details": self.details.value + data = { + "name": self.name, + "sample_id": self.sample_id, + "thickness": None, + "transmission": self.transmission, + "temperature": None, + "position": None, + "orientation": None, + "details": self.details } + if self.thickness is not None: + data["thickness"] = self.thickness.serialise_json() + if self.temperature is not None: + data["temperature"] = self.temperature.serialise_json() + if self.position is not None: + data["position"] = self.position.serialise_json() + if self.orientation is not None: + data["orientation"] = self.orientation.serialise_json() + return data class Process: @@ -272,7 +386,7 @@ def single_line_desc(self): """ Return a single line string representing the process """ - return f"{self.name.value} {self.date.value} {self.description.value}" + return f"{self.name} {self.date} {self.description}" def summary(self): return (f"Process:\n" @@ -285,11 +399,10 @@ def summary(self): def serialise_json(self): return { - "name": "", - "date": "", - "description": "", - "term": "", - "notes": "" + "name": self.name, + "date": self.date, + "description": self.description, + "term": self.term, } @@ -306,30 +419,16 @@ def summary(self): self.detector.summary() + self.source.summary()) -def decode_string(data): - """ This is some crazy stuff""" - - if isinstance(data, str): + def serialise_json(self): + data = { + "collimations": [c.serialise_json() for c in self.collimations], + "source": None, + "detector": [d.serialise_json() for d in self.detector] + } + if self.source is not None: + data["source"] = self.source.serialise_json() return data - elif isinstance(data, np.ndarray): - - if data.dtype == object: - - data = data.reshape(-1) - data = data[0] - - if isinstance(data, bytes): - return data.decode("utf-8") - - return str(data) - - else: - return data.tobytes().decode("utf-8") - - else: - return str(data) - @dataclass(kw_only=True) class Metadata: def __init__(self, target: AccessorTarget, instrument: Instrument): @@ -366,12 +465,16 @@ def summary(self): (self.instrument.summary() if self.instrument else "")) def serialise_json(self): - return { - "instrument": self.instrument.serialise_json(), - "process": self.process.serialise_json(), - "sample": self.sample.serialise_json(), - "transmission_spectrum": self.transmission_spectrum.serialise_json(), + serialized = { + "instrument": None, + "process": [p.serialise_json() for p in self.process], + "sample": None, "title": self.title, "run": self.run, "definition": self.definition - } \ No newline at end of file + } if self.sample is not None: + serialized["sample"] = self.sample.serialise_json() + if self.instrument is not None: + serialized["instrument"] = self.instrument.serialise_json() + + return serialized \ No newline at end of file From 5c534cc1605161c3caa7f44b2b5d973f5a7d0b42 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 19 Mar 2025 16:09:43 -0400 Subject: [PATCH 351/675] Update metadata model for recent changes --- ...2_remove_metadata_raw_metadata_and_more.py | 25 +++++++++++++++++++ sasdata/fair_database/data/models.py | 9 +------ 2 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0012_remove_metadata_raw_metadata_and_more.py diff --git a/sasdata/fair_database/data/migrations/0012_remove_metadata_raw_metadata_and_more.py b/sasdata/fair_database/data/migrations/0012_remove_metadata_raw_metadata_and_more.py new file mode 100644 index 000000000..f553206b4 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0012_remove_metadata_raw_metadata_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 5.1.6 on 2025-03-19 20:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0011_operationtree_operation_operationtree_parameters"), + ] + + operations = [ + migrations.RemoveField( + model_name="metadata", + name="raw_metadata", + ), + migrations.RemoveField( + model_name="metadata", + name="transmission_spectrum", + ), + migrations.AlterField( + model_name="metadata", + name="run", + field=models.JSONField(blank=True, null=True), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 08af5b944..982188a64 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -97,8 +97,7 @@ class MetaData(models.Model): title = models.CharField(max_length=500, default="Title") # run - # TODO: find out if this is expected to be long - run = models.CharField(max_length=500, blank=True, null=True) + run = models.JSONField(blank=True, null=True) # definition definition = models.TextField(blank=True, null=True) @@ -112,12 +111,6 @@ class MetaData(models.Model): # sample sample = models.JSONField(blank=True, null=True) - # transmission spectrum - transmission_spectrum = models.JSONField(blank=True, null=True) - - # raw metadata (for recreating in SasView only) - raw_metadata = models.JSONField(default=dict) - class OperationTree(models.Model): """Database model for tree of operations performed on a DataSet.""" From a445220bfd1414dc1b649d09c6d2d28a2b48ece3 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 19 Mar 2025 16:11:43 -0400 Subject: [PATCH 352/675] Account for metadata changes in test --- sasdata/fair_database/data/test/test_dataset.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index f3f09711b..04525bf16 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -13,13 +13,11 @@ class TestDataSet(APITestCase): def setUpTestData(cls): cls.empty_metadata = { "title": "New Metadata", - "run": "X", + "run": ["X"], "description": "test", "instrument": {}, "process": {}, "sample": {}, - "transmission_spectrum": {}, - "raw_metadata": {}, } cls.user1 = User.objects.create_user( id=1, username="testUser1", password="secret" From 8922ee291e220632ee96fc31d742e1a17a1cbe1f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 19 Mar 2025 16:20:39 -0400 Subject: [PATCH 353/675] Changes to OperationTree model --- ...operationtree_parent_operation_and_more.py | 39 +++++++++++++++++++ sasdata/fair_database/data/models.py | 18 ++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0013_remove_operationtree_parent_operation_and_more.py diff --git a/sasdata/fair_database/data/migrations/0013_remove_operationtree_parent_operation_and_more.py b/sasdata/fair_database/data/migrations/0013_remove_operationtree_parent_operation_and_more.py new file mode 100644 index 000000000..95a76ee93 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0013_remove_operationtree_parent_operation_and_more.py @@ -0,0 +1,39 @@ +# Generated by Django 5.1.6 on 2025-03-19 20:20 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0012_remove_metadata_raw_metadata_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="operationtree", + name="parent_operation", + ), + migrations.AddField( + model_name="operationtree", + name="parent_operation1", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="child_operations1", + to="data.operationtree", + ), + ), + migrations.AddField( + model_name="operationtree", + name="parent_operation2", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="child_operations2", + to="data.operationtree", + ), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 982188a64..c503ddab3 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -119,14 +119,28 @@ class OperationTree(models.Model): dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) # operation + # TODO: restrict to list of operations operation = models.CharField(max_length=10) # parameters parameters = models.JSONField(default=dict) # previous operation - parent_operation = models.ForeignKey( - "self", blank=True, null=True, on_delete=models.CASCADE + parent_operation1 = models.ForeignKey( + "self", + blank=True, + null=True, + on_delete=models.CASCADE, + related_name="child_operations1", + ) + + # optional second previous operation for binary operations + parent_operation2 = models.ForeignKey( + "self", + blank=True, + null=True, + on_delete=models.CASCADE, + related_name="child_operations2", ) From 93e194de79ed859b04ac961a1f772351d647fdec Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Mar 2025 10:07:52 -0400 Subject: [PATCH 354/675] Change metadata serialization in SasData serialization method --- sasdata/data.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index e0d35f019..75a6c4240 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -80,9 +80,9 @@ def deserialise_json(json_data: dict) -> "SasData": name = json_data["name"] data_contents = {} # deserialize Quantity dataset_type = json_data["dataset_type"] - raw_metadata = Group.deserialise_json(json_data["raw_metadata"]) + metadata = json_data["metadata"].deserialise_json() verbose = json_data["verbose"] - return SasData(name, data_contents, dataset_type, raw_metadata, verbose) + return SasData(name, data_contents, dataset_type, metadata, verbose) def serialise(self) -> str: return json.dumps(self._serialise_json()) @@ -93,9 +93,8 @@ def _serialise_json(self) -> dict[str, Any]: "name": self.name, "data_contents": {q: self._data_contents[q].serialise_json() for q in self._data_contents}, "dataset_type": None, # TODO: update when DatasetType is more finalized - "raw_metadata": self._raw_metadata.serialise_json(), "verbose": self._verbose, - "metadata": self.metadata.serialise_json(), # TODO: fix metadata eventually + "metadata": self.metadata.serialise_json(), "mask": {}, "model_requirements": {} } \ No newline at end of file From 1a8951114641fc18128e86aa0a1bee8255b5ea5a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Mar 2025 10:31:16 -0400 Subject: [PATCH 355/675] Change defaults and designate allowed operations for metadata/operations models --- ...ata_process_alter_metadata_run_and_more.py | 52 +++++++++++++++++++ sasdata/fair_database/data/models.py | 36 ++++++++++--- 2 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0014_alter_metadata_process_alter_metadata_run_and_more.py diff --git a/sasdata/fair_database/data/migrations/0014_alter_metadata_process_alter_metadata_run_and_more.py b/sasdata/fair_database/data/migrations/0014_alter_metadata_process_alter_metadata_run_and_more.py new file mode 100644 index 000000000..adb588d55 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0014_alter_metadata_process_alter_metadata_run_and_more.py @@ -0,0 +1,52 @@ +# Generated by Django 5.1.6 on 2025-03-20 14:29 + +import data.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0013_remove_operationtree_parent_operation_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="metadata", + name="process", + field=models.JSONField(default=data.models.empty_list), + ), + migrations.AlterField( + model_name="metadata", + name="run", + field=models.JSONField(default=data.models.empty_list), + ), + migrations.AlterField( + model_name="operationtree", + name="operation", + field=models.CharField( + choices=[ + ("zero", "0 [Add.Id.]"), + ("one", "1 [Mul.Id.]"), + ("constant", "Constant"), + ("variable", "Variable"), + ("neg", "Neg"), + ("reciprocal", "Inv"), + ("add", "Add"), + ("sub", "Sub"), + ("mul", "Mul"), + ("div", "Div"), + ("pow", "Pow"), + ("transpose", "Transpose"), + ("dot", "Dot"), + ("matmul", "MatMul"), + ("tensor_product", "TensorProduct"), + ], + max_length=20, + ), + ), + migrations.AlterField( + model_name="operationtree", + name="parameters", + field=models.JSONField(default=data.models.empty_dict), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index c503ddab3..c8cb0cd6e 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -89,7 +89,14 @@ class LabeledQuantity(models.Model): """ -# TODO: revisit metadata when sasdata metadata is rewritten +def empty_list(): + return [] + + +def empty_dict(): + return {} + + class MetaData(models.Model): """Database model for scattering metadata""" @@ -97,7 +104,7 @@ class MetaData(models.Model): title = models.CharField(max_length=500, default="Title") # run - run = models.JSONField(blank=True, null=True) + run = models.JSONField(default=empty_list) # definition definition = models.TextField(blank=True, null=True) @@ -106,7 +113,7 @@ class MetaData(models.Model): instrument = models.JSONField(blank=True, null=True) # process - process = models.JSONField(blank=True, null=True) + process = models.JSONField(default=empty_list) # sample sample = models.JSONField(blank=True, null=True) @@ -115,15 +122,32 @@ class MetaData(models.Model): class OperationTree(models.Model): """Database model for tree of operations performed on a DataSet.""" + OPERATION_CHOICES = { + "zero": "0 [Add.Id.]", + "one": "1 [Mul.Id.]", + "constant": "Constant", + "variable": "Variable", + "neg": "Neg", + "reciprocal": "Inv", + "add": "Add", + "sub": "Sub", + "mul": "Mul", + "div": "Div", + "pow": "Pow", + "transpose": "Transpose", + "dot": "Dot", + "matmul": "MatMul", + "tensor_product": "TensorProduct", + } + # Dataset the operation tree is performed on dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) # operation - # TODO: restrict to list of operations - operation = models.CharField(max_length=10) + operation = models.CharField(max_length=20, choices=OPERATION_CHOICES) # parameters - parameters = models.JSONField(default=dict) + parameters = models.JSONField(default=empty_dict) # previous operation parent_operation1 = models.ForeignKey( From 849a5e269ef2b75953f558c64886c2f6831c3753 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Mar 2025 11:42:17 -0400 Subject: [PATCH 356/675] Customize creation in OperationTreeSerializer --- sasdata/fair_database/data/serializers.py | 33 ++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 3f1912bf4..b3d3ff8e3 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -83,7 +83,38 @@ class Meta: fields = "__all__" +def constant_or_variable(operation: str): + return str in ["zero", "one", "constant", "variable"] + + +def binary(operation: str): + return str in ["add", "sub", "mul", "div", "dot", "matmul", "tensor_product"] + + class OperationTreeSerializer(serializers.ModelSerializer): class Meta: model = OperationTree - fields = "__all__" + fields = ["dataset", "operation", "parameters"] + + def create(self, validated_data): + parent_operation1 = None + parent_operation2 = None + if not constant_or_variable(validated_data["operation"]): + parent1 = validated_data["parameters"].pop("a") + parent1["dataset"] = validated_data["dataset"] + serializer1 = OperationTreeSerializer(data=parent1) + if serializer1.is_valid(raise_exception=True): + parent_operation1 = serializer1.save() + if binary(validated_data["operation"]): + parent2 = validated_data["parameters"].pop("b") + parent2["dataset"] = validated_data["dataset"] + serializer2 = OperationTreeSerializer(data=parent2) + if serializer2.is_valid(raise_exception=True): + parent_operation2 = serializer2.save() + return OperationTree.objects.create( + dataset=validated_data["dataset"], # TODO: check uuid vs object + operation=validated_data["operation"], + parameters=validated_data["parameters"], + parent_operation1=parent_operation1, + parent_operaton2=parent_operation2, + ) From f282e817c08b385d8b8098ce4a140f1cb5b02f48 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Mar 2025 13:20:06 -0400 Subject: [PATCH 357/675] Quantity model --- ...5_labeledquantity_dataset_data_contents.py | 47 +++++++++++++++++++ sasdata/fair_database/data/models.py | 4 +- 2 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0015_labeledquantity_dataset_data_contents.py diff --git a/sasdata/fair_database/data/migrations/0015_labeledquantity_dataset_data_contents.py b/sasdata/fair_database/data/migrations/0015_labeledquantity_dataset_data_contents.py new file mode 100644 index 000000000..877f2d386 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0015_labeledquantity_dataset_data_contents.py @@ -0,0 +1,47 @@ +# Generated by Django 5.1.6 on 2025-03-20 17:19 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0014_alter_metadata_process_alter_metadata_run_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="LabeledQuantity", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("label", models.CharField(max_length=20)), + ( + "dataset", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="data.dataset" + ), + ), + ( + "quantity", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="data.quantity" + ), + ), + ], + ), + migrations.AddField( + model_name="dataset", + name="data_contents", + field=models.ManyToManyField( + through="data.LabeledQuantity", to="data.quantity" + ), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index c8cb0cd6e..ff4200a6c 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -59,7 +59,7 @@ class DataSet(Data): ) # data contents - maybe ManyToManyField - # data_contents = models.ManyToManyField("Quantity", through="LabeledQuantity") + data_contents = models.ManyToManyField("Quantity", through="LabeledQuantity") # type of dataset # dataset_type = models.JSONField() @@ -81,12 +81,10 @@ class Quantity(models.Model): hash = models.IntegerField() -""" class LabeledQuantity(models.Model): dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) quantity = models.ForeignKey(Quantity, on_delete=models.CASCADE) label = models.CharField(max_length=20) -""" def empty_list(): From eb99c658f9c89f38f73f95ec70b3231a9966c3a2 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Mar 2025 17:42:49 -0400 Subject: [PATCH 358/675] Change SasData serializer to facilitate database deserialization --- sasdata/data.py | 7 ++++++- sasdata/fair_database/data/serializers.py | 13 ++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 75a6c4240..75b18169e 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -89,9 +89,14 @@ def serialise(self) -> str: # TODO: fix serializers eventually def _serialise_json(self) -> dict[str, Any]: + data = [] + for d in self._data_contents: + quantity = self._data_contents[d] + quantity["label"] = d + data.append(quantity) return { "name": self.name, - "data_contents": {q: self._data_contents[q].serialise_json() for q in self._data_contents}, + "data_contents": data, "dataset_type": None, # TODO: update when DatasetType is more finalized "verbose": self._verbose, "metadata": self.metadata.serialise_json(), diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index b3d3ff8e3..37e86cd85 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -30,10 +30,13 @@ class DataSetSerializer(serializers.ModelSerializer): files = serializers.PrimaryKeyRelatedField( required=False, many=True, allow_null=True, queryset=DataFile ) + data_contents = serializers.DictField() + # TODO: handle files better + # TODO: see if I can find a better way to handle the quantity part class Meta: model = DataSet - fields = "__all__" + fields = ["name", "files", "metadata"] def validate(self, data): if ( @@ -58,7 +61,13 @@ def create(self, validated_data): metadata = MetaDataSerializer.create( MetaDataSerializer(), validated_data=metadata_raw ) + data_contents = validated_data.pop("data_contents") dataset = DataSet.objects.create(metadata=metadata, **validated_data) + for d in data_contents: + serializer = QuantitySerializer(data=data_contents[d]) + if serializer.is_valid(): + quantity = serializer.save() + dataset.data_contents.add(quantity, through_defaults={"label": d}) return dataset # TODO: account for updating other attributes @@ -76,6 +85,8 @@ def update(self, instance, validated_data): instance.save() return instance + # TODO: custom method for database to serializer representation + class QuantitySerializer(serializers.ModelSerializer): class Meta: From bbf20ab85d2c7ef2a09f75a79686e38c1a5fbc08 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Mar 2025 18:25:19 -0400 Subject: [PATCH 359/675] Nested quantity serialization in DataSetSerializer --- sasdata/fair_database/data/serializers.py | 34 +++++++++++++++-------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 37e86cd85..6dfe2c851 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -25,18 +25,35 @@ class Meta: fields = "__all__" +class QuantitySerializer(serializers.ModelSerializer): + label = serializers.CharField(max_length=20) + + class Meta: + model = Quantity + fields = ["value", "variance", "units", "hash", "label"] + + class DataSetSerializer(serializers.ModelSerializer): metadata = MetaDataSerializer(read_only=False) files = serializers.PrimaryKeyRelatedField( required=False, many=True, allow_null=True, queryset=DataFile ) - data_contents = serializers.DictField() + data_contents = QuantitySerializer(many=True, read_only=False) # TODO: handle files better # TODO: see if I can find a better way to handle the quantity part class Meta: model = DataSet - fields = ["name", "files", "metadata"] + fields = [ + "id", + "name", + "files", + "metadata", + "data_contents", + "is_public", + "current_user", + "users", + ] def validate(self, data): if ( @@ -64,10 +81,9 @@ def create(self, validated_data): data_contents = validated_data.pop("data_contents") dataset = DataSet.objects.create(metadata=metadata, **validated_data) for d in data_contents: - serializer = QuantitySerializer(data=data_contents[d]) - if serializer.is_valid(): - quantity = serializer.save() - dataset.data_contents.add(quantity, through_defaults={"label": d}) + label = d.pop("label") + quantity = QuantitySerializer.create(QuantitySerializer(), validated_data=d) + dataset.data_contents.add(quantity, through_defaults={"label": label}) return dataset # TODO: account for updating other attributes @@ -88,12 +104,6 @@ def update(self, instance, validated_data): # TODO: custom method for database to serializer representation -class QuantitySerializer(serializers.ModelSerializer): - class Meta: - model = Quantity - fields = "__all__" - - def constant_or_variable(operation: str): return str in ["zero", "one", "constant", "variable"] From 40c5e81036cf54a215e40eb342c177df4e25531c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 20 Mar 2025 18:26:36 -0400 Subject: [PATCH 360/675] Add nested quantities to tests --- sasdata/fair_database/data/test/test_dataset.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 04525bf16..938590f12 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -19,6 +19,9 @@ def setUpTestData(cls): "process": {}, "sample": {}, } + cls.empty_data = [ + {"value": 0, "variance": 0, "units": "no", "hash": 0, "label": "test"} + ] cls.user1 = User.objects.create_user( id=1, username="testUser1", password="secret" ) @@ -112,7 +115,11 @@ def test_list_wrong_username(self): # Test creating a dataset with associated metadata def test_dataset_created(self): - dataset = {"name": "New Dataset", "metadata": self.empty_metadata} + dataset = { + "name": "New Dataset", + "metadata": self.empty_metadata, + "data_contents": self.empty_data, + } request = self.auth_client1.post("/v1/data/set/", data=dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] new_dataset = DataSet.objects.get(id=max_id) @@ -134,6 +141,7 @@ def test_dataset_created_unauthenticated(self): "name": "New Dataset", "metadata": self.empty_metadata, "is_public": True, + "data_contents": self.empty_data, } request = self.client.post("/v1/data/set/", data=dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -155,6 +163,7 @@ def test_no_private_unowned_dataset(self): "name": "Disallowed Dataset", "metadata": self.empty_metadata, "is_public": False, + "data_contents": self.empty_data, } request = self.client.post("/v1/data/set/", data=dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) @@ -166,6 +175,7 @@ def test_no_data_overwrite(self): "name": "Overwrite Dataset", "metadata": self.empty_metadata, "is_public": True, + "data_contents": self.empty_data, } request = self.auth_client2.post("/v1/data/set/", data=dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -239,6 +249,7 @@ def test_load_private_dataset(self): "name": "Dataset 2", "files": [], "metadata": None, + "data_contents": [], }, ) @@ -258,6 +269,7 @@ def test_load_public_dataset(self): "name": "Dataset 1", "files": [], "metadata": None, + "data_contents": [], }, ) @@ -277,6 +289,7 @@ def test_load_unowned_dataset(self): "name": "Dataset 3", "files": [], "metadata": None, + "data_contents": [], }, ) From f22bf0b9f21e00eae5677e5880d268f31fca2a9d Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 21 Mar 2025 15:37:52 -0400 Subject: [PATCH 361/675] Comment out failing sasdata tests until they get fixed --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e4d11c391..6a90cebd1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -77,9 +77,9 @@ jobs: ### Test sasdata - - name: Test with pytest - run: | - python -m pytest -v -s test + #- name: Test with pytest + # run: | + # python -m pytest -v -s test publish-to-pypi: name: Publish to pypi From 0fd100f0792356d3009b543efd81fa059f0d89fa Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 24 Mar 2025 13:48:33 -0400 Subject: [PATCH 362/675] Edits to github actions/yml files --- .github/workflows/test-fair-database.yml | 4 +--- .github/workflows/test.yml | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test-fair-database.yml b/.github/workflows/test-fair-database.yml index 531c93cf1..653cde1ee 100644 --- a/.github/workflows/test-fair-database.yml +++ b/.github/workflows/test-fair-database.yml @@ -45,9 +45,7 @@ jobs: - name: Build sasdata run: | # BUILD SASDATA - python setup.py clean - python setup.py build - python -m pip install . + python -m pip install -e . ### Build documentation (if enabled) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6a90cebd1..e4d11c391 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -77,9 +77,9 @@ jobs: ### Test sasdata - #- name: Test with pytest - # run: | - # python -m pytest -v -s test + - name: Test with pytest + run: | + python -m pytest -v -s test publish-to-pypi: name: Publish to pypi From a1ddd023817fb20b517c1cc3758feca243ca150b Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 24 Mar 2025 13:58:52 -0400 Subject: [PATCH 363/675] Clarify logic in permissions --- sasdata/fair_database/fair_database/permissions.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/fair_database/permissions.py b/sasdata/fair_database/fair_database/permissions.py index 4b5c98047..46908ff5d 100644 --- a/sasdata/fair_database/fair_database/permissions.py +++ b/sasdata/fair_database/fair_database/permissions.py @@ -6,10 +6,8 @@ def is_owner(request, obj): def has_access(request, obj): - return ( - is_owner(request, obj) - or request.user.is_authenticated - and request.user in obj.users.all() + return is_owner(request, obj) or ( + request.user.is_authenticated and request.user in obj.users.all() ) From 2b73af299b15b8b2ae5debd3e9d8dc2f785ae12c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 24 Mar 2025 14:02:25 -0400 Subject: [PATCH 364/675] Test tokens valid on multiple login --- sasdata/fair_database/user_app/tests.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index 36a8e8ec0..83d5c2abb 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -57,8 +57,12 @@ def test_login(self): def test_multiple_login(self): response = self.client1.post("/auth/login/", data=self.login_data_2) response2 = self.client2.post("/auth/login/", data=self.login_data_2) + response3 = self.client1.get("/auth/user/", headers=self.auth_header(response)) + response4 = self.client2.get("/auth/user/", headers=self.auth_header(response2)) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response2.status_code, status.HTTP_200_OK) + self.assertEqual(response3.status_code, status.HTTP_200_OK) + self.assertEqual(response4.status_code, status.HTTP_200_OK) self.assertNotEqual(response.content, response2.content) # Test get user information From f2fd1ee4a0c37c0d71e10e95a3f2f01144cca1da Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 24 Mar 2025 14:16:06 -0400 Subject: [PATCH 365/675] Remove mysterious extra spaces --- sasdata/quantities/quantity.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 0e778e208..2fce6a172 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1022,11 +1022,7 @@ def jacobian(self) -> list[Operation]: return [self.operation_tree.derivative(key) for key in self.reference_key_list] def _recalculate(self): - """ Recalculate the value of this object - primary use case is - - - - for testing """ + """ Recalculate the value of this object - primary use case isfor testing """ return self.operation_tree.evaluate(self.references) def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, int]: "Quantity"] = {}): From 753c97bef4198641de6f390804365926e516acf8 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 25 Mar 2025 13:13:13 -0400 Subject: [PATCH 366/675] Change operations model to refer to quantity --- ...tiontree_dataset_operationtree_quantity.py | 27 +++++++++++++++++++ sasdata/fair_database/data/models.py | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 sasdata/fair_database/data/migrations/0016_remove_operationtree_dataset_operationtree_quantity.py diff --git a/sasdata/fair_database/data/migrations/0016_remove_operationtree_dataset_operationtree_quantity.py b/sasdata/fair_database/data/migrations/0016_remove_operationtree_dataset_operationtree_quantity.py new file mode 100644 index 000000000..22b1e1fde --- /dev/null +++ b/sasdata/fair_database/data/migrations/0016_remove_operationtree_dataset_operationtree_quantity.py @@ -0,0 +1,27 @@ +# Generated by Django 5.1.6 on 2025-03-25 17:12 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0015_labeledquantity_dataset_data_contents"), + ] + + operations = [ + migrations.RemoveField( + model_name="operationtree", + name="dataset", + ), + migrations.AddField( + model_name="operationtree", + name="quantity", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + to="data.quantity", + ), + preserve_default=False, + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index ff4200a6c..dae980fad 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -139,7 +139,7 @@ class OperationTree(models.Model): } # Dataset the operation tree is performed on - dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) + quantity = models.ForeignKey(Quantity, on_delete=models.CASCADE) # operation operation = models.CharField(max_length=20, choices=OPERATION_CHOICES) From 9cae5959a20c44a22e322601c1e94f52da258820 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 25 Mar 2025 15:25:07 -0400 Subject: [PATCH 367/675] Add nested serialization for operation tree in quantity --- sasdata/fair_database/data/serializers.py | 72 +++++++++++++---------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 6dfe2c851..5f7909e12 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -25,12 +25,53 @@ class Meta: fields = "__all__" +class OperationTreeSerializer(serializers.ModelSerializer): + class Meta: + model = OperationTree + fields = ["quantity", "operation", "parameters"] + + def create(self, validated_data): + parent_operation1 = None + parent_operation2 = None + if not constant_or_variable(validated_data["operation"]): + parent1 = validated_data["parameters"].pop("a") + parent1["quantity"] = validated_data["quantity"] + serializer1 = OperationTreeSerializer(data=parent1) + if serializer1.is_valid(raise_exception=True): + parent_operation1 = serializer1.save() + if binary(validated_data["operation"]): + parent2 = validated_data["parameters"].pop("b") + parent2["quantity"] = validated_data["quantity"] + serializer2 = OperationTreeSerializer(data=parent2) + if serializer2.is_valid(raise_exception=True): + parent_operation2 = serializer2.save() + return OperationTree.objects.create( + dataset=validated_data["quantity"], # TODO: check uuid vs object + operation=validated_data["operation"], + parameters=validated_data["parameters"], + parent_operation1=parent_operation1, + parent_operaton2=parent_operation2, + ) + + class QuantitySerializer(serializers.ModelSerializer): label = serializers.CharField(max_length=20) + history = serializers.JSONField() class Meta: model = Quantity - fields = ["value", "variance", "units", "hash", "label"] + fields = ["value", "variance", "units", "hash", "label", "history"] + + # TODO: validation checks for history + + def create(self, validated_data): + operations_raw = validated_data.pop("history") + quantity = Quantity.objects.create(**validated_data) + operations_tree_raw = operations_raw["operation_tree"] + operations_tree_raw["quantity"] = quantity.id + serializer = OperationTreeSerializer(data=operations_tree_raw) + if serializer.is_valid(): + serializer.save() class DataSetSerializer(serializers.ModelSerializer): @@ -110,32 +151,3 @@ def constant_or_variable(operation: str): def binary(operation: str): return str in ["add", "sub", "mul", "div", "dot", "matmul", "tensor_product"] - - -class OperationTreeSerializer(serializers.ModelSerializer): - class Meta: - model = OperationTree - fields = ["dataset", "operation", "parameters"] - - def create(self, validated_data): - parent_operation1 = None - parent_operation2 = None - if not constant_or_variable(validated_data["operation"]): - parent1 = validated_data["parameters"].pop("a") - parent1["dataset"] = validated_data["dataset"] - serializer1 = OperationTreeSerializer(data=parent1) - if serializer1.is_valid(raise_exception=True): - parent_operation1 = serializer1.save() - if binary(validated_data["operation"]): - parent2 = validated_data["parameters"].pop("b") - parent2["dataset"] = validated_data["dataset"] - serializer2 = OperationTreeSerializer(data=parent2) - if serializer2.is_valid(raise_exception=True): - parent_operation2 = serializer2.save() - return OperationTree.objects.create( - dataset=validated_data["dataset"], # TODO: check uuid vs object - operation=validated_data["operation"], - parameters=validated_data["parameters"], - parent_operation1=parent_operation1, - parent_operaton2=parent_operation2, - ) From 51f1f84f6dafb5ec814e9d565f3550a85831dac3 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 25 Mar 2025 15:43:26 -0400 Subject: [PATCH 368/675] Make operations tree optional for a quantity --- sasdata/fair_database/data/serializers.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 5f7909e12..de09c7a06 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -56,7 +56,7 @@ def create(self, validated_data): class QuantitySerializer(serializers.ModelSerializer): label = serializers.CharField(max_length=20) - history = serializers.JSONField() + history = serializers.JSONField(required=False) # TODO: is this required? class Meta: model = Quantity @@ -65,13 +65,17 @@ class Meta: # TODO: validation checks for history def create(self, validated_data): - operations_raw = validated_data.pop("history") - quantity = Quantity.objects.create(**validated_data) - operations_tree_raw = operations_raw["operation_tree"] - operations_tree_raw["quantity"] = quantity.id - serializer = OperationTreeSerializer(data=operations_tree_raw) - if serializer.is_valid(): - serializer.save() + if "history" in validated_data: + operations_raw = validated_data.pop("history") + quantity = Quantity.objects.create(**validated_data) + operations_tree_raw = operations_raw["operation_tree"] + operations_tree_raw["quantity"] = quantity.id + serializer = OperationTreeSerializer(data=operations_tree_raw) + if serializer.is_valid(): + serializer.save() + return quantity + else: + return Quantity.objects.create(**validated_data) class DataSetSerializer(serializers.ModelSerializer): From ebf3b4733c8a8b70eece284304f102156aef2bd1 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 27 Mar 2025 11:50:31 -0400 Subject: [PATCH 369/675] Rename api clients in tests for clarity --- .../fair_database/data/test/test_datafile.py | 110 ++++++++++-------- .../fair_database/data/test/test_dataset.py | 24 ++-- sasdata/fair_database/user_app/tests.py | 16 ++- 3 files changed, 81 insertions(+), 69 deletions(-) diff --git a/sasdata/fair_database/data/test/test_datafile.py b/sasdata/fair_database/data/test/test_datafile.py index 7bbd9fb03..6ad136017 100644 --- a/sasdata/fair_database/data/test/test_datafile.py +++ b/sasdata/fair_database/data/test/test_datafile.py @@ -37,12 +37,12 @@ def setUpTestData(cls): cls.private_test_data.file.save( "cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb") ) - cls.client1 = APIClient() - cls.client1.force_authenticate(user=cls.user) + cls.client_authenticated = APIClient() + cls.client_authenticated.force_authenticate(user=cls.user) # Test list public data def test_does_list_public(self): - request = self.client1.get("/v1/data/file/") + request = self.client_authenticated.get("/v1/data/file/") self.assertEqual( request.data, {"public_data_ids": {1: "cyl_400_40.txt", 3: "cyl_400_20.txt"}}, @@ -50,37 +50,39 @@ def test_does_list_public(self): # Test list a user's private data def test_does_list_user(self): - request = self.client1.get( + request = self.client_authenticated.get( "/v1/data/file/", data={"username": "testUser"}, user=self.user ) self.assertEqual(request.data, {"user_data_ids": {3: "cyl_400_20.txt"}}) # Test list another user's public data def test_list_other_user(self): - client2 = APIClient() - request = client2.get( + client_unauthenticated = APIClient() + request = client_unauthenticated.get( "/v1/data/file/", data={"username": "testUser"}, user=self.user ) self.assertEqual(request.data, {"user_data_ids": {}}) # Test list a nonexistent user's data def test_list_nonexistent_user(self): - request = self.client1.get("/v1/data/file/", data={"username": "fakeUser"}) + request = self.client_authenticated.get( + "/v1/data/file/", data={"username": "fakeUser"} + ) self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) # Test loading a public data file def test_does_load_data_info_public(self): - request = self.client1.get("/v1/data/file/1/") + request = self.client_authenticated.get("/v1/data/file/1/") self.assertEqual(request.status_code, status.HTTP_200_OK) # Test loading private data with authorization def test_does_load_data_info_private(self): - request = self.client1.get("/v1/data/file/3/") + request = self.client_authenticated.get("/v1/data/file/3/") self.assertEqual(request.status_code, status.HTTP_200_OK) # Test loading data that does not exist def test_load_data_info_nonexistent(self): - request = self.client1.get("/v1/data/file/5/") + request = self.client_authenticated.get("/v1/data/file/5/") self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) @classmethod @@ -103,15 +105,15 @@ def setUpTestData(cls): id=1, current_user=cls.user, file_name="cyl_400_20.txt", is_public=False ) cls.data.file.save("cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb")) - cls.client1 = APIClient() - cls.client1.force_authenticate(user=cls.user) - cls.client2 = APIClient() + cls.client_authenticated = APIClient() + cls.client_authenticated.force_authenticate(user=cls.user) + cls.client_unauthenticated = APIClient() # Test data upload creates data in database def test_is_data_being_created(self): file = open(find("cyl_400_40.txt"), "rb") data = {"is_public": False, "file": file} - request = self.client1.post("/v1/data/file/", data=data) + request = self.client_authenticated.post("/v1/data/file/", data=data) max_id = DataFile.objects.aggregate(Max("id"))["id__max"] self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual( @@ -130,7 +132,7 @@ def test_is_data_being_created(self): def test_is_data_being_created_no_user(self): file = open(find("cyl_testdata.txt"), "rb") data = {"is_public": True, "file": file} - request = self.client2.post("/v1/data/file/", data=data) + request = self.client_unauthenticated.post("/v1/data/file/", data=data) max_id = DataFile.objects.aggregate(Max("id"))["id__max"] self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual( @@ -149,7 +151,7 @@ def test_is_data_being_created_no_user(self): def test_no_data_overwrite(self): file = open(find("apoferritin.txt")) data = {"is_public": True, "file": file, id: 1} - request = self.client1.post("/v1/data/file/", data=data) + request = self.client_authenticated.post("/v1/data/file/", data=data) self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual(DataFile.objects.get(id=1).file_name, "cyl_400_20.txt") max_id = DataFile.objects.aggregate(Max("id"))["id__max"] @@ -169,7 +171,7 @@ def test_no_data_overwrite(self): def test_does_file_upload_update(self): file = open(find("cyl_testdata1.txt")) data = {"file": file, "is_public": False} - request = self.client1.put("/v1/data/file/1/", data=data) + request = self.client_authenticated.put("/v1/data/file/1/", data=data) self.assertEqual( request.data, { @@ -193,7 +195,7 @@ def test_public_file_upload_update(self): ) file = open(find("conalbumin.txt")) data = {"file": file, "is_public": True} - request = self.client1.put("/v1/data/file/3/", data=data) + request = self.client_authenticated.put("/v1/data/file/3/", data=data) self.assertEqual( request.data, { @@ -210,19 +212,21 @@ def test_public_file_upload_update(self): def test_unauthorized_file_upload_update(self): file = open(find("cyl_400_40.txt")) data = {"file": file, "is_public": False} - request = self.client2.put("/v1/data/file/1/", data=data) + request = self.client_unauthenticated.put("/v1/data/file/1/", data=data) self.assertEqual(request.status_code, status.HTTP_401_UNAUTHORIZED) # Test update nonexistent file fails def test_file_upload_update_not_found(self): file = open(find("cyl_400_40.txt")) data = {"file": file, "is_public": False} - request = self.client2.put("/v1/data/file/5/", data=data) + request = self.client_unauthenticated.put("/v1/data/file/5/", data=data) self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) # Test file download def test_does_download(self): - request = self.client1.get("/v1/data/file/1/", data={"download": True}) + request = self.client_authenticated.get( + "/v1/data/file/1/", data={"download": True} + ) file_contents = b"".join(request.streaming_content) test_file = open(find("cyl_400_20.txt"), "rb") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -230,12 +234,16 @@ def test_does_download(self): # Test file download fails when unauthorized def test_unauthorized_download(self): - request2 = self.client2.get("/v1/data/file/1/", data={"download": True}) + request2 = self.client_unauthenticated.get( + "/v1/data/file/1/", data={"download": True} + ) self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) # Test download nonexistent file def test_download_nonexistent(self): - request = self.client1.get("/v1/data/file/5/", data={"download": True}) + request = self.client_authenticated.get( + "/v1/data/file/5/", data={"download": True} + ) self.assertEqual(request.status_code, status.HTTP_404_NOT_FOUND) # Test deleting a file @@ -243,13 +251,13 @@ def test_delete(self): DataFile.objects.create( id=6, current_user=self.user, file_name="test.txt", is_public=False ) - request = self.client1.delete("/v1/data/file/6/") + request = self.client_authenticated.delete("/v1/data/file/6/") self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertFalse(DataFile.objects.filter(pk=6).exists()) # Test deleting a file fails when unauthorized def test_delete_unauthorized(self): - request = self.client2.delete("/v1/data/file/1/") + request = self.client_unauthenticated.delete("/v1/data/file/1/") self.assertEqual(request.status_code, status.HTTP_401_UNAUTHORIZED) @classmethod @@ -279,21 +287,21 @@ def setUpTestData(cls): "cyl_400_20.txt", open(find("cyl_400_20.txt"), "rb") ) cls.shared_test_data.users.add(cls.user2) - cls.client1 = APIClient() - cls.client1.force_authenticate(cls.user1) - cls.client2 = APIClient() - cls.client2.force_authenticate(cls.user2) + cls.client_owner = APIClient() + cls.client_owner.force_authenticate(cls.user1) + cls.client_other = APIClient() + cls.client_other.force_authenticate(cls.user2) # test viewing no one with access def test_view_no_access(self): - request = self.client1.get("/v1/data/file/1/users/") + request = self.client_owner.get("/v1/data/file/1/users/") data = {"file": 1, "file_name": "cyl_400_40.txt", "users": []} self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, data) # test viewing list of users with access def test_view_access(self): - request = self.client1.get("/v1/data/file/2/users/") + request = self.client_owner.get("/v1/data/file/2/users/") data = {"file": 2, "file_name": "cyl_400_20.txt", "users": ["testUser2"]} self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, data) @@ -301,17 +309,17 @@ def test_view_access(self): # test granting another user access to private data def test_grant_access(self): data = {"username": "testUser2", "access": True} - request1 = self.client1.put("/v1/data/file/1/users/", data=data) - request2 = self.client2.get("/v1/data/file/1/") + request1 = self.client_owner.put("/v1/data/file/1/users/", data=data) + request2 = self.client_other.get("/v1/data/file/1/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) # test removing another user's access to private data def test_remove_access(self): data = {"username": "testUser2", "access": False} - request1 = self.client2.get("/v1/data/file/2/") - request2 = self.client1.put("/v1/data/file/2/users/", data=data) - request3 = self.client2.get("/v1/data/file/2/") + request1 = self.client_other.get("/v1/data/file/2/") + request2 = self.client_owner.put("/v1/data/file/2/users/", data=data) + request3 = self.client_other.get("/v1/data/file/2/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) @@ -319,9 +327,9 @@ def test_remove_access(self): # test removing access from a user that already lacks access def test_remove_no_access(self): data = {"username": "testUser2", "access": False} - request1 = self.client2.get("/v1/data/file/1/") - request2 = self.client1.put("/v1/data/file/1/users/", data=data) - request3 = self.client2.get("/v1/data/file/1/") + request1 = self.client_other.get("/v1/data/file/1/") + request2 = self.client_owner.put("/v1/data/file/1/users/", data=data) + request3 = self.client_other.get("/v1/data/file/1/") self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) @@ -329,17 +337,17 @@ def test_remove_no_access(self): # test owner's access cannot be removed def test_cant_revoke_own_access(self): data = {"username": "testUser", "access": False} - request1 = self.client1.put("/v1/data/file/1/users/", data=data) - request2 = self.client1.get("/v1/data/file/1/") + request1 = self.client_owner.put("/v1/data/file/1/users/", data=data) + request2 = self.client_owner.get("/v1/data/file/1/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) # test giving access to a user that already has access def test_grant_existing_access(self): data = {"username": "testUser2", "access": True} - request1 = self.client2.get("/v1/data/file/2/") - request2 = self.client1.put("/v1/data/file/2/users/", data=data) - request3 = self.client2.get("/v1/data/file/2/") + request1 = self.client_other.get("/v1/data/file/2/") + request2 = self.client_owner.put("/v1/data/file/2/users/", data=data) + request3 = self.client_other.get("/v1/data/file/2/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertEqual(request3.status_code, status.HTTP_200_OK) @@ -347,14 +355,14 @@ def test_grant_existing_access(self): # test that access is read-only for the file def test_no_edit_access(self): data = {"is_public": True} - request = self.client2.put("/v1/data/file/2/", data=data) + request = self.client_other.put("/v1/data/file/2/", data=data) self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) self.assertFalse(self.shared_test_data.is_public) # test that only the owner can view who has access def test_only_view_access_to_owned_file(self): - request1 = self.client2.get("/v1/data/file/1/users/") - request2 = self.client2.get("/v1/data/file/2/users/") + request1 = self.client_other.get("/v1/data/file/1/users/") + request2 = self.client_other.get("/v1/data/file/2/users/") self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) @@ -362,10 +370,10 @@ def test_only_view_access_to_owned_file(self): def test_only_edit_access_to_owned_file(self): data1 = {"username": "testUser2", "access": True} data2 = {"username": "testUser1", "access": False} - request1 = self.client2.put("/v1/data/file/1/users/", data=data1) - request2 = self.client2.put("/v1/data/file/2/users/", data=data2) - request3 = self.client2.get("/v1/data/file/1/") - request4 = self.client1.get("/v1/data/file/2/") + request1 = self.client_other.put("/v1/data/file/1/users/", data=data1) + request2 = self.client_other.put("/v1/data/file/2/users/", data=data2) + request3 = self.client_other.get("/v1/data/file/1/") + request4 = self.client_owner.get("/v1/data/file/2/") self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 938590f12..7467c6aab 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -388,14 +388,14 @@ def setUpTestData(cls): id=2, current_user=cls.user1, name="Dataset 2", metadata=None ) cls.shared_dataset.users.add(cls.user2) - cls.client1 = APIClient() - cls.client2 = APIClient() - cls.client1.force_authenticate(cls.user1) - cls.client2.force_authenticate(cls.user2) + cls.client_owner = APIClient() + cls.client_other = APIClient() + cls.client_owner.force_authenticate(cls.user1) + cls.client_other.force_authenticate(cls.user2) # Test listing no users with access def test_list_access_private(self): - request1 = self.client1.get("/v1/data/set/1/users/") + request1 = self.client_owner.get("/v1/data/set/1/users/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual( request1.data, {"data_id": 1, "name": "Dataset 1", "users": []} @@ -403,7 +403,7 @@ def test_list_access_private(self): # Test listing users with access def test_list_access_shared(self): - request1 = self.client1.get("/v1/data/set/2/users/") + request1 = self.client_owner.get("/v1/data/set/2/users/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual( request1.data, {"data_id": 2, "name": "Dataset 2", "users": ["testUser2"]} @@ -411,15 +411,15 @@ def test_list_access_shared(self): # Test only owner can view access def test_list_access_unauthorized(self): - request = self.client2.get("/v1/data/set/2/users/") + request = self.client_other.get("/v1/data/set/2/users/") self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) # Test granting access to a dataset def test_grant_access(self): - request1 = self.client1.put( + request1 = self.client_owner.put( "/v1/data/set/1/users/", data={"username": "testUser2", "access": True} ) - request2 = self.client2.get("/v1/data/set/1/") + request2 = self.client_other.get("/v1/data/set/1/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertIn( # codespell:ignore @@ -429,10 +429,10 @@ def test_grant_access(self): # Test revoking access to a dataset def test_revoke_access(self): - request1 = self.client1.put( + request1 = self.client_owner.put( "/v1/data/set/2/users/", data={"username": "testUser2", "access": False} ) - request2 = self.client2.get("/v1/data/set/2/") + request2 = self.client_other.get("/v1/data/set/2/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) self.assertNotIn(self.user2, DataSet.objects.get(id=2).users.all()) @@ -440,7 +440,7 @@ def test_revoke_access(self): # Test only the owner can change access def test_revoke_access_unauthorized(self): - request1 = self.client2.put( + request1 = self.client_other.put( "/v1/data/set/2/users/", data={"username": "testUser2", "access": False} ) self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index 83d5c2abb..eff7d728a 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -30,8 +30,8 @@ def setUpTestData(cls): cls.user = User.objects.create_user( id=1, username="testUser2", password="sasview!", email="email2@domain.org" ) - cls.client3 = APIClient() - cls.client3.force_authenticate(user=cls.user) + cls.client_authenticated = APIClient() + cls.client_authenticated.force_authenticate(user=cls.user) def auth_header(self, response): return {"Authorization": "Token " + response.data["token"]} @@ -67,7 +67,7 @@ def test_multiple_login(self): # Test get user information def test_user_get(self): - response = self.client3.get("/auth/user/") + response = self.client_authenticated.get("/auth/user/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( response.content, @@ -77,7 +77,7 @@ def test_user_get(self): # Test changing username def test_user_put_username(self): data = {"username": "newName"} - response = self.client3.put("/auth/user/", data=data) + response = self.client_authenticated.put("/auth/user/", data=data) self.user.username = "testUser2" self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( @@ -88,7 +88,7 @@ def test_user_put_username(self): # Test changing username and first and last name def test_user_put_name(self): data = {"username": "newName", "first_name": "Clark", "last_name": "Kent"} - response = self.client3.put("/auth/user/", data=data) + response = self.client_authenticated.put("/auth/user/", data=data) self.user.first_name = "" self.user.last_name = "" self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -155,8 +155,12 @@ def test_password_change(self): "old_password": "sasview!", } self.login_data_2["password"] = "sasview?" - response = self.client3.post("/auth/password/change/", data=data) + response = self.client_authenticated.post("/auth/password/change/", data=data) login_response = self.client1.post("/auth/login/", data=self.login_data_2) self.login_data_2["password"] = "sasview!" self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) + + @classmethod + def tearDownClass(cls): + cls.user.delete() From 8801967545880484290d2f449f983082c928d999 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 27 Mar 2025 12:02:17 -0400 Subject: [PATCH 370/675] Restrict Django test runner to database tests --- .github/workflows/test-fair-database.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-fair-database.yml b/.github/workflows/test-fair-database.yml index 653cde1ee..9652a8377 100644 --- a/.github/workflows/test-fair-database.yml +++ b/.github/workflows/test-fair-database.yml @@ -51,4 +51,4 @@ jobs: - name: Test with Django tests run: | - python sasdata/fair_database/manage.py test + python sasdata/fair_database/manage.py test sasdata.fair_database From b3007e78ee0095e646590faf01dffebd148f8992 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 1 Apr 2025 14:22:43 -0400 Subject: [PATCH 371/675] String representation of units for serialization --- sasdata/quantities/_units_base.py | 17 ----------------- sasdata/quantities/quantity.py | 4 ++-- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index cfe878c90..8f32edec7 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -200,17 +200,6 @@ def si_repr(self): return ''.join(tokens) - def serialise_json(self): - return { - "length": self.length, - "time": self.time, - "mass": self.mass, - "current": self.current, - "temperature": self.temperature, - "amount": self.moles_hint, - "angle": self.angle_hint - } - class Unit: def __init__(self, @@ -275,12 +264,6 @@ def __repr__(self): @staticmethod def parse(unit_string: str) -> "Unit": pass - - def serialise_json(self): - return { - "scale": self.scale, - "dimensions": self.dimensions.serialise_json() - } class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): """ This processor minimises the dimensionality of the unit by multiplying by as many units of the specified type as needed """ diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 2fce6a172..d5ea4c711 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1217,7 +1217,7 @@ def in_si_with_standard_error(self): @staticmethod def deserialise_json(json_data: dict) -> "Quantity": value = None # TODO QuantityType deserialisation - units = Unit.deserialise_json(json_data["units"]) + units = Unit.parse(json_data["units"]) standard_error = None #TODO QuantityType deserialisation hash_seed = json_data["hash_seed"] history = QuantityHistory.deserialise_json(json_data["history"]) @@ -1229,7 +1229,7 @@ def deserialise_json(json_data: dict) -> "Quantity": def serialise_json(self): return { "value": quantity_type_serialisation(self.value), - "units": self.units.serialise_json(), # Unit serialisation + "units": str(self.units), # Unit serialisation "variance": quantity_type_serialisation(self._variance), "hash_seed": self._hash_seed, # is this just a string? "history": self.history.serialise_json() From 882b326d1d0407b7299d45564b0b612e854011d8 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 3 Apr 2025 15:10:12 -0400 Subject: [PATCH 372/675] Change and test response for changing access to a DataFile --- .../fair_database/data/test/test_datafile.py | 45 +++++++++++++++++++ sasdata/fair_database/data/views.py | 4 +- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/test/test_datafile.py b/sasdata/fair_database/data/test/test_datafile.py index 6ad136017..75690c6dd 100644 --- a/sasdata/fair_database/data/test/test_datafile.py +++ b/sasdata/fair_database/data/test/test_datafile.py @@ -313,6 +313,15 @@ def test_grant_access(self): request2 = self.client_other.get("/v1/data/file/1/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertEqual( + request1.data, + { + "username": "testUser2", + "file": 1, + "file_name": "cyl_400_40.txt", + "access": True, + }, + ) # test removing another user's access to private data def test_remove_access(self): @@ -323,6 +332,15 @@ def test_remove_access(self): self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual( + request2.data, + { + "username": "testUser2", + "file": 2, + "file_name": "cyl_400_20.txt", + "access": False, + }, + ) # test removing access from a user that already lacks access def test_remove_no_access(self): @@ -333,6 +351,15 @@ def test_remove_no_access(self): self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual( + request2.data, + { + "username": "testUser2", + "file": 1, + "file_name": "cyl_400_40.txt", + "access": False, + }, + ) # test owner's access cannot be removed def test_cant_revoke_own_access(self): @@ -341,6 +368,15 @@ def test_cant_revoke_own_access(self): request2 = self.client_owner.get("/v1/data/file/1/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertEqual( + request1.data, + { + "username": "testUser", + "file": 1, + "file_name": "cyl_400_40.txt", + "access": True, + }, + ) # test giving access to a user that already has access def test_grant_existing_access(self): @@ -351,6 +387,15 @@ def test_grant_existing_access(self): self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertEqual(request3.status_code, status.HTTP_200_OK) + self.assertEqual( + request2.data, + { + "username": "testUser2", + "file": 2, + "file_name": "cyl_400_20.txt", + "access": True, + }, + ) # test that access is read-only for the file def test_no_edit_access(self): diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 1b8aefe88..44fb5b506 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -201,10 +201,10 @@ def put(self, request, data_id, version=None): else: db.users.remove(user) response_data = { - "user": user.username, + "username": user.username, "file": db.pk, "file_name": db.file_name, - "access": serializer.data["access"], + "access": (serializer.data["access"] or user == db.current_user), } return Response(response_data) From 7c96ee9ccebd2db34506ad3fe11cd5bce03983be Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 3 Apr 2025 15:52:05 -0400 Subject: [PATCH 373/675] Add hash value to quantity serialization --- sasdata/quantities/quantity.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index d5ea4c711..acef32fb2 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1232,6 +1232,7 @@ def serialise_json(self): "units": str(self.units), # Unit serialisation "variance": quantity_type_serialisation(self._variance), "hash_seed": self._hash_seed, # is this just a string? + "hash_value": self.hash_value, "history": self.history.serialise_json() } From cb9ec1a812ee0e97b78793c8214b0453487c4061 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 3 Apr 2025 15:58:29 -0400 Subject: [PATCH 374/675] Delete old method-based views --- sasdata/fair_database/data/views.py | 174 ---------------------------- 1 file changed, 174 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 44fb5b506..1c0586176 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -9,7 +9,6 @@ Http404, FileResponse, ) -from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status from rest_framework.views import APIView @@ -209,179 +208,6 @@ def put(self, request, data_id, version=None): return Response(response_data) -@api_view(["GET"]) -def list_data(request, username=None, version=None): - if request.method == "GET": - if username: - search_user = get_object_or_404(User, username=username) - data_list = {"user_data_ids": {}} - private_data = DataFile.objects.filter(current_user=search_user) - for x in private_data: - if permissions.check_permissions(request, x): - data_list["user_data_ids"][x.id] = x.file_name - else: - public_data = DataFile.objects.filter(is_public=True) - data_list = {"public_data_ids": {}} - for x in public_data: - if not permissions.check_permissions(request, x): - if not request.user.is_authenticated: - return HttpResponse("Unauthorized", status=401) - return HttpResponseForbidden() - data_list["public_data_ids"][x.id] = x.file_name - return Response(data_list) - return HttpResponseBadRequest("not get method") - - -@api_view(["GET"]) -def data_info(request, db_id, version=None): - if request.method == "GET": - loader = Loader() - data_db = get_object_or_404(DataFile, id=db_id) - if not permissions.check_permissions(request, data_db): - if not request.user.is_authenticated: - return HttpResponse("Must be authenticated to view", status=401) - return HttpResponseForbidden( - "Data is either not public or wrong auth token" - ) - data_list = loader.load(data_db.file.path) - contents = [str(data) for data in data_list] - return_data = {data_db.file_name: contents} - return Response(return_data) - return HttpResponseBadRequest() - - -@api_view(["POST", "PUT"]) -def upload(request, data_id=None, version=None): - # saves file - response_status = status.HTTP_200_OK - if request.method in ["POST", "PUT"] and data_id is None: - response_status = status.HTTP_201_CREATED - form = DataFileForm(request.data, request.FILES) - if form.is_valid(): - form.save() - db = DataFile.objects.get(pk=form.instance.pk) - - if request.user.is_authenticated: - serializer = DataFileSerializer( - db, - data={ - "file_name": os.path.basename(form.instance.file.path), - "current_user": request.user.id, - "users": [request.user.id], - }, - context={"is_public": db.is_public}, - ) - else: - serializer = DataFileSerializer( - db, - data={ - "file_name": os.path.basename(form.instance.file.path), - "current_user": None, - "users": [], - }, - context={"is_public": db.is_public}, - ) - - # updates file - elif request.method == "PUT": - db = get_object_or_404(DataFile, id=data_id) - if not permissions.check_permissions(request, db): - if not request.user.is_authenticated: - return HttpResponse("must be authenticated to modify", status=401) - return HttpResponseForbidden("must be the data owner to modify") - form = DataFileForm(request.data, request.FILES, instance=db) - if form.is_valid(): - form.save() - serializer = DataFileSerializer( - db, - data={ - "file_name": os.path.basename(form.instance.file.path), - "current_user": request.user.id, - }, - context={"is_public": db.is_public}, - partial=True, - ) - else: - return HttpResponseBadRequest() - - if serializer.is_valid(raise_exception=True): - serializer.save() - # TODO get warnings/errors later - return_data = { - "current_user": request.user.username, - "authenticated": request.user.is_authenticated, - "file_id": db.id, - "file_alternative_name": serializer.data["file_name"], - "is_public": serializer.data["is_public"], - } - return Response(return_data, status=response_status) - - -# view or control who has access to a file -@api_view(["GET", "PUT"]) -def manage_access(request, data_id, version=None): - db = get_object_or_404(DataFile, id=data_id) - if not permissions.is_owner(request, db): - if not request.user.is_authenticated: - return HttpResponse("Must be authenticated to manage access", status=401) - return HttpResponseForbidden("Must be the data owner to manage access") - if request.method == "GET": - response_data = { - "file": db.pk, - "file_name": db.file_name, - "users": [user.username for user in db.users.all()], - } - return Response(response_data) - elif request.method == "PUT": - serializer = AccessManagementSerializer(data=request.data) - serializer.is_valid() - user = get_object_or_404(User, username=serializer.data["username"]) - if serializer.data["access"]: - db.users.add(user) - else: - db.users.remove(user) - response_data = { - "user": user.username, - "file": db.pk, - "file_name": db.file_name, - "access": serializer.data["access"], - } - return Response(response_data) - return HttpResponseBadRequest() - - -# delete a file -@api_view(["DELETE"]) -def delete(request, data_id, version=None): - db = get_object_or_404(DataFile, id=data_id) - if not permissions.is_owner(request, db): - if not request.user.is_authenticated: - return HttpResponse("Must be authenticated to delete", status=401) - return HttpResponseForbidden("Must be the data owner to delete") - db.delete() - return Response(data={"success": True}) - - -# downloads a file -@api_view(["GET"]) -def download(request, data_id, version=None): - if request.method == "GET": - data = get_object_or_404(DataFile, id=data_id) - if not permissions.check_permissions(request, data): - if not request.user.is_authenticated: - return HttpResponse("Must be authenticated to download", status=401) - return HttpResponseForbidden("data is private") - # TODO add issues later - try: - file = open(data.file.path, "rb") - except Exception as e: - return HttpResponseBadRequest(str(e)) - if file is None: - raise Http404("File not found.") - return FileResponse(file, as_attachment=True) - return HttpResponseBadRequest() - - class DataSetView(APIView): """ View associated with the DataSet model. From bec304df7baba21ac34464a75acd00511b5fc979 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 3 Apr 2025 16:27:44 -0400 Subject: [PATCH 375/675] Change and test response for DataSet access management --- .../fair_database/data/test/test_dataset.py | 18 ++++++++++++++++++ sasdata/fair_database/data/views.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 7467c6aab..5d62a8800 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -425,6 +425,15 @@ def test_grant_access(self): self.assertIn( # codespell:ignore self.user2, DataSet.objects.get(id=1).users.all() ) + self.assertEqual( + request1.data, + { + "username": "testUser2", + "data_id": 1, + "name": "Dataset 1", + "access": True, + }, + ) self.private_dataset.users.remove(self.user2) # Test revoking access to a dataset @@ -436,6 +445,15 @@ def test_revoke_access(self): self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) self.assertNotIn(self.user2, DataSet.objects.get(id=2).users.all()) + self.assertEqual( + request1.data, + { + "username": "testUser2", + "data_id": 2, + "name": "Dataset 2", + "access": False, + }, + ) self.shared_dataset.users.add(self.user2) # Test only the owner can change access diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 1c0586176..3dc036f8f 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -338,7 +338,7 @@ def put(self, request, data_id, version=None): else: db.users.remove(user) response_data = { - "user": user.username, + "username": user.username, "data_id": db.id, "name": db.name, "access": serializer.data["access"], From 00db73a608c4e87c2a8eca927e04cc9d176c9195 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 4 Apr 2025 10:13:24 -0400 Subject: [PATCH 376/675] Serialization for QuantityType --- sasdata/quantities/quantity.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index acef32fb2..8248a207a 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -998,11 +998,19 @@ def hash_data_via_numpy(*data: ArrayLike): QuantityType = TypeVar("QuantityType") -# TODO: figure out how to handle np.ndarray serialization (save as file or otherwise) +# TODO: change QuantityType serialisation for greater efficiency def quantity_type_serialisation(var): - if isinstance(var, (str, int, float)): + if isinstance(var, np.ndarray): + return {"array_contents": var.tobytes(), "shape": var.shape} + else: + return var + +def quantity_type_deserialisation(var): + if isinstance(var, dict): + array = np.frombuffer(var["array_contents"]) + return np.reshape(array, shape=var["shape"]) + else: return var - return None class QuantityHistory: From 72f6903ee8cdcdacf96057cb42ccd5d9a75ba520 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 4 Apr 2025 14:42:54 -0400 Subject: [PATCH 377/675] Empty class for operation testing --- .../fair_database/data/test/test_dataset.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 5d62a8800..165f618fd 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -469,3 +469,25 @@ def tearDownClass(cls): cls.shared_dataset.delete() cls.user1.delete() cls.user2.delete() + + +class TestOperationTree(APITestCase): + """Tests for datasets with operation trees.""" + + @classmethod + def setUpTestData(cls): + pass + + # Test post with operation tree + + # Test post with invalid operation + + # Test get dataset with operation tree + + # Test nested operations + + # Different operations with different stored value types + + @classmethod + def tearDownClass(cls): + pass From 7737fec71988ad85bbb839e1e5178026211d63dc Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 4 Apr 2025 14:54:56 -0400 Subject: [PATCH 378/675] PublishedState and Session models --- .../migrations/0017_publishedstate_session.py | 80 +++++++++++++++++++ sasdata/fair_database/data/models.py | 13 ++- 2 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0017_publishedstate_session.py diff --git a/sasdata/fair_database/data/migrations/0017_publishedstate_session.py b/sasdata/fair_database/data/migrations/0017_publishedstate_session.py new file mode 100644 index 000000000..073e01652 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0017_publishedstate_session.py @@ -0,0 +1,80 @@ +# Generated by Django 5.1.6 on 2025-04-04 18:54 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0016_remove_operationtree_dataset_operationtree_quantity"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="PublishedState", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("published", models.BooleanField(default=False)), + ("doi", models.URLField()), + ], + ), + migrations.CreateModel( + name="Session", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "is_public", + models.BooleanField( + default=False, help_text="opt in to make your data public" + ), + ), + ( + "current_user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ("dataset", models.ManyToManyField(to="data.dataset")), + ( + "published_state", + models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="data.publishedstate", + ), + ), + ( + "users", + models.ManyToManyField( + blank=True, related_name="+", to=settings.AUTH_USER_MODEL + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index dae980fad..0205abedd 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -166,19 +166,21 @@ class OperationTree(models.Model): ) -''' class Session(Data): """Database model for a project save state.""" # dataset - # dataset = models.ManyToManyField(DataSet) + dataset = models.ManyToManyField(DataSet) # operation tree # operations = models.ForeignKey(OperationTree, on_delete=models.CASCADE) - published_state = models.ForeignKey("PublishedState", blank=True, null=True, on_delete=SET_NULL) + published_state = models.OneToOneField( + "PublishedState", blank=True, null=True, on_delete=models.SET_NULL + ) + -class PublishedState(): +class PublishedState(models.Model): """Database model for a project published state.""" # published @@ -186,6 +188,3 @@ class PublishedState(): # doi doi = models.URLField() - - -''' From 9a22b82328c10cf223f92c7a7436d982772de65d Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 4 Apr 2025 15:16:26 -0400 Subject: [PATCH 379/675] Empty classes for session views --- sasdata/fair_database/data/views.py | 59 ++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 3dc036f8f..d0063b48d 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -161,7 +161,7 @@ def delete(self, request, data_id, version=None): class DataFileUsersView(APIView): """ - View for the users that have access to a dataset. + View for the users that have access to a datafile. Functionality for accessing a list of users with access and granting or revoking access. @@ -344,3 +344,60 @@ def put(self, request, data_id, version=None): "access": serializer.data["access"], } return Response(response_data) + + +class SessionView(APIView): + """ + View associated with the Session model. + + Functionality for viewing a list of sessions and for creating a session. + """ + + # View a list of accessible sessions + def get(self, request, version=None): + pass + + # Create a session + def post(self, request, version=None): + pass + + # Create a session + def put(self, request, version=None): + pass + + +class SingleSessionView(APIView): + """ + View associated with single sessions. + + Functionality for viewing, modifying, and deleting individual sessions. + """ + + # get a specific session + def get(self, request, data_id, version=None): + pass + + # modify a session + def put(self, request, data_id, version=None): + pass + + # delete a session + def delete(self, request, data_id, version=None): + pass + + +class SessionUsersView(APIView): + """ + View for the users that have access to a session. + + Functionality for accessing a list of users with access and granting or + revoking access. + """ + + # view the users that have access to a specific session + def get(self, request, data_id, version=None): + pass + + # grant or revoke access to a session + def put(self, request, data_id, version=None): + pass From 3e8d00f1a01e952ab423835c2fe99f9abd1b5c33 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 4 Apr 2025 15:32:52 -0400 Subject: [PATCH 380/675] Basic serializer for session --- sasdata/fair_database/data/serializers.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index de09c7a06..e334dd678 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from data.models import DataFile, DataSet, MetaData, OperationTree, Quantity +from data.models import DataFile, DataSet, MetaData, OperationTree, Quantity, Session class DataFileSerializer(serializers.ModelSerializer): @@ -149,6 +149,12 @@ def update(self, instance, validated_data): # TODO: custom method for database to serializer representation +class SessionSerializer(serializers.ModelSerializer): + class Meta: + model = Session + fields = "__all__" + + def constant_or_variable(operation: str): return str in ["zero", "one", "constant", "variable"] From e518cfd796dabf5431f502f420643653a8e4f289 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 4 Apr 2025 15:38:22 -0400 Subject: [PATCH 381/675] Nested serializers in SessionSerializer --- sasdata/fair_database/data/serializers.py | 33 ++++++++++++++--------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index e334dd678..260a706d8 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -1,11 +1,11 @@ from rest_framework import serializers -from data.models import DataFile, DataSet, MetaData, OperationTree, Quantity, Session +from data import models class DataFileSerializer(serializers.ModelSerializer): class Meta: - model = DataFile + model = models.DataFile fields = "__all__" def validate(self, data): @@ -21,13 +21,13 @@ class AccessManagementSerializer(serializers.Serializer): class MetaDataSerializer(serializers.ModelSerializer): class Meta: - model = MetaData + model = models.MetaData fields = "__all__" class OperationTreeSerializer(serializers.ModelSerializer): class Meta: - model = OperationTree + model = models.OperationTree fields = ["quantity", "operation", "parameters"] def create(self, validated_data): @@ -45,7 +45,7 @@ def create(self, validated_data): serializer2 = OperationTreeSerializer(data=parent2) if serializer2.is_valid(raise_exception=True): parent_operation2 = serializer2.save() - return OperationTree.objects.create( + return models.OperationTree.objects.create( dataset=validated_data["quantity"], # TODO: check uuid vs object operation=validated_data["operation"], parameters=validated_data["parameters"], @@ -59,7 +59,7 @@ class QuantitySerializer(serializers.ModelSerializer): history = serializers.JSONField(required=False) # TODO: is this required? class Meta: - model = Quantity + model = models.Quantity fields = ["value", "variance", "units", "hash", "label", "history"] # TODO: validation checks for history @@ -67,7 +67,7 @@ class Meta: def create(self, validated_data): if "history" in validated_data: operations_raw = validated_data.pop("history") - quantity = Quantity.objects.create(**validated_data) + quantity = models.Quantity.objects.create(**validated_data) operations_tree_raw = operations_raw["operation_tree"] operations_tree_raw["quantity"] = quantity.id serializer = OperationTreeSerializer(data=operations_tree_raw) @@ -75,20 +75,20 @@ def create(self, validated_data): serializer.save() return quantity else: - return Quantity.objects.create(**validated_data) + return models.Quantity.objects.create(**validated_data) class DataSetSerializer(serializers.ModelSerializer): metadata = MetaDataSerializer(read_only=False) files = serializers.PrimaryKeyRelatedField( - required=False, many=True, allow_null=True, queryset=DataFile + required=False, many=True, allow_null=True, queryset=models.DataFile ) data_contents = QuantitySerializer(many=True, read_only=False) # TODO: handle files better # TODO: see if I can find a better way to handle the quantity part class Meta: - model = DataSet + model = models.DataSet fields = [ "id", "name", @@ -124,7 +124,7 @@ def create(self, validated_data): MetaDataSerializer(), validated_data=metadata_raw ) data_contents = validated_data.pop("data_contents") - dataset = DataSet.objects.create(metadata=metadata, **validated_data) + dataset = models.DataSet.objects.create(metadata=metadata, **validated_data) for d in data_contents: label = d.pop("label") quantity = QuantitySerializer.create(QuantitySerializer(), validated_data=d) @@ -149,9 +149,18 @@ def update(self, instance, validated_data): # TODO: custom method for database to serializer representation +class PublishedStateSerializer(serializers.ModelSerializer): + class Meta: + model = models.PublishedState + fields = "__all__" + + class SessionSerializer(serializers.ModelSerializer): + dataset = DataSetSerializer(read_only=False, many=True) + published_state = PublishedStateSerializer(read_only=False) + class Meta: - model = Session + model = models.Session fields = "__all__" From 5e8db7350c0bbb7eaab5ad125408d117a1cbab01 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 4 Apr 2025 15:40:50 -0400 Subject: [PATCH 382/675] session post method --- sasdata/fair_database/data/views.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index d0063b48d..183bdcec2 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -18,6 +18,7 @@ DataFileSerializer, DataSetSerializer, AccessManagementSerializer, + SessionSerializer, ) from data.models import DataFile, DataSet from data.forms import DataFileForm @@ -358,12 +359,18 @@ def get(self, request, version=None): pass # Create a session + # TODO: revisit response data def post(self, request, version=None): - pass + serializer = SessionSerializer(data=request.data, context={"request": request}) + if serializer.is_valid(raise_exception=True): + serializer.save() + db = serializer.instance + response = {"session_id": db.id, "is_public": db.is_public} + return Response(data=response, status=status.HTTP_201_CREATED) # Create a session def put(self, request, version=None): - pass + return self.post(request, version) class SingleSessionView(APIView): From 36b9d8c76a0154d2e938e81b74204e76a4943eba Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 4 Apr 2025 15:44:46 -0400 Subject: [PATCH 383/675] Session access management views --- sasdata/fair_database/data/views.py | 34 ++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 183bdcec2..db7ac09a6 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -20,7 +20,7 @@ AccessManagementSerializer, SessionSerializer, ) -from data.models import DataFile, DataSet +from data.models import DataFile, DataSet, Session from data.forms import DataFileForm from fair_database import permissions from fair_database.permissions import DataPermission @@ -403,8 +403,36 @@ class SessionUsersView(APIView): # view the users that have access to a specific session def get(self, request, data_id, version=None): - pass + db = get_object_or_404(Session, id=data_id) + if not permissions.is_owner(request, db): + if not request.user.is_authenticated: + return HttpResponse("Must be authenticated to view access", status=401) + return HttpResponseForbidden("Must be the session owner to view access") + response_data = { + "session_id": db.id, + "users": [user.username for user in db.users.all()], + } + return Response(response_data) # grant or revoke access to a session def put(self, request, data_id, version=None): - pass + db = get_object_or_404(Session, id=data_id) + if not permissions.is_owner(request, db): + if not request.user.is_authenticated: + return HttpResponse( + "Must be authenticated to manage access", status=401 + ) + return HttpResponseForbidden("Must be the dataset owner to manage access") + serializer = AccessManagementSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user = get_object_or_404(User, username=serializer.data["username"]) + if serializer.data["access"]: + db.users.add(user) + else: + db.users.remove(user) + response_data = { + "username": user.username, + "session_id": db.id, + "access": serializer.data["access"], + } + return Response(response_data) From 0f4c4ce3f63994f1f24f2caf70a93b21261d5088 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 4 Apr 2025 15:47:11 -0400 Subject: [PATCH 384/675] urls for sessions --- sasdata/fair_database/data/urls.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sasdata/fair_database/data/urls.py b/sasdata/fair_database/data/urls.py index e3f2a4191..f71702ce2 100644 --- a/sasdata/fair_database/data/urls.py +++ b/sasdata/fair_database/data/urls.py @@ -25,4 +25,15 @@ views.DataSetUsersView.as_view(), name="manage access to datasets", ), + path("session/", views.SessionView.as_view(), name="view and create sessions"), + path( + "session//", + views.SingleSessionView.as_view(), + name="load, modify, delete sessions", + ), + path( + "session//users/", + views.SessionUsersView.as_view(), + name="manage access to sessions", + ), ] From 6df2e1f3aeeff69a0bac3bec0eb438c861855464 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 4 Apr 2025 15:51:00 -0400 Subject: [PATCH 385/675] methods for SingleSessionView - might need to revise later --- sasdata/fair_database/data/views.py | 35 ++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index db7ac09a6..a1ca3924a 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -382,15 +382,44 @@ class SingleSessionView(APIView): # get a specific session def get(self, request, data_id, version=None): - pass + db = get_object_or_404(Session, id=data_id) + if not permissions.check_permissions(request, db): + if not request.user.is_authenticated: + return HttpResponse("Must be authenticated to view session", status=401) + return HttpResponseForbidden( + "You do not have permission to view this session." + ) + serializer = DataSetSerializer(db) + return Response(serializer.data) # modify a session def put(self, request, data_id, version=None): - pass + db = get_object_or_404(Session, id=data_id) + if not permissions.check_permissions(request, db): + if not request.user.is_authenticated: + return HttpResponse( + "Must be authenticated to modify session", status=401 + ) + return HttpResponseForbidden("Cannot modify a session you do not own") + serializer = DataSetSerializer( + db, request.data, context={"request": request}, partial=True + ) + if serializer.is_valid(raise_exception=True): + serializer.save() + data = {"data_id": db.id, "is_public": db.is_public} + return Response(data) # delete a session def delete(self, request, data_id, version=None): - pass + db = get_object_or_404(Session, id=data_id) + if not permissions.check_permissions(request, db): + if not request.user.is_authenticated: + return HttpResponse( + "Must be authenticated to delete a session", status=401 + ) + return HttpResponseForbidden("Not authorized to delete") + db.delete() + return Response({"success": True}) class SessionUsersView(APIView): From 99d8ffaa6a08df78ad3e2c461e86bd40d68c82cd Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 7 Apr 2025 15:20:57 -0400 Subject: [PATCH 386/675] Switch key from OperationTree to Quantity --- ..._remove_operationtree_quantity_and_more.py | 27 +++++++++++++++++++ sasdata/fair_database/data/models.py | 12 ++++----- 2 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0018_remove_operationtree_quantity_and_more.py diff --git a/sasdata/fair_database/data/migrations/0018_remove_operationtree_quantity_and_more.py b/sasdata/fair_database/data/migrations/0018_remove_operationtree_quantity_and_more.py new file mode 100644 index 000000000..13dc07b05 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0018_remove_operationtree_quantity_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 5.1.6 on 2025-04-07 19:20 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0017_publishedstate_session"), + ] + + operations = [ + migrations.RemoveField( + model_name="operationtree", + name="quantity", + ), + migrations.AddField( + model_name="quantity", + name="operation_tree", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="data.operationtree", + ), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 0205abedd..ada28d205 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -80,6 +80,11 @@ class Quantity(models.Model): # hash value hash = models.IntegerField() + # operation history of the quantity + operation_tree = models.OneToOneField( + "OperationTree", blank=True, null=True, on_delete=models.SET_NULL + ) + class LabeledQuantity(models.Model): dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) @@ -138,9 +143,6 @@ class OperationTree(models.Model): "tensor_product": "TensorProduct", } - # Dataset the operation tree is performed on - quantity = models.ForeignKey(Quantity, on_delete=models.CASCADE) - # operation operation = models.CharField(max_length=20, choices=OPERATION_CHOICES) @@ -172,9 +174,7 @@ class Session(Data): # dataset dataset = models.ManyToManyField(DataSet) - # operation tree - # operations = models.ForeignKey(OperationTree, on_delete=models.CASCADE) - + # publishing state of the session published_state = models.OneToOneField( "PublishedState", blank=True, null=True, on_delete=models.SET_NULL ) From c443f442918e3a360ae4a23d73355b65adf5968d Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 7 Apr 2025 15:50:15 -0400 Subject: [PATCH 387/675] Add nested OperationTree to Quantity serializer --- sasdata/fair_database/data/serializers.py | 31 ++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 260a706d8..0a518f005 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -55,24 +55,43 @@ def create(self, validated_data): class QuantitySerializer(serializers.ModelSerializer): + operation_tree = OperationTreeSerializer(read_only=False, required=False) label = serializers.CharField(max_length=20) history = serializers.JSONField(required=False) # TODO: is this required? class Meta: model = models.Quantity - fields = ["value", "variance", "units", "hash", "label", "history"] + fields = [ + "value", + "variance", + "units", + "hash", + "operation_tree", + "label", + "history", + ] # TODO: validation checks for history + def to_internal_value(self, data): + if "history" in data: + operations = data["history"]["operation_tree"] + if not operations["operation"] == "variable": + data_copy = data.copy() + data_copy["operation_tree"] = operations + return super().to_internal_value(data_copy) + return super().to_internal_value(data) + def create(self, validated_data): if "history" in validated_data: operations_raw = validated_data.pop("history") - quantity = models.Quantity.objects.create(**validated_data) operations_tree_raw = operations_raw["operation_tree"] - operations_tree_raw["quantity"] = quantity.id - serializer = OperationTreeSerializer(data=operations_tree_raw) - if serializer.is_valid(): - serializer.save() + operation_tree = OperationTreeSerializer.create( + OperationTreeSerializer(), validated_data=operations_tree_raw + ) + quantity = models.Quantity.objects.create( + operation_tree=operation_tree, **validated_data + ) return quantity else: return models.Quantity.objects.create(**validated_data) From 3ac32642881163a0b15b6bd3dc64ac3f5fbb57b3 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 7 Apr 2025 15:54:18 -0400 Subject: [PATCH 388/675] Add title to Session --- .../data/migrations/0019_session_title.py | 18 ++++++++++++++++++ sasdata/fair_database/data/models.py | 3 +++ 2 files changed, 21 insertions(+) create mode 100644 sasdata/fair_database/data/migrations/0019_session_title.py diff --git a/sasdata/fair_database/data/migrations/0019_session_title.py b/sasdata/fair_database/data/migrations/0019_session_title.py new file mode 100644 index 000000000..ddc138abb --- /dev/null +++ b/sasdata/fair_database/data/migrations/0019_session_title.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.6 on 2025-04-07 19:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0018_remove_operationtree_quantity_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="session", + name="title", + field=models.CharField(default="placeholder", max_length=200), + preserve_default=False, + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index ada28d205..e7ab7fc4f 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -171,6 +171,9 @@ class OperationTree(models.Model): class Session(Data): """Database model for a project save state.""" + # title + title = models.CharField(max_length=200) + # dataset dataset = models.ManyToManyField(DataSet) From 4cc6ca5e2e8200f022d7f1f48e6ce7d2c3d18018 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 8 Apr 2025 11:36:30 -0400 Subject: [PATCH 389/675] Use numerical_encoding in sasdata for QuantityType serialization/deserialization --- sasdata/quantities/quantity.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 8248a207a..5f8ab3238 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1224,9 +1224,9 @@ def in_si_with_standard_error(self): @staticmethod def deserialise_json(json_data: dict) -> "Quantity": - value = None # TODO QuantityType deserialisation + value = numerical_decode(json_data["value"]) units = Unit.parse(json_data["units"]) - standard_error = None #TODO QuantityType deserialisation + standard_error = numerical_decode(json_data["variance"]) ** 0.5 hash_seed = json_data["hash_seed"] history = QuantityHistory.deserialise_json(json_data["history"]) quantity = Quantity(value, units, standard_error, hash_seed) @@ -1236,9 +1236,9 @@ def deserialise_json(json_data: dict) -> "Quantity": # TODO: fill out actual values def serialise_json(self): return { - "value": quantity_type_serialisation(self.value), + "value": numerical_encode(self.value), "units": str(self.units), # Unit serialisation - "variance": quantity_type_serialisation(self._variance), + "variance": numerical_encode(self._variance), "hash_seed": self._hash_seed, # is this just a string? "hash_value": self.hash_value, "history": self.history.serialise_json() From 88174fae816e0e728bfeccc73bdab8cff9602a4a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 8 Apr 2025 13:54:47 -0400 Subject: [PATCH 390/675] Change some comments in models --- sasdata/fair_database/data/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index e7ab7fc4f..f28ef2367 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -44,8 +44,6 @@ class DataFile(Data): class DataSet(Data): """Database model for a set of data and associated metadata.""" - # TODO: Update when plan for this is finished. - # dataset name name = models.CharField(max_length=200) @@ -61,6 +59,7 @@ class DataSet(Data): # data contents - maybe ManyToManyField data_contents = models.ManyToManyField("Quantity", through="LabeledQuantity") + # TODO: update based on SasData class in data.py # type of dataset # dataset_type = models.JSONField() @@ -80,7 +79,8 @@ class Quantity(models.Model): # hash value hash = models.IntegerField() - # operation history of the quantity + # TODO: add field to store references portion of QuantityHistory + # operation history of the quantity - operation_tree from QuantityHistory operation_tree = models.OneToOneField( "OperationTree", blank=True, null=True, on_delete=models.SET_NULL ) From 623c48e8a72e929f97bc08fccb6c20d4926d6661 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 8 Apr 2025 13:55:40 -0400 Subject: [PATCH 391/675] Fix some bugs in operation tree deserialization --- sasdata/fair_database/data/serializers.py | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 0a518f005..d23f98978 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -3,6 +3,9 @@ from data import models +# TODO: more custom validation, particularly for specific nested dictionary structures + + class DataFileSerializer(serializers.ModelSerializer): class Meta: model = models.DataFile @@ -28,36 +31,34 @@ class Meta: class OperationTreeSerializer(serializers.ModelSerializer): class Meta: model = models.OperationTree - fields = ["quantity", "operation", "parameters"] + fields = ["operation", "parameters"] + # TODO: some kind of custom validation, custom def create(self, validated_data): parent_operation1 = None parent_operation2 = None if not constant_or_variable(validated_data["operation"]): parent1 = validated_data["parameters"].pop("a") - parent1["quantity"] = validated_data["quantity"] serializer1 = OperationTreeSerializer(data=parent1) if serializer1.is_valid(raise_exception=True): parent_operation1 = serializer1.save() if binary(validated_data["operation"]): parent2 = validated_data["parameters"].pop("b") - parent2["quantity"] = validated_data["quantity"] serializer2 = OperationTreeSerializer(data=parent2) if serializer2.is_valid(raise_exception=True): parent_operation2 = serializer2.save() return models.OperationTree.objects.create( - dataset=validated_data["quantity"], # TODO: check uuid vs object operation=validated_data["operation"], parameters=validated_data["parameters"], parent_operation1=parent_operation1, - parent_operaton2=parent_operation2, + parent_operation2=parent_operation2, ) class QuantitySerializer(serializers.ModelSerializer): operation_tree = OperationTreeSerializer(read_only=False, required=False) label = serializers.CharField(max_length=20) - history = serializers.JSONField(required=False) # TODO: is this required? + # history = serializers.JSONField(required=False) # TODO: is this required? class Meta: model = models.Quantity @@ -68,7 +69,7 @@ class Meta: "hash", "operation_tree", "label", - "history", + # "history", ] # TODO: validation checks for history @@ -83,11 +84,10 @@ def to_internal_value(self, data): return super().to_internal_value(data) def create(self, validated_data): - if "history" in validated_data: - operations_raw = validated_data.pop("history") - operations_tree_raw = operations_raw["operation_tree"] + if "operation_tree" in validated_data: + operations_data = validated_data.pop("operation_tree") operation_tree = OperationTreeSerializer.create( - OperationTreeSerializer(), validated_data=operations_tree_raw + OperationTreeSerializer(), validated_data=operations_data ) quantity = models.Quantity.objects.create( operation_tree=operation_tree, **validated_data @@ -184,8 +184,8 @@ class Meta: def constant_or_variable(operation: str): - return str in ["zero", "one", "constant", "variable"] + return operation in ["zero", "one", "constant", "variable"] def binary(operation: str): - return str in ["add", "sub", "mul", "div", "dot", "matmul", "tensor_product"] + return operation in ["add", "sub", "mul", "div", "dot", "matmul", "tensor_product"] From 11271c8960018607453705cc451aee4dc7ba71bd Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 8 Apr 2025 14:50:24 -0400 Subject: [PATCH 392/675] Test creation of operation trees --- .../fair_database/data/test/test_dataset.py | 193 +++++++++++++++++- 1 file changed, 187 insertions(+), 6 deletions(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 165f618fd..a1ea6a364 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -3,7 +3,7 @@ from rest_framework.test import APIClient, APITestCase from rest_framework import status -from data.models import DataSet +from data.models import DataSet, MetaData, OperationTree, Quantity class TestDataSet(APITestCase): @@ -476,18 +476,199 @@ class TestOperationTree(APITestCase): @classmethod def setUpTestData(cls): - pass + cls.dataset = { + "name": "Test Dataset", + "metadata": { + "title": "test metadata", + "run": 1, + "definition": "test", + "instrument": {"source": {}, "collimation": {}, "detectors": {}}, + }, + "data_contents": [ + { + "label": "test", + "value": {"array_contents": [0, 0, 0, 0], "shape": (2, 2)}, + "variance": {"array_contents": [0, 0, 0, 0], "shape": (2, 2)}, + "units": "none", + "hash": 0, + } + ], + "is_public": True, + } + cls.nested_operations = { + "operation_tree": { + "operation": "neg", + "parameters": { + "a": { + "operation": "mul", + "parameters": { + "a": { + "operation": "constant", + "parameters": {"value": {"type": "int", "value": 7}}, + }, + "b": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + }, + }, + }, + }, + "references": {}, + } + cls.user = User.objects.create_user(username="testUser", password="sasview!") + cls.client = APIClient() + cls.client.force_authenticate(cls.user) # Test post with operation tree + def test_operation_tree_created_unary(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "reciprocal", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + } + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + reciprocal = new_quantity.operation_tree + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual( + new_quantity.value, {"array_contents": [0, 0, 0, 0], "shape": [2, 2]} + ) + self.assertEqual(reciprocal.operation, "reciprocal") + self.assertEqual(reciprocal.parent_operation1.operation, "variable") + self.assertEqual(reciprocal.parameters, {}) + + def test_operation_tree_created_binary(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "add", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "b": {"operation": "constant", "parameters": {"value": 5}}, + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + add = new_quantity.operation_tree + variable = add.parent_operation1 + constant = add.parent_operation2 + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual(add.operation, "add") + self.assertEqual(add.parameters, {}) + self.assertEqual(variable.operation, "variable") + self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) + self.assertEqual(constant.operation, "constant") + self.assertEqual(constant.parameters, {"value": 5}) + + def test_operation_tree_created_pow(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "pow", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "power": 2, + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + pow = new_quantity.operation_tree + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual(pow.operation, "pow") + self.assertEqual(pow.parameters, {"power": 2}) + + def test_operation_tree_created_transpose(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "transpose", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "axes": [1, 0], + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + transpose = new_quantity.operation_tree + variable = transpose.parent_operation1 + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual(transpose.operation, "transpose") + self.assertEqual(transpose.parameters, {"axes": [1, 0]}) + self.assertEqual(variable.operation, "variable") + self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) + + def test_operation_tree_created_nested(self): + self.dataset["data_contents"][0]["history"] = self.nested_operations + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + negate = new_quantity.operation_tree + multiply = negate.parent_operation1 + constant = multiply.parent_operation1 + variable = multiply.parent_operation2 + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual(negate.operation, "neg") + self.assertEqual(negate.parameters, {}) + self.assertEqual(multiply.operation, "mul") + self.assertEqual(multiply.parameters, {}) + self.assertEqual(constant.operation, "constant") + self.assertEqual(constant.parameters, {"value": {"type": "int", "value": 7}}) + self.assertEqual(variable.operation, "variable") + self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) - # Test post with invalid operation + # for each, test creation and get - # Test get dataset with operation tree + # no history, or variable only # Test nested operations - # Different operations with different stored value types + # binary operation + + # unary operation + + # pow + + # transpose + + # tensordot + + # invalid operation (create only) + + def tearDown(self): + DataSet.objects.all().delete() + MetaData.objects.all().delete() + Quantity.objects.all().delete() + OperationTree.objects.all().delete() @classmethod def tearDownClass(cls): - pass + cls.user.delete() From 869cf2b99df5935bae23818c83548413209c5c17 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 8 Apr 2025 14:57:21 -0400 Subject: [PATCH 393/675] Test creating a quantity with no operations performed on it --- sasdata/fair_database/data/test/test_dataset.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index a1ea6a364..f2b7e02ca 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -521,6 +521,21 @@ def setUpTestData(cls): cls.client.force_authenticate(cls.user) # Test post with operation tree + def test_operation_tree_created_variable(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "variable", + "parameters": {"hash_value": 0, "name": "test"}, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertIsNone(new_quantity.operation_tree) + def test_operation_tree_created_unary(self): self.dataset["data_contents"][0]["history"] = { "operation_tree": { From 7222526ee3fa21c8d6ec154cc77e723c69f97e17 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 8 Apr 2025 15:06:24 -0400 Subject: [PATCH 394/675] Test creating an operation tree with an invalid operation --- sasdata/fair_database/data/test/test_dataset.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index f2b7e02ca..d139708e1 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -660,6 +660,14 @@ def test_operation_tree_created_nested(self): self.assertEqual(variable.operation, "variable") self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) + def test_create_operation_tree_invalid(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": {"operation": "fix", "parameters": {}}, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + # for each, test creation and get # no history, or variable only From a2b12bcdc05fde6238b7ea2823b047372c3c60af Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 8 Apr 2025 15:54:30 -0400 Subject: [PATCH 395/675] Store quantity label as part of quantity --- ...t_data_contents_quantity_label_and_more.py | 25 +++++++++++++++++++ .../migrations/0021_dataset_data_contents.py | 17 +++++++++++++ sasdata/fair_database/data/models.py | 8 ++---- 3 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0020_remove_dataset_data_contents_quantity_label_and_more.py create mode 100644 sasdata/fair_database/data/migrations/0021_dataset_data_contents.py diff --git a/sasdata/fair_database/data/migrations/0020_remove_dataset_data_contents_quantity_label_and_more.py b/sasdata/fair_database/data/migrations/0020_remove_dataset_data_contents_quantity_label_and_more.py new file mode 100644 index 000000000..7c2a4ecef --- /dev/null +++ b/sasdata/fair_database/data/migrations/0020_remove_dataset_data_contents_quantity_label_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 5.1.6 on 2025-04-08 19:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0019_session_title"), + ] + + operations = [ + migrations.RemoveField( + model_name="dataset", + name="data_contents", + ), + migrations.AddField( + model_name="quantity", + name="label", + field=models.CharField(default="placeholder", max_length=50), + preserve_default=False, + ), + migrations.DeleteModel( + name="LabeledQuantity", + ), + ] diff --git a/sasdata/fair_database/data/migrations/0021_dataset_data_contents.py b/sasdata/fair_database/data/migrations/0021_dataset_data_contents.py new file mode 100644 index 000000000..e4d46aadf --- /dev/null +++ b/sasdata/fair_database/data/migrations/0021_dataset_data_contents.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.6 on 2025-04-08 19:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0020_remove_dataset_data_contents_quantity_label_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="dataset", + name="data_contents", + field=models.ManyToManyField(to="data.quantity"), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index f28ef2367..5cee72323 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -57,7 +57,7 @@ class DataSet(Data): ) # data contents - maybe ManyToManyField - data_contents = models.ManyToManyField("Quantity", through="LabeledQuantity") + data_contents = models.ManyToManyField("Quantity") # TODO: update based on SasData class in data.py # type of dataset @@ -85,11 +85,7 @@ class Quantity(models.Model): "OperationTree", blank=True, null=True, on_delete=models.SET_NULL ) - -class LabeledQuantity(models.Model): - dataset = models.ForeignKey(DataSet, on_delete=models.CASCADE) - quantity = models.ForeignKey(Quantity, on_delete=models.CASCADE) - label = models.CharField(max_length=20) + label = models.CharField(max_length=50) def empty_list(): From c120970d832ff3af5d289848dc90b5fc2f423136 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 8 Apr 2025 16:07:14 -0400 Subject: [PATCH 396/675] Customize OperationTree serialization from database representation --- sasdata/fair_database/data/serializers.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index d23f98978..cc1c8b100 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -33,7 +33,15 @@ class Meta: model = models.OperationTree fields = ["operation", "parameters"] - # TODO: some kind of custom validation, custom + # TODO: some kind of custom validation + def to_representation(self, instance): + data = {"operation": instance.operation, "parameters": instance.parameters} + if instance.parent_operation1 is not None: + data["parameters"]["a"] = self.to_representation(instance.parent_operation1) + if instance.parent_operation2 is not None: + data["parameters"]["b"] = self.to_representation(instance.parent_operation2) + return data + def create(self, validated_data): parent_operation1 = None parent_operation2 = None @@ -75,7 +83,7 @@ class Meta: # TODO: validation checks for history def to_internal_value(self, data): - if "history" in data: + if "history" in data and "operation_tree" in data["history"]: operations = data["history"]["operation_tree"] if not operations["operation"] == "variable": data_copy = data.copy() @@ -145,9 +153,8 @@ def create(self, validated_data): data_contents = validated_data.pop("data_contents") dataset = models.DataSet.objects.create(metadata=metadata, **validated_data) for d in data_contents: - label = d.pop("label") quantity = QuantitySerializer.create(QuantitySerializer(), validated_data=d) - dataset.data_contents.add(quantity, through_defaults={"label": label}) + dataset.data_contents.add(quantity) return dataset # TODO: account for updating other attributes From 1cc85f378bb2d7f95cd0e2cdc7cfe7d2b644895d Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 8 Apr 2025 16:08:13 -0400 Subject: [PATCH 397/675] Test GET with unary operation --- .../fair_database/data/test/test_dataset.py | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index d139708e1..d87bdd485 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -562,6 +562,63 @@ def test_operation_tree_created_unary(self): self.assertEqual(reciprocal.parent_operation1.operation, "variable") self.assertEqual(reciprocal.parameters, {}) + def test_get_operation_tree_unary(self): + variable = OperationTree.objects.create( + id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} + ) + inv = OperationTree.objects.create( + id=2, operation="reciprocal", parent_operation1=variable + ) + quantity = Quantity.objects.create( + id=1, + value=0, + variance=0, + label="test", + units="none", + hash=1, + operation_tree=inv, + ) + dataset = DataSet.objects.create( + id=1, + current_user=self.user, + name="Test Dataset", + is_public=True, + metadata=None, + ) + dataset.data_contents.add(quantity) + request = self.client.get("/v1/data/set/1/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Test Dataset", + "files": [], + "metadata": None, + "data_contents": [ + { + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": { + "operation": "reciprocal", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + } + }, + }, + } + ], + }, + ) + def test_operation_tree_created_binary(self): self.dataset["data_contents"][0]["history"] = { "operation_tree": { From 4ca7ceeb05813b467f7158faadce69de0299a388 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 8 Apr 2025 16:23:11 -0400 Subject: [PATCH 398/675] More tests for GET with operation trees --- .../fair_database/data/test/test_dataset.py | 268 ++++++++++++++++++ 1 file changed, 268 insertions(+) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index d87bdd485..6c2615de9 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -648,6 +648,73 @@ def test_operation_tree_created_binary(self): self.assertEqual(constant.operation, "constant") self.assertEqual(constant.parameters, {"value": 5}) + def test_get_operation_tree_binary(self): + variable = OperationTree.objects.create( + id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} + ) + constant = OperationTree.objects.create( + id=2, operation="constant", parameters={"value": 1} + ) + add = OperationTree.objects.create( + id=3, + operation="add", + parent_operation1=variable, + parent_operation2=constant, + ) + quantity = Quantity.objects.create( + id=1, + value=0, + variance=0, + label="test", + units="none", + hash=1, + operation_tree=add, + ) + dataset = DataSet.objects.create( + id=1, + current_user=self.user, + name="Test Dataset", + is_public=True, + metadata=None, + ) + dataset.data_contents.add(quantity) + request = self.client.get("/v1/data/set/1/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Test Dataset", + "files": [], + "metadata": None, + "data_contents": [ + { + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": { + "operation": "add", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "b": { + "operation": "constant", + "parameters": {"value": 1}, + }, + }, + }, + } + ], + }, + ) + def test_operation_tree_created_pow(self): self.dataset["data_contents"][0]["history"] = { "operation_tree": { @@ -671,6 +738,64 @@ def test_operation_tree_created_pow(self): self.assertEqual(pow.operation, "pow") self.assertEqual(pow.parameters, {"power": 2}) + def test_get_operation_tree_pow(self): + variable = OperationTree.objects.create( + id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} + ) + pow = OperationTree.objects.create( + id=2, operation="pow", parent_operation1=variable, parameters={"power": 2} + ) + quantity = Quantity.objects.create( + id=1, + value=0, + variance=0, + label="test", + units="none", + hash=1, + operation_tree=pow, + ) + dataset = DataSet.objects.create( + id=1, + current_user=self.user, + name="Test Dataset", + is_public=True, + metadata=None, + ) + dataset.data_contents.add(quantity) + request = self.client.get("/v1/data/set/1/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Test Dataset", + "files": [], + "metadata": None, + "data_contents": [ + { + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": { + "operation": "pow", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "power": 2, + }, + }, + } + ], + }, + ) + def test_operation_tree_created_transpose(self): self.dataset["data_contents"][0]["history"] = { "operation_tree": { @@ -697,6 +822,67 @@ def test_operation_tree_created_transpose(self): self.assertEqual(variable.operation, "variable") self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) + def test_get_operation_tree_transpose(self): + variable = OperationTree.objects.create( + id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} + ) + inv = OperationTree.objects.create( + id=2, + operation="transpose", + parent_operation1=variable, + parameters={"axes": (1, 0)}, + ) + quantity = Quantity.objects.create( + id=1, + value=0, + variance=0, + label="test", + units="none", + hash=1, + operation_tree=inv, + ) + dataset = DataSet.objects.create( + id=1, + current_user=self.user, + name="Test Dataset", + is_public=True, + metadata=None, + ) + dataset.data_contents.add(quantity) + request = self.client.get("/v1/data/set/1/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Test Dataset", + "files": [], + "metadata": None, + "data_contents": [ + { + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": { + "operation": "transpose", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "axes": [1, 0], + }, + }, + } + ], + }, + ) + def test_operation_tree_created_nested(self): self.dataset["data_contents"][0]["history"] = self.nested_operations request = self.client.post("/v1/data/set/", data=self.dataset, format="json") @@ -717,6 +903,88 @@ def test_operation_tree_created_nested(self): self.assertEqual(variable.operation, "variable") self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) + def test_get_operation_tree_nested(self): + variable = OperationTree.objects.create( + id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} + ) + constant = OperationTree.objects.create( + id=2, + operation="constant", + parameters={"value": {"type": "int", "value": 7}}, + ) + multiply = OperationTree.objects.create( + id=3, + operation="mul", + parent_operation1=constant, + parent_operation2=variable, + ) + neg = OperationTree.objects.create( + id=4, operation="neg", parent_operation1=multiply + ) + quantity = Quantity.objects.create( + id=1, + value=0, + variance=0, + label="test", + units="none", + hash=1, + operation_tree=neg, + ) + dataset = DataSet.objects.create( + id=1, + current_user=self.user, + name="Test Dataset", + is_public=True, + metadata=None, + ) + dataset.data_contents.add(quantity) + request = self.client.get("/v1/data/set/1/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Test Dataset", + "files": [], + "metadata": None, + "data_contents": [ + { + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": { + "operation": "neg", + "parameters": { + "a": { + "operation": "mul", + "parameters": { + "a": { + "operation": "constant", + "parameters": { + "value": {"type": "int", "value": 7} + }, + }, + "b": { + "operation": "variable", + "parameters": { + "hash_value": 111, + "name": "x", + }, + }, + }, + } + }, + }, + } + ], + }, + ) + def test_create_operation_tree_invalid(self): self.dataset["data_contents"][0]["history"] = { "operation_tree": {"operation": "fix", "parameters": {}}, From 98801633dc867c5865de2b818d8d873488538a7a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 8 Apr 2025 16:28:37 -0400 Subject: [PATCH 399/675] Fix minor bug in OperationTree tests --- sasdata/fair_database/data/test/test_dataset.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 6c2615de9..6229612f0 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -516,7 +516,9 @@ def setUpTestData(cls): }, "references": {}, } - cls.user = User.objects.create_user(username="testUser", password="sasview!") + cls.user = User.objects.create_user( + id=1, username="testUser", password="sasview!" + ) cls.client = APIClient() cls.client.force_authenticate(cls.user) From 70faaf1413b57f54a42fc6addbf157aef31a2f3d Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 9 Apr 2025 10:42:31 -0400 Subject: [PATCH 400/675] Comments and planning for OperationTree testing --- .../fair_database/data/test/test_dataset.py | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 6229612f0..4495427b9 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -522,7 +522,7 @@ def setUpTestData(cls): cls.client = APIClient() cls.client.force_authenticate(cls.user) - # Test post with operation tree + # Test creating quantity with no operations performed def test_operation_tree_created_variable(self): self.dataset["data_contents"][0]["history"] = { "operation_tree": { @@ -538,6 +538,9 @@ def test_operation_tree_created_variable(self): self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertIsNone(new_quantity.operation_tree) + # Test accessing a quantity with no operations performed + + # Test creating quantity with unary operation def test_operation_tree_created_unary(self): self.dataset["data_contents"][0]["history"] = { "operation_tree": { @@ -564,6 +567,7 @@ def test_operation_tree_created_unary(self): self.assertEqual(reciprocal.parent_operation1.operation, "variable") self.assertEqual(reciprocal.parameters, {}) + # Test accessing quantity with unary operation def test_get_operation_tree_unary(self): variable = OperationTree.objects.create( id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} @@ -621,6 +625,7 @@ def test_get_operation_tree_unary(self): }, ) + # Test creating quantity with binary operation def test_operation_tree_created_binary(self): self.dataset["data_contents"][0]["history"] = { "operation_tree": { @@ -650,6 +655,7 @@ def test_operation_tree_created_binary(self): self.assertEqual(constant.operation, "constant") self.assertEqual(constant.parameters, {"value": 5}) + # Test accessing quantity with binary operation def test_get_operation_tree_binary(self): variable = OperationTree.objects.create( id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} @@ -717,6 +723,7 @@ def test_get_operation_tree_binary(self): }, ) + # Test creating quantity with exponent def test_operation_tree_created_pow(self): self.dataset["data_contents"][0]["history"] = { "operation_tree": { @@ -740,6 +747,7 @@ def test_operation_tree_created_pow(self): self.assertEqual(pow.operation, "pow") self.assertEqual(pow.parameters, {"power": 2}) + # Test accessing a quantity with exponent def test_get_operation_tree_pow(self): variable = OperationTree.objects.create( id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} @@ -798,6 +806,7 @@ def test_get_operation_tree_pow(self): }, ) + # Test creating a transposed quantity def test_operation_tree_created_transpose(self): self.dataset["data_contents"][0]["history"] = { "operation_tree": { @@ -824,6 +833,7 @@ def test_operation_tree_created_transpose(self): self.assertEqual(variable.operation, "variable") self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) + # Test accessing a transposed quantity def test_get_operation_tree_transpose(self): variable = OperationTree.objects.create( id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} @@ -885,6 +895,7 @@ def test_get_operation_tree_transpose(self): }, ) + # Test creating a quantity with multiple operations def test_operation_tree_created_nested(self): self.dataset["data_contents"][0]["history"] = self.nested_operations request = self.client.post("/v1/data/set/", data=self.dataset, format="json") @@ -905,6 +916,7 @@ def test_operation_tree_created_nested(self): self.assertEqual(variable.operation, "variable") self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) + # Test accessing a quantity with multiple operations def test_get_operation_tree_nested(self): variable = OperationTree.objects.create( id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} @@ -987,6 +999,11 @@ def test_get_operation_tree_nested(self): }, ) + # Test creating a quantity with tensordot + + # Test accessing a quantity with tensordot + + # Test creating a quantity with an invalid operation def test_create_operation_tree_invalid(self): self.dataset["data_contents"][0]["history"] = { "operation_tree": {"operation": "fix", "parameters": {}}, @@ -995,23 +1012,20 @@ def test_create_operation_tree_invalid(self): request = self.client.post("/v1/data/set/", data=self.dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) - # for each, test creation and get - - # no history, or variable only - - # Test nested operations - - # binary operation - - # unary operation - - # pow + # Test creating a quantity with a nested invalid operation - # transpose + # Test creating invalid operation parameters + # binary has a and b - both should be operations + # unary has a (operation) + # constant has value + # variable has name and hash_value + # pow has power + # transpose has axes + # tensordot has a_index and b_index - # tensordot + # Test creating nested invalid operation parameters - # invalid operation (create only) + # Test creating a quantity with no history def tearDown(self): DataSet.objects.all().delete() From de3c4ce5e752373807130272a1f9ac2ae3d07e6c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 9 Apr 2025 11:35:23 -0400 Subject: [PATCH 401/675] Reorganize OperationTree tests and move to dedicated file --- .../fair_database/data/test/test_dataset.py | 569 +----------------- .../data/test/test_operation_tree.py | 546 +++++++++++++++++ 2 files changed, 547 insertions(+), 568 deletions(-) create mode 100644 sasdata/fair_database/data/test/test_operation_tree.py diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 4495427b9..5d62a8800 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -3,7 +3,7 @@ from rest_framework.test import APIClient, APITestCase from rest_framework import status -from data.models import DataSet, MetaData, OperationTree, Quantity +from data.models import DataSet class TestDataSet(APITestCase): @@ -469,570 +469,3 @@ def tearDownClass(cls): cls.shared_dataset.delete() cls.user1.delete() cls.user2.delete() - - -class TestOperationTree(APITestCase): - """Tests for datasets with operation trees.""" - - @classmethod - def setUpTestData(cls): - cls.dataset = { - "name": "Test Dataset", - "metadata": { - "title": "test metadata", - "run": 1, - "definition": "test", - "instrument": {"source": {}, "collimation": {}, "detectors": {}}, - }, - "data_contents": [ - { - "label": "test", - "value": {"array_contents": [0, 0, 0, 0], "shape": (2, 2)}, - "variance": {"array_contents": [0, 0, 0, 0], "shape": (2, 2)}, - "units": "none", - "hash": 0, - } - ], - "is_public": True, - } - cls.nested_operations = { - "operation_tree": { - "operation": "neg", - "parameters": { - "a": { - "operation": "mul", - "parameters": { - "a": { - "operation": "constant", - "parameters": {"value": {"type": "int", "value": 7}}, - }, - "b": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - }, - }, - }, - }, - }, - "references": {}, - } - cls.user = User.objects.create_user( - id=1, username="testUser", password="sasview!" - ) - cls.client = APIClient() - cls.client.force_authenticate(cls.user) - - # Test creating quantity with no operations performed - def test_operation_tree_created_variable(self): - self.dataset["data_contents"][0]["history"] = { - "operation_tree": { - "operation": "variable", - "parameters": {"hash_value": 0, "name": "test"}, - }, - "references": {}, - } - request = self.client.post("/v1/data/set/", data=self.dataset, format="json") - max_id = DataSet.objects.aggregate(Max("id"))["id__max"] - new_dataset = DataSet.objects.get(id=max_id) - new_quantity = new_dataset.data_contents.get(hash=0) - self.assertEqual(request.status_code, status.HTTP_201_CREATED) - self.assertIsNone(new_quantity.operation_tree) - - # Test accessing a quantity with no operations performed - - # Test creating quantity with unary operation - def test_operation_tree_created_unary(self): - self.dataset["data_contents"][0]["history"] = { - "operation_tree": { - "operation": "reciprocal", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - } - }, - }, - "references": {}, - } - request = self.client.post("/v1/data/set/", data=self.dataset, format="json") - max_id = DataSet.objects.aggregate(Max("id"))["id__max"] - new_dataset = DataSet.objects.get(id=max_id) - new_quantity = new_dataset.data_contents.get(hash=0) - reciprocal = new_quantity.operation_tree - self.assertEqual(request.status_code, status.HTTP_201_CREATED) - self.assertEqual( - new_quantity.value, {"array_contents": [0, 0, 0, 0], "shape": [2, 2]} - ) - self.assertEqual(reciprocal.operation, "reciprocal") - self.assertEqual(reciprocal.parent_operation1.operation, "variable") - self.assertEqual(reciprocal.parameters, {}) - - # Test accessing quantity with unary operation - def test_get_operation_tree_unary(self): - variable = OperationTree.objects.create( - id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} - ) - inv = OperationTree.objects.create( - id=2, operation="reciprocal", parent_operation1=variable - ) - quantity = Quantity.objects.create( - id=1, - value=0, - variance=0, - label="test", - units="none", - hash=1, - operation_tree=inv, - ) - dataset = DataSet.objects.create( - id=1, - current_user=self.user, - name="Test Dataset", - is_public=True, - metadata=None, - ) - dataset.data_contents.add(quantity) - request = self.client.get("/v1/data/set/1/") - self.assertEqual(request.status_code, status.HTTP_200_OK) - self.assertEqual( - request.data, - { - "id": 1, - "current_user": 1, - "users": [], - "is_public": True, - "name": "Test Dataset", - "files": [], - "metadata": None, - "data_contents": [ - { - "label": "test", - "value": 0, - "variance": 0, - "units": "none", - "hash": 1, - "operation_tree": { - "operation": "reciprocal", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - } - }, - }, - } - ], - }, - ) - - # Test creating quantity with binary operation - def test_operation_tree_created_binary(self): - self.dataset["data_contents"][0]["history"] = { - "operation_tree": { - "operation": "add", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - }, - "b": {"operation": "constant", "parameters": {"value": 5}}, - }, - }, - "references": {}, - } - request = self.client.post("/v1/data/set/", data=self.dataset, format="json") - max_id = DataSet.objects.aggregate(Max("id"))["id__max"] - new_dataset = DataSet.objects.get(id=max_id) - new_quantity = new_dataset.data_contents.get(hash=0) - add = new_quantity.operation_tree - variable = add.parent_operation1 - constant = add.parent_operation2 - self.assertEqual(request.status_code, status.HTTP_201_CREATED) - self.assertEqual(add.operation, "add") - self.assertEqual(add.parameters, {}) - self.assertEqual(variable.operation, "variable") - self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) - self.assertEqual(constant.operation, "constant") - self.assertEqual(constant.parameters, {"value": 5}) - - # Test accessing quantity with binary operation - def test_get_operation_tree_binary(self): - variable = OperationTree.objects.create( - id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} - ) - constant = OperationTree.objects.create( - id=2, operation="constant", parameters={"value": 1} - ) - add = OperationTree.objects.create( - id=3, - operation="add", - parent_operation1=variable, - parent_operation2=constant, - ) - quantity = Quantity.objects.create( - id=1, - value=0, - variance=0, - label="test", - units="none", - hash=1, - operation_tree=add, - ) - dataset = DataSet.objects.create( - id=1, - current_user=self.user, - name="Test Dataset", - is_public=True, - metadata=None, - ) - dataset.data_contents.add(quantity) - request = self.client.get("/v1/data/set/1/") - self.assertEqual(request.status_code, status.HTTP_200_OK) - self.assertEqual( - request.data, - { - "id": 1, - "current_user": 1, - "users": [], - "is_public": True, - "name": "Test Dataset", - "files": [], - "metadata": None, - "data_contents": [ - { - "label": "test", - "value": 0, - "variance": 0, - "units": "none", - "hash": 1, - "operation_tree": { - "operation": "add", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - }, - "b": { - "operation": "constant", - "parameters": {"value": 1}, - }, - }, - }, - } - ], - }, - ) - - # Test creating quantity with exponent - def test_operation_tree_created_pow(self): - self.dataset["data_contents"][0]["history"] = { - "operation_tree": { - "operation": "pow", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - }, - "power": 2, - }, - }, - "references": {}, - } - request = self.client.post("/v1/data/set/", data=self.dataset, format="json") - max_id = DataSet.objects.aggregate(Max("id"))["id__max"] - new_dataset = DataSet.objects.get(id=max_id) - new_quantity = new_dataset.data_contents.get(hash=0) - pow = new_quantity.operation_tree - self.assertEqual(request.status_code, status.HTTP_201_CREATED) - self.assertEqual(pow.operation, "pow") - self.assertEqual(pow.parameters, {"power": 2}) - - # Test accessing a quantity with exponent - def test_get_operation_tree_pow(self): - variable = OperationTree.objects.create( - id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} - ) - pow = OperationTree.objects.create( - id=2, operation="pow", parent_operation1=variable, parameters={"power": 2} - ) - quantity = Quantity.objects.create( - id=1, - value=0, - variance=0, - label="test", - units="none", - hash=1, - operation_tree=pow, - ) - dataset = DataSet.objects.create( - id=1, - current_user=self.user, - name="Test Dataset", - is_public=True, - metadata=None, - ) - dataset.data_contents.add(quantity) - request = self.client.get("/v1/data/set/1/") - self.assertEqual(request.status_code, status.HTTP_200_OK) - self.assertEqual( - request.data, - { - "id": 1, - "current_user": 1, - "users": [], - "is_public": True, - "name": "Test Dataset", - "files": [], - "metadata": None, - "data_contents": [ - { - "label": "test", - "value": 0, - "variance": 0, - "units": "none", - "hash": 1, - "operation_tree": { - "operation": "pow", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - }, - "power": 2, - }, - }, - } - ], - }, - ) - - # Test creating a transposed quantity - def test_operation_tree_created_transpose(self): - self.dataset["data_contents"][0]["history"] = { - "operation_tree": { - "operation": "transpose", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - }, - "axes": [1, 0], - }, - }, - "references": {}, - } - request = self.client.post("/v1/data/set/", data=self.dataset, format="json") - max_id = DataSet.objects.aggregate(Max("id"))["id__max"] - new_dataset = DataSet.objects.get(id=max_id) - new_quantity = new_dataset.data_contents.get(hash=0) - transpose = new_quantity.operation_tree - variable = transpose.parent_operation1 - self.assertEqual(request.status_code, status.HTTP_201_CREATED) - self.assertEqual(transpose.operation, "transpose") - self.assertEqual(transpose.parameters, {"axes": [1, 0]}) - self.assertEqual(variable.operation, "variable") - self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) - - # Test accessing a transposed quantity - def test_get_operation_tree_transpose(self): - variable = OperationTree.objects.create( - id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} - ) - inv = OperationTree.objects.create( - id=2, - operation="transpose", - parent_operation1=variable, - parameters={"axes": (1, 0)}, - ) - quantity = Quantity.objects.create( - id=1, - value=0, - variance=0, - label="test", - units="none", - hash=1, - operation_tree=inv, - ) - dataset = DataSet.objects.create( - id=1, - current_user=self.user, - name="Test Dataset", - is_public=True, - metadata=None, - ) - dataset.data_contents.add(quantity) - request = self.client.get("/v1/data/set/1/") - self.assertEqual(request.status_code, status.HTTP_200_OK) - self.assertEqual( - request.data, - { - "id": 1, - "current_user": 1, - "users": [], - "is_public": True, - "name": "Test Dataset", - "files": [], - "metadata": None, - "data_contents": [ - { - "label": "test", - "value": 0, - "variance": 0, - "units": "none", - "hash": 1, - "operation_tree": { - "operation": "transpose", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - }, - "axes": [1, 0], - }, - }, - } - ], - }, - ) - - # Test creating a quantity with multiple operations - def test_operation_tree_created_nested(self): - self.dataset["data_contents"][0]["history"] = self.nested_operations - request = self.client.post("/v1/data/set/", data=self.dataset, format="json") - max_id = DataSet.objects.aggregate(Max("id"))["id__max"] - new_dataset = DataSet.objects.get(id=max_id) - new_quantity = new_dataset.data_contents.get(hash=0) - negate = new_quantity.operation_tree - multiply = negate.parent_operation1 - constant = multiply.parent_operation1 - variable = multiply.parent_operation2 - self.assertEqual(request.status_code, status.HTTP_201_CREATED) - self.assertEqual(negate.operation, "neg") - self.assertEqual(negate.parameters, {}) - self.assertEqual(multiply.operation, "mul") - self.assertEqual(multiply.parameters, {}) - self.assertEqual(constant.operation, "constant") - self.assertEqual(constant.parameters, {"value": {"type": "int", "value": 7}}) - self.assertEqual(variable.operation, "variable") - self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) - - # Test accessing a quantity with multiple operations - def test_get_operation_tree_nested(self): - variable = OperationTree.objects.create( - id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} - ) - constant = OperationTree.objects.create( - id=2, - operation="constant", - parameters={"value": {"type": "int", "value": 7}}, - ) - multiply = OperationTree.objects.create( - id=3, - operation="mul", - parent_operation1=constant, - parent_operation2=variable, - ) - neg = OperationTree.objects.create( - id=4, operation="neg", parent_operation1=multiply - ) - quantity = Quantity.objects.create( - id=1, - value=0, - variance=0, - label="test", - units="none", - hash=1, - operation_tree=neg, - ) - dataset = DataSet.objects.create( - id=1, - current_user=self.user, - name="Test Dataset", - is_public=True, - metadata=None, - ) - dataset.data_contents.add(quantity) - request = self.client.get("/v1/data/set/1/") - self.assertEqual(request.status_code, status.HTTP_200_OK) - self.assertEqual( - request.data, - { - "id": 1, - "current_user": 1, - "users": [], - "is_public": True, - "name": "Test Dataset", - "files": [], - "metadata": None, - "data_contents": [ - { - "label": "test", - "value": 0, - "variance": 0, - "units": "none", - "hash": 1, - "operation_tree": { - "operation": "neg", - "parameters": { - "a": { - "operation": "mul", - "parameters": { - "a": { - "operation": "constant", - "parameters": { - "value": {"type": "int", "value": 7} - }, - }, - "b": { - "operation": "variable", - "parameters": { - "hash_value": 111, - "name": "x", - }, - }, - }, - } - }, - }, - } - ], - }, - ) - - # Test creating a quantity with tensordot - - # Test accessing a quantity with tensordot - - # Test creating a quantity with an invalid operation - def test_create_operation_tree_invalid(self): - self.dataset["data_contents"][0]["history"] = { - "operation_tree": {"operation": "fix", "parameters": {}}, - "references": {}, - } - request = self.client.post("/v1/data/set/", data=self.dataset, format="json") - self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) - - # Test creating a quantity with a nested invalid operation - - # Test creating invalid operation parameters - # binary has a and b - both should be operations - # unary has a (operation) - # constant has value - # variable has name and hash_value - # pow has power - # transpose has axes - # tensordot has a_index and b_index - - # Test creating nested invalid operation parameters - - # Test creating a quantity with no history - - def tearDown(self): - DataSet.objects.all().delete() - MetaData.objects.all().delete() - Quantity.objects.all().delete() - OperationTree.objects.all().delete() - - @classmethod - def tearDownClass(cls): - cls.user.delete() diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py new file mode 100644 index 000000000..e4f19b9a8 --- /dev/null +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -0,0 +1,546 @@ +from django.contrib.auth.models import User +from django.db.models import Max +from rest_framework.test import APIClient, APITestCase +from rest_framework import status + +from data.models import DataSet, MetaData, OperationTree, Quantity + + +class TestCreateOperationTree(APITestCase): + """Tests for datasets with operation trees.""" + + @classmethod + def setUpTestData(cls): + cls.dataset = { + "name": "Test Dataset", + "metadata": { + "title": "test metadata", + "run": 1, + "definition": "test", + "instrument": {"source": {}, "collimation": {}, "detectors": {}}, + }, + "data_contents": [ + { + "label": "test", + "value": {"array_contents": [0, 0, 0, 0], "shape": (2, 2)}, + "variance": {"array_contents": [0, 0, 0, 0], "shape": (2, 2)}, + "units": "none", + "hash": 0, + } + ], + "is_public": True, + } + cls.nested_operations = { + "operation_tree": { + "operation": "neg", + "parameters": { + "a": { + "operation": "mul", + "parameters": { + "a": { + "operation": "constant", + "parameters": {"value": {"type": "int", "value": 7}}, + }, + "b": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + }, + }, + }, + }, + "references": {}, + } + cls.user = User.objects.create_user( + id=1, username="testUser", password="sasview!" + ) + cls.client = APIClient() + cls.client.force_authenticate(cls.user) + + # Test creating quantity with no operations performed + def test_operation_tree_created_variable(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "variable", + "parameters": {"hash_value": 0, "name": "test"}, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertIsNone(new_quantity.operation_tree) + + # Test creating quantity with unary operation + def test_operation_tree_created_unary(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "reciprocal", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + } + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + reciprocal = new_quantity.operation_tree + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual( + new_quantity.value, {"array_contents": [0, 0, 0, 0], "shape": [2, 2]} + ) + self.assertEqual(reciprocal.operation, "reciprocal") + self.assertEqual(reciprocal.parent_operation1.operation, "variable") + self.assertEqual(reciprocal.parameters, {}) + + # Test creating quantity with binary operation + def test_operation_tree_created_binary(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "add", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "b": {"operation": "constant", "parameters": {"value": 5}}, + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + add = new_quantity.operation_tree + variable = add.parent_operation1 + constant = add.parent_operation2 + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual(add.operation, "add") + self.assertEqual(add.parameters, {}) + self.assertEqual(variable.operation, "variable") + self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) + self.assertEqual(constant.operation, "constant") + self.assertEqual(constant.parameters, {"value": 5}) + + # Test creating quantity with exponent + def test_operation_tree_created_pow(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "pow", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "power": 2, + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + pow = new_quantity.operation_tree + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual(pow.operation, "pow") + self.assertEqual(pow.parameters, {"power": 2}) + + # Test creating a transposed quantity + def test_operation_tree_created_transpose(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "transpose", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "axes": [1, 0], + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + transpose = new_quantity.operation_tree + variable = transpose.parent_operation1 + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual(transpose.operation, "transpose") + self.assertEqual(transpose.parameters, {"axes": [1, 0]}) + self.assertEqual(variable.operation, "variable") + self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) + + # Test creating a quantity with multiple operations + def test_operation_tree_created_nested(self): + self.dataset["data_contents"][0]["history"] = self.nested_operations + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + negate = new_quantity.operation_tree + multiply = negate.parent_operation1 + constant = multiply.parent_operation1 + variable = multiply.parent_operation2 + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual(negate.operation, "neg") + self.assertEqual(negate.parameters, {}) + self.assertEqual(multiply.operation, "mul") + self.assertEqual(multiply.parameters, {}) + self.assertEqual(constant.operation, "constant") + self.assertEqual(constant.parameters, {"value": {"type": "int", "value": 7}}) + self.assertEqual(variable.operation, "variable") + self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) + + # Test creating a quantity with tensordot + + # Test creating a quantity with an invalid operation + def test_create_operation_tree_invalid(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": {"operation": "fix", "parameters": {}}, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + + # Test creating a quantity with a nested invalid operation + + # Test creating invalid operation parameters + # binary has a and b - both should be operations + # unary has a (operation) + # constant has value + # variable has name and hash_value + # pow has power + # transpose has axes + # tensordot has a_index and b_index + + # Test creating nested invalid operation parameters + + # Test creating a quantity with no history + + def tearDown(self): + DataSet.objects.all().delete() + MetaData.objects.all().delete() + Quantity.objects.all().delete() + OperationTree.objects.all().delete() + + @classmethod + def tearDownClass(cls): + cls.user.delete() + + +class TestGetOperationTree(APITestCase): + @classmethod + def setUpTestData(cls): + cls.user = User.objects.create_user( + id=1, username="testUser", password="sasview!" + ) + cls.quantity = Quantity.objects.create( + id=1, + value=0, + variance=0, + label="test", + units="none", + hash=1, + ) + cls.dataset = DataSet.objects.create( + id=1, + current_user=cls.user, + name="Test Dataset", + is_public=True, + metadata=None, + ) + cls.variable = OperationTree.objects.create( + id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} + ) + cls.constant = OperationTree.objects.create( + id=2, operation="constant", parameters={"value": 1} + ) + cls.dataset.data_contents.add(cls.quantity) + cls.client = APIClient() + cls.client.force_authenticate(cls.user) + + # Test accessing a quantity with no operations performed + def test_get_operation_tree_none(self): + request = self.client.get("/v1/data/set/1/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Test Dataset", + "files": [], + "metadata": None, + "data_contents": [ + { + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": None, + } + ], + }, + ) + + # Test accessing quantity with unary operation + def test_get_operation_tree_unary(self): + inv = OperationTree.objects.create( + id=3, operation="reciprocal", parent_operation1=self.variable + ) + self.quantity.operation_tree = inv + self.quantity.save() + request = self.client.get("/v1/data/set/1/") + inv.delete() + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Test Dataset", + "files": [], + "metadata": None, + "data_contents": [ + { + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": { + "operation": "reciprocal", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + } + }, + }, + } + ], + }, + ) + + # Test accessing quantity with binary operation + def test_get_operation_tree_binary(self): + add = OperationTree.objects.create( + id=3, + operation="add", + parent_operation1=self.variable, + parent_operation2=self.constant, + ) + self.quantity.operation_tree = add + self.quantity.save() + request = self.client.get("/v1/data/set/1/") + add.delete() + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Test Dataset", + "files": [], + "metadata": None, + "data_contents": [ + { + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": { + "operation": "add", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "b": { + "operation": "constant", + "parameters": {"value": 1}, + }, + }, + }, + } + ], + }, + ) + + # Test accessing a quantity with exponent + def test_get_operation_tree_pow(self): + power = OperationTree.objects.create( + id=3, + operation="pow", + parent_operation1=self.variable, + parameters={"power": 2}, + ) + self.quantity.operation_tree = power + self.quantity.save() + request = self.client.get("/v1/data/set/1/") + power.delete() + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Test Dataset", + "files": [], + "metadata": None, + "data_contents": [ + { + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": { + "operation": "pow", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "power": 2, + }, + }, + } + ], + }, + ) + + # Test accessing a quantity with multiple operations + def test_get_operation_tree_nested(self): + multiply = OperationTree.objects.create( + id=3, + operation="mul", + parent_operation1=self.constant, + parent_operation2=self.variable, + ) + neg = OperationTree.objects.create( + id=4, operation="neg", parent_operation1=multiply + ) + self.quantity.operation_tree = neg + self.quantity.save() + request = self.client.get("/v1/data/set/1/") + multiply.delete() + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Test Dataset", + "files": [], + "metadata": None, + "data_contents": [ + { + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": { + "operation": "neg", + "parameters": { + "a": { + "operation": "mul", + "parameters": { + "a": { + "operation": "constant", + "parameters": {"value": 1}, + }, + "b": { + "operation": "variable", + "parameters": { + "hash_value": 111, + "name": "x", + }, + }, + }, + } + }, + }, + } + ], + }, + ) + + # Test accessing a transposed quantity + def test_get_operation_tree_transpose(self): + trans = OperationTree.objects.create( + id=3, + operation="transpose", + parent_operation1=self.variable, + parameters={"axes": (1, 0)}, + ) + self.quantity.operation_tree = trans + self.quantity.save() + request = self.client.get("/v1/data/set/1/") + trans.delete() + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Test Dataset", + "files": [], + "metadata": None, + "data_contents": [ + { + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": { + "operation": "transpose", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "axes": [1, 0], + }, + }, + } + ], + }, + ) + + # Test accessing a quantity with tensordot + + @classmethod + def tearDownClass(cls): + cls.user.delete() + cls.quantity.delete() + cls.dataset.delete() + cls.variable.delete() + cls.constant.delete() From 6e508bb4280292a45eb26af8e733c70e683ffb83 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 9 Apr 2025 12:00:14 -0400 Subject: [PATCH 402/675] Tests for tensor product operation --- .../data/test/test_operation_tree.py | 116 ++++++++++++++---- 1 file changed, 94 insertions(+), 22 deletions(-) diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index e4f19b9a8..7b6a26893 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -30,27 +30,6 @@ def setUpTestData(cls): ], "is_public": True, } - cls.nested_operations = { - "operation_tree": { - "operation": "neg", - "parameters": { - "a": { - "operation": "mul", - "parameters": { - "a": { - "operation": "constant", - "parameters": {"value": {"type": "int", "value": 7}}, - }, - "b": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - }, - }, - }, - }, - }, - "references": {}, - } cls.user = User.objects.create_user( id=1, username="testUser", password="sasview!" ) @@ -183,7 +162,27 @@ def test_operation_tree_created_transpose(self): # Test creating a quantity with multiple operations def test_operation_tree_created_nested(self): - self.dataset["data_contents"][0]["history"] = self.nested_operations + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "neg", + "parameters": { + "a": { + "operation": "mul", + "parameters": { + "a": { + "operation": "constant", + "parameters": {"value": {"type": "int", "value": 7}}, + }, + "b": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + }, + }, + }, + }, + "references": {}, + } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] new_dataset = DataSet.objects.get(id=max_id) @@ -203,6 +202,30 @@ def test_operation_tree_created_nested(self): self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) # Test creating a quantity with tensordot + def test_operation_tree_created_tensor(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "tensor_product", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "b": {"operation": "constant", "parameters": {"value": 5}}, + "a_index": 1, + "b_index": 1, + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + tensor = new_quantity.operation_tree + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual(tensor.operation, "tensor_product") + self.assertEqual(tensor.parameters, {"a_index": 1, "b_index": 1}) # Test creating a quantity with an invalid operation def test_create_operation_tree_invalid(self): @@ -536,6 +559,55 @@ def test_get_operation_tree_transpose(self): ) # Test accessing a quantity with tensordot + def test_get_operation_tree_tensordot(self): + tensor = OperationTree.objects.create( + id=3, + operation="tensor_product", + parent_operation1=self.variable, + parent_operation2=self.constant, + parameters={"a_index": 1, "b_index": 1}, + ) + self.quantity.operation_tree = tensor + self.quantity.save() + request = self.client.get("/v1/data/set/1/") + tensor.delete() + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Test Dataset", + "files": [], + "metadata": None, + "data_contents": [ + { + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": { + "operation": "tensor_product", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "b": { + "operation": "constant", + "parameters": {"value": 1}, + }, + "a_index": 1, + "b_index": 1, + }, + }, + } + ], + }, + ) @classmethod def tearDownClass(cls): From 67ff22cca4af95a03da56c0113bc91aa7099bbed Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 9 Apr 2025 15:40:19 -0400 Subject: [PATCH 403/675] Validate nested operations and check parameters based on type --- sasdata/fair_database/data/serializers.py | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index cc1c8b100..ac122ccd1 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -34,6 +34,42 @@ class Meta: fields = ["operation", "parameters"] # TODO: some kind of custom validation + def validate_parameters(self, value): + if "a" in value: + serializer = OperationTreeSerializer(data=value["a"]) + serializer.is_valid(raise_exception=True) + if "b" in value: + serializer = OperationTreeSerializer(data=value["b"]) + serializer.is_valid(raise_exception=True) + return value + + def validate(self, data): + expected_parameters = { + "zero": [], + "one": [], + "constant": ["value"], + "variable": ["hash_value", "name"], + "neg": ["a"], + "reciprocal": ["a"], + "add": ["a", "b"], + "sub": ["a", "b"], + "mul": ["a", "b"], + "div": ["a", "b"], + "pow": ["a", "power"], + "transpose": ["a", "axes"], + "dot": ["a", "b"], + "matmul": ["a", "b"], + "tensor_product": ["a", "b", "a_index", "b_index"], + } + + for parameter in expected_parameters[data["operation"]]: + if parameter not in data["parameters"]: + raise serializers.ValidationError( + data["operation"] + " requires parameter " + parameter + ) + + return data + def to_representation(self, instance): data = {"operation": instance.operation, "parameters": instance.parameters} if instance.parent_operation1 is not None: From 7d5628083c14b874a6f16c601468131aea70dbb5 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 9 Apr 2025 15:42:21 -0400 Subject: [PATCH 404/675] Test creating invalid non-top-level operation fails --- .../data/test/test_operation_tree.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index 7b6a26893..e3d264434 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -235,8 +235,29 @@ def test_create_operation_tree_invalid(self): } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(len(DataSet.objects.all()), 0) + self.assertEqual(len(Quantity.objects.all()), 0) + self.assertEqual(len(OperationTree.objects.all()), 0) # Test creating a quantity with a nested invalid operation + def test_create_operation_tree_invalid_nested(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "reciprocal", + "parameters": { + "a": { + "operation": "fix", + "parameters": {}, + } + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(len(DataSet.objects.all()), 0) + self.assertEqual(len(Quantity.objects.all()), 0) + self.assertEqual(len(OperationTree.objects.all()), 0) # Test creating invalid operation parameters # binary has a and b - both should be operations From 71d6573e3745eaff0da726d8e2d5b097dbd1e9fe Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 10 Apr 2025 11:28:15 -0400 Subject: [PATCH 405/675] Separate class for invalid operation tree testing --- .../data/test/test_operation_tree.py | 50 ++++++++++++++++--- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index e3d264434..936ce4a88 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -227,6 +227,47 @@ def test_operation_tree_created_tensor(self): self.assertEqual(tensor.operation, "tensor_product") self.assertEqual(tensor.parameters, {"a_index": 1, "b_index": 1}) + # Test creating a quantity with no history + + def tearDown(self): + DataSet.objects.all().delete() + MetaData.objects.all().delete() + Quantity.objects.all().delete() + OperationTree.objects.all().delete() + + @classmethod + def tearDownClass(cls): + cls.user.delete() + + +class TestCreateInvalidOperationTree(APITestCase): + @classmethod + def setUpTestData(cls): + cls.dataset = { + "name": "Test Dataset", + "metadata": { + "title": "test metadata", + "run": 1, + "definition": "test", + "instrument": {"source": {}, "collimation": {}, "detectors": {}}, + }, + "data_contents": [ + { + "label": "test", + "value": {"array_contents": [0, 0, 0, 0], "shape": (2, 2)}, + "variance": {"array_contents": [0, 0, 0, 0], "shape": (2, 2)}, + "units": "none", + "hash": 0, + } + ], + "is_public": True, + } + cls.user = User.objects.create_user( + id=1, username="testUser", password="sasview!" + ) + cls.client = APIClient() + cls.client.force_authenticate(cls.user) + # Test creating a quantity with an invalid operation def test_create_operation_tree_invalid(self): self.dataset["data_contents"][0]["history"] = { @@ -270,14 +311,6 @@ def test_create_operation_tree_invalid_nested(self): # Test creating nested invalid operation parameters - # Test creating a quantity with no history - - def tearDown(self): - DataSet.objects.all().delete() - MetaData.objects.all().delete() - Quantity.objects.all().delete() - OperationTree.objects.all().delete() - @classmethod def tearDownClass(cls): cls.user.delete() @@ -314,6 +347,7 @@ def setUpTestData(cls): cls.client = APIClient() cls.client.force_authenticate(cls.user) + # TODO: make scrolling past these less painful by only including the parts of responses I care about # Test accessing a quantity with no operations performed def test_get_operation_tree_none(self): request = self.client.get("/v1/data/set/1/") From bc1058794db923bca2298823a602e4d28d882cb6 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 10 Apr 2025 11:35:33 -0400 Subject: [PATCH 406/675] Check operation trees only in operation tree get tests --- .../data/test/test_operation_tree.py | 279 ++++++------------ 1 file changed, 83 insertions(+), 196 deletions(-) diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index 936ce4a88..2aa6a6276 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -347,31 +347,19 @@ def setUpTestData(cls): cls.client = APIClient() cls.client.force_authenticate(cls.user) - # TODO: make scrolling past these less painful by only including the parts of responses I care about # Test accessing a quantity with no operations performed def test_get_operation_tree_none(self): request = self.client.get("/v1/data/set/1/") self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( - request.data, + request.data["data_contents"][0], { - "id": 1, - "current_user": 1, - "users": [], - "is_public": True, - "name": "Test Dataset", - "files": [], - "metadata": None, - "data_contents": [ - { - "label": "test", - "value": 0, - "variance": 0, - "units": "none", - "hash": 1, - "operation_tree": None, - } - ], + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": None, }, ) @@ -386,33 +374,22 @@ def test_get_operation_tree_unary(self): inv.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( - request.data, + request.data["data_contents"][0], { - "id": 1, - "current_user": 1, - "users": [], - "is_public": True, - "name": "Test Dataset", - "files": [], - "metadata": None, - "data_contents": [ - { - "label": "test", - "value": 0, - "variance": 0, - "units": "none", - "hash": 1, - "operation_tree": { - "operation": "reciprocal", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - } - }, - }, - } - ], + "label": "test", + "value": 0, + "variance": 0, + "units": "none", + "hash": 1, + "operation_tree": { + "operation": "reciprocal", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + } + }, + }, }, ) @@ -430,37 +407,19 @@ def test_get_operation_tree_binary(self): add.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( - request.data, + request.data["data_contents"][0]["operation_tree"], { - "id": 1, - "current_user": 1, - "users": [], - "is_public": True, - "name": "Test Dataset", - "files": [], - "metadata": None, - "data_contents": [ - { - "label": "test", - "value": 0, - "variance": 0, - "units": "none", - "hash": 1, - "operation_tree": { - "operation": "add", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - }, - "b": { - "operation": "constant", - "parameters": {"value": 1}, - }, - }, - }, - } - ], + "operation": "add", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "b": { + "operation": "constant", + "parameters": {"value": 1}, + }, + }, }, ) @@ -478,34 +437,16 @@ def test_get_operation_tree_pow(self): power.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( - request.data, + request.data["data_contents"][0]["operation_tree"], { - "id": 1, - "current_user": 1, - "users": [], - "is_public": True, - "name": "Test Dataset", - "files": [], - "metadata": None, - "data_contents": [ - { - "label": "test", - "value": 0, - "variance": 0, - "units": "none", - "hash": 1, - "operation_tree": { - "operation": "pow", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - }, - "power": 2, - }, - }, - } - ], + "operation": "pow", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "power": 2, + }, }, ) @@ -526,45 +467,27 @@ def test_get_operation_tree_nested(self): multiply.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( - request.data, + request.data["data_contents"][0]["operation_tree"], { - "id": 1, - "current_user": 1, - "users": [], - "is_public": True, - "name": "Test Dataset", - "files": [], - "metadata": None, - "data_contents": [ - { - "label": "test", - "value": 0, - "variance": 0, - "units": "none", - "hash": 1, - "operation_tree": { - "operation": "neg", - "parameters": { - "a": { - "operation": "mul", - "parameters": { - "a": { - "operation": "constant", - "parameters": {"value": 1}, - }, - "b": { - "operation": "variable", - "parameters": { - "hash_value": 111, - "name": "x", - }, - }, - }, - } + "operation": "neg", + "parameters": { + "a": { + "operation": "mul", + "parameters": { + "a": { + "operation": "constant", + "parameters": {"value": 1}, + }, + "b": { + "operation": "variable", + "parameters": { + "hash_value": 111, + "name": "x", + }, }, }, } - ], + }, }, ) @@ -582,34 +505,16 @@ def test_get_operation_tree_transpose(self): trans.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( - request.data, + request.data["data_contents"][0]["operation_tree"], { - "id": 1, - "current_user": 1, - "users": [], - "is_public": True, - "name": "Test Dataset", - "files": [], - "metadata": None, - "data_contents": [ - { - "label": "test", - "value": 0, - "variance": 0, - "units": "none", - "hash": 1, - "operation_tree": { - "operation": "transpose", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - }, - "axes": [1, 0], - }, - }, - } - ], + "operation": "transpose", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "axes": [1, 0], + }, }, ) @@ -628,39 +533,21 @@ def test_get_operation_tree_tensordot(self): tensor.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( - request.data, + request.data["data_contents"][0]["operation_tree"], { - "id": 1, - "current_user": 1, - "users": [], - "is_public": True, - "name": "Test Dataset", - "files": [], - "metadata": None, - "data_contents": [ - { - "label": "test", - "value": 0, - "variance": 0, - "units": "none", - "hash": 1, - "operation_tree": { - "operation": "tensor_product", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - }, - "b": { - "operation": "constant", - "parameters": {"value": 1}, - }, - "a_index": 1, - "b_index": 1, - }, - }, - } - ], + "operation": "tensor_product", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "b": { + "operation": "constant", + "parameters": {"value": 1}, + }, + "a_index": 1, + "b_index": 1, + }, }, ) From 5df73c519cbd4456b4f854eaa31a69aec3f251b6 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 10 Apr 2025 13:30:34 -0400 Subject: [PATCH 407/675] Test creating operations with missing parameters fails --- sasdata/fair_database/data/serializers.py | 4 +- .../data/test/test_operation_tree.py | 136 ++++++++++++++++-- 2 files changed, 127 insertions(+), 13 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index ac122ccd1..930548b0f 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -33,7 +33,6 @@ class Meta: model = models.OperationTree fields = ["operation", "parameters"] - # TODO: some kind of custom validation def validate_parameters(self, value): if "a" in value: serializer = OperationTreeSerializer(data=value["a"]) @@ -116,8 +115,7 @@ class Meta: # "history", ] - # TODO: validation checks for history - + # TODO: should variable-only history be assumed to refer to the same Quantity and ignored? def to_internal_value(self, data): if "history" in data and "operation_tree" in data["history"]: operations = data["history"]["operation_tree"] diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index 2aa6a6276..e65211a5d 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -300,16 +300,132 @@ def test_create_operation_tree_invalid_nested(self): self.assertEqual(len(Quantity.objects.all()), 0) self.assertEqual(len(OperationTree.objects.all()), 0) - # Test creating invalid operation parameters - # binary has a and b - both should be operations - # unary has a (operation) - # constant has value - # variable has name and hash_value - # pow has power - # transpose has axes - # tensordot has a_index and b_index - - # Test creating nested invalid operation parameters + # Test creating a unary operation with a missing parameter fails + def test_create_missing_parameter_unary(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": {"operation": "neg", "parameters": {}}, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(len(DataSet.objects.all()), 0) + self.assertEqual(len(Quantity.objects.all()), 0) + self.assertEqual(len(OperationTree.objects.all()), 0) + + # Test creating a binary operation with a missing parameter fails + def test_create_missing_parameter_binary(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "add", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + } + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(len(DataSet.objects.all()), 0) + self.assertEqual(len(Quantity.objects.all()), 0) + self.assertEqual(len(OperationTree.objects.all()), 0) + + # TODO: should variable-only history be ignored? + # Test creating a variable with a missing parameter fails + def test_create_missing_parameter_variable(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "neg", + "parameters": { + "a": {"operation": "variable", "parameters": {"name": "x"}} + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(len(DataSet.objects.all()), 0) + self.assertEqual(len(Quantity.objects.all()), 0) + self.assertEqual(len(OperationTree.objects.all()), 0) + + # Test creating a constant with a missing parameter fails + def test_create_missing_parameter_constant(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "neg", + "parameters": {"a": {"operation": "constant", "parameters": {}}}, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(len(DataSet.objects.all()), 0) + self.assertEqual(len(Quantity.objects.all()), 0) + self.assertEqual(len(OperationTree.objects.all()), 0) + + # Test creating an exponent with a missing parameter fails + def test_create_missing_parameter_pow(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "pow", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(len(DataSet.objects.all()), 0) + self.assertEqual(len(Quantity.objects.all()), 0) + self.assertEqual(len(OperationTree.objects.all()), 0) + + # Test creating a transpose with a missing parameter fails + def test_create_missing_parameter_transpose(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "transpose", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(len(DataSet.objects.all()), 0) + self.assertEqual(len(Quantity.objects.all()), 0) + self.assertEqual(len(OperationTree.objects.all()), 0) + + # Test creating a tensor with a missing parameter fails + def test_create_missing_parameter_tensor(self): + self.dataset["data_contents"][0]["history"] = { + "operation_tree": { + "operation": "tensor_product", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + }, + "b": {"operation": "constant", "parameters": {"value": 5}}, + "b_index": 1, + }, + }, + "references": {}, + } + request = self.client.post("/v1/data/set/", data=self.dataset, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(len(DataSet.objects.all()), 0) + self.assertEqual(len(Quantity.objects.all()), 0) + self.assertEqual(len(OperationTree.objects.all()), 0) @classmethod def tearDownClass(cls): From 4ea4c0d47038c7c09a65b32288afd82218040562 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 10 Apr 2025 13:34:41 -0400 Subject: [PATCH 408/675] Test creating a quantity with no history --- .../fair_database/data/test/test_operation_tree.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index e65211a5d..869497be3 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -36,7 +36,7 @@ def setUpTestData(cls): cls.client = APIClient() cls.client.force_authenticate(cls.user) - # Test creating quantity with no operations performed + # Test creating quantity with no operations performed (variable-only history) def test_operation_tree_created_variable(self): self.dataset["data_contents"][0]["history"] = { "operation_tree": { @@ -228,6 +228,17 @@ def test_operation_tree_created_tensor(self): self.assertEqual(tensor.parameters, {"a_index": 1, "b_index": 1}) # Test creating a quantity with no history + def test_operation_tree_created_no_history(self): + if "history" in self.dataset["data_contents"][0]: + self.dataset["data_contents"][0].pop("history") + request = self.client.post( + "/v1/data/set/", data=self.dataset, format="json" + ) + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + new_quantity = new_dataset.data_contents.get(hash=0) + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertIsNone(new_quantity.operation_tree) def tearDown(self): DataSet.objects.all().delete() From 4873dcb2aea59fe3362e98785d599316d759439c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 10 Apr 2025 13:46:01 -0400 Subject: [PATCH 409/675] Check whether deleting a dataset deletes its quantities (it doesn't) (this is kind of a problem) --- sasdata/fair_database/data/test/test_dataset.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 5d62a8800..dcec261ee 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -3,7 +3,7 @@ from rest_framework.test import APIClient, APITestCase from rest_framework import status -from data.models import DataSet +from data.models import DataSet, Quantity class TestDataSet(APITestCase): @@ -339,10 +339,15 @@ def test_update_unowned_dataset(self): # Test deleting a dataset def test_delete_dataset(self): + quantity = Quantity.objects.create( + id=1, value=0, variance=0, units="none", hash=0, label="test" + ) + self.private_dataset.data_contents.add(quantity) request = self.auth_client1.delete("/v1/data/set/2/") self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, {"success": True}) self.assertRaises(DataSet.DoesNotExist, DataSet.objects.get, id=2) + self.assertRaises(Quantity.DoesNotExist, Quantity.objects.get, id=1) self.private_dataset = DataSet.objects.create( id=2, current_user=self.user1, name="Dataset 2", metadata=None ) From a7e18e5e8e9f1a554615bdb7ece73e050497b49c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 10 Apr 2025 14:58:36 -0400 Subject: [PATCH 410/675] Test whether deleting a dataset/its quantities deletes operations (it doesn't do this either) --- .../fair_database/data/test/test_dataset.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index dcec261ee..fba60167f 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -3,7 +3,7 @@ from rest_framework.test import APIClient, APITestCase from rest_framework import status -from data.models import DataSet, Quantity +from data.models import DataSet, OperationTree, Quantity class TestDataSet(APITestCase): @@ -339,18 +339,29 @@ def test_update_unowned_dataset(self): # Test deleting a dataset def test_delete_dataset(self): + operation = OperationTree.objects.create(id=1, operation="zero", parameters={}) quantity = Quantity.objects.create( - id=1, value=0, variance=0, units="none", hash=0, label="test" - ) - self.private_dataset.data_contents.add(quantity) + id=1, + value=0, + variance=0, + units="none", + hash=0, + label="test", + dataset=self.private_dataset, + operation_tree=operation, + ) + # self.private_dataset.data_contents.add(quantity) request = self.auth_client1.delete("/v1/data/set/2/") self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, {"success": True}) self.assertRaises(DataSet.DoesNotExist, DataSet.objects.get, id=2) self.assertRaises(Quantity.DoesNotExist, Quantity.objects.get, id=1) + self.assertRaises(OperationTree.DoesNotExist, OperationTree.objects.get, id=1) self.private_dataset = DataSet.objects.create( id=2, current_user=self.user1, name="Dataset 2", metadata=None ) + operation.delete() + quantity.delete() # Test cannot delete a public dataset def test_delete_public_dataset(self): From f2b69182a461fd61eef20ebb75a3e5baea2ee581 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 10 Apr 2025 14:59:48 -0400 Subject: [PATCH 411/675] Try switching from many-to-many to one-to-many relationship for DataSet and Quantity --- ..._dataset_data_contents_quantity_dataset.py | 28 +++++++++++++++++++ sasdata/fair_database/data/models.py | 6 +++- sasdata/fair_database/data/serializers.py | 14 +++++++--- 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0022_remove_dataset_data_contents_quantity_dataset.py diff --git a/sasdata/fair_database/data/migrations/0022_remove_dataset_data_contents_quantity_dataset.py b/sasdata/fair_database/data/migrations/0022_remove_dataset_data_contents_quantity_dataset.py new file mode 100644 index 000000000..5de2687ae --- /dev/null +++ b/sasdata/fair_database/data/migrations/0022_remove_dataset_data_contents_quantity_dataset.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.6 on 2025-04-10 17:53 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0021_dataset_data_contents"), + ] + + operations = [ + migrations.RemoveField( + model_name="dataset", + name="data_contents", + ), + migrations.AddField( + model_name="quantity", + name="dataset", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + related_name="data_contents", + to="data.dataset", + ), + preserve_default=False, + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 5cee72323..23a018ac4 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -57,7 +57,7 @@ class DataSet(Data): ) # data contents - maybe ManyToManyField - data_contents = models.ManyToManyField("Quantity") + # data_contents = models.ManyToManyField("Quantity") # TODO: update based on SasData class in data.py # type of dataset @@ -87,6 +87,10 @@ class Quantity(models.Model): label = models.CharField(max_length=50) + dataset = models.ForeignKey( + DataSet, on_delete=models.CASCADE, related_name="data_contents" + ) + def empty_list(): return [] diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 930548b0f..97a142a30 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -101,6 +101,9 @@ def create(self, validated_data): class QuantitySerializer(serializers.ModelSerializer): operation_tree = OperationTreeSerializer(read_only=False, required=False) label = serializers.CharField(max_length=20) + dataset = serializers.PrimaryKeyRelatedField( + queryset=models.DataSet, required=False, allow_null=True + ) # history = serializers.JSONField(required=False) # TODO: is this required? class Meta: @@ -112,6 +115,7 @@ class Meta: "hash", "operation_tree", "label", + "dataset", # "history", ] @@ -126,17 +130,18 @@ def to_internal_value(self, data): return super().to_internal_value(data) def create(self, validated_data): + dataset = models.DataSet.objects.get(id=validated_data.pop("dataset")) if "operation_tree" in validated_data: operations_data = validated_data.pop("operation_tree") operation_tree = OperationTreeSerializer.create( OperationTreeSerializer(), validated_data=operations_data ) quantity = models.Quantity.objects.create( - operation_tree=operation_tree, **validated_data + dataset=dataset, operation_tree=operation_tree, **validated_data ) return quantity else: - return models.Quantity.objects.create(**validated_data) + return models.Quantity.objects.create(dataset=dataset, **validated_data) class DataSetSerializer(serializers.ModelSerializer): @@ -187,8 +192,9 @@ def create(self, validated_data): data_contents = validated_data.pop("data_contents") dataset = models.DataSet.objects.create(metadata=metadata, **validated_data) for d in data_contents: - quantity = QuantitySerializer.create(QuantitySerializer(), validated_data=d) - dataset.data_contents.add(quantity) + d["dataset"] = dataset.id + QuantitySerializer.create(QuantitySerializer(), validated_data=d) + # dataset.data_contents.add(quantity) return dataset # TODO: account for updating other attributes From 4328f5215ef2958e848d32eaa39d42e946498750 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 10 Apr 2025 15:11:46 -0400 Subject: [PATCH 412/675] Change operations test setup to account for altered model relationship --- .../data/test/test_operation_tree.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index 869497be3..394286924 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -449,6 +449,13 @@ def setUpTestData(cls): cls.user = User.objects.create_user( id=1, username="testUser", password="sasview!" ) + cls.dataset = DataSet.objects.create( + id=1, + current_user=cls.user, + name="Test Dataset", + is_public=True, + metadata=None, + ) cls.quantity = Quantity.objects.create( id=1, value=0, @@ -456,13 +463,7 @@ def setUpTestData(cls): label="test", units="none", hash=1, - ) - cls.dataset = DataSet.objects.create( - id=1, - current_user=cls.user, - name="Test Dataset", - is_public=True, - metadata=None, + dataset=cls.dataset, ) cls.variable = OperationTree.objects.create( id=1, operation="variable", parameters={"hash_value": 111, "name": "x"} @@ -470,7 +471,7 @@ def setUpTestData(cls): cls.constant = OperationTree.objects.create( id=2, operation="constant", parameters={"value": 1} ) - cls.dataset.data_contents.add(cls.quantity) + # cls.dataset.data_contents.add(cls.quantity) cls.client = APIClient() cls.client.force_authenticate(cls.user) From fed886530936aa141117d1b96cedd9ae451dfdca Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 10 Apr 2025 15:12:53 -0400 Subject: [PATCH 413/675] Remove dataset id from serialized quantity --- sasdata/fair_database/data/serializers.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 97a142a30..63940ac88 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -129,6 +129,12 @@ def to_internal_value(self, data): return super().to_internal_value(data_copy) return super().to_internal_value(data) + def to_representation(self, instance): + data = super().to_representation(instance) + if "dataset" in data: + data.pop("dataset") + return data + def create(self, validated_data): dataset = models.DataSet.objects.get(id=validated_data.pop("dataset")) if "operation_tree" in validated_data: From fd78607e618e9fd215776c8a0bc6564702943a5c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 10 Apr 2025 15:31:28 -0400 Subject: [PATCH 414/675] Prevent deleting parent operations instead of cascading --- ...perationtree_parent_operation1_and_more.py | 35 +++++++++++++++++++ sasdata/fair_database/data/models.py | 4 +-- 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0023_alter_operationtree_parent_operation1_and_more.py diff --git a/sasdata/fair_database/data/migrations/0023_alter_operationtree_parent_operation1_and_more.py b/sasdata/fair_database/data/migrations/0023_alter_operationtree_parent_operation1_and_more.py new file mode 100644 index 000000000..6032bbf60 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0023_alter_operationtree_parent_operation1_and_more.py @@ -0,0 +1,35 @@ +# Generated by Django 5.1.6 on 2025-04-10 19:29 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0022_remove_dataset_data_contents_quantity_dataset"), + ] + + operations = [ + migrations.AlterField( + model_name="operationtree", + name="parent_operation1", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="child_operations1", + to="data.operationtree", + ), + ), + migrations.AlterField( + model_name="operationtree", + name="parent_operation2", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="child_operations2", + to="data.operationtree", + ), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 23a018ac4..8d3442d5a 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -154,7 +154,7 @@ class OperationTree(models.Model): "self", blank=True, null=True, - on_delete=models.CASCADE, + on_delete=models.PROTECT, related_name="child_operations1", ) @@ -163,7 +163,7 @@ class OperationTree(models.Model): "self", blank=True, null=True, - on_delete=models.CASCADE, + on_delete=models.PROTECT, related_name="child_operations2", ) From 8b0594ca20e8f1f924beba89c2fbe7356eb8f839 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 10 Apr 2025 16:06:16 -0400 Subject: [PATCH 415/675] Automatically delete OperationTrees when quantities are deleted --- sasdata/fair_database/data/apps.py | 3 +++ sasdata/fair_database/data/signals.py | 23 +++++++++++++++++++ .../fair_database/data/test/test_dataset.py | 4 +--- .../data/test/test_operation_tree.py | 17 +++++++++++++- 4 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 sasdata/fair_database/data/signals.py diff --git a/sasdata/fair_database/data/apps.py b/sasdata/fair_database/data/apps.py index b882be950..6d27e7207 100644 --- a/sasdata/fair_database/data/apps.py +++ b/sasdata/fair_database/data/apps.py @@ -4,3 +4,6 @@ class DataConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "data" + + def ready(self): + from . import signals # noqa: F401 diff --git a/sasdata/fair_database/data/signals.py b/sasdata/fair_database/data/signals.py new file mode 100644 index 000000000..e6c10d6c9 --- /dev/null +++ b/sasdata/fair_database/data/signals.py @@ -0,0 +1,23 @@ +from django.db.models.signals import post_delete +from django.dispatch import receiver + +from data.models import OperationTree, Quantity + + +# TODO: is there a better way to do this than with signals +# delete the operation tree when a quantity is deleted +# see apps.py for signal connection +@receiver(post_delete, sender=Quantity) +def delete_operation_tree(sender, **kwargs): + if kwargs["instance"].operation_tree: + kwargs["instance"].operation_tree.delete() + + +# propagate deletion through the operation tree +# see apps.py for signal connection +@receiver(post_delete, sender=OperationTree) +def delete_parent_operations(sender, **kwargs): + if kwargs["instance"].parent_operation1: + kwargs["instance"].parent_operation1.delete() + if kwargs["instance"].parent_operation2: + kwargs["instance"].parent_operation2.delete() diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index fba60167f..52fe180d3 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -340,7 +340,7 @@ def test_update_unowned_dataset(self): # Test deleting a dataset def test_delete_dataset(self): operation = OperationTree.objects.create(id=1, operation="zero", parameters={}) - quantity = Quantity.objects.create( + Quantity.objects.create( id=1, value=0, variance=0, @@ -360,8 +360,6 @@ def test_delete_dataset(self): self.private_dataset = DataSet.objects.create( id=2, current_user=self.user1, name="Dataset 2", metadata=None ) - operation.delete() - quantity.delete() # Test cannot delete a public dataset def test_delete_public_dataset(self): diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index 394286924..1aae4ec7f 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -499,6 +499,8 @@ def test_get_operation_tree_unary(self): self.quantity.operation_tree = inv self.quantity.save() request = self.client.get("/v1/data/set/1/") + inv.parent_operation1 = None + inv.save() inv.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( @@ -532,6 +534,9 @@ def test_get_operation_tree_binary(self): self.quantity.operation_tree = add self.quantity.save() request = self.client.get("/v1/data/set/1/") + add.parent_operation1 = None + add.parent_operation2 = None + add.save() add.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( @@ -562,6 +567,8 @@ def test_get_operation_tree_pow(self): self.quantity.operation_tree = power self.quantity.save() request = self.client.get("/v1/data/set/1/") + power.parent_operation1 = None + power.save() power.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( @@ -592,7 +599,10 @@ def test_get_operation_tree_nested(self): self.quantity.operation_tree = neg self.quantity.save() request = self.client.get("/v1/data/set/1/") - multiply.delete() + multiply.parent_operation1 = None + multiply.parent_operation2 = None + multiply.save() + neg.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( request.data["data_contents"][0]["operation_tree"], @@ -630,6 +640,8 @@ def test_get_operation_tree_transpose(self): self.quantity.operation_tree = trans self.quantity.save() request = self.client.get("/v1/data/set/1/") + trans.parent_operation1 = None + trans.save() trans.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( @@ -658,6 +670,9 @@ def test_get_operation_tree_tensordot(self): self.quantity.operation_tree = tensor self.quantity.save() request = self.client.get("/v1/data/set/1/") + tensor.parent_operation1 = None + tensor.parent_operation2 = None + tensor.save() tensor.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( From 86574e04f8032f5e03c911f49932abbfeec1e2a3 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 10 Apr 2025 16:09:24 -0400 Subject: [PATCH 416/675] Test nested OperationTree instances are deleted --- sasdata/fair_database/data/test/test_dataset.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 52fe180d3..86212c7e7 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -339,7 +339,12 @@ def test_update_unowned_dataset(self): # Test deleting a dataset def test_delete_dataset(self): - operation = OperationTree.objects.create(id=1, operation="zero", parameters={}) + nested_operation = OperationTree.objects.create( + id=1, operation="zero", parameters={} + ) + operation = OperationTree.objects.create( + id=2, operation="neg", parent_operation1=nested_operation + ) Quantity.objects.create( id=1, value=0, @@ -357,6 +362,7 @@ def test_delete_dataset(self): self.assertRaises(DataSet.DoesNotExist, DataSet.objects.get, id=2) self.assertRaises(Quantity.DoesNotExist, Quantity.objects.get, id=1) self.assertRaises(OperationTree.DoesNotExist, OperationTree.objects.get, id=1) + self.assertRaises(OperationTree.DoesNotExist, OperationTree.objects.get, id=2) self.private_dataset = DataSet.objects.create( id=2, current_user=self.user1, name="Dataset 2", metadata=None ) From 2c93823fe09f88276e38206dd3a0769f6234c724 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 11 Apr 2025 11:32:28 -0400 Subject: [PATCH 417/675] Change direction of DataSet/MetaData relationship for automatic deletion --- ...emove_dataset_metadata_metadata_dataset.py | 28 +++++++++++++++++++ sasdata/fair_database/data/models.py | 15 ++++------ sasdata/fair_database/data/serializers.py | 16 +++++++---- .../fair_database/data/test/test_dataset.py | 16 +++++------ .../data/test/test_operation_tree.py | 1 - 5 files changed, 52 insertions(+), 24 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0024_remove_dataset_metadata_metadata_dataset.py diff --git a/sasdata/fair_database/data/migrations/0024_remove_dataset_metadata_metadata_dataset.py b/sasdata/fair_database/data/migrations/0024_remove_dataset_metadata_metadata_dataset.py new file mode 100644 index 000000000..956631402 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0024_remove_dataset_metadata_metadata_dataset.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.6 on 2025-04-11 15:17 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0023_alter_operationtree_parent_operation1_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="dataset", + name="metadata", + ), + migrations.AddField( + model_name="metadata", + name="dataset", + field=models.OneToOneField( + default=1, + on_delete=django.db.models.deletion.CASCADE, + related_name="metadata", + to="data.dataset", + ), + preserve_default=False, + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 8d3442d5a..7683d4e5f 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -50,15 +50,6 @@ class DataSet(Data): # associated files files = models.ManyToManyField(DataFile) - # metadata - maybe a foreign key? - # TODO: when MetaData is finished, set blank/null false - metadata = models.OneToOneField( - "MetaData", blank=True, null=True, on_delete=models.CASCADE - ) - - # data contents - maybe ManyToManyField - # data_contents = models.ManyToManyField("Quantity") - # TODO: update based on SasData class in data.py # type of dataset # dataset_type = models.JSONField() @@ -103,6 +94,7 @@ def empty_dict(): class MetaData(models.Model): """Database model for scattering metadata""" + # TODO: update based on changes in sasdata/metadata.py # title title = models.CharField(max_length=500, default="Title") @@ -121,6 +113,11 @@ class MetaData(models.Model): # sample sample = models.JSONField(blank=True, null=True) + # associated dataset + dataset = models.OneToOneField( + DataSet, on_delete=models.CASCADE, related_name="metadata" + ) + class OperationTree(models.Model): """Database model for tree of operations performed on a DataSet.""" diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 63940ac88..cbaf6c977 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -23,10 +23,18 @@ class AccessManagementSerializer(serializers.Serializer): class MetaDataSerializer(serializers.ModelSerializer): + dataset = serializers.PrimaryKeyRelatedField( + queryset=models.DataSet, required=False, allow_null=True + ) + class Meta: model = models.MetaData fields = "__all__" + def create(self, validated_data): + dataset = models.DataSet.objects.get(id=validated_data.pop("dataset")) + return models.MetaData.objects.create(dataset=dataset, **validated_data) + class OperationTreeSerializer(serializers.ModelSerializer): class Meta: @@ -192,15 +200,13 @@ def create(self, validated_data): if self.context["request"].user.is_authenticated: validated_data["current_user"] = self.context["request"].user metadata_raw = validated_data.pop("metadata") - metadata = MetaDataSerializer.create( - MetaDataSerializer(), validated_data=metadata_raw - ) data_contents = validated_data.pop("data_contents") - dataset = models.DataSet.objects.create(metadata=metadata, **validated_data) + dataset = models.DataSet.objects.create(**validated_data) + metadata_raw["dataset"] = dataset.id + MetaDataSerializer.create(MetaDataSerializer(), validated_data=metadata_raw) for d in data_contents: d["dataset"] = dataset.id QuantitySerializer.create(QuantitySerializer(), validated_data=d) - # dataset.data_contents.add(quantity) return dataset # TODO: account for updating other attributes diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 86212c7e7..831253289 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -36,13 +36,12 @@ def setUpTestData(cls): current_user=cls.user1, is_public=True, name="Dataset 1", - metadata=None, ) cls.private_dataset = DataSet.objects.create( - id=2, current_user=cls.user1, name="Dataset 2", metadata=None + id=2, current_user=cls.user1, name="Dataset 2" ) cls.unowned_dataset = DataSet.objects.create( - id=3, is_public=True, name="Dataset 3", metadata=None + id=3, is_public=True, name="Dataset 3" ) cls.private_dataset.users.add(cls.user3) cls.auth_client1 = APIClient() @@ -216,13 +215,12 @@ def setUpTestData(cls): current_user=cls.user1, is_public=True, name="Dataset 1", - metadata=None, ) cls.private_dataset = DataSet.objects.create( - id=2, current_user=cls.user1, name="Dataset 2", metadata=None + id=2, current_user=cls.user1, name="Dataset 2" ) cls.unowned_dataset = DataSet.objects.create( - id=3, is_public=True, name="Dataset 3", metadata=None + id=3, is_public=True, name="Dataset 3" ) cls.private_dataset.users.add(cls.user3) cls.auth_client1 = APIClient() @@ -364,7 +362,7 @@ def test_delete_dataset(self): self.assertRaises(OperationTree.DoesNotExist, OperationTree.objects.get, id=1) self.assertRaises(OperationTree.DoesNotExist, OperationTree.objects.get, id=2) self.private_dataset = DataSet.objects.create( - id=2, current_user=self.user1, name="Dataset 2", metadata=None + id=2, current_user=self.user1, name="Dataset 2" ) # Test cannot delete a public dataset @@ -402,10 +400,10 @@ def setUpTestData(cls): cls.user1 = User.objects.create_user(username="testUser1", password="secret") cls.user2 = User.objects.create_user(username="testUser2", password="secret") cls.private_dataset = DataSet.objects.create( - id=1, current_user=cls.user1, name="Dataset 1", metadata=None + id=1, current_user=cls.user1, name="Dataset 1" ) cls.shared_dataset = DataSet.objects.create( - id=2, current_user=cls.user1, name="Dataset 2", metadata=None + id=2, current_user=cls.user1, name="Dataset 2" ) cls.shared_dataset.users.add(cls.user2) cls.client_owner = APIClient() diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index 1aae4ec7f..751086d24 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -454,7 +454,6 @@ def setUpTestData(cls): current_user=cls.user, name="Test Dataset", is_public=True, - metadata=None, ) cls.quantity = Quantity.objects.create( id=1, From 7534ebf34603a08caf4b26b2f66887eb993caffd Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 11 Apr 2025 12:10:35 -0400 Subject: [PATCH 418/675] Change direction of Quantity/OperationTree relationship for automatic deletion --- ...remove_quantity_operation_tree_and_more.py | 28 ++++++++++++++++ sasdata/fair_database/data/models.py | 13 +++++--- sasdata/fair_database/data/serializers.py | 17 +++++++--- sasdata/fair_database/data/signals.py | 8 +---- .../fair_database/data/test/test_dataset.py | 16 ++++----- .../data/test/test_operation_tree.py | 33 ++++++++++--------- 6 files changed, 75 insertions(+), 40 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0025_remove_quantity_operation_tree_and_more.py diff --git a/sasdata/fair_database/data/migrations/0025_remove_quantity_operation_tree_and_more.py b/sasdata/fair_database/data/migrations/0025_remove_quantity_operation_tree_and_more.py new file mode 100644 index 000000000..93f1b269a --- /dev/null +++ b/sasdata/fair_database/data/migrations/0025_remove_quantity_operation_tree_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.6 on 2025-04-11 15:38 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0024_remove_dataset_metadata_metadata_dataset"), + ] + + operations = [ + migrations.RemoveField( + model_name="quantity", + name="operation_tree", + ), + migrations.AddField( + model_name="operationtree", + name="quantity", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="operation_tree", + to="data.quantity", + ), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 7683d4e5f..ee9263001 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -71,10 +71,6 @@ class Quantity(models.Model): hash = models.IntegerField() # TODO: add field to store references portion of QuantityHistory - # operation history of the quantity - operation_tree from QuantityHistory - operation_tree = models.OneToOneField( - "OperationTree", blank=True, null=True, on_delete=models.SET_NULL - ) label = models.CharField(max_length=50) @@ -164,6 +160,15 @@ class OperationTree(models.Model): related_name="child_operations2", ) + # related quantity, only set for base of tree + quantity = models.OneToOneField( + Quantity, + on_delete=models.CASCADE, + blank=True, + null=True, + related_name="operation_tree", + ) + class Session(Data): """Database model for a project save state.""" diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index cbaf6c977..7c5282eee 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -37,9 +37,13 @@ def create(self, validated_data): class OperationTreeSerializer(serializers.ModelSerializer): + quantity = serializers.PrimaryKeyRelatedField( + queryset=models.Quantity, required=False, allow_null=True + ) + class Meta: model = models.OperationTree - fields = ["operation", "parameters"] + fields = ["operation", "parameters", "quantity"] def validate_parameters(self, value): if "a" in value: @@ -86,6 +90,7 @@ def to_representation(self, instance): return data def create(self, validated_data): + quantity = None parent_operation1 = None parent_operation2 = None if not constant_or_variable(validated_data["operation"]): @@ -98,11 +103,14 @@ def create(self, validated_data): serializer2 = OperationTreeSerializer(data=parent2) if serializer2.is_valid(raise_exception=True): parent_operation2 = serializer2.save() + if "quantity" in validated_data: + quantity = models.Quantity.objects.get(id=validated_data.pop("quantity")) return models.OperationTree.objects.create( operation=validated_data["operation"], parameters=validated_data["parameters"], parent_operation1=parent_operation1, parent_operation2=parent_operation2, + quantity=quantity, ) @@ -147,12 +155,11 @@ def create(self, validated_data): dataset = models.DataSet.objects.get(id=validated_data.pop("dataset")) if "operation_tree" in validated_data: operations_data = validated_data.pop("operation_tree") - operation_tree = OperationTreeSerializer.create( + quantity = models.Quantity.objects.create(dataset=dataset, **validated_data) + operations_data["quantity"] = quantity.id + OperationTreeSerializer.create( OperationTreeSerializer(), validated_data=operations_data ) - quantity = models.Quantity.objects.create( - dataset=dataset, operation_tree=operation_tree, **validated_data - ) return quantity else: return models.Quantity.objects.create(dataset=dataset, **validated_data) diff --git a/sasdata/fair_database/data/signals.py b/sasdata/fair_database/data/signals.py index e6c10d6c9..c8b1613cb 100644 --- a/sasdata/fair_database/data/signals.py +++ b/sasdata/fair_database/data/signals.py @@ -1,16 +1,10 @@ from django.db.models.signals import post_delete from django.dispatch import receiver -from data.models import OperationTree, Quantity +from data.models import OperationTree # TODO: is there a better way to do this than with signals -# delete the operation tree when a quantity is deleted -# see apps.py for signal connection -@receiver(post_delete, sender=Quantity) -def delete_operation_tree(sender, **kwargs): - if kwargs["instance"].operation_tree: - kwargs["instance"].operation_tree.delete() # propagate deletion through the operation tree diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 831253289..0b5571ae3 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -337,13 +337,7 @@ def test_update_unowned_dataset(self): # Test deleting a dataset def test_delete_dataset(self): - nested_operation = OperationTree.objects.create( - id=1, operation="zero", parameters={} - ) - operation = OperationTree.objects.create( - id=2, operation="neg", parent_operation1=nested_operation - ) - Quantity.objects.create( + quantity = Quantity.objects.create( id=1, value=0, variance=0, @@ -351,9 +345,13 @@ def test_delete_dataset(self): hash=0, label="test", dataset=self.private_dataset, - operation_tree=operation, ) - # self.private_dataset.data_contents.add(quantity) + nested_operation = OperationTree.objects.create( + id=1, operation="zero", parameters={} + ) + OperationTree.objects.create( + id=2, operation="neg", parent_operation1=nested_operation, quantity=quantity + ) request = self.auth_client1.delete("/v1/data/set/2/") self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, {"success": True}) diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index 751086d24..3fe9a4dfd 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -36,6 +36,10 @@ def setUpTestData(cls): cls.client = APIClient() cls.client.force_authenticate(cls.user) + @staticmethod + def get_operation_tree(quantity): + return quantity.operation_tree + # Test creating quantity with no operations performed (variable-only history) def test_operation_tree_created_variable(self): self.dataset["data_contents"][0]["history"] = { @@ -50,7 +54,11 @@ def test_operation_tree_created_variable(self): new_dataset = DataSet.objects.get(id=max_id) new_quantity = new_dataset.data_contents.get(hash=0) self.assertEqual(request.status_code, status.HTTP_201_CREATED) - self.assertIsNone(new_quantity.operation_tree) + self.assertRaises( + Quantity.operation_tree.RelatedObjectDoesNotExist, + self.get_operation_tree, + quantity=new_quantity, + ) # Test creating quantity with unary operation def test_operation_tree_created_unary(self): @@ -493,10 +501,11 @@ def test_get_operation_tree_none(self): # Test accessing quantity with unary operation def test_get_operation_tree_unary(self): inv = OperationTree.objects.create( - id=3, operation="reciprocal", parent_operation1=self.variable + id=3, + operation="reciprocal", + parent_operation1=self.variable, + quantity=self.quantity, ) - self.quantity.operation_tree = inv - self.quantity.save() request = self.client.get("/v1/data/set/1/") inv.parent_operation1 = None inv.save() @@ -529,9 +538,8 @@ def test_get_operation_tree_binary(self): operation="add", parent_operation1=self.variable, parent_operation2=self.constant, + quantity=self.quantity, ) - self.quantity.operation_tree = add - self.quantity.save() request = self.client.get("/v1/data/set/1/") add.parent_operation1 = None add.parent_operation2 = None @@ -562,9 +570,8 @@ def test_get_operation_tree_pow(self): operation="pow", parent_operation1=self.variable, parameters={"power": 2}, + quantity=self.quantity, ) - self.quantity.operation_tree = power - self.quantity.save() request = self.client.get("/v1/data/set/1/") power.parent_operation1 = None power.save() @@ -593,10 +600,8 @@ def test_get_operation_tree_nested(self): parent_operation2=self.variable, ) neg = OperationTree.objects.create( - id=4, operation="neg", parent_operation1=multiply + id=4, operation="neg", parent_operation1=multiply, quantity=self.quantity ) - self.quantity.operation_tree = neg - self.quantity.save() request = self.client.get("/v1/data/set/1/") multiply.parent_operation1 = None multiply.parent_operation2 = None @@ -635,9 +640,8 @@ def test_get_operation_tree_transpose(self): operation="transpose", parent_operation1=self.variable, parameters={"axes": (1, 0)}, + quantity=self.quantity, ) - self.quantity.operation_tree = trans - self.quantity.save() request = self.client.get("/v1/data/set/1/") trans.parent_operation1 = None trans.save() @@ -665,9 +669,8 @@ def test_get_operation_tree_tensordot(self): parent_operation1=self.variable, parent_operation2=self.constant, parameters={"a_index": 1, "b_index": 1}, + quantity=self.quantity, ) - self.quantity.operation_tree = tensor - self.quantity.save() request = self.client.get("/v1/data/set/1/") tensor.parent_operation1 = None tensor.parent_operation2 = None From 003d06c4e914a568f15decf1a2e20cd69a44c303 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 11 Apr 2025 12:15:41 -0400 Subject: [PATCH 419/675] Change direction of Session/DataSet relationship for automatic deletion --- ..._remove_session_dataset_dataset_session.py | 28 +++++++++++++++++++ sasdata/fair_database/data/models.py | 11 ++++++-- sasdata/fair_database/data/serializers.py | 4 +-- 3 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0026_remove_session_dataset_dataset_session.py diff --git a/sasdata/fair_database/data/migrations/0026_remove_session_dataset_dataset_session.py b/sasdata/fair_database/data/migrations/0026_remove_session_dataset_dataset_session.py new file mode 100644 index 000000000..211577a9c --- /dev/null +++ b/sasdata/fair_database/data/migrations/0026_remove_session_dataset_dataset_session.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.6 on 2025-04-11 16:14 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0025_remove_quantity_operation_tree_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="session", + name="dataset", + ), + migrations.AddField( + model_name="dataset", + name="session", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="datasets", + to="data.session", + ), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index ee9263001..b15d4fbca 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -50,6 +50,14 @@ class DataSet(Data): # associated files files = models.ManyToManyField(DataFile) + session = models.ForeignKey( + "Session", + on_delete=models.CASCADE, + related_name="datasets", + blank=True, + null=True, + ) + # TODO: update based on SasData class in data.py # type of dataset # dataset_type = models.JSONField() @@ -176,9 +184,6 @@ class Session(Data): # title title = models.CharField(max_length=200) - # dataset - dataset = models.ManyToManyField(DataSet) - # publishing state of the session published_state = models.OneToOneField( "PublishedState", blank=True, null=True, on_delete=models.SET_NULL diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 7c5282eee..1401f411e 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -241,12 +241,12 @@ class Meta: class SessionSerializer(serializers.ModelSerializer): - dataset = DataSetSerializer(read_only=False, many=True) + datasets = DataSetSerializer(read_only=False, many=True) published_state = PublishedStateSerializer(read_only=False) class Meta: model = models.Session - fields = "__all__" + fields = ["title", "published_state", "datasets"] def constant_or_variable(operation: str): From 5dcfd9f3815059ecf03d642f25bcfbfabb226869 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 11 Apr 2025 13:14:32 -0400 Subject: [PATCH 420/675] Change direction of Session/PublishedState relationship for automatic deletion --- ...remove_session_published_state_and_more.py | 28 +++++++++++++++++++ sasdata/fair_database/data/models.py | 10 +++---- sasdata/fair_database/data/serializers.py | 4 +++ 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0027_remove_session_published_state_and_more.py diff --git a/sasdata/fair_database/data/migrations/0027_remove_session_published_state_and_more.py b/sasdata/fair_database/data/migrations/0027_remove_session_published_state_and_more.py new file mode 100644 index 000000000..d580a58ac --- /dev/null +++ b/sasdata/fair_database/data/migrations/0027_remove_session_published_state_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.6 on 2025-04-11 17:12 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0026_remove_session_dataset_dataset_session"), + ] + + operations = [ + migrations.RemoveField( + model_name="session", + name="published_state", + ), + migrations.AddField( + model_name="publishedstate", + name="session", + field=models.OneToOneField( + default=1, + on_delete=django.db.models.deletion.CASCADE, + related_name="published_state", + to="data.session", + ), + preserve_default=False, + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index b15d4fbca..87b871096 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -184,11 +184,6 @@ class Session(Data): # title title = models.CharField(max_length=200) - # publishing state of the session - published_state = models.OneToOneField( - "PublishedState", blank=True, null=True, on_delete=models.SET_NULL - ) - class PublishedState(models.Model): """Database model for a project published state.""" @@ -198,3 +193,8 @@ class PublishedState(models.Model): # doi doi = models.URLField() + + # session + session = models.OneToOneField( + Session, on_delete=models.CASCADE, related_name="published_state" + ) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 1401f411e..a686b4f3f 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -235,6 +235,10 @@ def update(self, instance, validated_data): class PublishedStateSerializer(serializers.ModelSerializer): + session = serializers.PrimaryKeyRelatedField( + queryset=models.Session, required=False, allow_null=True + ) + class Meta: model = models.PublishedState fields = "__all__" From adca6e32704aa3c44d1839f92a01d7ba46e5c22c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 11 Apr 2025 14:38:35 -0400 Subject: [PATCH 421/675] Change direction of OperationTree parent-child links for automatic delete --- sasdata/fair_database/data/apps.py | 3 - ...perationtree_parent_operation1_and_more.py | 37 ++++++++ ...operation_operationtree_child_operation.py | 17 ++++ sasdata/fair_database/data/models.py | 16 +--- sasdata/fair_database/data/serializers.py | 55 +++++++---- sasdata/fair_database/data/signals.py | 17 ---- .../fair_database/data/test/test_dataset.py | 6 +- .../data/test/test_operation_tree.py | 95 ++++++++++++------- 8 files changed, 156 insertions(+), 90 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0028_remove_operationtree_parent_operation1_and_more.py create mode 100644 sasdata/fair_database/data/migrations/0029_rename_next_operation_operationtree_child_operation.py delete mode 100644 sasdata/fair_database/data/signals.py diff --git a/sasdata/fair_database/data/apps.py b/sasdata/fair_database/data/apps.py index 6d27e7207..b882be950 100644 --- a/sasdata/fair_database/data/apps.py +++ b/sasdata/fair_database/data/apps.py @@ -4,6 +4,3 @@ class DataConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "data" - - def ready(self): - from . import signals # noqa: F401 diff --git a/sasdata/fair_database/data/migrations/0028_remove_operationtree_parent_operation1_and_more.py b/sasdata/fair_database/data/migrations/0028_remove_operationtree_parent_operation1_and_more.py new file mode 100644 index 000000000..9d65057e5 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0028_remove_operationtree_parent_operation1_and_more.py @@ -0,0 +1,37 @@ +# Generated by Django 5.1.6 on 2025-04-11 17:19 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0027_remove_session_published_state_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="operationtree", + name="parent_operation1", + ), + migrations.RemoveField( + model_name="operationtree", + name="parent_operation2", + ), + migrations.AddField( + model_name="operationtree", + name="label", + field=models.CharField(blank=True, max_length=10, null=True), + ), + migrations.AddField( + model_name="operationtree", + name="next_operation", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="parent_operations", + to="data.operationtree", + ), + ), + ] diff --git a/sasdata/fair_database/data/migrations/0029_rename_next_operation_operationtree_child_operation.py b/sasdata/fair_database/data/migrations/0029_rename_next_operation_operationtree_child_operation.py new file mode 100644 index 000000000..fa665269d --- /dev/null +++ b/sasdata/fair_database/data/migrations/0029_rename_next_operation_operationtree_child_operation.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.6 on 2025-04-11 17:36 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0028_remove_operationtree_parent_operation1_and_more"), + ] + + operations = [ + migrations.RenameField( + model_name="operationtree", + old_name="next_operation", + new_name="child_operation", + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 87b871096..89fa4985e 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -150,22 +150,14 @@ class OperationTree(models.Model): # parameters parameters = models.JSONField(default=empty_dict) - # previous operation - parent_operation1 = models.ForeignKey( - "self", - blank=True, - null=True, - on_delete=models.PROTECT, - related_name="child_operations1", - ) + label = models.CharField(max_length=10, blank=True, null=True) - # optional second previous operation for binary operations - parent_operation2 = models.ForeignKey( + child_operation = models.ForeignKey( "self", + on_delete=models.CASCADE, + related_name="parent_operations", blank=True, null=True, - on_delete=models.PROTECT, - related_name="child_operations2", ) # related quantity, only set for base of tree diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index a686b4f3f..c486cc728 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -40,10 +40,14 @@ class OperationTreeSerializer(serializers.ModelSerializer): quantity = serializers.PrimaryKeyRelatedField( queryset=models.Quantity, required=False, allow_null=True ) + child_operation = serializers.PrimaryKeyRelatedField( + queryset=models.OperationTree, required=False, allow_null=True + ) + label = serializers.CharField(max_length=10, required=False) class Meta: model = models.OperationTree - fields = ["operation", "parameters", "quantity"] + fields = ["operation", "parameters", "quantity", "label", "child_operation"] def validate_parameters(self, value): if "a" in value: @@ -83,35 +87,50 @@ def validate(self, data): def to_representation(self, instance): data = {"operation": instance.operation, "parameters": instance.parameters} - if instance.parent_operation1 is not None: - data["parameters"]["a"] = self.to_representation(instance.parent_operation1) - if instance.parent_operation2 is not None: - data["parameters"]["b"] = self.to_representation(instance.parent_operation2) + for parent_operation in instance.parent_operations.all(): + data["parameters"][parent_operation.label] = self.to_representation( + parent_operation + ) return data def create(self, validated_data): quantity = None + label = None + child_operation = None parent_operation1 = None parent_operation2 = None - if not constant_or_variable(validated_data["operation"]): - parent1 = validated_data["parameters"].pop("a") - serializer1 = OperationTreeSerializer(data=parent1) - if serializer1.is_valid(raise_exception=True): - parent_operation1 = serializer1.save() - if binary(validated_data["operation"]): - parent2 = validated_data["parameters"].pop("b") - serializer2 = OperationTreeSerializer(data=parent2) - if serializer2.is_valid(raise_exception=True): - parent_operation2 = serializer2.save() if "quantity" in validated_data: quantity = models.Quantity.objects.get(id=validated_data.pop("quantity")) - return models.OperationTree.objects.create( + if "label" in validated_data: + label = validated_data["label"] + if "child_operation" in validated_data: + child_operation = models.OperationTree.objects.get( + id=validated_data.pop("child_operation") + ) + if not constant_or_variable(validated_data["operation"]): + parent_operation1 = validated_data["parameters"].pop("a") + parent_operation1["label"] = "a" + if binary(validated_data["operation"]): + parent_operation2 = validated_data["parameters"].pop("b") + parent_operation2["label"] = "b" + operation_tree = models.OperationTree.objects.create( operation=validated_data["operation"], parameters=validated_data["parameters"], - parent_operation1=parent_operation1, - parent_operation2=parent_operation2, + label=label, quantity=quantity, + child_operation=child_operation, ) + if parent_operation1: + parent_operation1["child_operation"] = operation_tree.id + OperationTreeSerializer.create( + OperationTreeSerializer(), validated_data=parent_operation1 + ) + if parent_operation2: + parent_operation2["child_operation"] = operation_tree.id + OperationTreeSerializer.create( + OperationTreeSerializer(), validated_data=parent_operation2 + ) + return operation_tree class QuantitySerializer(serializers.ModelSerializer): diff --git a/sasdata/fair_database/data/signals.py b/sasdata/fair_database/data/signals.py deleted file mode 100644 index c8b1613cb..000000000 --- a/sasdata/fair_database/data/signals.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.db.models.signals import post_delete -from django.dispatch import receiver - -from data.models import OperationTree - - -# TODO: is there a better way to do this than with signals - - -# propagate deletion through the operation tree -# see apps.py for signal connection -@receiver(post_delete, sender=OperationTree) -def delete_parent_operations(sender, **kwargs): - if kwargs["instance"].parent_operation1: - kwargs["instance"].parent_operation1.delete() - if kwargs["instance"].parent_operation2: - kwargs["instance"].parent_operation2.delete() diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 0b5571ae3..583ca59ad 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -346,11 +346,9 @@ def test_delete_dataset(self): label="test", dataset=self.private_dataset, ) - nested_operation = OperationTree.objects.create( - id=1, operation="zero", parameters={} - ) + neg = OperationTree.objects.create(id=1, operation="neg", quantity=quantity) OperationTree.objects.create( - id=2, operation="neg", parent_operation1=nested_operation, quantity=quantity + id=2, operation="zero", parameters={}, child_operation=neg ) request = self.auth_client1.delete("/v1/data/set/2/") self.assertEqual(request.status_code, status.HTTP_200_OK) diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index 3fe9a4dfd..faa45c895 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -79,12 +79,14 @@ def test_operation_tree_created_unary(self): new_dataset = DataSet.objects.get(id=max_id) new_quantity = new_dataset.data_contents.get(hash=0) reciprocal = new_quantity.operation_tree + variable = reciprocal.parent_operations.all().get(label="a") self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual( new_quantity.value, {"array_contents": [0, 0, 0, 0], "shape": [2, 2]} ) self.assertEqual(reciprocal.operation, "reciprocal") - self.assertEqual(reciprocal.parent_operation1.operation, "variable") + self.assertEqual(variable.operation, "variable") + self.assertEqual(len(reciprocal.parent_operations.all()), 1) self.assertEqual(reciprocal.parameters, {}) # Test creating quantity with binary operation @@ -107,8 +109,8 @@ def test_operation_tree_created_binary(self): new_dataset = DataSet.objects.get(id=max_id) new_quantity = new_dataset.data_contents.get(hash=0) add = new_quantity.operation_tree - variable = add.parent_operation1 - constant = add.parent_operation2 + variable = add.parent_operations.get(label="a") + constant = add.parent_operations.get(label="b") self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual(add.operation, "add") self.assertEqual(add.parameters, {}) @@ -116,6 +118,7 @@ def test_operation_tree_created_binary(self): self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) self.assertEqual(constant.operation, "constant") self.assertEqual(constant.parameters, {"value": 5}) + self.assertEqual(len(add.parent_operations.all()), 2) # Test creating quantity with exponent def test_operation_tree_created_pow(self): @@ -161,7 +164,7 @@ def test_operation_tree_created_transpose(self): new_dataset = DataSet.objects.get(id=max_id) new_quantity = new_dataset.data_contents.get(hash=0) transpose = new_quantity.operation_tree - variable = transpose.parent_operation1 + variable = transpose.parent_operations.get() self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual(transpose.operation, "transpose") self.assertEqual(transpose.parameters, {"axes": [1, 0]}) @@ -196,9 +199,9 @@ def test_operation_tree_created_nested(self): new_dataset = DataSet.objects.get(id=max_id) new_quantity = new_dataset.data_contents.get(hash=0) negate = new_quantity.operation_tree - multiply = negate.parent_operation1 - constant = multiply.parent_operation1 - variable = multiply.parent_operation2 + multiply = negate.parent_operations.get() + constant = multiply.parent_operations.get(label="a") + variable = multiply.parent_operations.get(label="b") self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual(negate.operation, "neg") self.assertEqual(negate.parameters, {}) @@ -503,12 +506,14 @@ def test_get_operation_tree_unary(self): inv = OperationTree.objects.create( id=3, operation="reciprocal", - parent_operation1=self.variable, quantity=self.quantity, ) + self.variable.label = "a" + self.variable.child_operation = inv + self.variable.save() request = self.client.get("/v1/data/set/1/") - inv.parent_operation1 = None - inv.save() + self.variable.child_operation = None + self.variable.save() inv.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( @@ -536,14 +541,19 @@ def test_get_operation_tree_binary(self): add = OperationTree.objects.create( id=3, operation="add", - parent_operation1=self.variable, - parent_operation2=self.constant, quantity=self.quantity, ) + self.variable.label = "a" + self.variable.child_operation = add + self.variable.save() + self.constant.label = "b" + self.constant.child_operation = add + self.constant.save() request = self.client.get("/v1/data/set/1/") - add.parent_operation1 = None - add.parent_operation2 = None - add.save() + self.variable.child_operation = None + self.constant.child_operation = None + self.variable.save() + self.constant.save() add.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( @@ -568,13 +578,15 @@ def test_get_operation_tree_pow(self): power = OperationTree.objects.create( id=3, operation="pow", - parent_operation1=self.variable, parameters={"power": 2}, quantity=self.quantity, ) + self.variable.label = "a" + self.variable.child_operation = power + self.variable.save() request = self.client.get("/v1/data/set/1/") - power.parent_operation1 = None - power.save() + self.variable.child_operation = None + self.variable.save() power.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( @@ -593,19 +605,23 @@ def test_get_operation_tree_pow(self): # Test accessing a quantity with multiple operations def test_get_operation_tree_nested(self): - multiply = OperationTree.objects.create( - id=3, - operation="mul", - parent_operation1=self.constant, - parent_operation2=self.variable, - ) neg = OperationTree.objects.create( - id=4, operation="neg", parent_operation1=multiply, quantity=self.quantity + id=4, operation="neg", quantity=self.quantity + ) + multiply = OperationTree.objects.create( + id=3, operation="mul", child_operation=neg, label="a" ) + self.constant.label = "a" + self.constant.child_operation = multiply + self.constant.save() + self.variable.label = "b" + self.variable.child_operation = multiply + self.variable.save() request = self.client.get("/v1/data/set/1/") - multiply.parent_operation1 = None - multiply.parent_operation2 = None - multiply.save() + self.constant.child_operation = None + self.variable.child_operation = None + self.constant.save() + self.variable.save() neg.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( @@ -638,13 +654,15 @@ def test_get_operation_tree_transpose(self): trans = OperationTree.objects.create( id=3, operation="transpose", - parent_operation1=self.variable, parameters={"axes": (1, 0)}, quantity=self.quantity, ) + self.variable.label = "a" + self.variable.child_operation = trans + self.variable.save() request = self.client.get("/v1/data/set/1/") - trans.parent_operation1 = None - trans.save() + self.variable.child_operation = None + self.variable.save() trans.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( @@ -666,15 +684,20 @@ def test_get_operation_tree_tensordot(self): tensor = OperationTree.objects.create( id=3, operation="tensor_product", - parent_operation1=self.variable, - parent_operation2=self.constant, parameters={"a_index": 1, "b_index": 1}, quantity=self.quantity, ) + self.variable.label = "a" + self.variable.child_operation = tensor + self.variable.save() + self.constant.label = "b" + self.constant.child_operation = tensor + self.constant.save() request = self.client.get("/v1/data/set/1/") - tensor.parent_operation1 = None - tensor.parent_operation2 = None - tensor.save() + self.variable.child_operation = None + self.constant.child_operation = None + self.variable.save() + self.constant.save() tensor.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( From 7cb864332c2e28c21145ef0eca5f050be55d19f4 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 11 Apr 2025 15:06:11 -0400 Subject: [PATCH 422/675] Serializer for session --- sasdata/fair_database/data/serializers.py | 37 ++++++++++++++++------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index c486cc728..250c962a3 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -95,14 +95,11 @@ def to_representation(self, instance): def create(self, validated_data): quantity = None - label = None child_operation = None parent_operation1 = None parent_operation2 = None if "quantity" in validated_data: quantity = models.Quantity.objects.get(id=validated_data.pop("quantity")) - if "label" in validated_data: - label = validated_data["label"] if "child_operation" in validated_data: child_operation = models.OperationTree.objects.get( id=validated_data.pop("child_operation") @@ -114,11 +111,7 @@ def create(self, validated_data): parent_operation2 = validated_data["parameters"].pop("b") parent_operation2["label"] = "b" operation_tree = models.OperationTree.objects.create( - operation=validated_data["operation"], - parameters=validated_data["parameters"], - label=label, - quantity=quantity, - child_operation=child_operation, + quantity=quantity, child_operation=child_operation, **validated_data ) if parent_operation1: parent_operation1["child_operation"] = operation_tree.id @@ -190,8 +183,10 @@ class DataSetSerializer(serializers.ModelSerializer): required=False, many=True, allow_null=True, queryset=models.DataFile ) data_contents = QuantitySerializer(many=True, read_only=False) + session = serializers.PrimaryKeyRelatedField( + queryset=models.Session, required=False, allow_null=True + ) # TODO: handle files better - # TODO: see if I can find a better way to handle the quantity part class Meta: model = models.DataSet @@ -204,8 +199,15 @@ class Meta: "is_public", "current_user", "users", + "session", ] + def to_representation(self, instance): + data = super().to_representation(instance) + if "session" in data: + data.pop("session") + return data + def validate(self, data): if ( not self.context["request"].user.is_authenticated @@ -223,11 +225,14 @@ def validate(self, data): return data def create(self, validated_data): + session = None if self.context["request"].user.is_authenticated: validated_data["current_user"] = self.context["request"].user metadata_raw = validated_data.pop("metadata") + if session in validated_data: + session = models.Session.objects.get(id=validated_data["session"]) data_contents = validated_data.pop("data_contents") - dataset = models.DataSet.objects.create(**validated_data) + dataset = models.DataSet.objects.create(session=session, **validated_data) metadata_raw["dataset"] = dataset.id MetaDataSerializer.create(MetaDataSerializer(), validated_data=metadata_raw) for d in data_contents: @@ -265,12 +270,22 @@ class Meta: class SessionSerializer(serializers.ModelSerializer): datasets = DataSetSerializer(read_only=False, many=True) - published_state = PublishedStateSerializer(read_only=False) + published_state = PublishedStateSerializer(read_only=False, required=False) class Meta: model = models.Session fields = ["title", "published_state", "datasets"] + def create(self, validated_data): + if self.context["request"].user.is_authenticated: + validated_data["current_user"] = self.context["request"].user + datasets = validated_data.pop("datasets") + session = models.Session.objects.create(**validated_data) + for dataset in datasets: + dataset["session"] = session.id + DataSetSerializer.create(DataSetSerializer(), validated_data=dataset) + return session + def constant_or_variable(operation: str): return operation in ["zero", "one", "constant", "variable"] From ccd6c650e64d32549f1872ed5628599c090a6e89 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Fri, 11 Apr 2025 15:32:04 -0400 Subject: [PATCH 423/675] Get method for Sessions --- sasdata/fair_database/data/views.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index a1ca3924a..66ea169a9 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -356,7 +356,15 @@ class SessionView(APIView): # View a list of accessible sessions def get(self, request, version=None): - pass + session_list = {"session_ids": {}} + sessions = Session.objects.all() + if "username" in request.GET: + user = get_object_or_404(User, username=request.GET["username"]) + sessions = Session.objects.filter(current_user=user) + for session in sessions: + if permissions.check_permissions(request, session): + session_list["session_ids"][session.id] = session.title + return Response(data=session_list) # Create a session # TODO: revisit response data From 3ca2f875c596be493975de1c5b17e8320ae05172 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 14 Apr 2025 09:07:17 -0400 Subject: [PATCH 424/675] Return is_public in access management GET requests --- sasdata/fair_database/data/test/test_datafile.py | 14 ++++++++++++-- sasdata/fair_database/data/test/test_dataset.py | 11 +++++++++-- sasdata/fair_database/data/views.py | 5 +++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/data/test/test_datafile.py b/sasdata/fair_database/data/test/test_datafile.py index 75690c6dd..53b401c53 100644 --- a/sasdata/fair_database/data/test/test_datafile.py +++ b/sasdata/fair_database/data/test/test_datafile.py @@ -295,14 +295,24 @@ def setUpTestData(cls): # test viewing no one with access def test_view_no_access(self): request = self.client_owner.get("/v1/data/file/1/users/") - data = {"file": 1, "file_name": "cyl_400_40.txt", "users": []} + data = { + "file": 1, + "file_name": "cyl_400_40.txt", + "is_public": False, + "users": [], + } self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, data) # test viewing list of users with access def test_view_access(self): request = self.client_owner.get("/v1/data/file/2/users/") - data = {"file": 2, "file_name": "cyl_400_20.txt", "users": ["testUser2"]} + data = { + "file": 2, + "file_name": "cyl_400_20.txt", + "is_public": False, + "users": ["testUser2"], + } self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, data) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 583ca59ad..c0c145706 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -412,7 +412,8 @@ def test_list_access_private(self): request1 = self.client_owner.get("/v1/data/set/1/users/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual( - request1.data, {"data_id": 1, "name": "Dataset 1", "users": []} + request1.data, + {"data_id": 1, "name": "Dataset 1", "is_public": False, "users": []}, ) # Test listing users with access @@ -420,7 +421,13 @@ def test_list_access_shared(self): request1 = self.client_owner.get("/v1/data/set/2/users/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual( - request1.data, {"data_id": 2, "name": "Dataset 2", "users": ["testUser2"]} + request1.data, + { + "data_id": 2, + "name": "Dataset 2", + "is_public": False, + "users": ["testUser2"], + }, ) # Test only owner can view access diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 66ea169a9..41a09cc07 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -180,6 +180,7 @@ def get(self, request, data_id, version=None): response_data = { "file": db.pk, "file_name": db.file_name, + "is_public": db.is_public, "users": [user.username for user in db.users.all()], } return Response(response_data) @@ -318,6 +319,7 @@ def get(self, request, data_id, version=None): response_data = { "data_id": db.id, "name": db.name, + "is_public": db.is_public, "users": [user.username for user in db.users.all()], } return Response(response_data) @@ -447,6 +449,8 @@ def get(self, request, data_id, version=None): return HttpResponseForbidden("Must be the session owner to view access") response_data = { "session_id": db.id, + "title": db.title, + "is_public": db.is_public, "users": [user.username for user in db.users.all()], } return Response(response_data) @@ -470,6 +474,7 @@ def put(self, request, data_id, version=None): response_data = { "username": user.username, "session_id": db.id, + "title": db.title, "access": serializer.data["access"], } return Response(response_data) From 762d42dc9238669b61494d2f0b19b8546704b39c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 14 Apr 2025 10:13:52 -0400 Subject: [PATCH 425/675] Remove dataset id from MetaData instance serialization --- sasdata/fair_database/data/serializers.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 250c962a3..c05b69fa7 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -31,6 +31,12 @@ class Meta: model = models.MetaData fields = "__all__" + def to_representation(self, instance): + data = super().to_representation(instance) + if "dataset" in data: + data.pop("dataset") + return data + def create(self, validated_data): dataset = models.DataSet.objects.get(id=validated_data.pop("dataset")) return models.MetaData.objects.create(dataset=dataset, **validated_data) From 1586bcb5a3b3d39b57ec3c1f2d94014b23b3eb30 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 14 Apr 2025 10:14:42 -0400 Subject: [PATCH 426/675] Test GET dataset with metadata --- .../fair_database/data/test/test_dataset.py | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index c0c145706..b74c07496 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -3,7 +3,7 @@ from rest_framework.test import APIClient, APITestCase from rest_framework import status -from data.models import DataSet, OperationTree, Quantity +from data.models import DataSet, MetaData, OperationTree, Quantity class TestDataSet(APITestCase): @@ -222,6 +222,16 @@ def setUpTestData(cls): cls.unowned_dataset = DataSet.objects.create( id=3, is_public=True, name="Dataset 3" ) + cls.metadata = MetaData.objects.create( + id=1, + title="Metadata", + run=0, + definition="test", + instrument="none", + process="none", + sample="none", + dataset=cls.public_dataset, + ) cls.private_dataset.users.add(cls.user3) cls.auth_client1 = APIClient() cls.auth_client2 = APIClient() @@ -266,7 +276,15 @@ def test_load_public_dataset(self): "is_public": True, "name": "Dataset 1", "files": [], - "metadata": None, + "metadata": { + "id": 1, + "title": "Metadata", + "run": 0, + "definition": "test", + "instrument": "none", + "process": "none", + "sample": "none", + }, "data_contents": [], }, ) From f166df1a9d91ec6e45a353ad462f87c5859eb206 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 14 Apr 2025 10:51:58 -0400 Subject: [PATCH 427/675] Add comments to serializer classes --- sasdata/fair_database/data/serializers.py | 40 ++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index c05b69fa7..443a8337d 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -7,10 +7,14 @@ class DataFileSerializer(serializers.ModelSerializer): + """Serialization, deserialization, and validation for the DataFile model.""" + class Meta: model = models.DataFile fields = "__all__" + # TODO: check partial updates + # Check that private data has an owner def validate(self, data): if not self.context["is_public"] and not data["current_user"]: raise serializers.ValidationError("private data must have an owner") @@ -18,11 +22,20 @@ def validate(self, data): class AccessManagementSerializer(serializers.Serializer): + """ + Serialization, deserialization, and validation for granting and revoking + access to instances of any exposed model. + """ + + # The username of a user username = serializers.CharField(max_length=200, required=False) + # Whether that user has access access = serializers.BooleanField() class MetaDataSerializer(serializers.ModelSerializer): + """Serialization, deserialization, and validation for the MetaData model.""" + dataset = serializers.PrimaryKeyRelatedField( queryset=models.DataSet, required=False, allow_null=True ) @@ -31,18 +44,22 @@ class Meta: model = models.MetaData fields = "__all__" + # Serialize an entry in MetaData def to_representation(self, instance): data = super().to_representation(instance) if "dataset" in data: data.pop("dataset") return data + # Create an entry in MetaData def create(self, validated_data): dataset = models.DataSet.objects.get(id=validated_data.pop("dataset")) return models.MetaData.objects.create(dataset=dataset, **validated_data) class OperationTreeSerializer(serializers.ModelSerializer): + """Serialization, deserialization, and validation for the OperationTree model.""" + quantity = serializers.PrimaryKeyRelatedField( queryset=models.Quantity, required=False, allow_null=True ) @@ -55,6 +72,7 @@ class Meta: model = models.OperationTree fields = ["operation", "parameters", "quantity", "label", "child_operation"] + # Validate parent operations def validate_parameters(self, value): if "a" in value: serializer = OperationTreeSerializer(data=value["a"]) @@ -64,6 +82,7 @@ def validate_parameters(self, value): serializer.is_valid(raise_exception=True) return value + # Check that the operation has the correct parameters def validate(self, data): expected_parameters = { "zero": [], @@ -91,6 +110,7 @@ def validate(self, data): return data + # Serialize an OperationTree instance def to_representation(self, instance): data = {"operation": instance.operation, "parameters": instance.parameters} for parent_operation in instance.parent_operations.all(): @@ -99,6 +119,7 @@ def to_representation(self, instance): ) return data + # Create an OperationTree instance def create(self, validated_data): quantity = None child_operation = None @@ -133,12 +154,13 @@ def create(self, validated_data): class QuantitySerializer(serializers.ModelSerializer): + """Serialization, deserialization, and validation for the Quantity model.""" + operation_tree = OperationTreeSerializer(read_only=False, required=False) label = serializers.CharField(max_length=20) dataset = serializers.PrimaryKeyRelatedField( queryset=models.DataSet, required=False, allow_null=True ) - # history = serializers.JSONField(required=False) # TODO: is this required? class Meta: model = models.Quantity @@ -154,6 +176,7 @@ class Meta: ] # TODO: should variable-only history be assumed to refer to the same Quantity and ignored? + # Extract operation tree from history def to_internal_value(self, data): if "history" in data and "operation_tree" in data["history"]: operations = data["history"]["operation_tree"] @@ -163,12 +186,14 @@ def to_internal_value(self, data): return super().to_internal_value(data_copy) return super().to_internal_value(data) + # Serialize a Quantity instance def to_representation(self, instance): data = super().to_representation(instance) if "dataset" in data: data.pop("dataset") return data + # Create a Quantity instance def create(self, validated_data): dataset = models.DataSet.objects.get(id=validated_data.pop("dataset")) if "operation_tree" in validated_data: @@ -184,6 +209,8 @@ def create(self, validated_data): class DataSetSerializer(serializers.ModelSerializer): + """Serialization, deserialization, and validation for the DataSet model.""" + metadata = MetaDataSerializer(read_only=False) files = serializers.PrimaryKeyRelatedField( required=False, many=True, allow_null=True, queryset=models.DataFile @@ -208,12 +235,14 @@ class Meta: "session", ] + # Serialize a DataSet instance def to_representation(self, instance): data = super().to_representation(instance) if "session" in data: data.pop("session") return data + # Check that private data has an owner def validate(self, data): if ( not self.context["request"].user.is_authenticated @@ -230,6 +259,7 @@ def validate(self, data): raise serializers.ValidationError("private data must have an owner") return data + # Create a DataSet instance def create(self, validated_data): session = None if self.context["request"].user.is_authenticated: @@ -248,6 +278,7 @@ def create(self, validated_data): # TODO: account for updating other attributes # TODO: account for metadata potentially being null + # Update a DataSet instance def update(self, instance, validated_data): if "metadata" in validated_data: metadata_raw = validated_data.pop("metadata") @@ -265,6 +296,8 @@ def update(self, instance, validated_data): class PublishedStateSerializer(serializers.ModelSerializer): + """Serialization, deserialization, and validation for the PublishedState model.""" + session = serializers.PrimaryKeyRelatedField( queryset=models.Session, required=False, allow_null=True ) @@ -275,6 +308,8 @@ class Meta: class SessionSerializer(serializers.ModelSerializer): + """Serialization, deserialization, and validation for the Session model.""" + datasets = DataSetSerializer(read_only=False, many=True) published_state = PublishedStateSerializer(read_only=False, required=False) @@ -282,6 +317,7 @@ class Meta: model = models.Session fields = ["title", "published_state", "datasets"] + # Create a Session instance def create(self, validated_data): if self.context["request"].user.is_authenticated: validated_data["current_user"] = self.context["request"].user @@ -293,9 +329,11 @@ def create(self, validated_data): return session +# Determine if an operation does not have parent operations def constant_or_variable(operation: str): return operation in ["zero", "one", "constant", "variable"] +# Determine if an operation has two parent operations def binary(operation: str): return operation in ["add", "sub", "mul", "div", "dot", "matmul", "tensor_product"] From cb9ab836fbf43454f0775beef2c8ed51ef31f0c8 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 14 Apr 2025 11:52:02 -0400 Subject: [PATCH 428/675] Fix serialization of DataSet with files --- sasdata/fair_database/data/serializers.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 443a8337d..d6599ec1b 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -213,7 +213,7 @@ class DataSetSerializer(serializers.ModelSerializer): metadata = MetaDataSerializer(read_only=False) files = serializers.PrimaryKeyRelatedField( - required=False, many=True, allow_null=True, queryset=models.DataFile + required=False, many=True, allow_null=True, queryset=models.DataFile.objects ) data_contents = QuantitySerializer(many=True, read_only=False) session = serializers.PrimaryKeyRelatedField( @@ -262,13 +262,17 @@ def validate(self, data): # Create a DataSet instance def create(self, validated_data): session = None + files = [] if self.context["request"].user.is_authenticated: validated_data["current_user"] = self.context["request"].user metadata_raw = validated_data.pop("metadata") - if session in validated_data: + if "session" in validated_data: session = models.Session.objects.get(id=validated_data["session"]) data_contents = validated_data.pop("data_contents") + if "files" in validated_data: + files = validated_data.pop("files") dataset = models.DataSet.objects.create(session=session, **validated_data) + dataset.files.set(files) metadata_raw["dataset"] = dataset.id MetaDataSerializer.create(MetaDataSerializer(), validated_data=metadata_raw) for d in data_contents: From 2003e0eb0ace3d5a9c739785de041f748e212db7 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 14 Apr 2025 14:04:06 -0400 Subject: [PATCH 429/675] Check access to files in a dataset --- sasdata/fair_database/data/serializers.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index d6599ec1b..ba828ab3e 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -1,6 +1,7 @@ from rest_framework import serializers from data import models +from fair_database import permissions # TODO: more custom validation, particularly for specific nested dictionary structures @@ -242,6 +243,21 @@ def to_representation(self, instance): data.pop("session") return data + # Check that files exist and user has access to them + def validate_files(self, value): + for file in value: + # try: + # db = models.DataFile.objects.get(id=file) + # except models.DataFile.DoesNotExist: + # raise serializers.ValidationError("File " + str(file) + " does not exist") + if not file.is_public or permissions.has_access( + self.context["request"], file + ): + raise serializers.ValidationError( + "You do not have access to file " + str(file.id) + ) + return value + # Check that private data has an owner def validate(self, data): if ( From 299d236a3a0232d41cfa2f158831ee80019155d4 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 14 Apr 2025 14:08:43 -0400 Subject: [PATCH 430/675] Test DataSet creation with files --- .../fair_database/data/test/test_dataset.py | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index b74c07496..6fb03253a 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -1,9 +1,19 @@ +import os +import shutil + +from django.conf import settings from django.contrib.auth.models import User from django.db.models import Max from rest_framework.test import APIClient, APITestCase from rest_framework import status -from data.models import DataSet, MetaData, OperationTree, Quantity +from data.models import DataFile, DataSet, MetaData, OperationTree, Quantity + + +def find(filename): + return os.path.join( + os.path.dirname(__file__), "../../../example_data/1d_data", filename + ) class TestDataSet(APITestCase): @@ -156,6 +166,60 @@ def test_dataset_created_unauthenticated(self): new_dataset.delete() new_metadata.delete() + # Test creating a database with associated files + def test_dataset_created_with_files(self): + file = DataFile.objects.create( + id=1, file_name="cyl_testdata.txt", is_public=True + ) + file.file.save("cyl_testdata.txt", open(find("cyl_testdata.txt"))) + dataset = { + "name": "Dataset with file", + "metadata": self.empty_metadata, + "is_public": True, + "data_contents": self.empty_data, + "files": [1], + } + request = self.client.post("/v1/data/set/", data=dataset, format="json") + max_id = DataSet.objects.aggregate(Max("id"))["id__max"] + new_dataset = DataSet.objects.get(id=max_id) + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual( + request.data, + {"dataset_id": max_id, "name": "Dataset with file", "is_public": True}, + ) + self.assertTrue(file in new_dataset.files.all()) + new_dataset.delete() + file.delete() + + # Test that a dataset cannot be associated with inaccessible files + def test_no_dataset_with_private_files(self): + file = DataFile.objects.create( + id=1, file_name="cyl_testdata.txt", is_public=False, current_user=self.user2 + ) + file.file.save("cyl_testdata.txt", open(find("cyl_testdata.txt"))) + dataset = { + "name": "Dataset with file", + "metadata": self.empty_metadata, + "is_public": True, + "data_contents": self.empty_data, + "files": [1], + } + request = self.client.post("/v1/data/set/", data=dataset, format="json") + file.delete() + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + + # Test that a dataset cannot be associated with nonexistent files + def test_no_dataset_with_nonexistent_files(self): + dataset = { + "name": "Dataset with file", + "metadata": self.empty_metadata, + "is_public": True, + "data_contents": self.empty_data, + "files": [2], + } + request = self.client.post("/v1/data/set/", data=dataset, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + # Test that a private dataset cannot be created without an owner def test_no_private_unowned_dataset(self): dataset = { @@ -194,6 +258,7 @@ def tearDownClass(cls): cls.user1.delete() cls.user2.delete() cls.user3.delete() + shutil.rmtree(settings.MEDIA_ROOT) class TestSingleDataSet(APITestCase): From ceac365be49fe653bdc40160aae908bcebad9ac0 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 14 Apr 2025 14:51:43 -0400 Subject: [PATCH 431/675] Filter files in dataset by access --- sasdata/fair_database/data/serializers.py | 14 ++++++++++---- sasdata/fair_database/data/views.py | 3 ++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index ba828ab3e..6b2209cb3 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -241,15 +241,21 @@ def to_representation(self, instance): data = super().to_representation(instance) if "session" in data: data.pop("session") + if "request" in self.context: + files = [ + file.id + for file in instance.files.all() + if ( + file.is_public + or permissions.has_access(self.context["request"], file) + ) + ] + data["files"] = files return data # Check that files exist and user has access to them def validate_files(self, value): for file in value: - # try: - # db = models.DataFile.objects.get(id=file) - # except models.DataFile.DoesNotExist: - # raise serializers.ValidationError("File " + str(file) + " does not exist") if not file.is_public or permissions.has_access( self.context["request"], file ): diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 41a09cc07..0a25cf393 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -231,6 +231,7 @@ def get(self, request, version=None): data_list["dataset_ids"][dataset.id] = dataset.name return Response(data=data_list) + # TODO: enable uploading files as part of dataset creation, not just associating dataset with existing files # create a dataset def post(self, request, version=None): # TODO: JSON deserialization probably @@ -266,7 +267,7 @@ def get(self, request, data_id, version=None): return HttpResponseForbidden( "You do not have permission to view this dataset." ) - serializer = DataSetSerializer(db) + serializer = DataSetSerializer(db, context={"request": request}) return Response(serializer.data) # edit a specific dataset From 1943f931f0afb6537d16d7cee082d97af62b46bb Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 14 Apr 2025 14:52:33 -0400 Subject: [PATCH 432/675] Test loading dataset with files --- .../fair_database/data/test/test_dataset.py | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 6fb03253a..7fe2e991b 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -297,7 +297,12 @@ def setUpTestData(cls): sample="none", dataset=cls.public_dataset, ) + cls.file = DataFile.objects.create( + id=1, file_name="cyl_testdata.txt", is_public=False, current_user=cls.user1 + ) + cls.file.file.save("cyl_testdata.txt", open(find("cyl_testdata.txt"))) cls.private_dataset.users.add(cls.user3) + cls.public_dataset.files.add(cls.file) cls.auth_client1 = APIClient() cls.auth_client2 = APIClient() cls.auth_client3 = APIClient() @@ -312,7 +317,7 @@ def test_load_private_dataset(self): request2 = self.auth_client3.get("/v1/data/set/2/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) - self.assertDictEqual( + self.assertEqual( request1.data, { "id": 2, @@ -330,8 +335,10 @@ def test_load_private_dataset(self): def test_load_public_dataset(self): request1 = self.client.get("/v1/data/set/1/") request2 = self.auth_client2.get("/v1/data/set/1/") + request3 = self.auth_client1.get("/v1/data/set/1/") self.assertEqual(request1.status_code, status.HTTP_200_OK) self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertEqual(request3.status_code, status.HTTP_200_OK) self.assertDictEqual( request1.data, { @@ -353,6 +360,28 @@ def test_load_public_dataset(self): "data_contents": [], }, ) + self.assertEqual(request1.data, request2.data) + self.assertEqual( + request3.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Dataset 1", + "files": [1], + "metadata": { + "id": 1, + "title": "Metadata", + "run": 0, + "definition": "test", + "instrument": "none", + "process": "none", + "sample": "none", + }, + "data_contents": [], + }, + ) # Test successfully accessing an unowned public dataset def test_load_unowned_dataset(self): @@ -469,6 +498,8 @@ def tearDownClass(cls): cls.user1.delete() cls.user2.delete() cls.user3.delete() + cls.file.delete() + shutil.rmtree(settings.MEDIA_ROOT) class TestDataSetAccessManagement(APITestCase): From 7e866780b993696c988aa6ea0b26dbb810c7c488 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 14 Apr 2025 15:32:34 -0400 Subject: [PATCH 433/675] Start session tests --- .../fair_database/data/test/test_session.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 sasdata/fair_database/data/test/test_session.py diff --git a/sasdata/fair_database/data/test/test_session.py b/sasdata/fair_database/data/test/test_session.py new file mode 100644 index 000000000..bd3fa4acb --- /dev/null +++ b/sasdata/fair_database/data/test/test_session.py @@ -0,0 +1,65 @@ +from rest_framework.test import APITestCase + + +class TestSession(APITestCase): + @classmethod + def setUpTestData(cls): + pass + + # Test listing sessions + # Can see own private + # Can't see other private + # Can see private with access + # Can see public (authenticated and unauthenticated) + + # Test listing sessions by username + + # Test creating a session - public, private, unauthenticated + # Datasets have same access as session + + # Test post fails with dataset validation issue + + @classmethod + def tearDownClass(cls): + pass + + +class TestSingleSession(APITestCase): + @classmethod + def setUpTestData(cls): + pass + + # Test accessing session + # public, private, with/without access + + # Test updating session + # public, private, owner, not owner + + # Test deleting session + # Test delete cascades + + @classmethod + def tearDownClass(cls): + pass + + +class TestSessionAccessManagement(APITestCase): + @classmethod + def setUpTestData(cls): + pass + + # Test listing access + + # Test listing access not as the owner + + # Test granting access + + # Test revoking access + + # Test can't revoke own access + + # Test only owner can change access + + @classmethod + def tearDownClass(cls): + pass From 36a94ba038461ea5e5cde1d6c46741810513c473 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 14 Apr 2025 15:43:05 -0400 Subject: [PATCH 434/675] Copy session is_public value to composing datasets --- sasdata/fair_database/data/serializers.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 6b2209cb3..1a9224839 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -343,6 +343,14 @@ class Meta: model = models.Session fields = ["title", "published_state", "datasets"] + def to_internal_value(self, data): + data_copy = data.copy() + if "is_public" in data: + if "datasets" in data: + for dataset in data_copy["datasets"]: + dataset["is_public"] = data["is_public"] + return super().to_internal_value(data_copy) + # Create a Session instance def create(self, validated_data): if self.context["request"].user.is_authenticated: From f90bafa3c1b492d89e5e5a7e7d4701cc882641e0 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 14 Apr 2025 16:04:31 -0400 Subject: [PATCH 435/675] Test listing sessions --- .../fair_database/data/test/test_session.py | 103 ++++++++++++++++-- 1 file changed, 94 insertions(+), 9 deletions(-) diff --git a/sasdata/fair_database/data/test/test_session.py b/sasdata/fair_database/data/test/test_session.py index bd3fa4acb..656aa4b3a 100644 --- a/sasdata/fair_database/data/test/test_session.py +++ b/sasdata/fair_database/data/test/test_session.py @@ -1,18 +1,99 @@ -from rest_framework.test import APITestCase +from django.contrib.auth.models import User +from rest_framework.test import APIClient, APITestCase +from rest_framework import status + + +from data.models import Session class TestSession(APITestCase): @classmethod def setUpTestData(cls): - pass + cls.user1 = User.objects.create_user( + id=1, username="testUser1", password="secret" + ) + cls.user2 = User.objects.create_user( + id=2, username="testUser2", password="secret" + ) + cls.public_session = Session.objects.create( + id=1, current_user=cls.user1, title="Public Session", is_public=True + ) + cls.private_session = Session.objects.create( + id=2, current_user=cls.user1, title="Private Session", is_public=False + ) + cls.unowned_session = Session.objects.create( + id=3, title="Unowned Session", is_public=True + ) + cls.auth_client1 = APIClient() + cls.auth_client2 = APIClient() + cls.auth_client1.force_authenticate(cls.user1) + cls.auth_client2.force_authenticate(cls.user2) # Test listing sessions - # Can see own private - # Can't see other private - # Can see private with access - # Can see public (authenticated and unauthenticated) - - # Test listing sessions by username + def test_list_private(self): + request = self.auth_client1.get("/v1/data/session/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "session_ids": { + 1: "Public Session", + 2: "Private Session", + 3: "Unowned Session", + } + }, + ) + + # Test listing public sessions + def test_list_public(self): + request = self.auth_client2.get("/v1/data/session/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, {"session_ids": {1: "Public Session", 3: "Unowned Session"}} + ) + + # Test listing sessions while unauthenticated + def test_list_unauthenticated(self): + request = self.client.get("/v1/data/session/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, {"session_ids": {1: "Public Session", 3: "Unowned Session"}} + ) + + # Test listing a session with access granted + def test_list_granted_access(self): + self.private_session.users.add(self.user2) + request = self.auth_client2.get("/v1/data/session/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "session_ids": { + 1: "Public Session", + 2: "Private Session", + 3: "Unowned Session", + } + }, + ) + self.private_session.users.remove(self.user2) + + # Test listing by username + def test_list_username(self): + request = self.auth_client1.get( + "/v1/data/session/", data={"username": "testUser1"} + ) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, {"session_ids": {1: "Public Session", 2: "Private Session"}} + ) + + # Test listing by another user's username + def test_list_other_username(self): + request = self.auth_client2.get( + "/v1/data/session/", data={"username": "testUser1"} + ) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(request.data, {"session_ids": {1: "Public Session"}}) # Test creating a session - public, private, unauthenticated # Datasets have same access as session @@ -21,7 +102,11 @@ def setUpTestData(cls): @classmethod def tearDownClass(cls): - pass + cls.public_session.delete() + cls.private_session.delete() + cls.unowned_session.delete() + cls.user1.delete() + cls.user2.delete() class TestSingleSession(APITestCase): From 816ef958c6df853ba04e7c8f250abcc3ccfa61c2 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 15 Apr 2025 11:48:01 -0400 Subject: [PATCH 436/675] Fix bugs in DataSet creation nested in a Session --- sasdata/fair_database/data/serializers.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 1a9224839..99db82245 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -289,7 +289,7 @@ def create(self, validated_data): validated_data["current_user"] = self.context["request"].user metadata_raw = validated_data.pop("metadata") if "session" in validated_data: - session = models.Session.objects.get(id=validated_data["session"]) + session = models.Session.objects.get(id=validated_data.pop("session")) data_contents = validated_data.pop("data_contents") if "files" in validated_data: files = validated_data.pop("files") @@ -341,7 +341,14 @@ class SessionSerializer(serializers.ModelSerializer): class Meta: model = models.Session - fields = ["title", "published_state", "datasets"] + fields = [ + "title", + "published_state", + "datasets", + "current_user", + "is_public", + "users", + ] def to_internal_value(self, data): data_copy = data.copy() @@ -359,7 +366,9 @@ def create(self, validated_data): session = models.Session.objects.create(**validated_data) for dataset in datasets: dataset["session"] = session.id - DataSetSerializer.create(DataSetSerializer(), validated_data=dataset) + DataSetSerializer.create( + DataSetSerializer(context=self.context), validated_data=dataset + ) return session From 051cb0f907b00d1d4eb26021729070ef1284fa21 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 15 Apr 2025 11:59:28 -0400 Subject: [PATCH 437/675] Include username and authentication status in all post responses --- .../fair_database/data/test/test_dataset.py | 32 ++++++++++++++++--- sasdata/fair_database/data/views.py | 16 ++++++++-- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 7fe2e991b..494de3acf 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -136,7 +136,13 @@ def test_dataset_created(self): self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual( request.data, - {"dataset_id": max_id, "name": "New Dataset", "is_public": False}, + { + "dataset_id": max_id, + "name": "New Dataset", + "authenticated": True, + "current_user": "testUser1", + "is_public": False, + }, ) self.assertEqual(new_dataset.name, "New Dataset") self.assertEqual(new_metadata.title, "New Metadata") @@ -159,7 +165,13 @@ def test_dataset_created_unauthenticated(self): self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual( request.data, - {"dataset_id": max_id, "name": "New Dataset", "is_public": True}, + { + "dataset_id": max_id, + "name": "New Dataset", + "authenticated": False, + "current_user": "", + "is_public": True, + }, ) self.assertEqual(new_dataset.name, "New Dataset") self.assertIsNone(new_dataset.current_user) @@ -185,7 +197,13 @@ def test_dataset_created_with_files(self): self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual( request.data, - {"dataset_id": max_id, "name": "Dataset with file", "is_public": True}, + { + "dataset_id": max_id, + "name": "Dataset with file", + "authenticated": False, + "current_user": "", + "is_public": True, + }, ) self.assertTrue(file in new_dataset.files.all()) new_dataset.delete() @@ -246,7 +264,13 @@ def test_no_data_overwrite(self): self.assertEqual(DataSet.objects.get(id=2).name, "Dataset 2") self.assertEqual( request.data, - {"dataset_id": max_id, "name": "Overwrite Dataset", "is_public": True}, + { + "dataset_id": max_id, + "name": "Overwrite Dataset", + "authenticated": True, + "current_user": "testUser2", + "is_public": True, + }, ) DataSet.objects.get(id=max_id).delete() diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 0a25cf393..7711d2d57 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -240,7 +240,13 @@ def post(self, request, version=None): if serializer.is_valid(raise_exception=True): serializer.save() db = serializer.instance - response = {"dataset_id": db.id, "name": db.name, "is_public": db.is_public} + response = { + "dataset_id": db.id, + "name": db.name, + "authenticated": request.user.is_authenticated, + "current_user": request.user.username, + "is_public": db.is_public, + } return Response(data=response, status=status.HTTP_201_CREATED) # create a dataset @@ -376,7 +382,13 @@ def post(self, request, version=None): if serializer.is_valid(raise_exception=True): serializer.save() db = serializer.instance - response = {"session_id": db.id, "is_public": db.is_public} + response = { + "session_id": db.id, + "title": db.title, + "authenticated": request.user.is_authenticated, + "current_user": request.user.username, + "is_public": db.is_public, + } return Response(data=response, status=status.HTTP_201_CREATED) # Create a session From 67a58d63fb3d187cc0b82bc64e2849ae8b342928 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 15 Apr 2025 12:10:02 -0400 Subject: [PATCH 438/675] Disallow creating private sessions without an owner --- sasdata/fair_database/data/serializers.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 99db82245..b46e3dc81 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -350,6 +350,26 @@ class Meta: "users", ] + def validate(self, data): + if ( + not self.context["request"].user.is_authenticated + and "is_public" in data + and not data["is_public"] + ): + raise serializers.ValidationError("private sessions must have an owner") + if "current_user" in data and data["current_user"] is None: + if "is_public" in data: + if not "is_public": + raise serializers.ValidationError( + "private sessions must have an owner" + ) + else: + if not self.instance.is_public: + raise serializers.ValidationError( + "private sessions must have an owner" + ) + return data + def to_internal_value(self, data): data_copy = data.copy() if "is_public" in data: From a0150177046b914bd5d10f8f0dbd58d20579a2e0 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 15 Apr 2025 12:16:58 -0400 Subject: [PATCH 439/675] Test Session post method --- .../fair_database/data/test/test_session.py | 162 +++++++++++++++++- 1 file changed, 159 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/data/test/test_session.py b/sasdata/fair_database/data/test/test_session.py index 656aa4b3a..f71ba7913 100644 --- a/sasdata/fair_database/data/test/test_session.py +++ b/sasdata/fair_database/data/test/test_session.py @@ -1,9 +1,10 @@ from django.contrib.auth.models import User +from django.db.models import Max from rest_framework.test import APIClient, APITestCase from rest_framework import status -from data.models import Session +from data.models import DataSet, Session class TestSession(APITestCase): @@ -95,10 +96,165 @@ def test_list_other_username(self): self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual(request.data, {"session_ids": {1: "Public Session"}}) - # Test creating a session - public, private, unauthenticated - # Datasets have same access as session + # Test creating a public session + def test_session_created(self): + session = { + "title": "New session", + "datasets": [ + { + "name": "New dataset", + "metadata": { + "title": "New metadata", + "run": 0, + "description": "test", + "instrument": {}, + "process": {}, + "sample": {}, + }, + "data_contents": [], + } + ], + "is_public": True, + } + request = self.auth_client1.post( + "/v1/data/session/", data=session, format="json" + ) + max_id = Session.objects.aggregate(Max("id"))["id__max"] + new_session = Session.objects.get(id=max_id) + new_dataset = new_session.datasets.get() + new_metadata = new_dataset.metadata + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual( + request.data, + { + "session_id": max_id, + "title": "New session", + "authenticated": True, + "current_user": "testUser1", + "is_public": True, + }, + ) + self.assertEqual(new_session.title, "New session") + self.assertEqual(new_dataset.name, "New dataset") + self.assertEqual(new_metadata.title, "New metadata") + self.assertEqual(new_session.current_user, self.user1) + self.assertEqual(new_dataset.current_user, self.user1) + self.assertTrue(all([new_session.is_public, new_dataset.is_public])) + new_session.delete() + + # Test creating a private session + def test_session_created_private(self): + session = { + "title": "New session", + "datasets": [ + { + "name": "New dataset", + "metadata": { + "title": "New metadata", + "run": 0, + "description": "test", + "instrument": {}, + "process": {}, + "sample": {}, + }, + "data_contents": [], + } + ], + "is_public": False, + } + request = self.auth_client1.post( + "/v1/data/session/", data=session, format="json" + ) + max_id = Session.objects.aggregate(Max("id"))["id__max"] + new_session = Session.objects.get(id=max_id) + new_dataset = new_session.datasets.get() + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual( + request.data, + { + "session_id": max_id, + "title": "New session", + "authenticated": True, + "current_user": "testUser1", + "is_public": False, + }, + ) + self.assertEqual(new_session.current_user, self.user1) + self.assertEqual(new_dataset.current_user, self.user1) + self.assertTrue(all([(not new_session.is_public), (not new_dataset.is_public)])) + new_session.delete() + + # Test creating a session while unauthenticated + def test_session_created_unauthenticated(self): + session = { + "title": "New session", + "datasets": [ + { + "name": "New dataset", + "metadata": { + "title": "New metadata", + "run": 0, + "description": "test", + "instrument": {}, + "process": {}, + "sample": {}, + }, + "data_contents": [], + } + ], + "is_public": True, + } + request = self.client.post("/v1/data/session/", data=session, format="json") + max_id = Session.objects.aggregate(Max("id"))["id__max"] + new_session = Session.objects.get(id=max_id) + new_dataset = new_session.datasets.get() + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual( + request.data, + { + "session_id": max_id, + "title": "New session", + "authenticated": False, + "current_user": "", + "is_public": True, + }, + ) + self.assertIsNone(new_session.current_user) + self.assertIsNone(new_dataset.current_user) + self.assertTrue(all([new_session.is_public, new_dataset.is_public])) + new_session.delete() + + # Test that a private session must have an owner + def test_no_private_unowned_session(self): + session = {"title": "New session", "datasets": [], "is_public": False} + request = self.client.post("/v1/data/session/", data=session, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) # Test post fails with dataset validation issue + def test_no_session_invalid_dataset(self): + session = { + "title": "New session", + "datasets": [ + { + "metadata": { + "title": "New metadata", + "run": 0, + "description": "test", + "instrument": {}, + "process": {}, + "sample": {}, + }, + "data_contents": [], + } + ], + "is_public": True, + } + request = self.auth_client1.post( + "/v1/data/session/", data=session, format="json" + ) + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(len(Session.objects.all()), 3) + self.assertEqual(len(DataSet.objects.all()), 0) @classmethod def tearDownClass(cls): From 1ebefe857291e3db6900e7cd2200b2e253c1a120 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 15 Apr 2025 14:07:53 -0400 Subject: [PATCH 440/675] Use the right serializer for sessions and return id with get --- sasdata/fair_database/data/serializers.py | 1 + sasdata/fair_database/data/views.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index b46e3dc81..bdf351db2 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -342,6 +342,7 @@ class SessionSerializer(serializers.ModelSerializer): class Meta: model = models.Session fields = [ + "id", "title", "published_state", "datasets", diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 7711d2d57..684e5af78 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -412,7 +412,7 @@ def get(self, request, data_id, version=None): return HttpResponseForbidden( "You do not have permission to view this session." ) - serializer = DataSetSerializer(db) + serializer = SessionSerializer(db) return Response(serializer.data) # modify a session @@ -424,7 +424,7 @@ def put(self, request, data_id, version=None): "Must be authenticated to modify session", status=401 ) return HttpResponseForbidden("Cannot modify a session you do not own") - serializer = DataSetSerializer( + serializer = Session( db, request.data, context={"request": request}, partial=True ) if serializer.is_valid(raise_exception=True): From 5d0e25c6387fba82e6be2d103b199275718aabbf Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 15 Apr 2025 14:22:35 -0400 Subject: [PATCH 441/675] Test get method for individual sessions --- .../fair_database/data/test/test_session.py | 141 +++++++++++++++++- 1 file changed, 137 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/data/test/test_session.py b/sasdata/fair_database/data/test/test_session.py index f71ba7913..810c798b7 100644 --- a/sasdata/fair_database/data/test/test_session.py +++ b/sasdata/fair_database/data/test/test_session.py @@ -268,10 +268,139 @@ def tearDownClass(cls): class TestSingleSession(APITestCase): @classmethod def setUpTestData(cls): - pass + cls.user1 = User.objects.create_user( + id=1, username="testUser1", password="secret" + ) + cls.user2 = User.objects.create_user( + id=2, username="testUser2", password="secret" + ) + cls.public_session = Session.objects.create( + id=1, current_user=cls.user1, title="Public Session", is_public=True + ) + cls.private_session = Session.objects.create( + id=2, current_user=cls.user1, title="Private Session", is_public=False + ) + cls.unowned_session = Session.objects.create( + id=3, title="Unowned Session", is_public=True + ) + cls.public_dataset = DataSet.objects.create( + id=1, + current_user=cls.user1, + is_public=True, + name="Public Dataset", + session=cls.public_session, + ) + cls.private_dataset = DataSet.objects.create( + id=2, + current_user=cls.user1, + name="Private Dataset", + session=cls.private_session, + ) + cls.unowned_dataset = DataSet.objects.create( + id=3, is_public=True, name="Unowned Dataset", session=cls.unowned_session + ) + cls.auth_client1 = APIClient() + cls.auth_client2 = APIClient() + cls.auth_client1.force_authenticate(cls.user1) + cls.auth_client2.force_authenticate(cls.user2) - # Test accessing session - # public, private, with/without access + # Test loading another user's public session + def test_get_public_session(self): + request = self.auth_client2.get("/v1/data/session/1/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "title": "Public Session", + "published_state": None, + "datasets": [ + { + "id": 1, + "current_user": 1, + "users": [], + "is_public": True, + "name": "Public Dataset", + "files": [], + "metadata": None, + "data_contents": [], + } + ], + }, + ) + + # Test loading a private session as the owner + def test_get_private_session(self): + request = self.auth_client1.get("/v1/data/session/2/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 2, + "current_user": 1, + "users": [], + "is_public": False, + "title": "Private Session", + "published_state": None, + "datasets": [ + { + "id": 2, + "current_user": 1, + "users": [], + "is_public": False, + "name": "Private Dataset", + "files": [], + "metadata": None, + "data_contents": [], + } + ], + }, + ) + + # Test loading a private session as a user with granted access + def test_get_private_session_access_granted(self): + self.private_session.users.add(self.user2) + request = self.auth_client2.get("/v1/data/session/2/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.private_session.users.remove(self.user2) + + # Test loading an unowned session + def test_get_unowned_session(self): + request = self.auth_client1.get("/v1/data/session/3/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 3, + "current_user": None, + "users": [], + "is_public": True, + "title": "Unowned Session", + "published_state": None, + "datasets": [ + { + "id": 3, + "current_user": None, + "users": [], + "is_public": True, + "name": "Unowned Dataset", + "files": [], + "metadata": None, + "data_contents": [], + } + ], + }, + ) + + # Test loading another user's private session + def test_get_private_session_unauthorized(self): + request1 = self.auth_client2.get("/v1/data/session/2/") + request2 = self.client.get("/v1/data/session/2/") + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) # Test updating session # public, private, owner, not owner @@ -281,7 +410,11 @@ def setUpTestData(cls): @classmethod def tearDownClass(cls): - pass + cls.public_session.delete() + cls.private_session.delete() + cls.unowned_session.delete() + cls.user1.delete() + cls.user2.delete() class TestSessionAccessManagement(APITestCase): From 618a174c2c8ce2d84ebd34b3ad781fffdb687f43 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 15 Apr 2025 14:53:02 -0400 Subject: [PATCH 442/675] Test put method for individual sessions --- .../fair_database/data/test/test_session.py | 68 ++++++++++++++++++- sasdata/fair_database/data/views.py | 4 +- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/data/test/test_session.py b/sasdata/fair_database/data/test/test_session.py index 810c798b7..b157d31b7 100644 --- a/sasdata/fair_database/data/test/test_session.py +++ b/sasdata/fair_database/data/test/test_session.py @@ -181,7 +181,7 @@ def test_session_created_private(self): ) self.assertEqual(new_session.current_user, self.user1) self.assertEqual(new_dataset.current_user, self.user1) - self.assertTrue(all([(not new_session.is_public), (not new_dataset.is_public)])) + self.assertFalse(any([new_session.is_public, new_dataset.is_public])) new_session.delete() # Test creating a session while unauthenticated @@ -404,6 +404,72 @@ def test_get_private_session_unauthorized(self): # Test updating session # public, private, owner, not owner + def test_update_public_session(self): + request = self.auth_client1.put( + "/v1/data/session/1/", data={"is_public": False} + ) + session = Session.objects.get(id=1) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + {"session_id": 1, "title": "Public Session", "is_public": False}, + ) + self.assertFalse(session.is_public) + session.is_public = False + session.save() + + def test_update_public_session_unauthorized(self): + request1 = self.auth_client2.put( + "/v1/data/session/1/", data={"is_public": False} + ) + request2 = self.client.put("/v1/data/session/1/", data={"is_public": False}) + session = Session.objects.get(id=1) + session.users.add(self.user2) + request3 = self.auth_client2.put( + "/v1/data/session/1/", data={"is_public": False} + ) + session.users.remove(self.user2) + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) + self.assertTrue(Session.objects.get(id=1).is_public) + + def test_update_private_session(self): + request1 = self.auth_client1.put( + "/v1/data/session/2/", data={"is_public": True} + ) + session = Session.objects.get(id=2) + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual( + request1.data, + {"session_id": 2, "title": "Private Session", "is_public": True}, + ) + self.assertTrue(session.is_public) + session.is_public = False + session.save() + + def test_update_private_session_unauthorized(self): + request1 = self.auth_client2.put( + "/v1/data/session/2/", data={"is_public": True} + ) + request2 = self.client.put("/v1/data/session/2/", data={"is_public": True}) + session = Session.objects.get(id=2) + session.users.add(self.user2) + request3 = self.auth_client2.put( + "/v1/data/session/2/", data={"is_public": True} + ) + session.users.remove(self.user2) + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) + self.assertFalse(Session.objects.get(id=2).is_public) + + def test_update_unowned_session(self): + request = self.auth_client1.put( + "/v1/data/session/3/", data={"is_public": False} + ) + self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + self.assertTrue(Session.objects.get(id=3).is_public) # Test deleting session # Test delete cascades diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 684e5af78..eb052eab5 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -424,12 +424,12 @@ def put(self, request, data_id, version=None): "Must be authenticated to modify session", status=401 ) return HttpResponseForbidden("Cannot modify a session you do not own") - serializer = Session( + serializer = SessionSerializer( db, request.data, context={"request": request}, partial=True ) if serializer.is_valid(raise_exception=True): serializer.save() - data = {"data_id": db.id, "is_public": db.is_public} + data = {"session_id": db.id, "title": db.title, "is_public": db.is_public} return Response(data) # delete a session From 03a7b20abbb1282be46b20709345fd3d04a2b664 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 15 Apr 2025 15:10:24 -0400 Subject: [PATCH 443/675] Test Session delete method --- .../fair_database/data/test/test_session.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/sasdata/fair_database/data/test/test_session.py b/sasdata/fair_database/data/test/test_session.py index b157d31b7..552157c92 100644 --- a/sasdata/fair_database/data/test/test_session.py +++ b/sasdata/fair_database/data/test/test_session.py @@ -473,6 +473,38 @@ def test_update_unowned_session(self): # Test deleting session # Test delete cascades + def test_delete_private_session(self): + request = self.auth_client1.delete("/v1/data/session/2/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertRaises(Session.DoesNotExist, Session.objects.get, id=2) + self.assertRaises(DataSet.DoesNotExist, DataSet.objects.get, id=2) + self.private_session = Session.objects.create( + id=2, current_user=self.user1, title="Private Session", is_public=False + ) + self.private_dataset = DataSet.objects.create( + id=2, + current_user=self.user1, + name="Private Dataset", + session=self.private_session, + ) + + def test_delete_private_session_unauthorized(self): + request1 = self.auth_client2.delete("/v1/data/session/2/") + request2 = self.client.delete("/v1/data/session/2/") + self.private_session.users.add(self.user2) + request3 = self.auth_client2.delete("/v1/data/session/2/") + self.private_session.users.remove(self.user2) + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) + + def test_delete_public_session(self): + request = self.auth_client1.delete("/v1/data/session/1/") + self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + + def test_delete_unowned_session(self): + request = self.auth_client1.delete("/v1/data/session/3/") + self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) @classmethod def tearDownClass(cls): From 9f2e10058ee400f4cf2d014ec24edfaf1b416688 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 15 Apr 2025 15:37:22 -0400 Subject: [PATCH 444/675] Propagate access changes from session to composing datasets --- sasdata/fair_database/data/views.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index eb052eab5..fc525370a 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -482,8 +482,12 @@ def put(self, request, data_id, version=None): user = get_object_or_404(User, username=serializer.data["username"]) if serializer.data["access"]: db.users.add(user) + for dataset in db.datasets.all(): + dataset.users.add(user) else: db.users.remove(user) + for dataset in db.datasets.all(): + dataset.users.remove(user) response_data = { "username": user.username, "session_id": db.id, From 3f76d39f68a3967b9181c859088c8e610e0c47d9 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 15 Apr 2025 15:51:41 -0400 Subject: [PATCH 445/675] Test Session access management --- .../fair_database/data/test/test_session.py | 118 +++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/test/test_session.py b/sasdata/fair_database/data/test/test_session.py index 552157c92..45b5f20f0 100644 --- a/sasdata/fair_database/data/test/test_session.py +++ b/sasdata/fair_database/data/test/test_session.py @@ -518,9 +518,120 @@ def tearDownClass(cls): class TestSessionAccessManagement(APITestCase): @classmethod def setUpTestData(cls): - pass + cls.user1 = User.objects.create_user(username="testUser1", password="secret") + cls.user2 = User.objects.create_user(username="testUser2", password="secret") + cls.private_session = Session.objects.create( + id=1, current_user=cls.user1, title="Private Session", is_public=False + ) + cls.shared_session = Session.objects.create( + id=2, current_user=cls.user1, title="Shared Session", is_public=False + ) + cls.private_dataset = DataSet.objects.create( + id=1, + current_user=cls.user1, + name="Private Dataset", + session=cls.private_session, + ) + cls.shared_dataset = DataSet.objects.create( + id=2, + current_user=cls.user1, + name="Shared Dataset", + session=cls.shared_session, + ) + cls.shared_session.users.add(cls.user2) + cls.shared_dataset.users.add(cls.user2) + cls.client_owner = APIClient() + cls.client_other = APIClient() + cls.client_owner.force_authenticate(cls.user1) + cls.client_other.force_authenticate(cls.user2) # Test listing access + def test_list_access_private(self): + request = self.client_owner.get("/v1/data/session/1/users/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "session_id": 1, + "title": "Private Session", + "is_public": False, + "users": [], + }, + ) + + def test_list_access_shared(self): + request = self.client_owner.get("/v1/data/session/2/users/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "session_id": 2, + "title": "Shared Session", + "is_public": False, + "users": ["testUser2"], + }, + ) + + def test_list_access_unauthorized(self): + request1 = self.client_other.get("/v1/data/session/1/users/") + request2 = self.client_other.get("/v1/data/session/2/users/") + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + + def test_grant_access(self): + request1 = self.client_owner.put( + "/v1/data/session/1/users/", {"username": "testUser2", "access": True} + ) + request2 = self.client_other.get("/v1/data/session/1/") + request3 = self.client_other.get("/v1/data/set/1/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual( + request1.data, + { + "username": "testUser2", + "session_id": 1, + "title": "Private Session", + "access": True, + }, + ) + self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertEqual(request3.status_code, status.HTTP_200_OK) + self.assertIn(self.user2, self.private_session.users.all()) # codespell:ignore + self.assertIn(self.user2, self.private_dataset.users.all()) # codespell:ignore + self.private_session.users.remove(self.user2) + self.private_dataset.users.remove(self.user2) + + def test_revoke_access(self): + request1 = self.client_owner.put( + "/v1/data/session/2/users/", {"username": "testUser2", "access": False} + ) + request2 = self.client_other.get("/v1/data/session/2/") + request3 = self.client_other.get("/v1/data/session/2/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual( + request1.data, + { + "username": "testUser2", + "session_id": 2, + "title": "Shared Session", + "access": False, + }, + ) + self.assertNotIn(self.user2, self.shared_session.users.all()) + self.assertNotIn(self.user2, self.shared_dataset.users.all()) + self.shared_session.users.add(self.user2) + self.shared_dataset.users.add(self.user2) + + def test_revoke_access_unauthorized(self): + request1 = self.client_other.put( + "/v1/data/session/2/users/", {"username": "testUser2", "access": False} + ) + request2 = self.client_other.get("/v1/data/session/2/") + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertIn(self.user2, self.shared_session.users.all()) # codespell:ignore # Test listing access not as the owner @@ -534,4 +645,7 @@ def setUpTestData(cls): @classmethod def tearDownClass(cls): - pass + cls.private_session.delete() + cls.shared_session.delete() + cls.user1.delete() + cls.user2.delete() From c5ba40f1c28681605f74901e1a7775fe587ff1c8 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 16 Apr 2025 10:14:07 -0400 Subject: [PATCH 446/675] Return current_user as username not id --- sasdata/fair_database/data/test/test_dataset.py | 6 +++--- sasdata/fair_database/data/test/test_session.py | 4 ++-- sasdata/fair_database/data/views.py | 10 ++++++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 494de3acf..83c5b840c 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -345,7 +345,7 @@ def test_load_private_dataset(self): request1.data, { "id": 2, - "current_user": 1, + "current_user": "testUser1", "users": [3], "is_public": False, "name": "Dataset 2", @@ -367,7 +367,7 @@ def test_load_public_dataset(self): request1.data, { "id": 1, - "current_user": 1, + "current_user": "testUser1", "users": [], "is_public": True, "name": "Dataset 1", @@ -389,7 +389,7 @@ def test_load_public_dataset(self): request3.data, { "id": 1, - "current_user": 1, + "current_user": "testUser1", "users": [], "is_public": True, "name": "Dataset 1", diff --git a/sasdata/fair_database/data/test/test_session.py b/sasdata/fair_database/data/test/test_session.py index 45b5f20f0..aa1d6677b 100644 --- a/sasdata/fair_database/data/test/test_session.py +++ b/sasdata/fair_database/data/test/test_session.py @@ -312,7 +312,7 @@ def test_get_public_session(self): request.data, { "id": 1, - "current_user": 1, + "current_user": "testUser1", "users": [], "is_public": True, "title": "Public Session", @@ -340,7 +340,7 @@ def test_get_private_session(self): request.data, { "id": 2, - "current_user": 1, + "current_user": "testUser1", "users": [], "is_public": False, "title": "Private Session", diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index fc525370a..665c7620e 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -274,7 +274,10 @@ def get(self, request, data_id, version=None): "You do not have permission to view this dataset." ) serializer = DataSetSerializer(db, context={"request": request}) - return Response(serializer.data) + response_data = serializer.data + if db.current_user: + response_data["current_user"] = db.current_user.username + return Response(response_data) # edit a specific dataset def put(self, request, data_id, version=None): @@ -413,7 +416,10 @@ def get(self, request, data_id, version=None): "You do not have permission to view this session." ) serializer = SessionSerializer(db) - return Response(serializer.data) + response_data = serializer.data + if db.current_user: + response_data["current_user"] = db.current_user.username + return Response(response_data) # modify a session def put(self, request, data_id, version=None): From 9b7c753aa58584df6d9986da65ebf9b79350c624 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 16 Apr 2025 10:21:18 -0400 Subject: [PATCH 447/675] Comments of session testing --- .../fair_database/data/test/test_session.py | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/sasdata/fair_database/data/test/test_session.py b/sasdata/fair_database/data/test/test_session.py index aa1d6677b..01e1c857d 100644 --- a/sasdata/fair_database/data/test/test_session.py +++ b/sasdata/fair_database/data/test/test_session.py @@ -8,6 +8,8 @@ class TestSession(APITestCase): + """Test HTTP methods of SessionView.""" + @classmethod def setUpTestData(cls): cls.user1 = User.objects.create_user( @@ -266,6 +268,8 @@ def tearDownClass(cls): class TestSingleSession(APITestCase): + """Test HTTP methods of SingleSessionView.""" + @classmethod def setUpTestData(cls): cls.user1 = User.objects.create_user( @@ -402,8 +406,7 @@ def test_get_private_session_unauthorized(self): self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) - # Test updating session - # public, private, owner, not owner + # Test updating a public session def test_update_public_session(self): request = self.auth_client1.put( "/v1/data/session/1/", data={"is_public": False} @@ -418,6 +421,7 @@ def test_update_public_session(self): session.is_public = False session.save() + # Test that another user's public session cannot be updated def test_update_public_session_unauthorized(self): request1 = self.auth_client2.put( "/v1/data/session/1/", data={"is_public": False} @@ -434,6 +438,7 @@ def test_update_public_session_unauthorized(self): self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) self.assertTrue(Session.objects.get(id=1).is_public) + # Test updating a private session def test_update_private_session(self): request1 = self.auth_client1.put( "/v1/data/session/2/", data={"is_public": True} @@ -448,6 +453,7 @@ def test_update_private_session(self): session.is_public = False session.save() + # Test that another user's private session cannot be updated def test_update_private_session_unauthorized(self): request1 = self.auth_client2.put( "/v1/data/session/2/", data={"is_public": True} @@ -464,6 +470,7 @@ def test_update_private_session_unauthorized(self): self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) self.assertFalse(Session.objects.get(id=2).is_public) + # Test that an unowned session cannot be updated def test_update_unowned_session(self): request = self.auth_client1.put( "/v1/data/session/3/", data={"is_public": False} @@ -471,8 +478,7 @@ def test_update_unowned_session(self): self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) self.assertTrue(Session.objects.get(id=3).is_public) - # Test deleting session - # Test delete cascades + # Test deleting a private session def test_delete_private_session(self): request = self.auth_client1.delete("/v1/data/session/2/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -488,6 +494,7 @@ def test_delete_private_session(self): session=self.private_session, ) + # Test that another user's private session cannot be deleted def test_delete_private_session_unauthorized(self): request1 = self.auth_client2.delete("/v1/data/session/2/") request2 = self.client.delete("/v1/data/session/2/") @@ -498,10 +505,12 @@ def test_delete_private_session_unauthorized(self): self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(request3.status_code, status.HTTP_403_FORBIDDEN) + # Test that a public session cannot be deleted def test_delete_public_session(self): request = self.auth_client1.delete("/v1/data/session/1/") self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + # Test that an unowned session cannot be deleted def test_delete_unowned_session(self): request = self.auth_client1.delete("/v1/data/session/3/") self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) @@ -516,6 +525,8 @@ def tearDownClass(cls): class TestSessionAccessManagement(APITestCase): + """Test HTTP methods of SessionUsersView.""" + @classmethod def setUpTestData(cls): cls.user1 = User.objects.create_user(username="testUser1", password="secret") @@ -545,7 +556,7 @@ def setUpTestData(cls): cls.client_owner.force_authenticate(cls.user1) cls.client_other.force_authenticate(cls.user2) - # Test listing access + # Test listing access to an unshared session def test_list_access_private(self): request = self.client_owner.get("/v1/data/session/1/users/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -559,6 +570,7 @@ def test_list_access_private(self): }, ) + # Test listing access to a shared session def test_list_access_shared(self): request = self.client_owner.get("/v1/data/session/2/users/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -572,12 +584,14 @@ def test_list_access_shared(self): }, ) + # Test that only the owner can view access def test_list_access_unauthorized(self): request1 = self.client_other.get("/v1/data/session/1/users/") request2 = self.client_other.get("/v1/data/session/2/users/") self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + # Test granting access to a session def test_grant_access(self): request1 = self.client_owner.put( "/v1/data/session/1/users/", {"username": "testUser2", "access": True} @@ -601,6 +615,7 @@ def test_grant_access(self): self.private_session.users.remove(self.user2) self.private_dataset.users.remove(self.user2) + # Test revoking access to a session def test_revoke_access(self): request1 = self.client_owner.put( "/v1/data/session/2/users/", {"username": "testUser2", "access": False} @@ -624,6 +639,7 @@ def test_revoke_access(self): self.shared_session.users.add(self.user2) self.shared_dataset.users.add(self.user2) + # Test that only the owner can change access def test_revoke_access_unauthorized(self): request1 = self.client_other.put( "/v1/data/session/2/users/", {"username": "testUser2", "access": False} @@ -633,16 +649,6 @@ def test_revoke_access_unauthorized(self): self.assertEqual(request2.status_code, status.HTTP_200_OK) self.assertIn(self.user2, self.shared_session.users.all()) # codespell:ignore - # Test listing access not as the owner - - # Test granting access - - # Test revoking access - - # Test can't revoke own access - - # Test only owner can change access - @classmethod def tearDownClass(cls): cls.private_session.delete() From 1229ab248d568acdfddd53ae78d1a093d13520a3 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 16 Apr 2025 10:29:54 -0400 Subject: [PATCH 448/675] Add field for history references to Quantity --- .../0030_quantity_derived_quantity.py | 24 +++++++++++++++++++ sasdata/fair_database/data/models.py | 10 ++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0030_quantity_derived_quantity.py diff --git a/sasdata/fair_database/data/migrations/0030_quantity_derived_quantity.py b/sasdata/fair_database/data/migrations/0030_quantity_derived_quantity.py new file mode 100644 index 000000000..6584811b6 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0030_quantity_derived_quantity.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.6 on 2025-04-16 14:29 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0029_rename_next_operation_operationtree_child_operation"), + ] + + operations = [ + migrations.AddField( + model_name="quantity", + name="derived_quantity", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="references", + to="data.quantity", + ), + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 89fa4985e..036a4e94a 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -78,14 +78,20 @@ class Quantity(models.Model): # hash value hash = models.IntegerField() - # TODO: add field to store references portion of QuantityHistory - label = models.CharField(max_length=50) dataset = models.ForeignKey( DataSet, on_delete=models.CASCADE, related_name="data_contents" ) + derived_quantity = models.ForeignKey( + "self", + related_name="references", + on_delete=models.CASCADE, + blank=True, + null=True, + ) + def empty_list(): return [] From 9d2e2e7ac1a354e24b4f8a19e543aeab831d3036 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 16 Apr 2025 11:28:40 -0400 Subject: [PATCH 449/675] Serialization for Quantity variable references --- sasdata/fair_database/data/serializers.py | 58 ++++++++++++++----- .../fair_database/data/test/test_dataset.py | 9 ++- .../data/test/test_operation_tree.py | 32 +++++----- 3 files changed, 70 insertions(+), 29 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index bdf351db2..774f7a8ac 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -158,6 +158,9 @@ class QuantitySerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the Quantity model.""" operation_tree = OperationTreeSerializer(read_only=False, required=False) + derived_quantity = serializers.PrimaryKeyRelatedField( + queryset=models.Quantity, required=False, allow_null=True + ) label = serializers.CharField(max_length=20) dataset = serializers.PrimaryKeyRelatedField( queryset=models.DataSet, required=False, allow_null=True @@ -171,20 +174,33 @@ class Meta: "units", "hash", "operation_tree", + "references", "label", "dataset", + "derived_quantity", # "history", ] + def validate_references(self, value): + for ref in value: + serializer = QuantitySerializer(data=ref) + serializer.is_valid(raise_exception=True) + # TODO: should variable-only history be assumed to refer to the same Quantity and ignored? # Extract operation tree from history def to_internal_value(self, data): - if "history" in data and "operation_tree" in data["history"]: - operations = data["history"]["operation_tree"] - if not operations["operation"] == "variable": - data_copy = data.copy() - data_copy["operation_tree"] = operations - return super().to_internal_value(data_copy) + if "history" in data: + data_copy = data.copy() + if "operation_tree" in data["history"]: + operations = data["history"]["operation_tree"] + if ( + "operation" in operations + and not operations["operation"] == "variable" + ): + data_copy["operation_tree"] = operations + if "references" in data["history"]: + data_copy["references"] = data["history"]["references"] + return super().to_internal_value(data_copy) return super().to_internal_value(data) # Serialize a Quantity instance @@ -192,21 +208,37 @@ def to_representation(self, instance): data = super().to_representation(instance) if "dataset" in data: data.pop("dataset") + if "derived_quantity" in data: + data.pop("derived_quantity") return data # Create a Quantity instance def create(self, validated_data): dataset = models.DataSet.objects.get(id=validated_data.pop("dataset")) + operations_tree = None + derived_quantity = None + references = None if "operation_tree" in validated_data: - operations_data = validated_data.pop("operation_tree") - quantity = models.Quantity.objects.create(dataset=dataset, **validated_data) - operations_data["quantity"] = quantity.id + operations_tree = validated_data.pop("operation_tree") + if "derived_quantity" in validated_data: + derived_quantity = models.Quantity.objects.get( + id=validated_data.pop("derived_quantity") + ) + if "references" in validated_data: + references = validated_data.pop("references") + quantity = models.Quantity.objects.create( + dataset=dataset, derived_quantity=derived_quantity, **validated_data + ) + if operations_tree: + operations_tree["quantity"] = quantity.id OperationTreeSerializer.create( - OperationTreeSerializer(), validated_data=operations_data + OperationTreeSerializer(), validated_data=operations_tree ) - return quantity - else: - return models.Quantity.objects.create(dataset=dataset, **validated_data) + if references: + for ref in references: + ref["derived_quantity"] = quantity.id + QuantitySerializer.create(QuantitySerializer(), validated_data=ref) + return quantity class DataSetSerializer(serializers.ModelSerializer): diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 83c5b840c..385b5c923 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -30,7 +30,14 @@ def setUpTestData(cls): "sample": {}, } cls.empty_data = [ - {"value": 0, "variance": 0, "units": "no", "hash": 0, "label": "test"} + { + "value": 0, + "variance": 0, + "units": "no", + "hash": 0, + "label": "test", + "history": {"operation_tree": {}, "references": []}, + } ] cls.user1 = User.objects.create_user( id=1, username="testUser1", password="secret" diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index faa45c895..1dc8cdaa3 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -47,7 +47,7 @@ def test_operation_tree_created_variable(self): "operation": "variable", "parameters": {"hash_value": 0, "name": "test"}, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -72,7 +72,7 @@ def test_operation_tree_created_unary(self): } }, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -102,7 +102,7 @@ def test_operation_tree_created_binary(self): "b": {"operation": "constant", "parameters": {"value": 5}}, }, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -133,7 +133,7 @@ def test_operation_tree_created_pow(self): "power": 2, }, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -157,7 +157,7 @@ def test_operation_tree_created_transpose(self): "axes": [1, 0], }, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -192,7 +192,7 @@ def test_operation_tree_created_nested(self): }, }, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -227,7 +227,7 @@ def test_operation_tree_created_tensor(self): "b_index": 1, }, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -294,7 +294,7 @@ def setUpTestData(cls): def test_create_operation_tree_invalid(self): self.dataset["data_contents"][0]["history"] = { "operation_tree": {"operation": "fix", "parameters": {}}, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) @@ -314,7 +314,7 @@ def test_create_operation_tree_invalid_nested(self): } }, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) @@ -346,7 +346,7 @@ def test_create_missing_parameter_binary(self): } }, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) @@ -364,7 +364,7 @@ def test_create_missing_parameter_variable(self): "a": {"operation": "variable", "parameters": {"name": "x"}} }, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) @@ -379,7 +379,7 @@ def test_create_missing_parameter_constant(self): "operation": "neg", "parameters": {"a": {"operation": "constant", "parameters": {}}}, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) @@ -399,7 +399,7 @@ def test_create_missing_parameter_pow(self): }, }, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) @@ -419,7 +419,7 @@ def test_create_missing_parameter_transpose(self): }, }, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) @@ -441,7 +441,7 @@ def test_create_missing_parameter_tensor(self): "b_index": 1, }, }, - "references": {}, + "references": [], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) @@ -498,6 +498,7 @@ def test_get_operation_tree_none(self): "units": "none", "hash": 1, "operation_tree": None, + "references": [], }, ) @@ -533,6 +534,7 @@ def test_get_operation_tree_unary(self): } }, }, + "references": [], }, ) From 2c76297ec7b6642f4c04573232b59833c9399292 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 16 Apr 2025 11:34:33 -0400 Subject: [PATCH 450/675] Change serialization of QuantityHistory references to match API --- sasdata/quantities/quantity.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 5f8ab3238..4b535ee44 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1114,9 +1114,9 @@ def deserialise_json(json_data: dict) -> "QuantityHistory": def serialise_json(self): return { "operation_tree": self.operation_tree.serialise(), - "references": { - key: self.references[key].serialise_json() for key in self.references - } + "references": [ + ref.serialise_json() for ref in self.references.values() + ] } From e85c67c244bb1f87750d6917ef67cad44dd2337f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 16 Apr 2025 11:44:54 -0400 Subject: [PATCH 451/675] Serialize Quantity operation_tree and references as history --- sasdata/fair_database/data/serializers.py | 7 +++- .../data/test/test_operation_tree.py | 34 +++++++++++-------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 774f7a8ac..3ca26bfd7 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -165,6 +165,7 @@ class QuantitySerializer(serializers.ModelSerializer): dataset = serializers.PrimaryKeyRelatedField( queryset=models.DataSet, required=False, allow_null=True ) + history = serializers.JSONField(required=False, allow_null=True) class Meta: model = models.Quantity @@ -178,7 +179,7 @@ class Meta: "label", "dataset", "derived_quantity", - # "history", + "history", ] def validate_references(self, value): @@ -200,6 +201,7 @@ def to_internal_value(self, data): data_copy["operation_tree"] = operations if "references" in data["history"]: data_copy["references"] = data["history"]["references"] + data_copy.pop("history") return super().to_internal_value(data_copy) return super().to_internal_value(data) @@ -210,6 +212,9 @@ def to_representation(self, instance): data.pop("dataset") if "derived_quantity" in data: data.pop("derived_quantity") + data["history"] = {} + data["history"]["operation_tree"] = data.pop("operation_tree") + data["history"]["references"] = data.pop("references") return data # Create a Quantity instance diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index 1dc8cdaa3..9a04cb758 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -497,8 +497,10 @@ def test_get_operation_tree_none(self): "variance": 0, "units": "none", "hash": 1, - "operation_tree": None, - "references": [], + "history": { + "operation_tree": None, + "references": [], + }, }, ) @@ -525,16 +527,18 @@ def test_get_operation_tree_unary(self): "variance": 0, "units": "none", "hash": 1, - "operation_tree": { - "operation": "reciprocal", - "parameters": { - "a": { - "operation": "variable", - "parameters": {"hash_value": 111, "name": "x"}, - } + "history": { + "operation_tree": { + "operation": "reciprocal", + "parameters": { + "a": { + "operation": "variable", + "parameters": {"hash_value": 111, "name": "x"}, + } + }, }, + "references": [], }, - "references": [], }, ) @@ -559,7 +563,7 @@ def test_get_operation_tree_binary(self): add.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( - request.data["data_contents"][0]["operation_tree"], + request.data["data_contents"][0]["history"]["operation_tree"], { "operation": "add", "parameters": { @@ -592,7 +596,7 @@ def test_get_operation_tree_pow(self): power.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( - request.data["data_contents"][0]["operation_tree"], + request.data["data_contents"][0]["history"]["operation_tree"], { "operation": "pow", "parameters": { @@ -627,7 +631,7 @@ def test_get_operation_tree_nested(self): neg.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( - request.data["data_contents"][0]["operation_tree"], + request.data["data_contents"][0]["history"]["operation_tree"], { "operation": "neg", "parameters": { @@ -668,7 +672,7 @@ def test_get_operation_tree_transpose(self): trans.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( - request.data["data_contents"][0]["operation_tree"], + request.data["data_contents"][0]["history"]["operation_tree"], { "operation": "transpose", "parameters": { @@ -703,7 +707,7 @@ def test_get_operation_tree_tensordot(self): tensor.delete() self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( - request.data["data_contents"][0]["operation_tree"], + request.data["data_contents"][0]["history"]["operation_tree"], { "operation": "tensor_product", "parameters": { From a932e52d10fb69d1ba29fde30ade9000f0a261da Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 16 Apr 2025 11:51:06 -0400 Subject: [PATCH 452/675] Remove infinite serialization loop of quantities and histories --- sasdata/quantities/quantity.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 4b535ee44..a154949bd 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1115,7 +1115,7 @@ def serialise_json(self): return { "operation_tree": self.operation_tree.serialise(), "references": [ - ref.serialise_json() for ref in self.references.values() + ref.serialise_json_no_history() for ref in self.references.values() ] } @@ -1233,7 +1233,6 @@ def deserialise_json(json_data: dict) -> "Quantity": quantity.history = history return quantity - # TODO: fill out actual values def serialise_json(self): return { "value": numerical_encode(self.value), @@ -1244,6 +1243,16 @@ def serialise_json(self): "history": self.history.serialise_json() } + def serialise_json_no_history(self): + return { + "value": numerical_encode(self.value), + "units": str(self.units), # Unit serialisation + "variance": numerical_encode(self._variance), + "hash_seed": self._hash_seed, # is this just a string? + "hash_value": self.hash_value, + "history": {} + } + def __mul__(self: Self, other: ArrayLike | Self ) -> Self: if isinstance(other, Quantity): return DerivedQuantity( From aa0f88c048fccae78622293a6eb0d95202a2aa72 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 16 Apr 2025 15:13:43 -0400 Subject: [PATCH 453/675] Separate model for history reference quantities --- ...tity_derived_quantity_referencequantity.py | 45 ++++++++++++ ...lter_referencequantity_derived_quantity.py | 24 +++++++ sasdata/fair_database/data/models.py | 18 ++++- sasdata/fair_database/data/serializers.py | 70 +++++++++++++------ 4 files changed, 131 insertions(+), 26 deletions(-) create mode 100644 sasdata/fair_database/data/migrations/0031_remove_quantity_derived_quantity_referencequantity.py create mode 100644 sasdata/fair_database/data/migrations/0032_alter_referencequantity_derived_quantity.py diff --git a/sasdata/fair_database/data/migrations/0031_remove_quantity_derived_quantity_referencequantity.py b/sasdata/fair_database/data/migrations/0031_remove_quantity_derived_quantity_referencequantity.py new file mode 100644 index 000000000..24702a449 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0031_remove_quantity_derived_quantity_referencequantity.py @@ -0,0 +1,45 @@ +# Generated by Django 5.1.6 on 2025-04-16 18:52 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0030_quantity_derived_quantity"), + ] + + operations = [ + migrations.RemoveField( + model_name="quantity", + name="derived_quantity", + ), + migrations.CreateModel( + name="ReferenceQuantity", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("value", models.JSONField()), + ("variance", models.JSONField()), + ("units", models.CharField(max_length=200)), + ("hash", models.IntegerField()), + ( + "derived_quantity", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="references", + to="data.quantity", + ), + ), + ], + ), + ] diff --git a/sasdata/fair_database/data/migrations/0032_alter_referencequantity_derived_quantity.py b/sasdata/fair_database/data/migrations/0032_alter_referencequantity_derived_quantity.py new file mode 100644 index 000000000..77aa82774 --- /dev/null +++ b/sasdata/fair_database/data/migrations/0032_alter_referencequantity_derived_quantity.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.6 on 2025-04-16 19:18 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("data", "0031_remove_quantity_derived_quantity_referencequantity"), + ] + + operations = [ + migrations.AlterField( + model_name="referencequantity", + name="derived_quantity", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + related_name="references", + to="data.quantity", + ), + preserve_default=False, + ), + ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 036a4e94a..e194edffa 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -84,12 +84,24 @@ class Quantity(models.Model): DataSet, on_delete=models.CASCADE, related_name="data_contents" ) + +class ReferenceQuantity(models.Model): + # data value + value = models.JSONField() + + # variance of the data + variance = models.JSONField() + + # units + units = models.CharField(max_length=200) + + # hash value + hash = models.IntegerField() + derived_quantity = models.ForeignKey( - "self", + Quantity, related_name="references", on_delete=models.CASCADE, - blank=True, - null=True, ) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 3ca26bfd7..2f463a7bc 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -154,13 +154,39 @@ def create(self, validated_data): return operation_tree +class ReferenceQuantitySerializer(serializers.ModelSerializer): + derived_quantity = serializers.PrimaryKeyRelatedField( + queryset=models.Quantity, required=False + ) + + class Meta: + model = models.ReferenceQuantity + fields = ["value", "variance", "units", "hash", "derived_quantity"] + + def to_representation(self, instance): + data = super().to_representation(instance) + if "derived_quantity" in data: + data.pop("derived_quantity") + return data + + def create(self, validated_data): + derived_quantity = models.Quantity.objects.get( + id=validated_data.pop("derived_quantity") + ) + if "label" in validated_data: + validated_data.pop("label") + if "history" in validated_data: + validated_data.pop("history") + return models.ReferenceQuantity.objects.create( + derived_quantity=derived_quantity, **validated_data + ) + + class QuantitySerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the Quantity model.""" operation_tree = OperationTreeSerializer(read_only=False, required=False) - derived_quantity = serializers.PrimaryKeyRelatedField( - queryset=models.Quantity, required=False, allow_null=True - ) + references = ReferenceQuantitySerializer(many=True, read_only=False, required=False) label = serializers.CharField(max_length=20) dataset = serializers.PrimaryKeyRelatedField( queryset=models.DataSet, required=False, allow_null=True @@ -178,14 +204,14 @@ class Meta: "references", "label", "dataset", - "derived_quantity", "history", ] - def validate_references(self, value): - for ref in value: - serializer = QuantitySerializer(data=ref) - serializer.is_valid(raise_exception=True) + def validate_history(self, value): + if "references" in value: + for ref in value["references"]: + serializer = ReferenceQuantitySerializer(data=ref) + serializer.is_valid(raise_exception=True) # TODO: should variable-only history be assumed to refer to the same Quantity and ignored? # Extract operation tree from history @@ -199,10 +225,11 @@ def to_internal_value(self, data): and not operations["operation"] == "variable" ): data_copy["operation_tree"] = operations - if "references" in data["history"]: - data_copy["references"] = data["history"]["references"] - data_copy.pop("history") - return super().to_internal_value(data_copy) + return_data = super().to_internal_value(data_copy) + return_data["history"] = data["history"] + return return_data + else: + return super().to_internal_value(data_copy) return super().to_internal_value(data) # Serialize a Quantity instance @@ -221,19 +248,14 @@ def to_representation(self, instance): def create(self, validated_data): dataset = models.DataSet.objects.get(id=validated_data.pop("dataset")) operations_tree = None - derived_quantity = None references = None if "operation_tree" in validated_data: operations_tree = validated_data.pop("operation_tree") - if "derived_quantity" in validated_data: - derived_quantity = models.Quantity.objects.get( - id=validated_data.pop("derived_quantity") - ) - if "references" in validated_data: - references = validated_data.pop("references") - quantity = models.Quantity.objects.create( - dataset=dataset, derived_quantity=derived_quantity, **validated_data - ) + if "history" in validated_data: + history = validated_data.pop("history") + if history and "references" in history: + references = history.pop("references") + quantity = models.Quantity.objects.create(dataset=dataset, **validated_data) if operations_tree: operations_tree["quantity"] = quantity.id OperationTreeSerializer.create( @@ -242,7 +264,9 @@ def create(self, validated_data): if references: for ref in references: ref["derived_quantity"] = quantity.id - QuantitySerializer.create(QuantitySerializer(), validated_data=ref) + ReferenceQuantitySerializer.create( + ReferenceQuantitySerializer(), validated_data=ref + ) return quantity From d7781737cdbbdc6131dadae5fd0538f69ae8e22a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 16 Apr 2025 15:24:23 -0400 Subject: [PATCH 454/675] Test references in quantity history --- .../data/test/test_operation_tree.py | 81 ++++++++++++++++--- 1 file changed, 71 insertions(+), 10 deletions(-) diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index 9a04cb758..f5bd77848 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -3,7 +3,7 @@ from rest_framework.test import APIClient, APITestCase from rest_framework import status -from data.models import DataSet, MetaData, OperationTree, Quantity +from data.models import DataSet, MetaData, OperationTree, Quantity, ReferenceQuantity class TestCreateOperationTree(APITestCase): @@ -47,7 +47,16 @@ def test_operation_tree_created_variable(self): "operation": "variable", "parameters": {"hash_value": 0, "name": "test"}, }, - "references": [], + "references": [ + { + "label": "test", + "value": {"array_contents": [0, 0, 0, 0], "shape": (2, 2)}, + "variance": {"array_contents": [0, 0, 0, 0], "shape": (2, 2)}, + "units": "none", + "hash": 0, + "history": {}, + } + ], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -59,6 +68,7 @@ def test_operation_tree_created_variable(self): self.get_operation_tree, quantity=new_quantity, ) + self.assertEqual(len(new_quantity.references.all()), 0) # Test creating quantity with unary operation def test_operation_tree_created_unary(self): @@ -72,7 +82,9 @@ def test_operation_tree_created_unary(self): } }, }, - "references": [], + "references": [ + {"value": 5, "variance": 0, "units": "none", "hash": 111, "history": {}} + ], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -88,6 +100,9 @@ def test_operation_tree_created_unary(self): self.assertEqual(variable.operation, "variable") self.assertEqual(len(reciprocal.parent_operations.all()), 1) self.assertEqual(reciprocal.parameters, {}) + self.assertEqual(len(ReferenceQuantity.objects.all()), 1) + self.assertEqual(len(new_quantity.references.all()), 1) + self.assertEqual(new_quantity.references.get(hash=111).value, 5) # Test creating quantity with binary operation def test_operation_tree_created_binary(self): @@ -102,7 +117,9 @@ def test_operation_tree_created_binary(self): "b": {"operation": "constant", "parameters": {"value": 5}}, }, }, - "references": [], + "references": [ + {"value": 5, "variance": 0, "units": "none", "hash": 111, "history": {}} + ], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -119,6 +136,8 @@ def test_operation_tree_created_binary(self): self.assertEqual(constant.operation, "constant") self.assertEqual(constant.parameters, {"value": 5}) self.assertEqual(len(add.parent_operations.all()), 2) + self.assertEqual(len(new_quantity.references.all()), 1) + self.assertEqual(new_quantity.references.get(hash=111).value, 5) # Test creating quantity with exponent def test_operation_tree_created_pow(self): @@ -133,7 +152,9 @@ def test_operation_tree_created_pow(self): "power": 2, }, }, - "references": [], + "references": [ + {"value": 5, "variance": 0, "units": "none", "hash": 111, "history": {}} + ], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -143,6 +164,8 @@ def test_operation_tree_created_pow(self): self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual(pow.operation, "pow") self.assertEqual(pow.parameters, {"power": 2}) + self.assertEqual(len(new_quantity.references.all()), 1) + self.assertEqual(new_quantity.references.get(hash=111).value, 5) # Test creating a transposed quantity def test_operation_tree_created_transpose(self): @@ -157,7 +180,9 @@ def test_operation_tree_created_transpose(self): "axes": [1, 0], }, }, - "references": [], + "references": [ + {"value": 5, "variance": 0, "units": "none", "hash": 111, "history": {}} + ], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -170,6 +195,8 @@ def test_operation_tree_created_transpose(self): self.assertEqual(transpose.parameters, {"axes": [1, 0]}) self.assertEqual(variable.operation, "variable") self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) + self.assertEqual(len(new_quantity.references.all()), 1) + self.assertEqual(new_quantity.references.get(hash=111).value, 5) # Test creating a quantity with multiple operations def test_operation_tree_created_nested(self): @@ -192,7 +219,9 @@ def test_operation_tree_created_nested(self): }, }, }, - "references": [], + "references": [ + {"value": 5, "variance": 0, "units": "none", "hash": 111, "history": {}} + ], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -211,6 +240,8 @@ def test_operation_tree_created_nested(self): self.assertEqual(constant.parameters, {"value": {"type": "int", "value": 7}}) self.assertEqual(variable.operation, "variable") self.assertEqual(variable.parameters, {"hash_value": 111, "name": "x"}) + self.assertEqual(len(new_quantity.references.all()), 1) + self.assertEqual(new_quantity.references.get(hash=111).value, 5) # Test creating a quantity with tensordot def test_operation_tree_created_tensor(self): @@ -227,7 +258,9 @@ def test_operation_tree_created_tensor(self): "b_index": 1, }, }, - "references": [], + "references": [ + {"value": 5, "variance": 0, "units": "none", "hash": 111, "history": {}} + ], } request = self.client.post("/v1/data/set/", data=self.dataset, format="json") max_id = DataSet.objects.aggregate(Max("id"))["id__max"] @@ -237,6 +270,8 @@ def test_operation_tree_created_tensor(self): self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual(tensor.operation, "tensor_product") self.assertEqual(tensor.parameters, {"a_index": 1, "b_index": 1}) + self.assertEqual(len(new_quantity.references.all()), 1) + self.assertEqual(new_quantity.references.get(hash=111).value, 5) # Test creating a quantity with no history def test_operation_tree_created_no_history(self): @@ -250,6 +285,7 @@ def test_operation_tree_created_no_history(self): new_quantity = new_dataset.data_contents.get(hash=0) self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertIsNone(new_quantity.operation_tree) + self.assertEqual(len(new_quantity.references.all()), 0) def tearDown(self): DataSet.objects.all().delete() @@ -449,6 +485,8 @@ def test_create_missing_parameter_tensor(self): self.assertEqual(len(Quantity.objects.all()), 0) self.assertEqual(len(OperationTree.objects.all()), 0) + # TODO: Test variables have corresponding reference quantities + @classmethod def tearDownClass(cls): cls.user.delete() @@ -481,13 +519,29 @@ def setUpTestData(cls): cls.constant = OperationTree.objects.create( id=2, operation="constant", parameters={"value": 1} ) - # cls.dataset.data_contents.add(cls.quantity) + cls.ref_quantity = ReferenceQuantity.objects.create( + id=1, + value=5, + variance=0, + units="none", + hash=111, + derived_quantity=cls.quantity, + ) cls.client = APIClient() cls.client.force_authenticate(cls.user) # Test accessing a quantity with no operations performed def test_get_operation_tree_none(self): + self.ref_quantity.delete() request = self.client.get("/v1/data/set/1/") + self.ref_quantity = ReferenceQuantity.objects.create( + id=1, + value=5, + variance=0, + units="none", + hash=111, + derived_quantity=self.quantity, + ) self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertEqual( request.data["data_contents"][0], @@ -537,7 +591,14 @@ def test_get_operation_tree_unary(self): } }, }, - "references": [], + "references": [ + { + "value": 5, + "variance": 0, + "units": "none", + "hash": 111, + } + ], }, }, ) From 9793c6233759b60660163afc78e88ccdc50c09fd Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 16 Apr 2025 16:02:50 -0400 Subject: [PATCH 455/675] Add tests to test updating a DataSet --- sasdata/fair_database/data/serializers.py | 8 +-- .../fair_database/data/test/test_dataset.py | 61 +++++++++++++++++++ 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 2f463a7bc..95c8016c5 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -5,6 +5,7 @@ # TODO: more custom validation, particularly for specific nested dictionary structures +# TODO: custom update methods for nested structures class DataFileSerializer(serializers.ModelSerializer): @@ -333,7 +334,7 @@ def validate(self, data): and not data["is_public"] ): raise serializers.ValidationError("private data must have an owner") - if "current_user" in data and data["current_user"] is None: + if "current_user" in data and data["current_user"] == "": if "is_public" in data: if not "is_public": raise serializers.ValidationError("private data must have an owner") @@ -364,7 +365,6 @@ def create(self, validated_data): return dataset # TODO: account for updating other attributes - # TODO: account for metadata potentially being null # Update a DataSet instance def update(self, instance, validated_data): if "metadata" in validated_data: @@ -379,8 +379,6 @@ def update(self, instance, validated_data): instance.save() return instance - # TODO: custom method for database to serializer representation - class PublishedStateSerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the PublishedState model.""" @@ -419,7 +417,7 @@ def validate(self, data): and not data["is_public"] ): raise serializers.ValidationError("private sessions must have an owner") - if "current_user" in data and data["current_user"] is None: + if "current_user" in data and data["current_user"] == "": if "is_public" in data: if not "is_public": raise serializers.ValidationError( diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 385b5c923..6d2769083 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -478,6 +478,67 @@ def test_update_unowned_dataset(self): self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) + # Test updating metadata + def test_update_dataset_metadata(self): + new_metadata = { + "title": "Updated Metadata", + "run": ["X"], + "definition": "update test", + "instrument": "none", + "process": "none", + "sample": "none", + } + request = self.auth_client1.put( + "/v1/data/set/1/", data={"metadata": new_metadata}, format="json" + ) + dataset = DataSet.objects.get(id=1) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(dataset.metadata.title, "Updated Metadata") + self.assertEqual(dataset.metadata.id, 1) + self.assertEqual(len(MetaData.objects.all()), 1) + dataset.metadata.delete() + self.metadata = MetaData.objects.create( + id=1, + title="Metadata", + run=0, + definition="test", + instrument="none", + process="none", + sample="none", + dataset=self.public_dataset, + ) + + # Test partially updating metadata + def test_update_dataset_partial_metadata(self): + request = self.auth_client1.put( + "/v1/data/set/1/", + data={"metadata": {"title": "Different Title"}}, + format="json", + ) + dataset = DataSet.objects.get(id=1) + metadata = dataset.metadata + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(metadata.title, "Different Title") + self.assertEqual(metadata.definition, "test") + self.assertEqual(metadata.id, 1) + metadata.title = "Metadata" + metadata.save() + + # Test that a dataset cannot be updated to be private and unowned + def test_update_dataset_no_private_unowned(self): + request1 = self.auth_client1.put("/v1/data/set/2/", data={"current_user": ""}) + request2 = self.auth_client1.put( + "/v1/data/set/1/", data={"current_user": "", "is_public": False} + ) + public_dataset = DataSet.objects.get(id=1) + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertEqual(DataSet.objects.get(id=2).current_user, self.user1) + self.assertEqual(public_dataset.current_user, self.user1) + self.assertFalse(public_dataset.is_public) + public_dataset.is_public = True + public_dataset.save() + # Test deleting a dataset def test_delete_dataset(self): quantity = Quantity.objects.create( From 9b6c977cc1bb776b7ba93bbf6e1166395660ec13 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Wed, 16 Apr 2025 16:15:20 -0400 Subject: [PATCH 456/675] Propagate update to session is_public to datasets --- sasdata/fair_database/data/serializers.py | 7 +++++++ sasdata/fair_database/data/test/test_dataset.py | 1 - sasdata/fair_database/data/test/test_session.py | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 95c8016c5..86c985f61 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -451,6 +451,13 @@ def create(self, validated_data): ) return session + def update(self, instance, validated_data): + if "is_public" in validated_data: + for dataset in instance.datasets.all(): + dataset.is_public = validated_data["is_public"] + dataset.save() + return super().update(instance, validated_data) + # Determine if an operation does not have parent operations def constant_or_variable(operation: str): diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 6d2769083..c4709b305 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -468,7 +468,6 @@ def test_update_public_dataset(self): self.assertEqual(DataSet.objects.get(id=1).name, "Different name") self.public_dataset.save() - # TODO: test updating metadata once metadata is figured out # TODO: test invalid updates if and when those are figured out # Test changing an unowned dataset diff --git a/sasdata/fair_database/data/test/test_session.py b/sasdata/fair_database/data/test/test_session.py index 01e1c857d..c84d2b8d4 100644 --- a/sasdata/fair_database/data/test/test_session.py +++ b/sasdata/fair_database/data/test/test_session.py @@ -450,6 +450,7 @@ def test_update_private_session(self): {"session_id": 2, "title": "Private Session", "is_public": True}, ) self.assertTrue(session.is_public) + self.assertTrue(session.datasets.get().is_public) session.is_public = False session.save() From 9d0e7b57723b02b6be537ddcf68bc84fcc29c75f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 11:00:20 -0400 Subject: [PATCH 457/675] PublishedState serialization and minor serializer create method simplifications --- sasdata/fair_database/data/serializers.py | 68 +++++++++++------------ 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 86c985f61..6f21aca5c 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -53,11 +53,6 @@ def to_representation(self, instance): data.pop("dataset") return data - # Create an entry in MetaData - def create(self, validated_data): - dataset = models.DataSet.objects.get(id=validated_data.pop("dataset")) - return models.MetaData.objects.create(dataset=dataset, **validated_data) - class OperationTreeSerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the OperationTree model.""" @@ -123,32 +118,22 @@ def to_representation(self, instance): # Create an OperationTree instance def create(self, validated_data): - quantity = None - child_operation = None parent_operation1 = None parent_operation2 = None - if "quantity" in validated_data: - quantity = models.Quantity.objects.get(id=validated_data.pop("quantity")) - if "child_operation" in validated_data: - child_operation = models.OperationTree.objects.get( - id=validated_data.pop("child_operation") - ) if not constant_or_variable(validated_data["operation"]): parent_operation1 = validated_data["parameters"].pop("a") parent_operation1["label"] = "a" if binary(validated_data["operation"]): parent_operation2 = validated_data["parameters"].pop("b") parent_operation2["label"] = "b" - operation_tree = models.OperationTree.objects.create( - quantity=quantity, child_operation=child_operation, **validated_data - ) + operation_tree = models.OperationTree.objects.create(**validated_data) if parent_operation1: - parent_operation1["child_operation"] = operation_tree.id + parent_operation1["child_operation"] = operation_tree OperationTreeSerializer.create( OperationTreeSerializer(), validated_data=parent_operation1 ) if parent_operation2: - parent_operation2["child_operation"] = operation_tree.id + parent_operation2["child_operation"] = operation_tree OperationTreeSerializer.create( OperationTreeSerializer(), validated_data=parent_operation2 ) @@ -171,16 +156,11 @@ def to_representation(self, instance): return data def create(self, validated_data): - derived_quantity = models.Quantity.objects.get( - id=validated_data.pop("derived_quantity") - ) if "label" in validated_data: validated_data.pop("label") if "history" in validated_data: validated_data.pop("history") - return models.ReferenceQuantity.objects.create( - derived_quantity=derived_quantity, **validated_data - ) + return models.ReferenceQuantity.objects.create(**validated_data) class QuantitySerializer(serializers.ModelSerializer): @@ -247,7 +227,6 @@ def to_representation(self, instance): # Create a Quantity instance def create(self, validated_data): - dataset = models.DataSet.objects.get(id=validated_data.pop("dataset")) operations_tree = None references = None if "operation_tree" in validated_data: @@ -256,15 +235,15 @@ def create(self, validated_data): history = validated_data.pop("history") if history and "references" in history: references = history.pop("references") - quantity = models.Quantity.objects.create(dataset=dataset, **validated_data) + quantity = models.Quantity.objects.create(**validated_data) if operations_tree: - operations_tree["quantity"] = quantity.id + operations_tree["quantity"] = quantity OperationTreeSerializer.create( OperationTreeSerializer(), validated_data=operations_tree ) if references: for ref in references: - ref["derived_quantity"] = quantity.id + ref["derived_quantity"] = quantity ReferenceQuantitySerializer.create( ReferenceQuantitySerializer(), validated_data=ref ) @@ -345,22 +324,19 @@ def validate(self, data): # Create a DataSet instance def create(self, validated_data): - session = None files = [] if self.context["request"].user.is_authenticated: validated_data["current_user"] = self.context["request"].user metadata_raw = validated_data.pop("metadata") - if "session" in validated_data: - session = models.Session.objects.get(id=validated_data.pop("session")) data_contents = validated_data.pop("data_contents") if "files" in validated_data: files = validated_data.pop("files") - dataset = models.DataSet.objects.create(session=session, **validated_data) + dataset = models.DataSet.objects.create(**validated_data) dataset.files.set(files) - metadata_raw["dataset"] = dataset.id + metadata_raw["dataset"] = dataset MetaDataSerializer.create(MetaDataSerializer(), validated_data=metadata_raw) for d in data_contents: - d["dataset"] = dataset.id + d["dataset"] = dataset QuantitySerializer.create(QuantitySerializer(), validated_data=d) return dataset @@ -391,6 +367,20 @@ class Meta: model = models.PublishedState fields = "__all__" + def to_internal_value(self, data): + data_copy = data.copy() + data_copy["doi"] = "http://127.0.0.1:8000/v1/data/session/" + return data_copy + + def create(self, validated_data): + # TODO: generate DOI + validated_data["doi"] = ( + "http://127.0.0.1:8000/v1/data/session/" + + str(validated_data["session"].id) + + "/" + ) + models.PublishedState.objects.create(**validated_data) + class SessionSerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the Session model.""" @@ -440,12 +430,20 @@ def to_internal_value(self, data): # Create a Session instance def create(self, validated_data): + published_state = None if self.context["request"].user.is_authenticated: validated_data["current_user"] = self.context["request"].user + if "published_state" in validated_data: + published_state = validated_data.pop("published_state") datasets = validated_data.pop("datasets") session = models.Session.objects.create(**validated_data) + if published_state: + published_state["session"] = session + PublishedStateSerializer.create( + PublishedStateSerializer(), validated_data=published_state + ) for dataset in datasets: - dataset["session"] = session.id + dataset["session"] = session DataSetSerializer.create( DataSetSerializer(context=self.context), validated_data=dataset ) From af2bbcc6d75b962e511331b705f6f9537358bca1 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 11:16:06 -0400 Subject: [PATCH 458/675] Outline of PublishedState views --- sasdata/fair_database/data/views.py | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 665c7620e..1c7fbd0f3 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -501,3 +501,44 @@ def put(self, request, data_id, version=None): "access": serializer.data["access"], } return Response(response_data) + + +class PublishedStateView(APIView): + """ + View associated with the PublishedState model. + + Functionality for viewing a list of session published states and for + creating a published state. + """ + + # View a list of accessible sessions' published states + def get(self, request, version=None): + pass + + # Create a published state for an existing session + def post(self, request, version=None): + pass + + # Create a published state for an existing session + def put(self, request, version=None): + return self.post(request, version) + + +class SinglePublishedStateView(APIView): + """ + View associated with specific session published states. + + Functionality for viewing, modifying, and deleting individual published states. + """ + + # View a specific published state + def get(self, request, ps_id, version=None): + pass + + # Modify a published state + def put(self, request, ps_id, version=None): + pass + + # Delete a published state + def delete(self, request, ps_id, version=None): + pass From 8d263321cfc410c82b23eafa68e636cb7af25d47 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 11:31:22 -0400 Subject: [PATCH 459/675] PublishedState list method --- sasdata/fair_database/data/views.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 1c7fbd0f3..973560a58 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -20,7 +20,7 @@ AccessManagementSerializer, SessionSerializer, ) -from data.models import DataFile, DataSet, Session +from data.models import DataFile, DataSet, PublishedState, Session from data.forms import DataFileForm from fair_database import permissions from fair_database.permissions import DataPermission @@ -513,7 +513,19 @@ class PublishedStateView(APIView): # View a list of accessible sessions' published states def get(self, request, version=None): - pass + ps_list = {"published_state_ids": {}} + published_states = PublishedState.objects.all() + if "username" in request.GET: + user = get_object_or_404(User, username=request.GET["username"]) + published_states = PublishedState.objects.filter(session__current_user=user) + for ps in published_states: + if permissions.check_permissions(request, ps.session): + ps_list["published_state_ids"][ps.id] = { + "title": ps.session.title, + "published": ps.published, + "doi": ps.doi, + } + return Response(data=ps_list) # Create a published state for an existing session def post(self, request, version=None): From c253c157705458868290d35923155999662d7517 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 11:37:39 -0400 Subject: [PATCH 460/675] Serializer to restrict PublishedState update to published field --- sasdata/fair_database/data/serializers.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 6f21aca5c..a1169644b 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -382,6 +382,12 @@ def create(self, validated_data): models.PublishedState.objects.create(**validated_data) +class PublishedStateUpdateSerializer(serializers.ModelSerializer): + class Meta: + model = models.PublishedState + fields = ["published"] + + class SessionSerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the Session model.""" From a3347cc01b7cbb01f1a5728c6ab2806ea6171f1b Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 11:57:48 -0400 Subject: [PATCH 461/675] Post method for PublishedState --- sasdata/fair_database/data/views.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 973560a58..0d95cb885 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -19,6 +19,7 @@ DataSetSerializer, AccessManagementSerializer, SessionSerializer, + PublishedStateSerializer, ) from data.models import DataFile, DataSet, PublishedState, Session from data.forms import DataFileForm @@ -529,7 +530,31 @@ def get(self, request, version=None): # Create a published state for an existing session def post(self, request, version=None): - pass + serializer = PublishedStateSerializer( + data=request.data, context={"request": request} + ) + if not permissions.is_owner(request, serializer.data["session"]): + if not request.user.is_authenticated: + return HttpResponse( + "Must be authenticated to create a published state for a session", + status=401, + ) + return HttpResponseForbidden( + "Must be the session owner to create a published state for a session" + ) + if serializer.is_valid(raise_exception=True): + serializer.save() + db = serializer.instance + response = { + "published_state_id": db.id, + "session_id": db.session.id, + "title": db.session.title, + "doi": db.doi, + "published": db.published, + "current_user": request.user.username, + "is_public": db.session.is_public, + } + return Response(data=response, status=status.HTTP_201_CREATED) # Create a published state for an existing session def put(self, request, version=None): From bc7c53094904bb14a0298082849ab8b9b8546432 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 12:00:10 -0400 Subject: [PATCH 462/675] Get method for single PublishedState --- sasdata/fair_database/data/views.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 0d95cb885..e0775398a 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -570,7 +570,20 @@ class SinglePublishedStateView(APIView): # View a specific published state def get(self, request, ps_id, version=None): - pass + db = get_object_or_404(PublishedState, id=ps_id) + if not permissions.check_permissions(request, db.session): + if not request.user.is_authenticated: + return HttpResponse( + "Must be authenticated to view published state", status=401 + ) + return HttpResponseForbidden( + "You do not have permission to view this published state." + ) + serializer = PublishedStateSerializer(db) + response_data = serializer.data + if db.current_user: + response_data["current_user"] = db.current_user.username + return Response(response_data) # Modify a published state def put(self, request, ps_id, version=None): From 872531476aeea44dfc17076f3bcf845a6b9b158c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 12:03:38 -0400 Subject: [PATCH 463/675] Update method for PublishedState --- sasdata/fair_database/data/views.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index e0775398a..e8b3ab399 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -20,6 +20,7 @@ AccessManagementSerializer, SessionSerializer, PublishedStateSerializer, + PublishedStateUpdateSerializer, ) from data.models import DataFile, DataSet, PublishedState, Session from data.forms import DataFileForm @@ -587,7 +588,28 @@ def get(self, request, ps_id, version=None): # Modify a published state def put(self, request, ps_id, version=None): - pass + db = get_object_or_404(Session, id=ps_id) + if not permissions.check_permissions(request, db.session): + if not request.user.is_authenticated: + return HttpResponse( + "Must be authenticated to modify published state", status=401 + ) + return HttpResponseForbidden( + "Cannot modify a published state you do not own" + ) + serializer = PublishedStateUpdateSerializer( + db, request.data, context={"request": request}, partial=True + ) + if serializer.is_valid(raise_exception=True): + serializer.save() + data = { + "published_state_id": db.id, + "session_id": db.session.id, + "title": db.session.title, + "published": db.published, + "is_public": db.session.is_public, + } + return Response(data) # Delete a published state def delete(self, request, ps_id, version=None): From e499ea32b38c099ca29467d5e259840be332babd Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 12:05:53 -0400 Subject: [PATCH 464/675] Delete method for PublishedState --- sasdata/fair_database/data/views.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index e8b3ab399..34d8216b7 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -588,7 +588,7 @@ def get(self, request, ps_id, version=None): # Modify a published state def put(self, request, ps_id, version=None): - db = get_object_or_404(Session, id=ps_id) + db = get_object_or_404(PublishedState, id=ps_id) if not permissions.check_permissions(request, db.session): if not request.user.is_authenticated: return HttpResponse( @@ -613,4 +613,12 @@ def put(self, request, ps_id, version=None): # Delete a published state def delete(self, request, ps_id, version=None): - pass + db = get_object_or_404(PublishedState, id=ps_id) + if not permissions.check_permissions(request, db): + if not request.user.is_authenticated: + return HttpResponse( + "Must be authenticated to delete a published state", status=401 + ) + return HttpResponseForbidden("Not authorized to delete") + db.delete() + return Response({"success": True}) From e0ed9c04caf6f236fd463323e920d983d9a17ca4 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 13:18:23 -0400 Subject: [PATCH 465/675] Planning for PublishedState testing --- .../data/test/test_published_state.py | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 sasdata/fair_database/data/test/test_published_state.py diff --git a/sasdata/fair_database/data/test/test_published_state.py b/sasdata/fair_database/data/test/test_published_state.py new file mode 100644 index 000000000..8d0d28f84 --- /dev/null +++ b/sasdata/fair_database/data/test/test_published_state.py @@ -0,0 +1,54 @@ +from rest_framework.test import APITestCase + + +class TestSessionWithPublishedState(APITestCase): + @classmethod + def setUpTestData(cls): + pass + + # Test creating a session with a published state + # Should this just be a part of sessions testing? + + # Test GET on a session with a published state + + # Test PUT on nested published state + + # Test cascading delete + + @classmethod + def tearDownClass(cls): + pass + + +class TestPublishedStateView(APITestCase): + @classmethod + def setUpTestData(cls): + pass + + # Test listing published states - various permissions + + # Test creating a published state + + # Test can only create a published state for your own sessions + + # Test can't create a second published state for a session + + @classmethod + def tearDownClass(cls): + pass + + +class TestSinglePublishedStateView(APITestCase): + @classmethod + def setUpTestData(cls): + pass + + # Test viewing a published state - various permissions + + # Test updating a published state + + # Test deleting a published state - session not deleted + + @classmethod + def tearDownClass(cls): + pass From 45402c45e8867ad656f5b9e40f9bfd02460b9cad Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 13:26:16 -0400 Subject: [PATCH 466/675] PublishedState urls --- sasdata/fair_database/data/urls.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sasdata/fair_database/data/urls.py b/sasdata/fair_database/data/urls.py index f71702ce2..0e94f60c7 100644 --- a/sasdata/fair_database/data/urls.py +++ b/sasdata/fair_database/data/urls.py @@ -36,4 +36,14 @@ views.SessionUsersView.as_view(), name="manage access to sessions", ), + path( + "published/", + views.PublishedStateView.as_view(), + name="view and create published states", + ), + path( + "published//", + views.SinglePublishedStateView.as_view(), + name="load, modify, delete published states", + ), ] From 1e1c7b1819bf24b2e091dc2f193a6ba7f3ca989a Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 13:35:00 -0400 Subject: [PATCH 467/675] Test listing published states --- .../data/test/test_published_state.py | 145 +++++++++++++++++- 1 file changed, 142 insertions(+), 3 deletions(-) diff --git a/sasdata/fair_database/data/test/test_published_state.py b/sasdata/fair_database/data/test/test_published_state.py index 8d0d28f84..1b293e633 100644 --- a/sasdata/fair_database/data/test/test_published_state.py +++ b/sasdata/fair_database/data/test/test_published_state.py @@ -1,4 +1,8 @@ -from rest_framework.test import APITestCase +from django.contrib.auth.models import User +from rest_framework import status +from rest_framework.test import APIClient, APITestCase + +from data.models import PublishedState, Session class TestSessionWithPublishedState(APITestCase): @@ -23,9 +27,140 @@ def tearDownClass(cls): class TestPublishedStateView(APITestCase): @classmethod def setUpTestData(cls): - pass + cls.user1 = User.objects.create_user( + id=1, username="testUser1", password="secret" + ) + cls.user2 = User.objects.create_user( + id=2, username="testUser2", password="secret" + ) + cls.public_session = Session.objects.create( + id=1, current_user=cls.user1, title="Public Session", is_public=True + ) + cls.private_session = Session.objects.create( + id=2, current_user=cls.user1, title="Private Session", is_public=False + ) + cls.unowned_session = Session.objects.create( + id=3, title="Unowned Session", is_public=True + ) + cls.public_ps = PublishedState.objects.create( + id=1, + doi="http://127.0.0.1:8000/v1/data/session/1/", + published=True, + session=cls.public_session, + ) + cls.private_ps = PublishedState.objects.create( + id=2, + doi="http://127.0.0.1:8000/v1/data/session/2/", + published=False, + session=cls.private_session, + ) + cls.unowned_ps = PublishedState.objects.create( + id=3, + doi="http://127.0.0.1:8000/v1/data/session/3/", + published=True, + session=cls.unowned_session, + ) + cls.auth_client1 = APIClient() + cls.auth_client2 = APIClient() + cls.auth_client1.force_authenticate(cls.user1) + cls.auth_client2.force_authenticate(cls.user2) # Test listing published states - various permissions + def test_list_published_states_private(self): + request = self.auth_client1.get("/v1/data/published/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "published_state_ids": { + 1: { + "title": "Public Session", + "published": True, + "doi": "http://127.0.0.1:8000/v1/data/session/1/", + }, + 2: { + "title": "Private Session", + "published": False, + "doi": "http://127.0.0.1:8000/v1/data/session/2/", + }, + 3: { + "title": "Unowned Session", + "published": True, + "doi": "http://127.0.0.1:8000/v1/data/session/3/", + }, + } + }, + ) + + def test_list_published_states_public(self): + request = self.auth_client2.get("/v1/data/published/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "published_state_ids": { + 1: { + "title": "Public Session", + "published": True, + "doi": "http://127.0.0.1:8000/v1/data/session/1/", + }, + 3: { + "title": "Unowned Session", + "published": True, + "doi": "http://127.0.0.1:8000/v1/data/session/3/", + }, + } + }, + ) + + def test_list_published_states_shared(self): + self.private_session.users.add(self.user2) + request = self.auth_client2.get("/v1/data/published/") + self.private_session.users.remove(self.user2) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "published_state_ids": { + 1: { + "title": "Public Session", + "published": True, + "doi": "http://127.0.0.1:8000/v1/data/session/1/", + }, + 2: { + "title": "Private Session", + "published": False, + "doi": "http://127.0.0.1:8000/v1/data/session/2/", + }, + 3: { + "title": "Unowned Session", + "published": True, + "doi": "http://127.0.0.1:8000/v1/data/session/3/", + }, + } + }, + ) + + def test_list_published_states_unauthenticated(self): + request = self.client.get("/v1/data/published/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "published_state_ids": { + 1: { + "title": "Public Session", + "published": True, + "doi": "http://127.0.0.1:8000/v1/data/session/1/", + }, + 3: { + "title": "Unowned Session", + "published": True, + "doi": "http://127.0.0.1:8000/v1/data/session/3/", + }, + } + }, + ) # Test creating a published state @@ -35,7 +170,11 @@ def setUpTestData(cls): @classmethod def tearDownClass(cls): - pass + cls.public_session.delete() + cls.private_session.delete() + cls.unowned_session.delete() + cls.user1.delete() + cls.user2.delete() class TestSinglePublishedStateView(APITestCase): From c3deba3f35c16a0ad1d6c1ffb97cdca4c3d1e617 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 14:19:00 -0400 Subject: [PATCH 468/675] Fix bugs in PublishedState creation --- sasdata/fair_database/data/serializers.py | 6 +++--- sasdata/fair_database/data/views.py | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index a1169644b..e743e21b4 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -360,7 +360,7 @@ class PublishedStateSerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the PublishedState model.""" session = serializers.PrimaryKeyRelatedField( - queryset=models.Session, required=False, allow_null=True + queryset=models.Session.objects, required=False, allow_null=True ) class Meta: @@ -370,7 +370,7 @@ class Meta: def to_internal_value(self, data): data_copy = data.copy() data_copy["doi"] = "http://127.0.0.1:8000/v1/data/session/" - return data_copy + return super().to_internal_value(data_copy) def create(self, validated_data): # TODO: generate DOI @@ -379,7 +379,7 @@ def create(self, validated_data): + str(validated_data["session"].id) + "/" ) - models.PublishedState.objects.create(**validated_data) + return models.PublishedState.objects.create(**validated_data) class PublishedStateUpdateSerializer(serializers.ModelSerializer): diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 34d8216b7..b6ef0a3bd 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -534,16 +534,17 @@ def post(self, request, version=None): serializer = PublishedStateSerializer( data=request.data, context={"request": request} ) - if not permissions.is_owner(request, serializer.data["session"]): - if not request.user.is_authenticated: - return HttpResponse( - "Must be authenticated to create a published state for a session", - status=401, - ) - return HttpResponseForbidden( - "Must be the session owner to create a published state for a session" - ) if serializer.is_valid(raise_exception=True): + print(serializer.validated_data["session"]) + if not permissions.is_owner(request, serializer.validated_data["session"]): + if not request.user.is_authenticated: + return HttpResponse( + "Must be authenticated to create a published state for a session", + status=401, + ) + return HttpResponseForbidden( + "Must be the session owner to create a published state for a session" + ) serializer.save() db = serializer.instance response = { From e1953f53121ebbe2079d0c69368f44625dcfa01d Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 14:42:27 -0400 Subject: [PATCH 469/675] Check that session doesn't already have a published state --- sasdata/fair_database/data/serializers.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index e743e21b4..1d94e35b4 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -367,6 +367,16 @@ class Meta: model = models.PublishedState fields = "__all__" + def validate_session(self, value): + try: + published = value.published_state + if published is not None: + raise serializers.ValidationError( + "Only one published state per session" + ) + except models.Session.published_state.RelatedObjectDoesNotExist: + return value + def to_internal_value(self, data): data_copy = data.copy() data_copy["doi"] = "http://127.0.0.1:8000/v1/data/session/" From d1a9099e47e1e057df1270f3672f61692d404b08 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 14:42:48 -0400 Subject: [PATCH 470/675] Test creating a published state --- .../data/test/test_published_state.py | 112 ++++++++++++++++-- sasdata/fair_database/data/views.py | 1 - 2 files changed, 99 insertions(+), 14 deletions(-) diff --git a/sasdata/fair_database/data/test/test_published_state.py b/sasdata/fair_database/data/test/test_published_state.py index 1b293e633..a3deb06a6 100644 --- a/sasdata/fair_database/data/test/test_published_state.py +++ b/sasdata/fair_database/data/test/test_published_state.py @@ -1,10 +1,16 @@ from django.contrib.auth.models import User +from django.db.models import Max from rest_framework import status from rest_framework.test import APIClient, APITestCase from data.models import PublishedState, Session +# TODO: account for non-placeholder doi +def doi_generator(id: int): + return "http://127.0.0.1:8000/v1/data/session/" + str(id) + "/" + + class TestSessionWithPublishedState(APITestCase): @classmethod def setUpTestData(cls): @@ -42,21 +48,24 @@ def setUpTestData(cls): cls.unowned_session = Session.objects.create( id=3, title="Unowned Session", is_public=True ) + cls.unpublished_session = Session.objects.create( + id=4, current_user=cls.user1, title="Publishable Session", is_public=True + ) cls.public_ps = PublishedState.objects.create( id=1, - doi="http://127.0.0.1:8000/v1/data/session/1/", + doi=doi_generator(1), published=True, session=cls.public_session, ) cls.private_ps = PublishedState.objects.create( id=2, - doi="http://127.0.0.1:8000/v1/data/session/2/", + doi=doi_generator(2), published=False, session=cls.private_session, ) cls.unowned_ps = PublishedState.objects.create( id=3, - doi="http://127.0.0.1:8000/v1/data/session/3/", + doi=doi_generator(3), published=True, session=cls.unowned_session, ) @@ -76,17 +85,17 @@ def test_list_published_states_private(self): 1: { "title": "Public Session", "published": True, - "doi": "http://127.0.0.1:8000/v1/data/session/1/", + "doi": doi_generator(1), }, 2: { "title": "Private Session", "published": False, - "doi": "http://127.0.0.1:8000/v1/data/session/2/", + "doi": doi_generator(2), }, 3: { "title": "Unowned Session", "published": True, - "doi": "http://127.0.0.1:8000/v1/data/session/3/", + "doi": doi_generator(3), }, } }, @@ -102,12 +111,12 @@ def test_list_published_states_public(self): 1: { "title": "Public Session", "published": True, - "doi": "http://127.0.0.1:8000/v1/data/session/1/", + "doi": doi_generator(1), }, 3: { "title": "Unowned Session", "published": True, - "doi": "http://127.0.0.1:8000/v1/data/session/3/", + "doi": doi_generator(3), }, } }, @@ -125,17 +134,17 @@ def test_list_published_states_shared(self): 1: { "title": "Public Session", "published": True, - "doi": "http://127.0.0.1:8000/v1/data/session/1/", + "doi": doi_generator(1), }, 2: { "title": "Private Session", "published": False, - "doi": "http://127.0.0.1:8000/v1/data/session/2/", + "doi": doi_generator(2), }, 3: { "title": "Unowned Session", "published": True, - "doi": "http://127.0.0.1:8000/v1/data/session/3/", + "doi": doi_generator(3), }, } }, @@ -151,17 +160,94 @@ def test_list_published_states_unauthenticated(self): 1: { "title": "Public Session", "published": True, - "doi": "http://127.0.0.1:8000/v1/data/session/1/", + "doi": doi_generator(1), }, 3: { "title": "Unowned Session", "published": True, - "doi": "http://127.0.0.1:8000/v1/data/session/3/", + "doi": doi_generator(3), }, } }, ) + def test_published_state_created_private(self): + self.unpublished_session.is_public = False + self.unpublished_session.save() + published_state = {"published": True, "session": 4} + request = self.auth_client1.post("/v1/data/published/", data=published_state) + max_id = PublishedState.objects.aggregate(Max("id"))["id__max"] + new_ps = PublishedState.objects.get(id=max_id) + self.publishable_session = Session.objects.get(id=4) + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual( + request.data, + { + "published_state_id": max_id, + "session_id": 4, + "title": "Publishable Session", + "doi": doi_generator(4), + "published": True, + "current_user": "testUser1", + "is_public": False, + }, + ) + self.assertEqual(self.publishable_session.published_state, new_ps) + self.assertEqual(new_ps.session, self.publishable_session) + new_ps.delete() + self.unpublished_session.is_public = True + self.unpublished_session.save() + + def test_published_state_created_public(self): + published_state = {"published": False, "session": 4} + request = self.auth_client1.post("/v1/data/published/", data=published_state) + max_id = PublishedState.objects.aggregate(Max("id"))["id__max"] + new_ps = PublishedState.objects.get(id=max_id) + self.publishable_session = Session.objects.get(id=4) + self.assertEqual(request.status_code, status.HTTP_201_CREATED) + self.assertEqual( + request.data, + { + "published_state_id": max_id, + "session_id": 4, + "title": "Publishable Session", + "doi": doi_generator(4), + "published": False, + "current_user": "testUser1", + "is_public": True, + }, + ) + self.assertEqual(self.publishable_session.published_state, new_ps) + self.assertEqual(new_ps.session, self.publishable_session) + new_ps.delete() + + def test_published_state_created_unowned(self): + self.unpublished_session.current_user = None + self.unpublished_session.save() + published_state = {"published": True, "session": 4} + request = self.auth_client1.post("/v1/data/published/", data=published_state) + self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(len(PublishedState.objects.all()), 3) + self.unpublished_session.current_user = self.user1 + self.unpublished_session.save() + + def test_published_state_created_unauthenticated(self): + published_state = {"published": True, "session": 4} + request = self.client.post("/v1/data/published/", data=published_state) + self.assertEqual(request.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertEqual(len(PublishedState.objects.all()), 3) + + def test_published_state_created_unauthorized(self): + published_state = {"published": True, "session": 4} + request = self.auth_client2.post("/v1/data/published/", data=published_state) + self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(len(PublishedState.objects.all()), 3) + + def test_no_duplicate_published_states(self): + published_state = {"published": True, "session": 1} + request = self.auth_client1.post("/v1/data/published/", data=published_state) + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + # Test creating a published state # Test can only create a published state for your own sessions diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index b6ef0a3bd..0ea984b44 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -535,7 +535,6 @@ def post(self, request, version=None): data=request.data, context={"request": request} ) if serializer.is_valid(raise_exception=True): - print(serializer.validated_data["session"]) if not permissions.is_owner(request, serializer.validated_data["session"]): if not request.user.is_authenticated: return HttpResponse( From c213f6c8d892b839ab6f6cda6067ce259e80ef1c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 15:15:35 -0400 Subject: [PATCH 471/675] Add fields to PublishedState get response --- sasdata/fair_database/data/views.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 0ea984b44..2f6204612 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -582,8 +582,12 @@ def get(self, request, ps_id, version=None): ) serializer = PublishedStateSerializer(db) response_data = serializer.data - if db.current_user: - response_data["current_user"] = db.current_user.username + response_data["title"] = db.session.title + if db.session.current_user: + response_data["current_user"] = db.session.current_user.username + else: + response_data["current_user"] = "" + response_data["is_public"] = db.session.is_public return Response(response_data) # Modify a published state From 5a67a816e69c30a15cc937e850fc947384d1191f Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 15:16:07 -0400 Subject: [PATCH 472/675] Test individual PublishedState get method --- .../data/test/test_published_state.py | 118 +++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/test/test_published_state.py b/sasdata/fair_database/data/test/test_published_state.py index a3deb06a6..d2dd22960 100644 --- a/sasdata/fair_database/data/test/test_published_state.py +++ b/sasdata/fair_database/data/test/test_published_state.py @@ -266,9 +266,119 @@ def tearDownClass(cls): class TestSinglePublishedStateView(APITestCase): @classmethod def setUpTestData(cls): - pass + cls.user1 = User.objects.create_user( + id=1, username="testUser1", password="secret" + ) + cls.user2 = User.objects.create_user( + id=2, username="testUser2", password="secret" + ) + cls.public_session = Session.objects.create( + id=1, current_user=cls.user1, title="Public Session", is_public=True + ) + cls.private_session = Session.objects.create( + id=2, current_user=cls.user1, title="Private Session", is_public=False + ) + cls.unowned_session = Session.objects.create( + id=3, title="Unowned Session", is_public=True + ) + cls.public_ps = PublishedState.objects.create( + id=1, + doi=doi_generator(1), + published=True, + session=cls.public_session, + ) + cls.private_ps = PublishedState.objects.create( + id=2, + doi=doi_generator(2), + published=False, + session=cls.private_session, + ) + cls.unowned_ps = PublishedState.objects.create( + id=3, + doi=doi_generator(3), + published=True, + session=cls.unowned_session, + ) + cls.auth_client1 = APIClient() + cls.auth_client2 = APIClient() + cls.auth_client1.force_authenticate(cls.user1) + cls.auth_client2.force_authenticate(cls.user2) # Test viewing a published state - various permissions + def test_get_public_published_state(self): + request1 = self.auth_client2.get("/v1/data/published/1/") + request2 = self.client.get("/v1/data/published/1/") + self.assertEqual(request1.status_code, status.HTTP_200_OK) + self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertEqual( + request1.data, + { + "id": 1, + "doi": doi_generator(1), + "published": True, + "session": 1, + "title": "Public Session", + "current_user": "testUser1", + "is_public": True, + }, + ) + self.assertEqual(request1.data, request2.data) + + def test_get_private_published_state(self): + request = self.auth_client1.get("/v1/data/published/2/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 2, + "doi": doi_generator(2), + "published": False, + "session": 2, + "title": "Private Session", + "current_user": "testUser1", + "is_public": False, + }, + ) + + def test_get_unowned_published_state(self): + request = self.auth_client1.get("/v1/data/published/3/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 3, + "doi": doi_generator(3), + "published": True, + "session": 3, + "title": "Unowned Session", + "current_user": "", + "is_public": True, + }, + ) + + def test_get_shared_published_state(self): + self.private_session.users.add(self.user2) + request = self.auth_client2.get("/v1/data/published/2/") + self.private_session.users.remove(self.user2) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "id": 2, + "doi": doi_generator(2), + "published": False, + "session": 2, + "title": "Private Session", + "current_user": "testUser1", + "is_public": False, + }, + ) + + def test_get_private_published_state_unauthorized(self): + request1 = self.client.get("/v1/data/published/2/") + request2 = self.auth_client2.get("/v1/data/published/2/") + self.assertEqual(request1.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) # Test updating a published state @@ -276,4 +386,8 @@ def setUpTestData(cls): @classmethod def tearDownClass(cls): - pass + cls.public_session.delete() + cls.private_session.delete() + cls.unowned_session.delete() + cls.user1.delete() + cls.user2.delete() From 48884866357a32320510ae69ce70d8df701596d8 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 16:02:58 -0400 Subject: [PATCH 473/675] Test PublishedState update method --- .../data/test/test_published_state.py | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/sasdata/fair_database/data/test/test_published_state.py b/sasdata/fair_database/data/test/test_published_state.py index d2dd22960..70c60c13f 100644 --- a/sasdata/fair_database/data/test/test_published_state.py +++ b/sasdata/fair_database/data/test/test_published_state.py @@ -381,6 +381,80 @@ def test_get_private_published_state_unauthorized(self): self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) # Test updating a published state + def test_update_public_published_state(self): + request = self.auth_client1.put( + "/v1/data/published/1/", data={"published": False} + ) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "published_state_id": 1, + "session_id": 1, + "title": "Public Session", + "published": False, + "is_public": True, + }, + ) + self.assertFalse(PublishedState.objects.get(id=1).published) + self.public_ps.save() + + def test_update_private_published_state(self): + request = self.auth_client1.put( + "/v1/data/published/2/", data={"published": True} + ) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "published_state_id": 2, + "session_id": 2, + "title": "Private Session", + "published": True, + "is_public": False, + }, + ) + self.assertTrue(PublishedState.objects.get(id=2).published) + self.private_ps.save() + + def test_update_unowned_published_state(self): + request1 = self.auth_client1.put( + "/v1/data/published/3/", data={"published": False} + ) + request2 = self.client.put("/v1/data/published/3/", data={"published": False}) + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertTrue(PublishedState.objects.get(id=3).published) + + def test_update_public_published_state_unauthorized(self): + request1 = self.auth_client2.put( + "/v1/data/published/1/", data={"published": False} + ) + self.public_session.users.add(self.user2) + request2 = self.auth_client2.put( + "/v1/data/published/1/", data={"published": False} + ) + self.public_session.users.remove(self.user2) + request3 = self.client.put("/v1/data/published/1/", data={"published": False}) + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request3.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertTrue(PublishedState.objects.get(id=1).published) + + def test_update_private_published_state_unauthorized(self): + request1 = self.auth_client2.put( + "/v1/data/published/2/", data={"published": True} + ) + self.public_session.users.add(self.user2) + request2 = self.auth_client2.put( + "/v1/data/published/2/", data={"published": True} + ) + self.public_session.users.remove(self.user2) + request3 = self.client.put("/v1/data/published/2/", data={"published": True}) + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request3.status_code, status.HTTP_401_UNAUTHORIZED) + self.assertFalse(PublishedState.objects.get(id=2).published) # Test deleting a published state - session not deleted From 1fb3ef6ee2471808f249c5cb4d1ae11c91706db6 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Thu, 17 Apr 2025 16:16:36 -0400 Subject: [PATCH 474/675] Test PublishedState delete method --- .../data/test/test_published_state.py | 30 +++++++++++++++++++ sasdata/fair_database/data/views.py | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/sasdata/fair_database/data/test/test_published_state.py b/sasdata/fair_database/data/test/test_published_state.py index 70c60c13f..f2abfc4a9 100644 --- a/sasdata/fair_database/data/test/test_published_state.py +++ b/sasdata/fair_database/data/test/test_published_state.py @@ -457,6 +457,36 @@ def test_update_private_published_state_unauthorized(self): self.assertFalse(PublishedState.objects.get(id=2).published) # Test deleting a published state - session not deleted + def test_delete_private_published_state(self): + request = self.auth_client1.delete("/v1/data/published/2/") + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(len(PublishedState.objects.all()), 2) + self.assertEqual(len(Session.objects.all()), 3) + self.assertRaises(PublishedState.DoesNotExist, PublishedState.objects.get, id=2) + self.private_ps = PublishedState.objects.create( + id=2, + doi=doi_generator(2), + published=False, + session=self.private_session, + ) + + def test_delete_private_published_state_unauthorized(self): + request1 = self.auth_client2.delete("/v1/data/published/2/") + self.private_session.users.add(self.user2) + request2 = self.auth_client2.delete("/v1/data/published/2/") + self.private_session.users.remove(self.user2) + request3 = self.client.delete("/v1/data/published/2/") + self.assertEqual(request1.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(request3.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_cant_delete_public_published_state(self): + request = self.auth_client1.delete("/v1/data/published/1/") + self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + + def test_delete_unowned_published_state(self): + request = self.auth_client1.delete("/v1/data/published/3/") + self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) @classmethod def tearDownClass(cls): diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 2f6204612..c3ed3e406 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -618,7 +618,7 @@ def put(self, request, ps_id, version=None): # Delete a published state def delete(self, request, ps_id, version=None): db = get_object_or_404(PublishedState, id=ps_id) - if not permissions.check_permissions(request, db): + if not permissions.check_permissions(request, db.session): if not request.user.is_authenticated: return HttpResponse( "Must be authenticated to delete a published state", status=401 From 2055ad74b3c47f5ecd9567209a03126ae6014678 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 21 Apr 2025 11:03:01 -0400 Subject: [PATCH 475/675] Session creation example script --- .../fair_database/create_example_session.py | 87 +++++++++++++++++++ .../fair_database/upload_example_data.py | 2 +- 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 sasdata/fair_database/fair_database/create_example_session.py diff --git a/sasdata/fair_database/fair_database/create_example_session.py b/sasdata/fair_database/fair_database/create_example_session.py new file mode 100644 index 000000000..70d255c39 --- /dev/null +++ b/sasdata/fair_database/fair_database/create_example_session.py @@ -0,0 +1,87 @@ +import requests + +session = { + "title": "Example Session", + "datasets": [ + { + "name": "Dataset 1", + "metadata": { + "title": "Metadata 1", + "run": 1, + "description": "test", + "instrument": {}, + "process": {}, + "sample": {}, + }, + "data_contents": [ + { + "value": 0, + "variance": 0, + "units": "no", + "hash": 0, + "label": "Quantity 1", + "history": {"operation_tree": {}, "references": []}, + } + ], + }, + { + "name": "Dataset 2", + "metadata": { + "title": "Metadata 2", + "run": 2, + "description": "test", + "instrument": {}, + "process": {}, + "sample": {}, + }, + "data_contents": [ + { + "label": "Quantity 2", + "value": {"array_contents": [0, 0, 0, 0], "shape": (2, 2)}, + "variance": {"array_contents": [0, 0, 0, 0], "shape": (2, 2)}, + "units": "none", + "hash": 0, + "history": { + "operation_tree": { + "operation": "neg", + "parameters": { + "a": { + "operation": "mul", + "parameters": { + "a": { + "operation": "constant", + "parameters": { + "value": {"type": "int", "value": 7} + }, + }, + "b": { + "operation": "variable", + "parameters": { + "hash_value": 111, + "name": "x", + }, + }, + }, + }, + }, + }, + "references": [ + { + "value": 5, + "variance": 0, + "units": "none", + "hash": 111, + "history": {}, + } + ], + }, + } + ], + }, + ], + "is_public": True, +} + +url = "http://127.0.0.1:8000/v1/data/session/" + +requests.request("POST", url, json=session) diff --git a/sasdata/fair_database/fair_database/upload_example_data.py b/sasdata/fair_database/fair_database/upload_example_data.py index 1b16fdca4..60e21af48 100644 --- a/sasdata/fair_database/fair_database/upload_example_data.py +++ b/sasdata/fair_database/fair_database/upload_example_data.py @@ -35,7 +35,7 @@ def parse_sesans(): def upload_file(file_path): - url = "http://localhost:8000/v1/data/upload/" + url = "http://localhost:8000/v1/data/file/" file = open(file_path, "rb") requests.request("POST", url, data={"is_public": True}, files={"file": file}) From 9aa8e25cfb6c146bbdbb9272562afcec8db6d50b Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 21 Apr 2025 11:38:28 -0400 Subject: [PATCH 476/675] Add models to admin interface --- sasdata/fair_database/data/admin.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sasdata/fair_database/data/admin.py b/sasdata/fair_database/data/admin.py index e000e5320..1a5e431cc 100644 --- a/sasdata/fair_database/data/admin.py +++ b/sasdata/fair_database/data/admin.py @@ -1,4 +1,11 @@ from django.contrib import admin -from data.models import DataFile +from data import models -admin.site.register(DataFile) +admin.site.register(models.DataFile) +admin.site.register(models.Session) +admin.site.register(models.PublishedState) +admin.site.register(models.DataSet) +admin.site.register(models.MetaData) +admin.site.register(models.Quantity) +admin.site.register(models.OperationTree) +admin.site.register(models.ReferenceQuantity) From 66ef37e6c377558327146d4a12aaa0ce787addde Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 21 Apr 2025 11:39:15 -0400 Subject: [PATCH 477/675] Account for json strings in post requests --- sasdata/fair_database/data/views.py | 31 +++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index c3ed3e406..466e008fc 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -1,4 +1,5 @@ import os +import json from django.contrib.auth.models import User from django.shortcuts import get_object_or_404 @@ -236,9 +237,15 @@ def get(self, request, version=None): # TODO: enable uploading files as part of dataset creation, not just associating dataset with existing files # create a dataset def post(self, request, version=None): - # TODO: JSON deserialization probably # TODO: revisit request data format - serializer = DataSetSerializer(data=request.data, context={"request": request}) + if isinstance(request.data, str): + serializer = DataSetSerializer( + data=json.loads(request.data), context={"request": request} + ) + else: + serializer = DataSetSerializer( + data=request.data, context={"request": request} + ) if serializer.is_valid(raise_exception=True): serializer.save() db = serializer.instance @@ -383,7 +390,14 @@ def get(self, request, version=None): # Create a session # TODO: revisit response data def post(self, request, version=None): - serializer = SessionSerializer(data=request.data, context={"request": request}) + if isinstance(request.data, str): + serializer = SessionSerializer( + data=json.loads(request.data), context={"request": request} + ) + else: + serializer = SessionSerializer( + data=request.data, context={"request": request} + ) if serializer.is_valid(raise_exception=True): serializer.save() db = serializer.instance @@ -531,9 +545,14 @@ def get(self, request, version=None): # Create a published state for an existing session def post(self, request, version=None): - serializer = PublishedStateSerializer( - data=request.data, context={"request": request} - ) + if isinstance(request.data, str): + serializer = PublishedStateSerializer( + data=json.loads(request.data), context={"request": request} + ) + else: + serializer = PublishedStateSerializer( + data=request.data, context={"request": request} + ) if serializer.is_valid(raise_exception=True): if not permissions.is_owner(request, serializer.validated_data["session"]): if not request.user.is_authenticated: From 636a0aa84381d6f7eea76dcf6e0c76de129214a8 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 21 Apr 2025 11:40:32 -0400 Subject: [PATCH 478/675] Enable session-based authentication --- sasdata/fair_database/fair_database/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index 6918a4de2..31ce030a3 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -94,7 +94,7 @@ REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": [ "knox.auth.TokenAuthentication", - #'rest_framework.authentication.SessionAuthentication', + "rest_framework.authentication.SessionAuthentication", ], #'DEFAULT_PERMISSION_CLASSES': [ # 'fair_database.permissions.DataPermission', From b115876dfac593856df887d204444ee8496aded2 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 21 Apr 2025 12:14:06 -0400 Subject: [PATCH 479/675] Comments for PublishedState tests --- .../data/test/test_published_state.py | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/sasdata/fair_database/data/test/test_published_state.py b/sasdata/fair_database/data/test/test_published_state.py index f2abfc4a9..28805d804 100644 --- a/sasdata/fair_database/data/test/test_published_state.py +++ b/sasdata/fair_database/data/test/test_published_state.py @@ -31,6 +31,8 @@ def tearDownClass(cls): class TestPublishedStateView(APITestCase): + """Test HTTP methods of PublishedStateView.""" + @classmethod def setUpTestData(cls): cls.user1 = User.objects.create_user( @@ -74,7 +76,7 @@ def setUpTestData(cls): cls.auth_client1.force_authenticate(cls.user1) cls.auth_client2.force_authenticate(cls.user2) - # Test listing published states - various permissions + # Test listing published states including those of owned private sessions def test_list_published_states_private(self): request = self.auth_client1.get("/v1/data/published/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -101,6 +103,7 @@ def test_list_published_states_private(self): }, ) + # Test listing published states of public sessions def test_list_published_states_public(self): request = self.auth_client2.get("/v1/data/published/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -122,6 +125,7 @@ def test_list_published_states_public(self): }, ) + # Test listing published states including sessions with access granted def test_list_published_states_shared(self): self.private_session.users.add(self.user2) request = self.auth_client2.get("/v1/data/published/") @@ -150,6 +154,7 @@ def test_list_published_states_shared(self): }, ) + # Test listing published states while unauthenticated def test_list_published_states_unauthenticated(self): request = self.client.get("/v1/data/published/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -171,6 +176,7 @@ def test_list_published_states_unauthenticated(self): }, ) + # Test creating a published state for a private session def test_published_state_created_private(self): self.unpublished_session.is_public = False self.unpublished_session.save() @@ -198,6 +204,7 @@ def test_published_state_created_private(self): self.unpublished_session.is_public = True self.unpublished_session.save() + # Test creating a published state for a public session def test_published_state_created_public(self): published_state = {"published": False, "session": 4} request = self.auth_client1.post("/v1/data/published/", data=published_state) @@ -221,6 +228,7 @@ def test_published_state_created_public(self): self.assertEqual(new_ps.session, self.publishable_session) new_ps.delete() + # Test that you can't create a published state for an unowned session def test_published_state_created_unowned(self): self.unpublished_session.current_user = None self.unpublished_session.save() @@ -231,29 +239,26 @@ def test_published_state_created_unowned(self): self.unpublished_session.current_user = self.user1 self.unpublished_session.save() + # Test that an unauthenticated user cannot create a published state def test_published_state_created_unauthenticated(self): published_state = {"published": True, "session": 4} request = self.client.post("/v1/data/published/", data=published_state) self.assertEqual(request.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(len(PublishedState.objects.all()), 3) + # Test that a user cannot create a published state for a session they don't own def test_published_state_created_unauthorized(self): published_state = {"published": True, "session": 4} request = self.auth_client2.post("/v1/data/published/", data=published_state) self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(len(PublishedState.objects.all()), 3) + # Test that only one published state can be created per session def test_no_duplicate_published_states(self): published_state = {"published": True, "session": 1} request = self.auth_client1.post("/v1/data/published/", data=published_state) self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) - # Test creating a published state - - # Test can only create a published state for your own sessions - - # Test can't create a second published state for a session - @classmethod def tearDownClass(cls): cls.public_session.delete() @@ -264,6 +269,8 @@ def tearDownClass(cls): class TestSinglePublishedStateView(APITestCase): + """Test HTTP methods of SinglePublishedStateView.""" + @classmethod def setUpTestData(cls): cls.user1 = User.objects.create_user( @@ -304,7 +311,7 @@ def setUpTestData(cls): cls.auth_client1.force_authenticate(cls.user1) cls.auth_client2.force_authenticate(cls.user2) - # Test viewing a published state - various permissions + # Test viewing a published state of a public session def test_get_public_published_state(self): request1 = self.auth_client2.get("/v1/data/published/1/") request2 = self.client.get("/v1/data/published/1/") @@ -324,6 +331,7 @@ def test_get_public_published_state(self): ) self.assertEqual(request1.data, request2.data) + # Test viewing a published state of a private session def test_get_private_published_state(self): request = self.auth_client1.get("/v1/data/published/2/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -340,6 +348,7 @@ def test_get_private_published_state(self): }, ) + # Test viewing a published state of an unowned session def test_get_unowned_published_state(self): request = self.auth_client1.get("/v1/data/published/3/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -356,6 +365,7 @@ def test_get_unowned_published_state(self): }, ) + # Test viewing a published state of a session with access granted def test_get_shared_published_state(self): self.private_session.users.add(self.user2) request = self.auth_client2.get("/v1/data/published/2/") @@ -374,13 +384,14 @@ def test_get_shared_published_state(self): }, ) + # Test a user can't view a published state of a private session they don't own def test_get_private_published_state_unauthorized(self): request1 = self.client.get("/v1/data/published/2/") request2 = self.auth_client2.get("/v1/data/published/2/") self.assertEqual(request1.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) - # Test updating a published state + # Test updating a published state of a public session def test_update_public_published_state(self): request = self.auth_client1.put( "/v1/data/published/1/", data={"published": False} @@ -399,6 +410,7 @@ def test_update_public_published_state(self): self.assertFalse(PublishedState.objects.get(id=1).published) self.public_ps.save() + # Test updating a published state of a private session def test_update_private_published_state(self): request = self.auth_client1.put( "/v1/data/published/2/", data={"published": True} @@ -417,6 +429,7 @@ def test_update_private_published_state(self): self.assertTrue(PublishedState.objects.get(id=2).published) self.private_ps.save() + # Test a user can't update the published state of an unowned session def test_update_unowned_published_state(self): request1 = self.auth_client1.put( "/v1/data/published/3/", data={"published": False} @@ -426,6 +439,7 @@ def test_update_unowned_published_state(self): self.assertEqual(request2.status_code, status.HTTP_401_UNAUTHORIZED) self.assertTrue(PublishedState.objects.get(id=3).published) + # Test a user can't update a public published state unauthorized def test_update_public_published_state_unauthorized(self): request1 = self.auth_client2.put( "/v1/data/published/1/", data={"published": False} @@ -441,6 +455,7 @@ def test_update_public_published_state_unauthorized(self): self.assertEqual(request3.status_code, status.HTTP_401_UNAUTHORIZED) self.assertTrue(PublishedState.objects.get(id=1).published) + # Test a user can't update a private published state unauthorized def test_update_private_published_state_unauthorized(self): request1 = self.auth_client2.put( "/v1/data/published/2/", data={"published": True} @@ -456,7 +471,7 @@ def test_update_private_published_state_unauthorized(self): self.assertEqual(request3.status_code, status.HTTP_401_UNAUTHORIZED) self.assertFalse(PublishedState.objects.get(id=2).published) - # Test deleting a published state - session not deleted + # Test deleting a published state of a private session def test_delete_private_published_state(self): request = self.auth_client1.delete("/v1/data/published/2/") self.assertEqual(request.status_code, status.HTTP_200_OK) @@ -470,6 +485,7 @@ def test_delete_private_published_state(self): session=self.private_session, ) + # Test a user can't delete a private published state unauthorized def test_delete_private_published_state_unauthorized(self): request1 = self.auth_client2.delete("/v1/data/published/2/") self.private_session.users.add(self.user2) @@ -480,10 +496,12 @@ def test_delete_private_published_state_unauthorized(self): self.assertEqual(request2.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(request3.status_code, status.HTTP_401_UNAUTHORIZED) + # Test a user can't delete a published state of a public def test_cant_delete_public_published_state(self): request = self.auth_client1.delete("/v1/data/published/1/") self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) + # Test a user can't delete an unowned published state def test_delete_unowned_published_state(self): request = self.auth_client1.delete("/v1/data/published/3/") self.assertEqual(request.status_code, status.HTTP_403_FORBIDDEN) From 5afd16cd9b04187bcabc9d40094aef28938fa4fe Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 21 Apr 2025 13:53:52 -0400 Subject: [PATCH 480/675] Test that metadata is required for dataset creation --- sasdata/fair_database/data/test/test_dataset.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index c4709b305..b463e95e6 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -245,6 +245,15 @@ def test_no_dataset_with_nonexistent_files(self): request = self.client.post("/v1/data/set/", data=dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + def test_metadata_required(self): + dataset = { + "name": "No metadata", + "is_public": True, + "data_contents": self.empty_data, + } + request = self.auth_client1.post("/v1/data/set/", data=dataset, format="json") + self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + # Test that a private dataset cannot be created without an owner def test_no_private_unowned_dataset(self): dataset = { From 68866a2da1a185427fc79b4b169776cb9af829de Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 21 Apr 2025 14:42:19 -0400 Subject: [PATCH 481/675] Minor changes suggested by Jeff --- sasdata/fair_database/data/models.py | 29 +++++++++++++------ .../fair_database/permissions.py | 6 ++-- sasdata/fair_database/user_app/tests.py | 4 +-- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index e194edffa..7814f877d 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -3,6 +3,14 @@ from django.core.files.storage import FileSystemStorage +def empty_list(): + return [] + + +def empty_dict(): + return {} + + class Data(models.Model): """Base model for data.""" @@ -86,6 +94,15 @@ class Quantity(models.Model): class ReferenceQuantity(models.Model): + """ + Database models for quantities referenced by variables in an OperationTree. + + Corresponds to the references dictionary in the QuantityHistory class in + sasdata/quantity.py. ReferenceQuantities should be essentially the same as + Quantities but with no operations performed on them and therefore no + OperationTree. + """ + # data value value = models.JSONField() @@ -105,14 +122,6 @@ class ReferenceQuantity(models.Model): ) -def empty_list(): - return [] - - -def empty_dict(): - return {} - - class MetaData(models.Model): """Database model for scattering metadata""" @@ -178,7 +187,8 @@ class OperationTree(models.Model): null=True, ) - # related quantity, only set for base of tree + # quantity the operation produces + # only set for base of tree (the most recent operation) quantity = models.OneToOneField( Quantity, on_delete=models.CASCADE, @@ -201,6 +211,7 @@ class PublishedState(models.Model): # published published = models.BooleanField(default=False) + # TODO: update doi as needed when DOI generation is implemented # doi doi = models.URLField() diff --git a/sasdata/fair_database/fair_database/permissions.py b/sasdata/fair_database/fair_database/permissions.py index 46908ff5d..1446c52ae 100644 --- a/sasdata/fair_database/fair_database/permissions.py +++ b/sasdata/fair_database/fair_database/permissions.py @@ -14,11 +14,9 @@ def has_access(request, obj): class DataPermission(BasePermission): def has_object_permission(self, request, view, obj): if request.method == "GET": - if obj.is_public or has_access(request, obj): - return True + return obj.is_public or has_access(request, obj) elif request.method == "DELETE": - if not obj.is_public and is_owner(request, obj): - return True + return not obj.is_public and is_owner(request, obj) else: return is_owner(request, obj) diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index eff7d728a..62ccb080d 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -120,10 +120,10 @@ def test_register_logout(self): self.client1.post("/auth/register/", data=self.register_data) response = self.client1.post("/auth/logout/") response2 = self.client1.get("/auth/user/") - User.objects.get(username="testUser").delete() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.content, b'{"detail":"Successfully logged out."}') self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) + User.objects.get(username="testUser").delete() def test_multiple_logout(self): self.client1.post("/auth/login/", data=self.login_data_2) @@ -142,10 +142,10 @@ def test_register_login(self): ) logout_response = self.client1.post("/auth/logout/") login_response = self.client1.post("/auth/login/", data=self.login_data) - User.objects.get(username="testUser").delete() self.assertEqual(register_response.status_code, status.HTTP_201_CREATED) self.assertEqual(logout_response.status_code, status.HTTP_200_OK) self.assertEqual(login_response.status_code, status.HTTP_200_OK) + User.objects.get(username="testUser").delete() # Test password is successfully changed def test_password_change(self): From cbe15efe9015f4b4b9f3885ccb050e1852fe5e16 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 21 Apr 2025 16:31:26 -0400 Subject: [PATCH 482/675] Nested PublishedState update in Session --- sasdata/fair_database/data/serializers.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 1d94e35b4..3be15fd26 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -1,3 +1,4 @@ +from django.core.exceptions import ObjectDoesNotExist from rest_framework import serializers from data import models @@ -470,6 +471,19 @@ def update(self, instance, validated_data): for dataset in instance.datasets.all(): dataset.is_public = validated_data["is_public"] dataset.save() + if "published_state" in validated_data: + pb_raw = validated_data.pop("published_state") + try: + PublishedStateUpdateSerializer.update( + PublishedStateUpdateSerializer(), + instance.published_state, + validated_data=pb_raw, + ) + except ObjectDoesNotExist: + pb_raw["session"] = instance + PublishedStateSerializer.create( + PublishedStateSerializer(), validated_data=pb_raw + ) return super().update(instance, validated_data) From 426f6d745f0b9225708849282665a6fea2835832 Mon Sep 17 00:00:00 2001 From: summerhenson Date: Mon, 21 Apr 2025 16:32:25 -0400 Subject: [PATCH 483/675] Test nested PublishedState --- .../fair_database/data/test/test_session.py | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/sasdata/fair_database/data/test/test_session.py b/sasdata/fair_database/data/test/test_session.py index c84d2b8d4..d305d01bb 100644 --- a/sasdata/fair_database/data/test/test_session.py +++ b/sasdata/fair_database/data/test/test_session.py @@ -3,8 +3,7 @@ from rest_framework.test import APIClient, APITestCase from rest_framework import status - -from data.models import DataSet, Session +from data.models import DataSet, PublishedState, Session class TestSession(APITestCase): @@ -117,6 +116,7 @@ def test_session_created(self): } ], "is_public": True, + "published_state": {"published": False}, } request = self.auth_client1.post( "/v1/data/session/", data=session, format="json" @@ -125,6 +125,7 @@ def test_session_created(self): new_session = Session.objects.get(id=max_id) new_dataset = new_session.datasets.get() new_metadata = new_dataset.metadata + new_published_state = new_session.published_state self.assertEqual(request.status_code, status.HTTP_201_CREATED) self.assertEqual( request.data, @@ -142,6 +143,7 @@ def test_session_created(self): self.assertEqual(new_session.current_user, self.user1) self.assertEqual(new_dataset.current_user, self.user1) self.assertTrue(all([new_session.is_public, new_dataset.is_public])) + self.assertFalse(new_published_state.published) new_session.delete() # Test creating a private session @@ -303,6 +305,12 @@ def setUpTestData(cls): cls.unowned_dataset = DataSet.objects.create( id=3, is_public=True, name="Unowned Dataset", session=cls.unowned_session ) + cls.private_published_state = PublishedState.objects.create( + id=2, + session=cls.private_session, + published=False, + doi="http://localhost:8000/v1/data/session/2/", + ) cls.auth_client1 = APIClient() cls.auth_client2 = APIClient() cls.auth_client1.force_authenticate(cls.user1) @@ -320,7 +328,6 @@ def test_get_public_session(self): "users": [], "is_public": True, "title": "Public Session", - "published_state": None, "datasets": [ { "id": 1, @@ -333,6 +340,7 @@ def test_get_public_session(self): "data_contents": [], } ], + "published_state": None, }, ) @@ -348,7 +356,12 @@ def test_get_private_session(self): "users": [], "is_public": False, "title": "Private Session", - "published_state": None, + "published_state": { + "id": 2, + "published": False, + "doi": "http://localhost:8000/v1/data/session/2/", + "session": 2, + }, "datasets": [ { "id": 2, @@ -421,6 +434,17 @@ def test_update_public_session(self): session.is_public = False session.save() + def test_update_session_new_published_state(self): + request = self.auth_client1.put( + "/v1/data/session/1/", + data={"published_state": {"published": False}}, + format="json", + ) + new_published_state = Session.objects.get(id=1).published_state + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertFalse(new_published_state.published) + new_published_state.delete() + # Test that another user's public session cannot be updated def test_update_public_session_unauthorized(self): request1 = self.auth_client2.put( @@ -454,6 +478,16 @@ def test_update_private_session(self): session.is_public = False session.save() + def test_update_session_published_state(self): + request = self.auth_client1.put( + "/v1/data/session/2/", + data={"published_state": {"published": True}}, + format="json", + ) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertTrue(PublishedState.objects.get(id=2).published) + self.private_published_state.save() + # Test that another user's private session cannot be updated def test_update_private_session_unauthorized(self): request1 = self.auth_client2.put( @@ -485,6 +519,7 @@ def test_delete_private_session(self): self.assertEqual(request.status_code, status.HTTP_200_OK) self.assertRaises(Session.DoesNotExist, Session.objects.get, id=2) self.assertRaises(DataSet.DoesNotExist, DataSet.objects.get, id=2) + self.assertRaises(PublishedState.DoesNotExist, PublishedState.objects.get, id=2) self.private_session = Session.objects.create( id=2, current_user=self.user1, title="Private Session", is_public=False ) @@ -494,6 +529,12 @@ def test_delete_private_session(self): name="Private Dataset", session=self.private_session, ) + self.private_published_state = PublishedState.objects.create( + id=2, + session=self.private_session, + published=False, + doi="http://localhost:8000/v1/data/session/2/", + ) # Test that another user's private session cannot be deleted def test_delete_private_session_unauthorized(self): From d9bc0707c6544a53121b4671025947be3d7dae6c Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 22 Apr 2025 11:25:22 -0400 Subject: [PATCH 484/675] Test listing published states by username --- .../data/test/test_published_state.py | 109 ++++++++++++++---- 1 file changed, 88 insertions(+), 21 deletions(-) diff --git a/sasdata/fair_database/data/test/test_published_state.py b/sasdata/fair_database/data/test/test_published_state.py index 28805d804..4dc56acf9 100644 --- a/sasdata/fair_database/data/test/test_published_state.py +++ b/sasdata/fair_database/data/test/test_published_state.py @@ -11,26 +11,7 @@ def doi_generator(id: int): return "http://127.0.0.1:8000/v1/data/session/" + str(id) + "/" -class TestSessionWithPublishedState(APITestCase): - @classmethod - def setUpTestData(cls): - pass - - # Test creating a session with a published state - # Should this just be a part of sessions testing? - - # Test GET on a session with a published state - - # Test PUT on nested published state - - # Test cascading delete - - @classmethod - def tearDownClass(cls): - pass - - -class TestPublishedStateView(APITestCase): +class TestPublishedState(APITestCase): """Test HTTP methods of PublishedStateView.""" @classmethod @@ -176,6 +157,92 @@ def test_list_published_states_unauthenticated(self): }, ) + # Test listing a user's own published states + def test_list_user_published_states_private(self): + request = self.auth_client1.get( + "/v1/data/published/", data={"username": "testUser1"} + ) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "published_state_ids": { + 1: { + "title": "Public Session", + "published": True, + "doi": doi_generator(1), + }, + 2: { + "title": "Private Session", + "published": False, + "doi": doi_generator(2), + }, + } + }, + ) + + # Test listing another user's published states + def test_list_user_published_states_public(self): + request = self.auth_client2.get( + "/v1/data/published/", data={"username": "testUser1"} + ) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "published_state_ids": { + 1: { + "title": "Public Session", + "published": True, + "doi": doi_generator(1), + } + } + }, + ) + + # Test listing another user's published states with access granted + def test_list_user_published_states_shared(self): + self.private_session.users.add(self.user2) + request = self.auth_client2.get( + "/v1/data/published/", data={"username": "testUser1"} + ) + self.private_session.users.remove(self.user2) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "published_state_ids": { + 1: { + "title": "Public Session", + "published": True, + "doi": doi_generator(1), + }, + 2: { + "title": "Private Session", + "published": False, + "doi": doi_generator(2), + }, + } + }, + ) + + # Test listing a user's published states while unauthenticated + def test_list_user_published_states_unauthenticated(self): + request = self.client.get("/v1/data/published/", data={"username": "testUser1"}) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual( + request.data, + { + "published_state_ids": { + 1: { + "title": "Public Session", + "published": True, + "doi": doi_generator(1), + } + } + }, + ) + # Test creating a published state for a private session def test_published_state_created_private(self): self.unpublished_session.is_public = False @@ -268,7 +335,7 @@ def tearDownClass(cls): cls.user2.delete() -class TestSinglePublishedStateView(APITestCase): +class TestSinglePublishedState(APITestCase): """Test HTTP methods of SinglePublishedStateView.""" @classmethod From e00b5d9de2e0f59afcee433cfc118274f16f3fcb Mon Sep 17 00:00:00 2001 From: summerhenson Date: Tue, 22 Apr 2025 14:26:48 -0400 Subject: [PATCH 485/675] Deserialization methods for sasdata structures --- sasdata/data.py | 9 +- sasdata/metadata.py | 244 ++++++++++++++++++++++++++++++--- sasdata/quantities/quantity.py | 18 +-- 3 files changed, 239 insertions(+), 32 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 75b18169e..1dca971a4 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -78,11 +78,12 @@ def deserialise(data: str) -> "SasData": @staticmethod def deserialise_json(json_data: dict) -> "SasData": name = json_data["name"] - data_contents = {} # deserialize Quantity - dataset_type = json_data["dataset_type"] + data_contents = {} + dataset_type = json_data["dataset_type"] # TODO: update when DatasetType is more finalized metadata = json_data["metadata"].deserialise_json() - verbose = json_data["verbose"] - return SasData(name, data_contents, dataset_type, metadata, verbose) + for quantity in json_data["data_contents"]: + data_contents[quantity["label"]] = Quantity.deserialise_json(quantity) + return SasData(name, data_contents, dataset_type, metadata) def serialise(self) -> str: return json.dumps(self._serialise_json()) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 6176c2bf9..bef28e655 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -20,6 +20,19 @@ class Vec3: y : Quantity[float] | None z : Quantity[float] | None + @staticmethod + def deserialise_json(json_data: dict): + x = None + y = None + z = None + if "x" in json_data: + x = Quantity.deserialise_json(json_data["x"]) + if "y" in json_data: + y = Quantity.deserialise_json(json_data["y"]) + if "z" in json_data: + z = Quantity.deserialise_json(json_data["z"]) + return Vec3(x=x, y=y, z=z) + def serialise_json(self): data = { "x": None, @@ -41,6 +54,19 @@ class Rot3: pitch : Quantity[float] | None yaw : Quantity[float] | None + @staticmethod + def deserialise_json(json_data: dict): + roll = None + pitch = None + yaw = None + if "roll" in json_data: + roll = Quantity.deserialise_json(json_data["roll"]) + if "pitch" in json_data: + pitch = Quantity.deserialise_json(json_data["pitch"]) + if "yaw" in json_data: + yaw = Quantity.deserialise_json(json_data["yaw"]) + return Rot3(roll=roll, pitch=pitch, yaw=yaw) + def serialise_json(self): data = { "roll": None, @@ -103,13 +129,47 @@ def __init__(self, target_object: AccessorTarget): def summary(self): return (f"Detector:\n" - f" Name: {self.name.value}\n" - f" Distance: {self.distance.value}\n" - f" Offset: {self.offset.value}\n" - f" Orientation: {self.orientation.value}\n" - f" Beam center: {self.beam_center.value}\n" - f" Pixel size: {self.pixel_size.value}\n" - f" Slit length: {self.slit_length.value}\n") + f" Name: {self.name}\n" + f" Distance: {self.distance}\n" + f" Offset: {self.offset}\n" + f" Orientation: {self.orientation}\n" + f" Beam center: {self.beam_center}\n" + f" Pixel size: {self.pixel_size}\n" + f" Slit length: {self.slit_length}\n") + + @staticmethod + def deserialise_json(json_data: dict): + name = None + distance = None + offset = None + orientation = None + beam_center = None + pixel_size = None + slit_length = None + if "name" in json_data: + name = json_data["name"] + if "distance" in json_data: + distance = Quantity.deserialise_json(json_data["distance"]) + if "offset" in json_data: + offset = Vec3.deserialise_json(json_data["offset"]) + if "orientation" in json_data: + orientation = Rot3.deserialise_json(json_data["orientation"]) + if "beam_center" in json_data: + beam_center = Vec3.deserialise_json(json_data["beam_center"]) + if "pixel_size" in json_data: + pixel_size = Vec3.deserialise_json(json_data["pixel_size"]) + if "slit_length" in json_data: + slit_length = Quantity.deserialise_json(json_data["slit_length"]) + return Detector( + name=name, + distance=distance, + offset=offset, + orientation=orientation, + beam_center=beam_center, + pixel_size=pixel_size, + slit_length=slit_length + ) + def serialise_json(self): data = { @@ -168,6 +228,27 @@ def summary(self): f" Aperture size: {self.size}\n" f" Aperture distance: {self.distance}\n") + @staticmethod + def deserialise_json(json_data: dict): + distance = None + size = None + size_name = None + name = None + type_ = None + if "distance" in json_data: + distance = Quantity.deserialise_json(json_data["distance"]) + if "size" in json_data: + size = Vec3.deserialise_json(json_data["size"]) + if "size_name" in json_data: + size_name = json_data["size_name"] + if "name" in json_data: + name = json_data["name"] + if "type" in json_data: + type_ = json_data["type"] + return Aperture( + distance=distance, size=size, size_name=size_name, name=name, type_=type_ + ) + def serialise_json(self): data = { "distance": None, @@ -201,6 +282,15 @@ def summary(self): f"Collimation:\n" f" Length: {self.length}\n") + @staticmethod + def deserialise_json(json_data: dict): + length = None + apertures = [] + if "length" in json_data: + length = Quantity.deserialise_json(json_data["length"]) + if "apertures" in json_data: + apertures = [Aperture.deserialise_json(a) for a in json_data["apertures"]] + def serialise_json(self): data = { "length": None, @@ -215,6 +305,16 @@ class BeamSize: name: str | None size: Vec3 | None + @staticmethod + def deserialise_json(json_data: dict): + name = None + size = None + if "name" in json_data: + name = json_data["name"] + if "size" in json_data: + size = Vec3.deserialise_json(json_data["size"]) + return BeamSize(name=name, size=size) + def serialise_json(self): data = { "name": self.name, @@ -252,6 +352,39 @@ def summary(self) -> str: f" Beam Size: {self.beam_size}\n" ) + @staticmethod + def deserialise_json(json_data: dict): + radiation = None + beam_shape = None + beam_size = None + wavelength = None + wavelength_min = None + wavelength_max = None + wavelength_spread = None + if "radiation" in json_data: + radiation = json_data["radiation"] + if "beam_shape" in json_data: + beam_shape = json_data["beam_shape"] + if "beam_size" in json_data: + beam_size = BeamSize.deserialise_json(json_data["beam_size"]) + if "wavelength" in json_data: + wavelength = Quantity.deserialise_json(json_data["wavelength"]) + if "wavelength_min" in json_data: + wavelength_min = Quantity.deserialise_json(json_data["wavelength_min"]) + if "wavelength_max" in json_data: + wavelength_max = Quantity.deserialise_json(json_data["wavelength_max"]) + if "wavelength_spread" in json_data: + wavelength_spread = Quantity.deserialise_json(json_data["wavelength_spread"]) + return Source( + radiation=radiation, + beam_shape=beam_shape, + beam_size=beam_size, + wavelength=wavelength, + wavelength_min=wavelength_min, + wavelength_max=wavelength_max, + wavelength_spread=wavelength_spread + ) + def serialise_json(self): data = { "radiation": self.radiation, @@ -332,18 +465,46 @@ def __init__(self, target_object: AccessorTarget): def summary(self) -> str: return (f"Sample:\n" - f" ID: {self.sample_id.value}\n" - f" Transmission: {self.transmission.value}\n" - f" Thickness: {self.thickness.value}\n" - f" Temperature: {self.temperature.value}\n" - f" Position: {self.position.value}\n" - f" Orientation: {self.orientation.value}\n") - # - # _str += " Details:\n" - # for item in self.details: - # _str += " %s\n" % item - # - # return _str + f" ID: {self.sample_id}\n" + f" Transmission: {self.transmission}\n" + f" Thickness: {self.thickness}\n" + f" Temperature: {self.temperature}\n" + f" Position: {self.position}\n" + f" Orientation: {self.orientation}\n") + + @staticmethod + def deserialise_json(json_data): + name = None + sample_id = None + thickness = None + transmission = None + temperature = None + position = None + orientation = None + details = [] + if "name" in json_data: + name = json_data["name"] + if "sample_id" in json_data: + sample_id = json_data["sample_id"] + if "thickness" in json_data: + thickness = Quantity.deserialise_json(json_data["thickness"]) + if "temperature" in json_data: + temperature = Quantity.deserialise_json(json_data["temperature"]) + if "position" in json_data: + position = Vec3.deserialise_json(json_data["position"]) + if "orientation" in json_data: + orientation = Rot3.deserialise_json(json_data["orientation"]) + return Sample( + name=name, + sample_id=sample_id, + thickness=thickness, + transmission=transmission, + temperature=temperature, + position=position, + orientation=orientation, + details=details + ) + def serialise_json(self): data = { @@ -397,6 +558,22 @@ def summary(self): f" Notes: {self.notes.value}\n" ) + @staticmethod + def deserialise_json(json_data: dict): + name = None + date = None + description = None + term = None + if "name" in json_data: + name = json_data["name"] + if "date" in json_data: + date = json_data["date"] + if "description" in json_data: + description = json_data["description"] + if "term" in json_data: + term = json_data["term"] + return Process(name=name, date=date, description=description, term=term) + def serialise_json(self): return { "name": self.name, @@ -419,6 +596,19 @@ def summary(self): self.detector.summary() + self.source.summary()) + @staticmethod + def deserialize_json(json_data: dict): + collimations = [] + source = None + detector= [] + if "collimations" in json_data: + collimations = [Collimation.deserialise_json(c) for c in json_data["collimations"]] + if "source" in json_data: + source = Source.deserialise_json(json_data["source"]) + if "detector" in json_data: + detector = [Detector.deserialise_json(d) for d in json_data["detector"]] + return Instrument(collimations=collimations, source=source, detector=detector) + def serialise_json(self): data = { "collimations": [c.serialise_json() for c in self.collimations], @@ -464,6 +654,22 @@ def summary(self): self.sample.summary() + (self.instrument.summary() if self.instrument else "")) + @staticmethod + def deserialize_json(json_data: dict): + title = json_data["title"] + run = json_data["run"] + definition = json_data["definition"] + process = [Process.deserialise_json(p) for p in json_data["process"]] + sample = None + instrument = None + if json_data["sample"] is not None: + sample = Sample.deserialise_json(json_data["sample"]) + if json_data["instrument"] is not None: + instrument = Instrument.deserialize_json(json_data["instrument"]) + return Metadata( + title=title, run=run, definition=definition, process=process, sample=sample, instrument=instrument + ) + def serialise_json(self): serialized = { "instrument": None, diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index a154949bd..70941d6d4 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1225,11 +1225,11 @@ def in_si_with_standard_error(self): @staticmethod def deserialise_json(json_data: dict) -> "Quantity": value = numerical_decode(json_data["value"]) - units = Unit.parse(json_data["units"]) + units_ = Unit.parse(json_data["units"]) standard_error = numerical_decode(json_data["variance"]) ** 0.5 hash_seed = json_data["hash_seed"] history = QuantityHistory.deserialise_json(json_data["history"]) - quantity = Quantity(value, units, standard_error, hash_seed) + quantity = Quantity(value, units_, standard_error, hash_seed) quantity.history = history return quantity @@ -1494,11 +1494,11 @@ def with_standard_error(self, standard_error: Quantity): @staticmethod def deserialise_json(json_data: dict) -> "NamedQuantity": name = json_data["name"] - value = None # TODO: figure out QuantityType deserialization - units = Unit.deserialise_json(json_data["units"]) - standard_error = None # TODO: QuantityType deserialization + value = numerical_decode(json_data["value"]) + units_ = Unit.parse(json_data["units"]) + standard_error = numerical_decode(json_data["variance"]) ** 0.5 history = QuantityHistory.deserialise_json(json_data["history"]) - quantity = NamedQuantity(name, value, units, standard_error) + quantity = NamedQuantity(name, value, units_, standard_error) quantity.history = history return quantity @@ -1541,8 +1541,8 @@ def variance(self) -> Quantity: @staticmethod def deserialise_json(json_data: dict) -> "DerivedQuantity": - value = None # TODO: figure out QuantityType - units = Unit.deserialise_json(json_data["units"]) + value = numerical_decode(json_data["value"]) + units_ = Unit.parse(json_data["units"]) history = QuantityHistory.deserialise_json(json_data["history"]) - quantity = DerivedQuantity(value, units, history) + quantity = DerivedQuantity(value, units_, history) return quantity From 5998beb0e8e4a3638b9df9d9f41a8b8ae989c3d6 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 31 Jul 2024 11:46:44 +0100 Subject: [PATCH 486/675] Data sketch --- sasdata/data.py | 11 +- sasdata/dataset_types.py | 35 +- .../data/migrations/0001_initial.py | 274 +++- .../migrations/0002_rename_data_datafile.py | 18 - .../0003_alter_datafile_is_public.py | 19 - ...aset_metadata_dataset_metadata_and_more.py | 127 -- ...t_datafile_users_dataset_users_and_more.py | 58 - ...lter_datafile_users_alter_dataset_users.py | 28 - ...lter_datafile_users_alter_dataset_users.py | 28 - ...lter_datafile_users_alter_dataset_users.py | 28 - ...definition_metadata_instrument_and_more.py | 52 - .../migrations/0010_alter_dataset_metadata.py | 23 - ...tree_operation_operationtree_parameters.py | 23 - ...2_remove_metadata_raw_metadata_and_more.py | 25 - ...operationtree_parent_operation_and_more.py | 39 - ...ata_process_alter_metadata_run_and_more.py | 52 - ...5_labeledquantity_dataset_data_contents.py | 47 - ...tiontree_dataset_operationtree_quantity.py | 27 - .../migrations/0017_publishedstate_session.py | 80 -- ..._remove_operationtree_quantity_and_more.py | 27 - .../data/migrations/0019_session_title.py | 18 - ...t_data_contents_quantity_label_and_more.py | 25 - .../migrations/0021_dataset_data_contents.py | 17 - ..._dataset_data_contents_quantity_dataset.py | 28 - ...perationtree_parent_operation1_and_more.py | 35 - ...emove_dataset_metadata_metadata_dataset.py | 28 - ...remove_quantity_operation_tree_and_more.py | 28 - ..._remove_session_dataset_dataset_session.py | 28 - ...remove_session_published_state_and_more.py | 28 - ...perationtree_parent_operation1_and_more.py | 37 - ...operation_operationtree_child_operation.py | 17 - .../0030_quantity_derived_quantity.py | 24 - ...tity_derived_quantity_referencequantity.py | 45 - ...lter_referencequantity_derived_quantity.py | 24 - sasdata/fair_database/data/models.py | 17 +- sasdata/fair_database/data/serializers.py | 72 +- .../fair_database/data/test/test_datafile.py | 1 + .../fair_database/data/test/test_dataset.py | 41 +- .../data/test/test_operation_tree.py | 6 +- .../data/test/test_published_state.py | 1 + .../fair_database/data/test/test_session.py | 2 + sasdata/fair_database/data/views.py | 60 + sasdata/fair_database/documentation.yaml | 1172 +++++++++++++++++ .../fair_database/create_example_session.py | 16 +- .../fair_database/permissions.py | 4 + .../fair_database/fair_database/settings.py | 13 +- sasdata/fair_database/fair_database/urls.py | 16 + sasdata/fair_database/requirements.txt | 1 + sasdata/fair_database/user_app/tests.py | 4 + sasdata/fair_database/user_app/util.py | 1 + sasdata/metadata.py | 692 +--------- sasdata/model_requirements.py | 9 +- sasdata/quantities/quantities.py | 146 ++ sasdata/transforms/operation.py | 19 + 54 files changed, 1906 insertions(+), 1790 deletions(-) delete mode 100644 sasdata/fair_database/data/migrations/0002_rename_data_datafile.py delete mode 100644 sasdata/fair_database/data/migrations/0003_alter_datafile_is_public.py delete mode 100644 sasdata/fair_database/data/migrations/0004_quantity_dataset_metadata_dataset_metadata_and_more.py delete mode 100644 sasdata/fair_database/data/migrations/0005_remove_metadata_dataset_datafile_users_dataset_users_and_more.py delete mode 100644 sasdata/fair_database/data/migrations/0006_alter_datafile_users_alter_dataset_users.py delete mode 100644 sasdata/fair_database/data/migrations/0007_alter_datafile_users_alter_dataset_users.py delete mode 100644 sasdata/fair_database/data/migrations/0008_alter_datafile_users_alter_dataset_users.py delete mode 100644 sasdata/fair_database/data/migrations/0009_metadata_definition_metadata_instrument_and_more.py delete mode 100644 sasdata/fair_database/data/migrations/0010_alter_dataset_metadata.py delete mode 100644 sasdata/fair_database/data/migrations/0011_operationtree_operation_operationtree_parameters.py delete mode 100644 sasdata/fair_database/data/migrations/0012_remove_metadata_raw_metadata_and_more.py delete mode 100644 sasdata/fair_database/data/migrations/0013_remove_operationtree_parent_operation_and_more.py delete mode 100644 sasdata/fair_database/data/migrations/0014_alter_metadata_process_alter_metadata_run_and_more.py delete mode 100644 sasdata/fair_database/data/migrations/0015_labeledquantity_dataset_data_contents.py delete mode 100644 sasdata/fair_database/data/migrations/0016_remove_operationtree_dataset_operationtree_quantity.py delete mode 100644 sasdata/fair_database/data/migrations/0017_publishedstate_session.py delete mode 100644 sasdata/fair_database/data/migrations/0018_remove_operationtree_quantity_and_more.py delete mode 100644 sasdata/fair_database/data/migrations/0019_session_title.py delete mode 100644 sasdata/fair_database/data/migrations/0020_remove_dataset_data_contents_quantity_label_and_more.py delete mode 100644 sasdata/fair_database/data/migrations/0021_dataset_data_contents.py delete mode 100644 sasdata/fair_database/data/migrations/0022_remove_dataset_data_contents_quantity_dataset.py delete mode 100644 sasdata/fair_database/data/migrations/0023_alter_operationtree_parent_operation1_and_more.py delete mode 100644 sasdata/fair_database/data/migrations/0024_remove_dataset_metadata_metadata_dataset.py delete mode 100644 sasdata/fair_database/data/migrations/0025_remove_quantity_operation_tree_and_more.py delete mode 100644 sasdata/fair_database/data/migrations/0026_remove_session_dataset_dataset_session.py delete mode 100644 sasdata/fair_database/data/migrations/0027_remove_session_published_state_and_more.py delete mode 100644 sasdata/fair_database/data/migrations/0028_remove_operationtree_parent_operation1_and_more.py delete mode 100644 sasdata/fair_database/data/migrations/0029_rename_next_operation_operationtree_child_operation.py delete mode 100644 sasdata/fair_database/data/migrations/0030_quantity_derived_quantity.py delete mode 100644 sasdata/fair_database/data/migrations/0031_remove_quantity_derived_quantity_referencequantity.py delete mode 100644 sasdata/fair_database/data/migrations/0032_alter_referencequantity_derived_quantity.py create mode 100644 sasdata/fair_database/documentation.yaml create mode 100644 sasdata/quantities/quantities.py create mode 100644 sasdata/transforms/operation.py diff --git a/sasdata/data.py b/sasdata/data.py index 1dca971a4..638bad1e0 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,14 +1,9 @@ -import json -from enum import Enum -from typing import TypeVar, Any, Self from dataclasses import dataclass +from units_temp import Quantity, NamedQuantity import numpy as np -from sasdata.quantities.quantity import NamedQuantity, Quantity -from sasdata.metadata import Metadata, Instrument -from sasdata.quantities.accessors import AccessorTarget -from sasdata.data_backing import Group, key_tree +from sasdata.model_requirements import ModellingRequirements class SasData: @@ -103,4 +98,4 @@ def _serialise_json(self) -> dict[str, Any]: "metadata": self.metadata.serialise_json(), "mask": {}, "model_requirements": {} - } \ No newline at end of file + } diff --git a/sasdata/dataset_types.py b/sasdata/dataset_types.py index c7d2f5927..27848b3e2 100644 --- a/sasdata/dataset_types.py +++ b/sasdata/dataset_types.py @@ -2,8 +2,6 @@ from dataclasses import dataclass -import sasdata.quantities.units as units - # # VERY ROUGH DRAFT - FOR PROTOTYPING PURPOSES # @@ -56,23 +54,22 @@ class DatasetType: # # The unit options should only be those compatible with the field # - -unit_kinds = { - "Q": units.inverse_length, - "I": units.inverse_length, - "Qx": units.inverse_length, - "Qy": units.inverse_length, - "Qz": units.inverse_length, - "dI": units.inverse_length, - "dQ": units.inverse_length, - "dQx": units.inverse_length, - "dQy": units.inverse_length, - "dQz": units.inverse_length, - "z": units.length, - "G": units.area, - "shadow": units.dimensionless, - "temperature": units.temperature, - "magnetic field": units.magnetic_flux_density +default_units = { + "Q": "1/A", + "I": "1/cm", + "Qx": "1/A", + "Qy": "1/A", + "Qz": "1/A", + "dI": "1/A", + "dQ": "1/A", + "dQx": "1/A", + "dQy": "1/A", + "dQz": "1/A", + "z": "A", + "G": "", + "shaddow": "", + "temperature": "K", + "magnetic field": "T" } # diff --git a/sasdata/fair_database/data/migrations/0001_initial.py b/sasdata/fair_database/data/migrations/0001_initial.py index ce7cee7d6..e8f7219a6 100644 --- a/sasdata/fair_database/data/migrations/0001_initial.py +++ b/sasdata/fair_database/data/migrations/0001_initial.py @@ -1,5 +1,6 @@ -# Generated by Django 5.1.5 on 2025-01-23 18:41 +# Generated by Django 5.1.6 on 2025-04-23 18:08 +import data.models import django.core.files.storage import django.db.models.deletion from django.conf import settings @@ -15,7 +16,7 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name="Data", + name="DataFile", fields=[ ( "id", @@ -26,6 +27,12 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), + ( + "is_public", + models.BooleanField( + default=False, help_text="opt in to make your data public" + ), + ), ( "file_name", models.CharField( @@ -45,22 +52,281 @@ class Migration(migrations.Migration): upload_to="uploaded_files", ), ), + ( + "current_user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "users", + models.ManyToManyField( + blank=True, related_name="+", to=settings.AUTH_USER_MODEL + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="DataSet", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "is_public", + models.BooleanField( + default=False, help_text="opt in to make your data public" + ), + ), + ("name", models.CharField(max_length=200)), + ( + "current_user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ("files", models.ManyToManyField(to="data.datafile")), + ( + "users", + models.ManyToManyField( + blank=True, related_name="+", to=settings.AUTH_USER_MODEL + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="MetaData", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(default="Title", max_length=500)), + ("run", models.JSONField(default=data.models.empty_list)), + ("definition", models.TextField(blank=True, null=True)), + ("instrument", models.JSONField(blank=True, null=True)), + ("process", models.JSONField(default=data.models.empty_list)), + ("sample", models.JSONField(blank=True, null=True)), + ( + "dataset", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="metadata", + to="data.dataset", + ), + ), + ], + ), + migrations.CreateModel( + name="Quantity", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("value", models.JSONField()), + ("variance", models.JSONField()), + ("units", models.CharField(max_length=200)), + ("hash", models.IntegerField()), + ("label", models.CharField(max_length=50)), + ( + "dataset", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="data_contents", + to="data.dataset", + ), + ), + ], + ), + migrations.CreateModel( + name="OperationTree", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "operation", + models.CharField( + choices=[ + ("zero", "0 [Add.Id.]"), + ("one", "1 [Mul.Id.]"), + ("constant", "Constant"), + ("variable", "Variable"), + ("neg", "Neg"), + ("reciprocal", "Inv"), + ("add", "Add"), + ("sub", "Sub"), + ("mul", "Mul"), + ("div", "Div"), + ("pow", "Pow"), + ("transpose", "Transpose"), + ("dot", "Dot"), + ("matmul", "MatMul"), + ("tensor_product", "TensorProduct"), + ], + max_length=20, + ), + ), + ("parameters", models.JSONField(default=data.models.empty_dict)), + ("label", models.CharField(blank=True, max_length=10, null=True)), + ( + "child_operation", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="parent_operations", + to="data.operationtree", + ), + ), + ( + "quantity", + models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="operation_tree", + to="data.quantity", + ), + ), + ], + ), + migrations.CreateModel( + name="ReferenceQuantity", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("value", models.JSONField()), + ("variance", models.JSONField()), + ("units", models.CharField(max_length=200)), + ("hash", models.IntegerField()), + ( + "derived_quantity", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="references", + to="data.quantity", + ), + ), + ], + ), + migrations.CreateModel( + name="Session", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), ( "is_public", models.BooleanField( - default=False, - help_text="opt in to submit your data into example pool", + default=False, help_text="opt in to make your data public" ), ), + ("title", models.CharField(max_length=200)), ( "current_user", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, + related_name="+", to=settings.AUTH_USER_MODEL, ), ), + ( + "users", + models.ManyToManyField( + blank=True, related_name="+", to=settings.AUTH_USER_MODEL + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="PublishedState", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("published", models.BooleanField(default=False)), + ("doi", models.URLField()), + ( + "session", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="published_state", + to="data.session", + ), + ), ], ), + migrations.AddField( + model_name="dataset", + name="session", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="datasets", + to="data.session", + ), + ), ] diff --git a/sasdata/fair_database/data/migrations/0002_rename_data_datafile.py b/sasdata/fair_database/data/migrations/0002_rename_data_datafile.py deleted file mode 100644 index 80a255488..000000000 --- a/sasdata/fair_database/data/migrations/0002_rename_data_datafile.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.5 on 2025-02-10 19:30 - -from django.conf import settings -from django.db import migrations - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0001_initial"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.RenameModel( - old_name="Data", - new_name="DataFile", - ), - ] diff --git a/sasdata/fair_database/data/migrations/0003_alter_datafile_is_public.py b/sasdata/fair_database/data/migrations/0003_alter_datafile_is_public.py deleted file mode 100644 index e6415eb80..000000000 --- a/sasdata/fair_database/data/migrations/0003_alter_datafile_is_public.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.1.5 on 2025-02-13 16:53 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0002_rename_data_datafile"), - ] - - operations = [ - migrations.AlterField( - model_name="datafile", - name="is_public", - field=models.BooleanField( - default=False, help_text="opt in to make your data public" - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0004_quantity_dataset_metadata_dataset_metadata_and_more.py b/sasdata/fair_database/data/migrations/0004_quantity_dataset_metadata_dataset_metadata_and_more.py deleted file mode 100644 index e56dc51dc..000000000 --- a/sasdata/fair_database/data/migrations/0004_quantity_dataset_metadata_dataset_metadata_and_more.py +++ /dev/null @@ -1,127 +0,0 @@ -# Generated by Django 5.1.6 on 2025-02-26 16:34 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0003_alter_datafile_is_public"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name="Quantity", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("value", models.JSONField()), - ("variance", models.JSONField()), - ("units", models.CharField(max_length=200)), - ("hash", models.IntegerField()), - ], - ), - migrations.CreateModel( - name="DataSet", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "is_public", - models.BooleanField( - default=False, help_text="opt in to make your data public" - ), - ), - ("name", models.CharField(max_length=200)), - ( - "current_user", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - to=settings.AUTH_USER_MODEL, - ), - ), - ("files", models.ManyToManyField(to="data.datafile")), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="MetaData", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "dataset", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - related_name="associated_data", - to="data.dataset", - ), - ), - ], - ), - migrations.AddField( - model_name="dataset", - name="metadata", - field=models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - related_name="associated_metadata", - to="data.metadata", - ), - ), - migrations.CreateModel( - name="OperationTree", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "dataset", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="data.dataset" - ), - ), - ( - "parent_operation", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - to="data.operationtree", - ), - ), - ], - ), - ] diff --git a/sasdata/fair_database/data/migrations/0005_remove_metadata_dataset_datafile_users_dataset_users_and_more.py b/sasdata/fair_database/data/migrations/0005_remove_metadata_dataset_datafile_users_dataset_users_and_more.py deleted file mode 100644 index 3a333c2ff..000000000 --- a/sasdata/fair_database/data/migrations/0005_remove_metadata_dataset_datafile_users_dataset_users_and_more.py +++ /dev/null @@ -1,58 +0,0 @@ -# Generated by Django 5.1.6 on 2025-02-26 16:54 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0004_quantity_dataset_metadata_dataset_metadata_and_more"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.RemoveField( - model_name="metadata", - name="dataset", - ), - migrations.AddField( - model_name="datafile", - name="users", - field=models.ManyToManyField(related_name="+", to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name="dataset", - name="users", - field=models.ManyToManyField(related_name="+", to=settings.AUTH_USER_MODEL), - ), - migrations.AlterField( - model_name="datafile", - name="current_user", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - migrations.AlterField( - model_name="dataset", - name="current_user", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - migrations.AlterField( - model_name="dataset", - name="metadata", - field=models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, to="data.metadata" - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0006_alter_datafile_users_alter_dataset_users.py b/sasdata/fair_database/data/migrations/0006_alter_datafile_users_alter_dataset_users.py deleted file mode 100644 index 4d2ee4de3..000000000 --- a/sasdata/fair_database/data/migrations/0006_alter_datafile_users_alter_dataset_users.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.1.6 on 2025-02-26 20:53 - -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0005_remove_metadata_dataset_datafile_users_dataset_users_and_more"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AlterField( - model_name="datafile", - name="users", - field=models.ManyToManyField( - default=[], related_name="+", to=settings.AUTH_USER_MODEL - ), - ), - migrations.AlterField( - model_name="dataset", - name="users", - field=models.ManyToManyField( - default=[], related_name="+", to=settings.AUTH_USER_MODEL - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0007_alter_datafile_users_alter_dataset_users.py b/sasdata/fair_database/data/migrations/0007_alter_datafile_users_alter_dataset_users.py deleted file mode 100644 index 6540d4e77..000000000 --- a/sasdata/fair_database/data/migrations/0007_alter_datafile_users_alter_dataset_users.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.1.6 on 2025-02-26 21:03 - -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0006_alter_datafile_users_alter_dataset_users"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AlterField( - model_name="datafile", - name="users", - field=models.ManyToManyField( - blank=True, default=[], related_name="+", to=settings.AUTH_USER_MODEL - ), - ), - migrations.AlterField( - model_name="dataset", - name="users", - field=models.ManyToManyField( - blank=True, default=[], related_name="+", to=settings.AUTH_USER_MODEL - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0008_alter_datafile_users_alter_dataset_users.py b/sasdata/fair_database/data/migrations/0008_alter_datafile_users_alter_dataset_users.py deleted file mode 100644 index 56dbb1778..000000000 --- a/sasdata/fair_database/data/migrations/0008_alter_datafile_users_alter_dataset_users.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.1.6 on 2025-02-26 21:04 - -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0007_alter_datafile_users_alter_dataset_users"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AlterField( - model_name="datafile", - name="users", - field=models.ManyToManyField( - blank=True, related_name="+", to=settings.AUTH_USER_MODEL - ), - ), - migrations.AlterField( - model_name="dataset", - name="users", - field=models.ManyToManyField( - blank=True, related_name="+", to=settings.AUTH_USER_MODEL - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0009_metadata_definition_metadata_instrument_and_more.py b/sasdata/fair_database/data/migrations/0009_metadata_definition_metadata_instrument_and_more.py deleted file mode 100644 index e6ae8a4ac..000000000 --- a/sasdata/fair_database/data/migrations/0009_metadata_definition_metadata_instrument_and_more.py +++ /dev/null @@ -1,52 +0,0 @@ -# Generated by Django 5.1.6 on 2025-03-05 15:59 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0008_alter_datafile_users_alter_dataset_users"), - ] - - operations = [ - migrations.AddField( - model_name="metadata", - name="definition", - field=models.TextField(blank=True, null=True), - ), - migrations.AddField( - model_name="metadata", - name="instrument", - field=models.JSONField(blank=True, null=True), - ), - migrations.AddField( - model_name="metadata", - name="process", - field=models.JSONField(blank=True, null=True), - ), - migrations.AddField( - model_name="metadata", - name="raw_metadata", - field=models.JSONField(default=dict), - ), - migrations.AddField( - model_name="metadata", - name="run", - field=models.CharField(blank=True, max_length=500, null=True), - ), - migrations.AddField( - model_name="metadata", - name="sample", - field=models.JSONField(blank=True, null=True), - ), - migrations.AddField( - model_name="metadata", - name="title", - field=models.CharField(default="Title", max_length=500), - ), - migrations.AddField( - model_name="metadata", - name="transmission_spectrum", - field=models.JSONField(blank=True, null=True), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0010_alter_dataset_metadata.py b/sasdata/fair_database/data/migrations/0010_alter_dataset_metadata.py deleted file mode 100644 index a8acab29f..000000000 --- a/sasdata/fair_database/data/migrations/0010_alter_dataset_metadata.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.1.6 on 2025-03-11 18:46 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0009_metadata_definition_metadata_instrument_and_more"), - ] - - operations = [ - migrations.AlterField( - model_name="dataset", - name="metadata", - field=models.OneToOneField( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - to="data.metadata", - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0011_operationtree_operation_operationtree_parameters.py b/sasdata/fair_database/data/migrations/0011_operationtree_operation_operationtree_parameters.py deleted file mode 100644 index f4a943881..000000000 --- a/sasdata/fair_database/data/migrations/0011_operationtree_operation_operationtree_parameters.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.1.6 on 2025-03-14 14:15 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0010_alter_dataset_metadata"), - ] - - operations = [ - migrations.AddField( - model_name="operationtree", - name="operation", - field=models.CharField(default="undefined", max_length=10), - preserve_default=False, - ), - migrations.AddField( - model_name="operationtree", - name="parameters", - field=models.JSONField(default=dict), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0012_remove_metadata_raw_metadata_and_more.py b/sasdata/fair_database/data/migrations/0012_remove_metadata_raw_metadata_and_more.py deleted file mode 100644 index f553206b4..000000000 --- a/sasdata/fair_database/data/migrations/0012_remove_metadata_raw_metadata_and_more.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 5.1.6 on 2025-03-19 20:08 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0011_operationtree_operation_operationtree_parameters"), - ] - - operations = [ - migrations.RemoveField( - model_name="metadata", - name="raw_metadata", - ), - migrations.RemoveField( - model_name="metadata", - name="transmission_spectrum", - ), - migrations.AlterField( - model_name="metadata", - name="run", - field=models.JSONField(blank=True, null=True), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0013_remove_operationtree_parent_operation_and_more.py b/sasdata/fair_database/data/migrations/0013_remove_operationtree_parent_operation_and_more.py deleted file mode 100644 index 95a76ee93..000000000 --- a/sasdata/fair_database/data/migrations/0013_remove_operationtree_parent_operation_and_more.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 5.1.6 on 2025-03-19 20:20 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0012_remove_metadata_raw_metadata_and_more"), - ] - - operations = [ - migrations.RemoveField( - model_name="operationtree", - name="parent_operation", - ), - migrations.AddField( - model_name="operationtree", - name="parent_operation1", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="child_operations1", - to="data.operationtree", - ), - ), - migrations.AddField( - model_name="operationtree", - name="parent_operation2", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="child_operations2", - to="data.operationtree", - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0014_alter_metadata_process_alter_metadata_run_and_more.py b/sasdata/fair_database/data/migrations/0014_alter_metadata_process_alter_metadata_run_and_more.py deleted file mode 100644 index adb588d55..000000000 --- a/sasdata/fair_database/data/migrations/0014_alter_metadata_process_alter_metadata_run_and_more.py +++ /dev/null @@ -1,52 +0,0 @@ -# Generated by Django 5.1.6 on 2025-03-20 14:29 - -import data.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0013_remove_operationtree_parent_operation_and_more"), - ] - - operations = [ - migrations.AlterField( - model_name="metadata", - name="process", - field=models.JSONField(default=data.models.empty_list), - ), - migrations.AlterField( - model_name="metadata", - name="run", - field=models.JSONField(default=data.models.empty_list), - ), - migrations.AlterField( - model_name="operationtree", - name="operation", - field=models.CharField( - choices=[ - ("zero", "0 [Add.Id.]"), - ("one", "1 [Mul.Id.]"), - ("constant", "Constant"), - ("variable", "Variable"), - ("neg", "Neg"), - ("reciprocal", "Inv"), - ("add", "Add"), - ("sub", "Sub"), - ("mul", "Mul"), - ("div", "Div"), - ("pow", "Pow"), - ("transpose", "Transpose"), - ("dot", "Dot"), - ("matmul", "MatMul"), - ("tensor_product", "TensorProduct"), - ], - max_length=20, - ), - ), - migrations.AlterField( - model_name="operationtree", - name="parameters", - field=models.JSONField(default=data.models.empty_dict), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0015_labeledquantity_dataset_data_contents.py b/sasdata/fair_database/data/migrations/0015_labeledquantity_dataset_data_contents.py deleted file mode 100644 index 877f2d386..000000000 --- a/sasdata/fair_database/data/migrations/0015_labeledquantity_dataset_data_contents.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 5.1.6 on 2025-03-20 17:19 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0014_alter_metadata_process_alter_metadata_run_and_more"), - ] - - operations = [ - migrations.CreateModel( - name="LabeledQuantity", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("label", models.CharField(max_length=20)), - ( - "dataset", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="data.dataset" - ), - ), - ( - "quantity", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="data.quantity" - ), - ), - ], - ), - migrations.AddField( - model_name="dataset", - name="data_contents", - field=models.ManyToManyField( - through="data.LabeledQuantity", to="data.quantity" - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0016_remove_operationtree_dataset_operationtree_quantity.py b/sasdata/fair_database/data/migrations/0016_remove_operationtree_dataset_operationtree_quantity.py deleted file mode 100644 index 22b1e1fde..000000000 --- a/sasdata/fair_database/data/migrations/0016_remove_operationtree_dataset_operationtree_quantity.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 5.1.6 on 2025-03-25 17:12 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0015_labeledquantity_dataset_data_contents"), - ] - - operations = [ - migrations.RemoveField( - model_name="operationtree", - name="dataset", - ), - migrations.AddField( - model_name="operationtree", - name="quantity", - field=models.ForeignKey( - default=1, - on_delete=django.db.models.deletion.CASCADE, - to="data.quantity", - ), - preserve_default=False, - ), - ] diff --git a/sasdata/fair_database/data/migrations/0017_publishedstate_session.py b/sasdata/fair_database/data/migrations/0017_publishedstate_session.py deleted file mode 100644 index 073e01652..000000000 --- a/sasdata/fair_database/data/migrations/0017_publishedstate_session.py +++ /dev/null @@ -1,80 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-04 18:54 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0016_remove_operationtree_dataset_operationtree_quantity"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name="PublishedState", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("published", models.BooleanField(default=False)), - ("doi", models.URLField()), - ], - ), - migrations.CreateModel( - name="Session", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "is_public", - models.BooleanField( - default=False, help_text="opt in to make your data public" - ), - ), - ( - "current_user", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ("dataset", models.ManyToManyField(to="data.dataset")), - ( - "published_state", - models.OneToOneField( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - to="data.publishedstate", - ), - ), - ( - "users", - models.ManyToManyField( - blank=True, related_name="+", to=settings.AUTH_USER_MODEL - ), - ), - ], - options={ - "abstract": False, - }, - ), - ] diff --git a/sasdata/fair_database/data/migrations/0018_remove_operationtree_quantity_and_more.py b/sasdata/fair_database/data/migrations/0018_remove_operationtree_quantity_and_more.py deleted file mode 100644 index 13dc07b05..000000000 --- a/sasdata/fair_database/data/migrations/0018_remove_operationtree_quantity_and_more.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-07 19:20 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0017_publishedstate_session"), - ] - - operations = [ - migrations.RemoveField( - model_name="operationtree", - name="quantity", - ), - migrations.AddField( - model_name="quantity", - name="operation_tree", - field=models.OneToOneField( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - to="data.operationtree", - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0019_session_title.py b/sasdata/fair_database/data/migrations/0019_session_title.py deleted file mode 100644 index ddc138abb..000000000 --- a/sasdata/fair_database/data/migrations/0019_session_title.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-07 19:53 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0018_remove_operationtree_quantity_and_more"), - ] - - operations = [ - migrations.AddField( - model_name="session", - name="title", - field=models.CharField(default="placeholder", max_length=200), - preserve_default=False, - ), - ] diff --git a/sasdata/fair_database/data/migrations/0020_remove_dataset_data_contents_quantity_label_and_more.py b/sasdata/fair_database/data/migrations/0020_remove_dataset_data_contents_quantity_label_and_more.py deleted file mode 100644 index 7c2a4ecef..000000000 --- a/sasdata/fair_database/data/migrations/0020_remove_dataset_data_contents_quantity_label_and_more.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-08 19:58 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0019_session_title"), - ] - - operations = [ - migrations.RemoveField( - model_name="dataset", - name="data_contents", - ), - migrations.AddField( - model_name="quantity", - name="label", - field=models.CharField(default="placeholder", max_length=50), - preserve_default=False, - ), - migrations.DeleteModel( - name="LabeledQuantity", - ), - ] diff --git a/sasdata/fair_database/data/migrations/0021_dataset_data_contents.py b/sasdata/fair_database/data/migrations/0021_dataset_data_contents.py deleted file mode 100644 index e4d46aadf..000000000 --- a/sasdata/fair_database/data/migrations/0021_dataset_data_contents.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-08 19:59 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0020_remove_dataset_data_contents_quantity_label_and_more"), - ] - - operations = [ - migrations.AddField( - model_name="dataset", - name="data_contents", - field=models.ManyToManyField(to="data.quantity"), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0022_remove_dataset_data_contents_quantity_dataset.py b/sasdata/fair_database/data/migrations/0022_remove_dataset_data_contents_quantity_dataset.py deleted file mode 100644 index 5de2687ae..000000000 --- a/sasdata/fair_database/data/migrations/0022_remove_dataset_data_contents_quantity_dataset.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-10 17:53 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0021_dataset_data_contents"), - ] - - operations = [ - migrations.RemoveField( - model_name="dataset", - name="data_contents", - ), - migrations.AddField( - model_name="quantity", - name="dataset", - field=models.ForeignKey( - default=1, - on_delete=django.db.models.deletion.CASCADE, - related_name="data_contents", - to="data.dataset", - ), - preserve_default=False, - ), - ] diff --git a/sasdata/fair_database/data/migrations/0023_alter_operationtree_parent_operation1_and_more.py b/sasdata/fair_database/data/migrations/0023_alter_operationtree_parent_operation1_and_more.py deleted file mode 100644 index 6032bbf60..000000000 --- a/sasdata/fair_database/data/migrations/0023_alter_operationtree_parent_operation1_and_more.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-10 19:29 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0022_remove_dataset_data_contents_quantity_dataset"), - ] - - operations = [ - migrations.AlterField( - model_name="operationtree", - name="parent_operation1", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="child_operations1", - to="data.operationtree", - ), - ), - migrations.AlterField( - model_name="operationtree", - name="parent_operation2", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="child_operations2", - to="data.operationtree", - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0024_remove_dataset_metadata_metadata_dataset.py b/sasdata/fair_database/data/migrations/0024_remove_dataset_metadata_metadata_dataset.py deleted file mode 100644 index 956631402..000000000 --- a/sasdata/fair_database/data/migrations/0024_remove_dataset_metadata_metadata_dataset.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-11 15:17 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0023_alter_operationtree_parent_operation1_and_more"), - ] - - operations = [ - migrations.RemoveField( - model_name="dataset", - name="metadata", - ), - migrations.AddField( - model_name="metadata", - name="dataset", - field=models.OneToOneField( - default=1, - on_delete=django.db.models.deletion.CASCADE, - related_name="metadata", - to="data.dataset", - ), - preserve_default=False, - ), - ] diff --git a/sasdata/fair_database/data/migrations/0025_remove_quantity_operation_tree_and_more.py b/sasdata/fair_database/data/migrations/0025_remove_quantity_operation_tree_and_more.py deleted file mode 100644 index 93f1b269a..000000000 --- a/sasdata/fair_database/data/migrations/0025_remove_quantity_operation_tree_and_more.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-11 15:38 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0024_remove_dataset_metadata_metadata_dataset"), - ] - - operations = [ - migrations.RemoveField( - model_name="quantity", - name="operation_tree", - ), - migrations.AddField( - model_name="operationtree", - name="quantity", - field=models.OneToOneField( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="operation_tree", - to="data.quantity", - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0026_remove_session_dataset_dataset_session.py b/sasdata/fair_database/data/migrations/0026_remove_session_dataset_dataset_session.py deleted file mode 100644 index 211577a9c..000000000 --- a/sasdata/fair_database/data/migrations/0026_remove_session_dataset_dataset_session.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-11 16:14 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0025_remove_quantity_operation_tree_and_more"), - ] - - operations = [ - migrations.RemoveField( - model_name="session", - name="dataset", - ), - migrations.AddField( - model_name="dataset", - name="session", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="datasets", - to="data.session", - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0027_remove_session_published_state_and_more.py b/sasdata/fair_database/data/migrations/0027_remove_session_published_state_and_more.py deleted file mode 100644 index d580a58ac..000000000 --- a/sasdata/fair_database/data/migrations/0027_remove_session_published_state_and_more.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-11 17:12 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0026_remove_session_dataset_dataset_session"), - ] - - operations = [ - migrations.RemoveField( - model_name="session", - name="published_state", - ), - migrations.AddField( - model_name="publishedstate", - name="session", - field=models.OneToOneField( - default=1, - on_delete=django.db.models.deletion.CASCADE, - related_name="published_state", - to="data.session", - ), - preserve_default=False, - ), - ] diff --git a/sasdata/fair_database/data/migrations/0028_remove_operationtree_parent_operation1_and_more.py b/sasdata/fair_database/data/migrations/0028_remove_operationtree_parent_operation1_and_more.py deleted file mode 100644 index 9d65057e5..000000000 --- a/sasdata/fair_database/data/migrations/0028_remove_operationtree_parent_operation1_and_more.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-11 17:19 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0027_remove_session_published_state_and_more"), - ] - - operations = [ - migrations.RemoveField( - model_name="operationtree", - name="parent_operation1", - ), - migrations.RemoveField( - model_name="operationtree", - name="parent_operation2", - ), - migrations.AddField( - model_name="operationtree", - name="label", - field=models.CharField(blank=True, max_length=10, null=True), - ), - migrations.AddField( - model_name="operationtree", - name="next_operation", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="parent_operations", - to="data.operationtree", - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0029_rename_next_operation_operationtree_child_operation.py b/sasdata/fair_database/data/migrations/0029_rename_next_operation_operationtree_child_operation.py deleted file mode 100644 index fa665269d..000000000 --- a/sasdata/fair_database/data/migrations/0029_rename_next_operation_operationtree_child_operation.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-11 17:36 - -from django.db import migrations - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0028_remove_operationtree_parent_operation1_and_more"), - ] - - operations = [ - migrations.RenameField( - model_name="operationtree", - old_name="next_operation", - new_name="child_operation", - ), - ] diff --git a/sasdata/fair_database/data/migrations/0030_quantity_derived_quantity.py b/sasdata/fair_database/data/migrations/0030_quantity_derived_quantity.py deleted file mode 100644 index 6584811b6..000000000 --- a/sasdata/fair_database/data/migrations/0030_quantity_derived_quantity.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-16 14:29 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0029_rename_next_operation_operationtree_child_operation"), - ] - - operations = [ - migrations.AddField( - model_name="quantity", - name="derived_quantity", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="references", - to="data.quantity", - ), - ), - ] diff --git a/sasdata/fair_database/data/migrations/0031_remove_quantity_derived_quantity_referencequantity.py b/sasdata/fair_database/data/migrations/0031_remove_quantity_derived_quantity_referencequantity.py deleted file mode 100644 index 24702a449..000000000 --- a/sasdata/fair_database/data/migrations/0031_remove_quantity_derived_quantity_referencequantity.py +++ /dev/null @@ -1,45 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-16 18:52 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0030_quantity_derived_quantity"), - ] - - operations = [ - migrations.RemoveField( - model_name="quantity", - name="derived_quantity", - ), - migrations.CreateModel( - name="ReferenceQuantity", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("value", models.JSONField()), - ("variance", models.JSONField()), - ("units", models.CharField(max_length=200)), - ("hash", models.IntegerField()), - ( - "derived_quantity", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="references", - to="data.quantity", - ), - ), - ], - ), - ] diff --git a/sasdata/fair_database/data/migrations/0032_alter_referencequantity_derived_quantity.py b/sasdata/fair_database/data/migrations/0032_alter_referencequantity_derived_quantity.py deleted file mode 100644 index 77aa82774..000000000 --- a/sasdata/fair_database/data/migrations/0032_alter_referencequantity_derived_quantity.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 5.1.6 on 2025-04-16 19:18 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("data", "0031_remove_quantity_derived_quantity_referencequantity"), - ] - - operations = [ - migrations.AlterField( - model_name="referencequantity", - name="derived_quantity", - field=models.ForeignKey( - default=1, - on_delete=django.db.models.deletion.CASCADE, - related_name="references", - to="data.quantity", - ), - preserve_default=False, - ), - ] diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 7814f877d..09bae3c07 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -3,22 +3,25 @@ from django.core.files.storage import FileSystemStorage +# method for empty list default value def empty_list(): return [] +# method for empty dictionary default value def empty_dict(): return {} class Data(models.Model): - """Base model for data.""" + """Base model for data with access-related information.""" # owner of the data current_user = models.ForeignKey( User, blank=True, null=True, on_delete=models.CASCADE, related_name="+" ) + # users that have been granted view access to the data users = models.ManyToManyField(User, blank=True, related_name="+") # is the data public? @@ -58,6 +61,7 @@ class DataSet(Data): # associated files files = models.ManyToManyField(DataFile) + # session the dataset is a part of, if any session = models.ForeignKey( "Session", on_delete=models.CASCADE, @@ -86,8 +90,10 @@ class Quantity(models.Model): # hash value hash = models.IntegerField() + # label, e.g. Q or I(Q) label = models.CharField(max_length=50) + # data set the quantity is a part of dataset = models.ForeignKey( DataSet, on_delete=models.CASCADE, related_name="data_contents" ) @@ -115,6 +121,7 @@ class ReferenceQuantity(models.Model): # hash value hash = models.IntegerField() + # Quantity whose OperationTree this is a reference for derived_quantity = models.ForeignKey( Quantity, related_name="references", @@ -122,10 +129,10 @@ class ReferenceQuantity(models.Model): ) +# TODO: update based on changes in sasdata/metadata.py class MetaData(models.Model): """Database model for scattering metadata""" - # TODO: update based on changes in sasdata/metadata.py # title title = models.CharField(max_length=500, default="Title") @@ -153,6 +160,7 @@ class MetaData(models.Model): class OperationTree(models.Model): """Database model for tree of operations performed on a DataSet.""" + # possible operations OPERATION_CHOICES = { "zero": "0 [Add.Id.]", "one": "1 [Mul.Id.]", @@ -177,8 +185,11 @@ class OperationTree(models.Model): # parameters parameters = models.JSONField(default=empty_dict) + # label (a or b) if the operation is a parameter of a child operation + # maintains ordering of binary operation parameters label = models.CharField(max_length=10, blank=True, null=True) + # operation this operation is a parameter for, if any child_operation = models.ForeignKey( "self", on_delete=models.CASCADE, @@ -188,7 +199,7 @@ class OperationTree(models.Model): ) # quantity the operation produces - # only set for base of tree (the most recent operation) + # only set for base of tree (the quantity's most recent operation) quantity = models.OneToOneField( Quantity, on_delete=models.CASCADE, diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 3be15fd26..3eb712199 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -9,6 +9,16 @@ # TODO: custom update methods for nested structures +# Determine if an operation does not have parent operations +def constant_or_variable(operation: str): + return operation in ["zero", "one", "constant", "variable"] + + +# Determine if an operation has two parent operations +def binary(operation: str): + return operation in ["add", "sub", "mul", "div", "dot", "matmul", "tensor_product"] + + class DataFileSerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the DataFile model.""" @@ -39,6 +49,7 @@ class AccessManagementSerializer(serializers.Serializer): class MetaDataSerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the MetaData model.""" + # associated dataset dataset = serializers.PrimaryKeyRelatedField( queryset=models.DataSet, required=False, allow_null=True ) @@ -58,12 +69,15 @@ def to_representation(self, instance): class OperationTreeSerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the OperationTree model.""" + # associated quantity, for root operation quantity = serializers.PrimaryKeyRelatedField( queryset=models.Quantity, required=False, allow_null=True ) + # operation this operation is a parameter for, for non-root operations child_operation = serializers.PrimaryKeyRelatedField( queryset=models.OperationTree, required=False, allow_null=True ) + # parameter label, for non-root operations label = serializers.CharField(max_length=10, required=False) class Meta: @@ -142,6 +156,9 @@ def create(self, validated_data): class ReferenceQuantitySerializer(serializers.ModelSerializer): + """Serialization, deserialization, and validation for the ReferenceQuantity model.""" + + # quantity whose operation tree this is a reference for derived_quantity = serializers.PrimaryKeyRelatedField( queryset=models.Quantity, required=False ) @@ -150,12 +167,14 @@ class Meta: model = models.ReferenceQuantity fields = ["value", "variance", "units", "hash", "derived_quantity"] + # serialize a ReferenceQuantity instance def to_representation(self, instance): data = super().to_representation(instance) if "derived_quantity" in data: data.pop("derived_quantity") return data + # create a ReferenceQuantity instance def create(self, validated_data): if "label" in validated_data: validated_data.pop("label") @@ -167,12 +186,17 @@ def create(self, validated_data): class QuantitySerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the Quantity model.""" + # associated operation tree operation_tree = OperationTreeSerializer(read_only=False, required=False) + # references for the operation tree references = ReferenceQuantitySerializer(many=True, read_only=False, required=False) + # quantity label label = serializers.CharField(max_length=20) + # dataset this is a part of dataset = serializers.PrimaryKeyRelatedField( queryset=models.DataSet, required=False, allow_null=True ) + # serialized JSON form of operation tree and references history = serializers.JSONField(required=False, allow_null=True) class Meta: @@ -189,6 +213,7 @@ class Meta: "history", ] + # validate references def validate_history(self, value): if "references" in value: for ref in value["references"]: @@ -254,11 +279,15 @@ def create(self, validated_data): class DataSetSerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the DataSet model.""" + # associated metadata metadata = MetaDataSerializer(read_only=False) + # associated files files = serializers.PrimaryKeyRelatedField( required=False, many=True, allow_null=True, queryset=models.DataFile.objects ) + # quantities that make up the dataset data_contents = QuantitySerializer(many=True, read_only=False) + # session the dataset is a part of, if any session = serializers.PrimaryKeyRelatedField( queryset=models.Session, required=False, allow_null=True ) @@ -281,8 +310,6 @@ class Meta: # Serialize a DataSet instance def to_representation(self, instance): data = super().to_representation(instance) - if "session" in data: - data.pop("session") if "request" in self.context: files = [ file.id @@ -298,7 +325,7 @@ def to_representation(self, instance): # Check that files exist and user has access to them def validate_files(self, value): for file in value: - if not file.is_public or permissions.has_access( + if not file.is_public and not permissions.has_access( self.context["request"], file ): raise serializers.ValidationError( @@ -314,9 +341,11 @@ def validate(self, data): and not data["is_public"] ): raise serializers.ValidationError("private data must have an owner") - if "current_user" in data and data["current_user"] == "": + if "current_user" in data and ( + data["current_user"] == "" or data["current_user"] is None + ): if "is_public" in data: - if not "is_public": + if not data["is_public"]: raise serializers.ValidationError("private data must have an owner") else: if not self.instance.is_public: @@ -351,15 +380,13 @@ def update(self, instance, validated_data): ) instance.metadata = new_metadata instance.save() - instance.is_public = validated_data.get("is_public", instance.is_public) - instance.name = validated_data.get("name", instance.name) - instance.save() - return instance + return super().update(instance, validated_data) class PublishedStateSerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the PublishedState model.""" + # associated session session = serializers.PrimaryKeyRelatedField( queryset=models.Session.objects, required=False, allow_null=True ) @@ -368,6 +395,7 @@ class Meta: model = models.PublishedState fields = "__all__" + # check that session does not already have a published state def validate_session(self, value): try: published = value.published_state @@ -378,11 +406,13 @@ def validate_session(self, value): except models.Session.published_state.RelatedObjectDoesNotExist: return value + # set a placeholder DOI def to_internal_value(self, data): data_copy = data.copy() data_copy["doi"] = "http://127.0.0.1:8000/v1/data/session/" return super().to_internal_value(data_copy) + # create a PublishedState instance def create(self, validated_data): # TODO: generate DOI validated_data["doi"] = ( @@ -394,6 +424,8 @@ def create(self, validated_data): class PublishedStateUpdateSerializer(serializers.ModelSerializer): + """Serialization for PublishedState updates. Restricts changes to published field.""" + class Meta: model = models.PublishedState fields = ["published"] @@ -402,7 +434,9 @@ class Meta: class SessionSerializer(serializers.ModelSerializer): """Serialization, deserialization, and validation for the Session model.""" + # datasets that make up the session datasets = DataSetSerializer(read_only=False, many=True) + # associated published state, if any published_state = PublishedStateSerializer(read_only=False, required=False) class Meta: @@ -417,6 +451,7 @@ class Meta: "users", ] + # disallow private unowned sessions def validate(self, data): if ( not self.context["request"].user.is_authenticated @@ -437,6 +472,7 @@ def validate(self, data): ) return data + # propagate is_public to datasets def to_internal_value(self, data): data_copy = data.copy() if "is_public" in data: @@ -445,6 +481,13 @@ def to_internal_value(self, data): dataset["is_public"] = data["is_public"] return super().to_internal_value(data_copy) + # serialize a session instance + def to_representation(self, instance): + session = super().to_representation(instance) + for dataset in session["datasets"]: + dataset.pop("session") + return session + # Create a Session instance def create(self, validated_data): published_state = None @@ -466,6 +509,7 @@ def create(self, validated_data): ) return session + # update a session instance def update(self, instance, validated_data): if "is_public" in validated_data: for dataset in instance.datasets.all(): @@ -485,13 +529,3 @@ def update(self, instance, validated_data): PublishedStateSerializer(), validated_data=pb_raw ) return super().update(instance, validated_data) - - -# Determine if an operation does not have parent operations -def constant_or_variable(operation: str): - return operation in ["zero", "one", "constant", "variable"] - - -# Determine if an operation has two parent operations -def binary(operation: str): - return operation in ["add", "sub", "mul", "div", "dot", "matmul", "tensor_product"] diff --git a/sasdata/fair_database/data/test/test_datafile.py b/sasdata/fair_database/data/test/test_datafile.py index 53b401c53..9c9aae858 100644 --- a/sasdata/fair_database/data/test/test_datafile.py +++ b/sasdata/fair_database/data/test/test_datafile.py @@ -11,6 +11,7 @@ from data.models import DataFile +# path to a file in example_data/1d_data def find(filename): return os.path.join( os.path.dirname(__file__), "../../../example_data/1d_data", filename diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index b463e95e6..414ea72a8 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -10,6 +10,7 @@ from data.models import DataFile, DataSet, MetaData, OperationTree, Quantity +# path to a file in example_data/1d_data def find(filename): return os.path.join( os.path.dirname(__file__), "../../../example_data/1d_data", filename @@ -245,6 +246,7 @@ def test_no_dataset_with_nonexistent_files(self): request = self.client.post("/v1/data/set/", data=dataset, format="json") self.assertEqual(request.status_code, status.HTTP_400_BAD_REQUEST) + # Test that a dataset cannot be created without metadata def test_metadata_required(self): dataset = { "name": "No metadata", @@ -368,6 +370,7 @@ def test_load_private_dataset(self): "files": [], "metadata": None, "data_contents": [], + "session": None, }, ) @@ -398,6 +401,7 @@ def test_load_public_dataset(self): "sample": "none", }, "data_contents": [], + "session": None, }, ) self.assertEqual(request1.data, request2.data) @@ -420,6 +424,7 @@ def test_load_public_dataset(self): "sample": "none", }, "data_contents": [], + "session": None, }, ) @@ -440,6 +445,7 @@ def test_load_unowned_dataset(self): "files": [], "metadata": None, "data_contents": [], + "session": None, }, ) @@ -532,6 +538,33 @@ def test_update_dataset_partial_metadata(self): metadata.title = "Metadata" metadata.save() + # Test updating a dataset's files + def test_update_dataset_files(self): + request = self.auth_client1.put("/v1/data/set/2/", data={"files": [1]}) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(len(DataSet.objects.get(id=2).files.all()), 1) + self.private_dataset.files.remove(self.file) + + # Test replacing a dataset's files + def test_update_dataset_replace_files(self): + file = DataFile.objects.create( + id=2, file_name="cyl_testdata1.txt", is_public=True, current_user=self.user1 + ) + file.file.save("cyl_testdata1.txt", open(find("cyl_testdata1.txt"))) + request = self.auth_client1.put("/v1/data/set/1/", data={"files": [2]}) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(len(DataSet.objects.get(id=1).files.all()), 1) + self.assertTrue(file in DataSet.objects.get(id=1).files.all()) + self.public_dataset.files.add(self.file) + self.public_dataset.files.remove(file) + + # Test updating a dataset to have no files + def test_update_dataset_clear_files(self): + request = self.auth_client1.put("/v1/data/set/1/", data={"files": [""]}) + self.assertEqual(request.status_code, status.HTTP_200_OK) + self.assertEqual(len(DataSet.objects.get(id=1).files.all()), 0) + self.public_dataset.files.add(self.file) + # Test that a dataset cannot be updated to be private and unowned def test_update_dataset_no_private_unowned(self): request1 = self.auth_client1.put("/v1/data/set/2/", data={"current_user": ""}) @@ -539,13 +572,11 @@ def test_update_dataset_no_private_unowned(self): "/v1/data/set/1/", data={"current_user": "", "is_public": False} ) public_dataset = DataSet.objects.get(id=1) - self.assertEqual(request1.status_code, status.HTTP_200_OK) - self.assertEqual(request2.status_code, status.HTTP_200_OK) + self.assertEqual(request1.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(request2.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(DataSet.objects.get(id=2).current_user, self.user1) self.assertEqual(public_dataset.current_user, self.user1) - self.assertFalse(public_dataset.is_public) - public_dataset.is_public = True - public_dataset.save() + self.assertTrue(public_dataset.is_public) # Test deleting a dataset def test_delete_dataset(self): diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index f5bd77848..de3ee25ef 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -7,7 +7,7 @@ class TestCreateOperationTree(APITestCase): - """Tests for datasets with operation trees.""" + """Tests for creating datasets with operation trees.""" @classmethod def setUpTestData(cls): @@ -299,6 +299,8 @@ def tearDownClass(cls): class TestCreateInvalidOperationTree(APITestCase): + """Tests for creating datasets with invalid operation trees.""" + @classmethod def setUpTestData(cls): cls.dataset = { @@ -493,6 +495,8 @@ def tearDownClass(cls): class TestGetOperationTree(APITestCase): + """Tests for retrieving datasets with operation trees.""" + @classmethod def setUpTestData(cls): cls.user = User.objects.create_user( diff --git a/sasdata/fair_database/data/test/test_published_state.py b/sasdata/fair_database/data/test/test_published_state.py index 4dc56acf9..d54f13121 100644 --- a/sasdata/fair_database/data/test/test_published_state.py +++ b/sasdata/fair_database/data/test/test_published_state.py @@ -7,6 +7,7 @@ # TODO: account for non-placeholder doi +# Get the placeholder DOI for a session based on id def doi_generator(id: int): return "http://127.0.0.1:8000/v1/data/session/" + str(id) + "/" diff --git a/sasdata/fair_database/data/test/test_session.py b/sasdata/fair_database/data/test/test_session.py index d305d01bb..1f1f95a7b 100644 --- a/sasdata/fair_database/data/test/test_session.py +++ b/sasdata/fair_database/data/test/test_session.py @@ -434,6 +434,7 @@ def test_update_public_session(self): session.is_public = False session.save() + # Test creating a published state by updating a session def test_update_session_new_published_state(self): request = self.auth_client1.put( "/v1/data/session/1/", @@ -478,6 +479,7 @@ def test_update_private_session(self): session.is_public = False session.save() + # Test updating a published state through its session def test_update_session_published_state(self): request = self.auth_client1.put( "/v1/data/session/2/", diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 466e008fc..7399e6764 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -13,6 +13,7 @@ from rest_framework.response import Response from rest_framework import status from rest_framework.views import APIView +from drf_spectacular.utils import extend_schema from sasdata.dataloader.loader import Loader from data.serializers import ( @@ -37,6 +38,9 @@ class DataFileView(APIView): """ # List of datafiles + @extend_schema( + description="Retrieve a list of accessible data files by id and filename." + ) def get(self, request, version=None): if "username" in request.GET: search_user = get_object_or_404(User, username=request.GET["username"]) @@ -54,6 +58,7 @@ def get(self, request, version=None): return Response(data_list) # Create a datafile + @extend_schema(description="Upload a data file.") def post(self, request, version=None): form = DataFileForm(request.data, request.FILES) if form.is_valid(): @@ -83,6 +88,7 @@ def post(self, request, version=None): return Response(return_data, status=status.HTTP_201_CREATED) # Create a datafile + @extend_schema(description="Upload a data file.") def put(self, request, version=None): return self.post(request, version) @@ -95,6 +101,9 @@ class SingleDataFileView(APIView): """ # Load the contents of a datafile or download the file to a device + @extend_schema( + description="Retrieve the contents of a data file or download a file." + ) def get(self, request, data_id, version=None): data = get_object_or_404(DataFile, id=data_id) if "download" in request.GET and request.GET["download"]: @@ -123,6 +132,7 @@ def get(self, request, data_id, version=None): return Response(return_data) # Modify a datafile + @extend_schema(description="Make changes to a data file that you own.") def put(self, request, data_id, version=None): db = get_object_or_404(DataFile, id=data_id) if not permissions.check_permissions(request, db): @@ -153,6 +163,7 @@ def put(self, request, data_id, version=None): return Response(return_data) # Delete a datafile + @extend_schema(description="Delete a data file that you own.") def delete(self, request, data_id, version=None): db = get_object_or_404(DataFile, id=data_id) if not permissions.is_owner(request, db): @@ -172,6 +183,10 @@ class DataFileUsersView(APIView): """ # View users with access to a datafile + @extend_schema( + description="Retrieve a list of users that have been granted access to" + " a data file and the file's publicity status." + ) def get(self, request, data_id, version=None): db = get_object_or_404(DataFile, id=data_id) if not permissions.is_owner(request, db): @@ -189,6 +204,7 @@ def get(self, request, data_id, version=None): return Response(response_data) # Grant or revoke access to a datafile + @extend_schema(description="Grant or revoke a user's access to a data file.") def put(self, request, data_id, version=None): db = get_object_or_404(DataFile, id=data_id) if not permissions.is_owner(request, db): @@ -223,6 +239,7 @@ class DataSetView(APIView): permission_classes = [DataPermission] # get a list of accessible datasets + @extend_schema(description="Retrieve a list of accessible datasets by id and name.") def get(self, request, version=None): data_list = {"dataset_ids": {}} data = DataSet.objects.all() @@ -236,6 +253,7 @@ def get(self, request, version=None): # TODO: enable uploading files as part of dataset creation, not just associating dataset with existing files # create a dataset + @extend_schema(description="Upload a dataset.") def post(self, request, version=None): # TODO: revisit request data format if isinstance(request.data, str): @@ -259,6 +277,7 @@ def post(self, request, version=None): return Response(data=response, status=status.HTTP_201_CREATED) # create a dataset + @extend_schema(description="Upload a dataset.") def put(self, request, version=None): return self.post(request, version) @@ -274,6 +293,7 @@ class SingleDataSetView(APIView): permission_classes = [DataPermission] # get a specific dataset + @extend_schema(description="Retrieve a dataset.") def get(self, request, data_id, version=None): db = get_object_or_404(DataSet, id=data_id) if not permissions.check_permissions(request, db): @@ -289,6 +309,7 @@ def get(self, request, data_id, version=None): return Response(response_data) # edit a specific dataset + @extend_schema(description="Make changes to a dataset that you own.") def put(self, request, data_id, version=None): db = get_object_or_404(DataSet, id=data_id) if not permissions.check_permissions(request, db): @@ -300,12 +321,23 @@ def put(self, request, data_id, version=None): serializer = DataSetSerializer( db, request.data, context={"request": request}, partial=True ) + clear_files = "files" in request.data and not request.data["files"] + if clear_files: + data_copy = request.data.copy() + data_copy.pop("files") + serializer = DataSetSerializer( + db, data_copy, context={"request": request}, partial=True + ) if serializer.is_valid(raise_exception=True): serializer.save() + if clear_files: + db.files.clear() + db.save() data = {"data_id": db.id, "name": db.name, "is_public": db.is_public} return Response(data) # delete a dataset + @extend_schema(description="Delete a dataset that you own.") def delete(self, request, data_id, version=None): db = get_object_or_404(DataSet, id=data_id) if not permissions.check_permissions(request, db): @@ -329,6 +361,10 @@ class DataSetUsersView(APIView): permission_classes = [DataPermission] # get a list of users with access to dataset data_id + @extend_schema( + description="Retrieve a list of users that have been granted access to" + " a dataset and the dataset's publicity status." + ) def get(self, request, data_id, version=None): db = get_object_or_404(DataSet, id=data_id) if not permissions.is_owner(request, db): @@ -344,6 +380,7 @@ def get(self, request, data_id, version=None): return Response(response_data) # grant or revoke a user's access to dataset data_id + @extend_schema(description="Grant or revoke a user's access to a dataset.") def put(self, request, data_id, version=None): db = get_object_or_404(DataSet, id=data_id) if not permissions.is_owner(request, db): @@ -376,6 +413,9 @@ class SessionView(APIView): """ # View a list of accessible sessions + @extend_schema( + description="Retrieve a list of accessible sessions by name and title." + ) def get(self, request, version=None): session_list = {"session_ids": {}} sessions = Session.objects.all() @@ -389,6 +429,7 @@ def get(self, request, version=None): # Create a session # TODO: revisit response data + @extend_schema(description="Upload a session.") def post(self, request, version=None): if isinstance(request.data, str): serializer = SessionSerializer( @@ -411,6 +452,7 @@ def post(self, request, version=None): return Response(data=response, status=status.HTTP_201_CREATED) # Create a session + @extend_schema(description="Upload a session.") def put(self, request, version=None): return self.post(request, version) @@ -423,6 +465,7 @@ class SingleSessionView(APIView): """ # get a specific session + @extend_schema(description="Retrieve a session.") def get(self, request, data_id, version=None): db = get_object_or_404(Session, id=data_id) if not permissions.check_permissions(request, db): @@ -438,6 +481,7 @@ def get(self, request, data_id, version=None): return Response(response_data) # modify a session + @extend_schema(description="Make changes to a session that you own.") def put(self, request, data_id, version=None): db = get_object_or_404(Session, id=data_id) if not permissions.check_permissions(request, db): @@ -455,6 +499,7 @@ def put(self, request, data_id, version=None): return Response(data) # delete a session + @extend_schema(description="Delete a session that you own.") def delete(self, request, data_id, version=None): db = get_object_or_404(Session, id=data_id) if not permissions.check_permissions(request, db): @@ -476,6 +521,10 @@ class SessionUsersView(APIView): """ # view the users that have access to a specific session + @extend_schema( + description="Retrieve a list of users that have been granted access to" + " a session and the session's publicity status." + ) def get(self, request, data_id, version=None): db = get_object_or_404(Session, id=data_id) if not permissions.is_owner(request, db): @@ -491,6 +540,7 @@ def get(self, request, data_id, version=None): return Response(response_data) # grant or revoke access to a session + @extend_schema(description="Grant or revoke a user's access to a data file.") def put(self, request, data_id, version=None): db = get_object_or_404(Session, id=data_id) if not permissions.is_owner(request, db): @@ -528,6 +578,9 @@ class PublishedStateView(APIView): """ # View a list of accessible sessions' published states + @extend_schema( + description="Retrieve a list of published states of accessible sessions." + ) def get(self, request, version=None): ps_list = {"published_state_ids": {}} published_states = PublishedState.objects.all() @@ -544,6 +597,7 @@ def get(self, request, version=None): return Response(data=ps_list) # Create a published state for an existing session + @extend_schema(description="Create a published state for an existing session.") def post(self, request, version=None): if isinstance(request.data, str): serializer = PublishedStateSerializer( @@ -577,6 +631,7 @@ def post(self, request, version=None): return Response(data=response, status=status.HTTP_201_CREATED) # Create a published state for an existing session + @extend_schema(description="Create a published state for an existing session.") def put(self, request, version=None): return self.post(request, version) @@ -589,6 +644,7 @@ class SinglePublishedStateView(APIView): """ # View a specific published state + @extend_schema(description="Retrieve a published state.") def get(self, request, ps_id, version=None): db = get_object_or_404(PublishedState, id=ps_id) if not permissions.check_permissions(request, db.session): @@ -610,6 +666,9 @@ def get(self, request, ps_id, version=None): return Response(response_data) # Modify a published state + @extend_schema( + description="Make changes to the published state of a session that you own." + ) def put(self, request, ps_id, version=None): db = get_object_or_404(PublishedState, id=ps_id) if not permissions.check_permissions(request, db.session): @@ -635,6 +694,7 @@ def put(self, request, ps_id, version=None): return Response(data) # Delete a published state + @extend_schema(description="Delete the published state of a session that you own.") def delete(self, request, ps_id, version=None): db = get_object_or_404(PublishedState, id=ps_id) if not permissions.check_permissions(request, db.session): diff --git a/sasdata/fair_database/documentation.yaml b/sasdata/fair_database/documentation.yaml new file mode 100644 index 000000000..22a0488fb --- /dev/null +++ b/sasdata/fair_database/documentation.yaml @@ -0,0 +1,1172 @@ +openapi: 3.0.3 +info: + title: SasView Database + version: 0.1.0 + description: A database following the FAIR data principles for SasView, a small + angle scattering analysis application. +paths: + /{version}/data/file/: + get: + operationId: data_file_list + description: Retrieve a list of accessible data files by id and filename. + parameters: + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: "#components/schemas/DataFileList" + post: + operationId: data_file_create + description: Upload a data file. + parameters: + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + requestBody: + required: true + content: + multipart/form-data: + schema: + $ref: "#components/schemas/DataFileCreate" + responses: + '201': + description: CREATED + content: + application/json: + schema: + $ref: "#components/schemas/DataFileCreated" + put: + operationId: data_file_create_2 + description: Upload a data file. + parameters: + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + requestBody: + required: true + content: + multipart/form-data: + schema: + $ref: "components/schemas/DataFileCreate" + responses: + '201': + description: CREATED + content: + application/json: + schema: + $ref: "#components/schemas/DataFileCreated" + /{version}/data/file/{data_id}/: + get: + operationId: data_file_retrieve + description: Retrieve the contents of a data file or download a file. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + requestBlock: + schema: + $ref: "#components/schemas/DataFileGet" + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: "#components/schemas/DataFile" + - $ref: "components/schemas/DataFileDownload" + put: + operationId: data_file_update_2 + description: Make changes to a data file that you own. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + requestBody: + required: true + content: + multipart/form-data: + schema: + $ref: "components/schemas/DataFileCreate" + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: "#components/schemas/DataFileCreated" + delete: + operationId: data_file_destroy + description: Delete a data file that you own. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: "#components/schemas/Delete" + /{version}/data/file/{data_id}/users/: + get: + operationId: data_file_users_retrieve + description: Retrieve a list of users that have been granted access to a data + file and the file's publicity status. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + description: OK + content: + application/json: + schema: + allOf: + - $ref: "#components/schemas/UsersList" + - type: object + properties: + file: + type: integer + file_name: + type: string + put: + operationId: data_file_users_update + description: Grant or revoke a user's access to a data file. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + requestBody: + required: true + content: + application/json: + schema: + $ref: "#components/schemas/ManageAccess" + responses: + '200': + description: OK + content: + application/json: + schema: + allOf: + - $ref: "#components/schemas/ManageAccess" + - type: object + properties: + file: + type: integer + file_name: + type: string + /{version}/data/published/: + get: + operationId: data_published_retrieve + description: Retrieve a list of published states of accessible sessions. + parameters: + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + description: OK + post: + operationId: data_published_create + description: Create a published state for an existing session. + parameters: + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '201': + description: CREATED + put: + operationId: data_published_update + description: Create a published state for an existing session. + parameters: + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '201': + description: CREATED + /{version}/data/published/{ps_id}/: + get: + operationId: data_published_retrieve_2 + description: Retrieve a published state. + parameters: + - in: path + name: ps_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + description: OK + put: + operationId: data_published_update_2 + description: Make changes to the published state of a session that you own. + parameters: + - in: path + name: ps_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + description: OK + delete: + operationId: data_published_destroy + description: Delete the published state of a session that you own. + parameters: + - in: path + name: ps_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + description: OK + /{version}/data/session/: + get: + operationId: data_session_retrieve + description: Retrieve a list of accessible sessions by name and title. + parameters: + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + description: OK + post: + operationId: data_session_create + description: Upload a session. + parameters: + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '201': + description: CREATED + put: + operationId: data_session_update + description: Upload a session. + parameters: + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '201': + description: CREATED + /{version}/data/session/{data_id}/: + get: + operationId: data_session_retrieve_2 + description: Retrieve a session. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + description: OK + put: + operationId: data_session_update_2 + description: Make changes to a session that you own. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + description: OK + delete: + operationId: data_session_destroy + description: Delete a session that you own. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + description: OK + /{version}/data/session/{data_id}/users/: + get: + operationId: data_session_users_retrieve + description: Retrieve a list of users that have been granted access to a session + and the session's publicity status. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + description: OK + put: + operationId: data_session_users_update + description: Grant or revoke a user's access to a data file. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + description: OK + /{version}/data/set/: + get: + operationId: data_set_retrieve + description: Retrieve a list of accessible datasets by id and name. + parameters: + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + responses: + '200': + description: OK + post: + operationId: data_set_create + description: Upload a dataset. + parameters: + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + responses: + '201': + description: CREATED + put: + operationId: data_set_update + description: Upload a dataset. + parameters: + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + responses: + '201': + description: CREATED + /{version}/data/set/{data_id}/: + get: + operationId: data_set_retrieve_2 + description: Retrieve a dataset. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + responses: + '200': + description: OK + put: + operationId: data_set_update_2 + description: Make changes to a dataset that you own. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + responses: + '200': + description: OK + delete: + operationId: data_set_destroy + description: Delete a dataset that you own. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + responses: + '200': + description: OK + /{version}/data/set/{data_id}/users/: + get: + operationId: data_set_users_retrieve + description: Retrieve a list of users that have been granted access to a dataset + and the dataset's publicity status. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + responses: + '200': + description: OK + put: + operationId: data_set_users_update + description: Grant or revoke a user's access to a dataset. + parameters: + - in: path + name: data_id + schema: + type: integer + required: true + - in: path + name: version + schema: + type: string + pattern: ^(v1)$ + required: true + tags: + - data + security: + - knoxApiToken: [] + - cookieAuth: [] + responses: + '200': + description: OK + /auth/login/: + post: + operationId: auth_login_create + description: |- + Check the credentials and return the REST Token + if the credentials are valid and authenticated. + Calls Django Auth login method to register User ID + in Django session framework + + Accept the following POST parameters: username, password + Return the REST Framework Token Object's key. + tags: + - auth + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Login' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Login' + multipart/form-data: + schema: + $ref: '#/components/schemas/Login' + required: true + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Login' + description: '' + /auth/logout/: + post: + operationId: auth_logout_create + description: |- + Calls Django logout method and delete the Token object + assigned to the current User object. + + Accepts/Returns nothing. + tags: + - auth + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/RestAuthDetail' + description: '' + /auth/password/change/: + post: + operationId: auth_password_change_create + description: |- + Calls Django Auth SetPasswordForm save method. + + Accepts the following POST parameters: new_password1, new_password2 + Returns the success/fail message. + tags: + - auth + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PasswordChange' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PasswordChange' + multipart/form-data: + schema: + $ref: '#/components/schemas/PasswordChange' + required: true + security: + - knoxApiToken: [] + - cookieAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/RestAuthDetail' + description: '' + /auth/register/: + post: + operationId: auth_register_create + description: |- + Registers a new user. + + Accepts the following POST parameters: username, email, password1, password2. + tags: + - auth + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Register' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Register' + multipart/form-data: + schema: + $ref: '#/components/schemas/Register' + required: true + security: + - knoxApiToken: [] + - cookieAuth: [] + - {} + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/Register' + description: '' + /auth/user/: + get: + operationId: auth_user_retrieve + description: |- + Reads and updates UserModel fields + Accepts GET, PUT, PATCH methods. + + Default accepted fields: username, first_name, last_name + Default display fields: pk, username, email, first_name, last_name + Read-only fields: pk, email + + Returns UserModel fields. + tags: + - auth + security: + - knoxApiToken: [] + - cookieAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/UserDetails' + description: '' + put: + operationId: auth_user_update + description: |- + Reads and updates UserModel fields + Accepts GET, PUT, PATCH methods. + + Default accepted fields: username, first_name, last_name + Default display fields: pk, username, email, first_name, last_name + Read-only fields: pk, email + + Returns UserModel fields. + tags: + - auth + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserDetails' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/UserDetails' + multipart/form-data: + schema: + $ref: '#/components/schemas/UserDetails' + required: true + security: + - knoxApiToken: [] + - cookieAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/UserDetails' + description: '' + patch: + operationId: auth_user_partial_update + description: |- + Reads and updates UserModel fields + Accepts GET, PUT, PATCH methods. + + Default accepted fields: username, first_name, last_name + Default display fields: pk, username, email, first_name, last_name + Read-only fields: pk, email + + Returns UserModel fields. + tags: + - auth + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PatchedUserDetails' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PatchedUserDetails' + multipart/form-data: + schema: + $ref: '#/components/schemas/PatchedUserDetails' + security: + - knoxApiToken: [] + - cookieAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/UserDetails' + description: '' +components: + schemas: + Delete: + type: object + properties: + success: + type: boolean + UsersList: + type: object + properties: + is_public: + type: boolean + users: + type: array + items: + type: string + ManageAccess: + type: object + properties: + username: + type: string + access: + type: boolean + DataFileList: + type: object + properties: + data_ids: + type: object + additionalProperties: + filename: + type: string + DataFileCreate: + type: object + properties: + filename: + type: string + file: + type: string + format: binary + DataFileCreated: + type: object + properties: + current_user: + type: string + authenticated: + type: boolean + file_id: + type: integer + file_alternative_name: + type: string + is_public: + type: boolean + DataFileGet: + type: object + properties: + download: + type: boolean + DataFile: + type: object + properties: + filename: + type: object + additionalProperties: + type: array + items: + type: string + DataFileDownload: + type: string + format: binary + Login: + type: object + properties: + username: + type: string + email: + type: string + format: email + password: + type: string + required: + - password + DataSetList: + type: object + properties: + dataset_ids: + type: object + additionalProperties: + name: + type: string + DataSetCreate: + type: object + properties: + name: + type: string + is_public: + type: boolean + data_contents: + type: array + items: + type: object + properties: + label: + type: string + value: + type: object + additionalProperties: true + PasswordChange: + type: object + properties: + new_password1: + type: string + maxLength: 128 + new_password2: + type: string + maxLength: 128 + required: + - new_password1 + - new_password2 + PatchedUserDetails: + type: object + description: User model w/o password + properties: + pk: + type: integer + readOnly: true + title: ID + username: + type: string + description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_ + only. + pattern: ^[\w.@+-]+$ + maxLength: 150 + email: + type: string + format: email + readOnly: true + title: Email address + first_name: + type: string + maxLength: 150 + last_name: + type: string + maxLength: 150 + Register: + type: object + properties: + username: + type: string + maxLength: 150 + minLength: 1 + email: + type: string + format: email + password1: + type: string + writeOnly: true + password2: + type: string + writeOnly: true + required: + - password1 + - password2 + - username + RestAuthDetail: + type: object + properties: + detail: + type: string + readOnly: true + required: + - detail + UserDetails: + type: object + description: User model w/o password + properties: + pk: + type: integer + readOnly: true + title: ID + username: + type: string + description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_ + only. + pattern: ^[\w.@+-]+$ + maxLength: 150 + email: + type: string + format: email + readOnly: true + title: Email address + first_name: + type: string + maxLength: 150 + last_name: + type: string + maxLength: 150 + required: + - email + - pk + - username + securitySchemes: + cookieAuth: + type: apiKey + in: cookie + name: sessionid + knoxApiToken: + type: apiKey + in: header + name: Authorization + description: Token-based authentication with required prefix "Token" diff --git a/sasdata/fair_database/fair_database/create_example_session.py b/sasdata/fair_database/fair_database/create_example_session.py index 70d255c39..6c10d9905 100644 --- a/sasdata/fair_database/fair_database/create_example_session.py +++ b/sasdata/fair_database/fair_database/create_example_session.py @@ -79,9 +79,19 @@ ], }, ], - "is_public": True, + "is_public": False, } url = "http://127.0.0.1:8000/v1/data/session/" - -requests.request("POST", url, json=session) +login_data = {"email": "test@test.org", "username": "testUser", "password": "sasview!"} +response = requests.post("http://127.0.0.1:8000/auth/login/", data=login_data) +if response.status_code != 200: + register_data = { + "email": "test@test.org", + "username": "testUser", + "password1": "sasview!", + "password2": "sasview!", + } + response = requests.post("http://127.0.0.1:8000/auth/register/", data=register_data) +token = response.json()["token"] +requests.request("POST", url, json=session, headers={"Authorization": "Token " + token}) diff --git a/sasdata/fair_database/fair_database/permissions.py b/sasdata/fair_database/fair_database/permissions.py index 1446c52ae..74be5f33a 100644 --- a/sasdata/fair_database/fair_database/permissions.py +++ b/sasdata/fair_database/fair_database/permissions.py @@ -1,10 +1,12 @@ from rest_framework.permissions import BasePermission +# check if a request is made by an object's owner def is_owner(request, obj): return request.user.is_authenticated and request.user == obj.current_user +# check if a request is made by a user with read access def has_access(request, obj): return is_owner(request, obj) or ( request.user.is_authenticated and request.user in obj.users.all() @@ -12,6 +14,7 @@ def has_access(request, obj): class DataPermission(BasePermission): + # check if a request has the correct permissions for a specific object def has_object_permission(self, request, view, obj): if request.method == "GET": return obj.is_public or has_access(request, obj) @@ -21,5 +24,6 @@ def has_object_permission(self, request, view, obj): return is_owner(request, obj) +# check if a request has the correct permissions for a specific object def check_permissions(request, obj): return DataPermission().has_object_permission(request, None, obj) diff --git a/sasdata/fair_database/fair_database/settings.py b/sasdata/fair_database/fair_database/settings.py index 31ce030a3..f3ec69ed9 100644 --- a/sasdata/fair_database/fair_database/settings.py +++ b/sasdata/fair_database/fair_database/settings.py @@ -50,6 +50,7 @@ "dj_rest_auth.registration", "knox", "user_app.apps.UserAppConfig", + "drf_spectacular", ] SITE_ID = 1 @@ -96,9 +97,7 @@ "knox.auth.TokenAuthentication", "rest_framework.authentication.SessionAuthentication", ], - #'DEFAULT_PERMISSION_CLASSES': [ - # 'fair_database.permissions.DataPermission', - # ], + "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", } REST_AUTH = { @@ -108,6 +107,14 @@ "TOKEN_CREATOR": "user_app.util.create_knox_token", } +SPECTACULAR_SETTINGS = { + "TITLE": "SasView Database", + "DESCRIPTION": "A database following the FAIR data principles for SasView," + " a small angle scattering analysis application.", + "VERSION": "0.1.0", + "SERVE_INCLUDE_SCHEMA": False, +} + # allauth settings HEADLESS_ONLY = True ACCOUNT_EMAIL_VERIFICATION = "none" diff --git a/sasdata/fair_database/fair_database/urls.py b/sasdata/fair_database/fair_database/urls.py index 0cfeb0aca..56c88ce21 100644 --- a/sasdata/fair_database/fair_database/urls.py +++ b/sasdata/fair_database/fair_database/urls.py @@ -17,10 +17,26 @@ from django.contrib import admin from django.urls import include, path, re_path +from drf_spectacular.views import ( + SpectacularAPIView, + SpectacularRedocView, + SpectacularSwaggerView, +) urlpatterns = [ re_path(r"^(?P(v1))/data/", include("data.urls")), path("admin/", admin.site.urls), path("accounts/", include("allauth.urls")), # needed for social auth path("auth/", include("user_app.urls")), + path("api/schema/", SpectacularAPIView.as_view(), name="schema"), + path( + "api/schema/swagger-ui/", + SpectacularSwaggerView.as_view(url_name="schema"), + name="swagger-ui", + ), + path( + "api/schema/redoc/", + SpectacularRedocView.as_view(url_name="schema"), + name="redoc", + ), ] diff --git a/sasdata/fair_database/requirements.txt b/sasdata/fair_database/requirements.txt index 5eb1b6b95..22b32934b 100644 --- a/sasdata/fair_database/requirements.txt +++ b/sasdata/fair_database/requirements.txt @@ -5,3 +5,4 @@ djangorestframework dj-rest-auth django-allauth django-rest-knox +drf-spectacular diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index 62ccb080d..7d0bae94c 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -7,6 +7,8 @@ # Create your tests here. class AuthTests(TestCase): + """Tests for authentication endpoints.""" + @classmethod def setUpTestData(cls): cls.client1 = APIClient() @@ -33,6 +35,7 @@ def setUpTestData(cls): cls.client_authenticated = APIClient() cls.client_authenticated.force_authenticate(user=cls.user) + # Create an authentication header for a given token def auth_header(self, response): return {"Authorization": "Token " + response.data["token"]} @@ -125,6 +128,7 @@ def test_register_logout(self): self.assertEqual(response2.status_code, status.HTTP_401_UNAUTHORIZED) User.objects.get(username="testUser").delete() + # Test multiple logins for the same account log out independently def test_multiple_logout(self): self.client1.post("/auth/login/", data=self.login_data_2) token = self.client2.post("/auth/login/", data=self.login_data_2) diff --git a/sasdata/fair_database/user_app/util.py b/sasdata/fair_database/user_app/util.py index c6b43cc63..dc7b35026 100644 --- a/sasdata/fair_database/user_app/util.py +++ b/sasdata/fair_database/user_app/util.py @@ -1,6 +1,7 @@ from knox.models import AuthToken +# create an authentication token def create_knox_token(token_model, user, serializer): token = AuthToken.objects.create(user=user) return token diff --git a/sasdata/metadata.py b/sasdata/metadata.py index bef28e655..4042d2cb9 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -1,91 +1,8 @@ -from tokenize import String - -import numpy as np -from numpy.typing import ArrayLike - -import sasdata.quantities.units as units -from sasdata.quantities.absolute_temperature import AbsoluteTemperatureAccessor -from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor, \ - DimensionlessAccessor, FloatAccessor, TemperatureAccessor, AccessorTarget - - -from dataclasses import dataclass - -from sasdata.quantities.quantity import Quantity - -@dataclass(kw_only=True) -class Vec3: - """A three-vector of measured quantities""" - x : Quantity[float] | None - y : Quantity[float] | None - z : Quantity[float] | None - - @staticmethod - def deserialise_json(json_data: dict): - x = None - y = None - z = None - if "x" in json_data: - x = Quantity.deserialise_json(json_data["x"]) - if "y" in json_data: - y = Quantity.deserialise_json(json_data["y"]) - if "z" in json_data: - z = Quantity.deserialise_json(json_data["z"]) - return Vec3(x=x, y=y, z=z) - - def serialise_json(self): - data = { - "x": None, - "y": None, - "z": None - } - if self.x is not None: - data["x"] = self.x.serialise_json() - if self.y is not None: - data["y"] = self.y.serialise_json() - if self.z is not None: - data["z"] = self.z.serialise_json() - return data - -@dataclass(kw_only=True) -class Rot3: - """A measured rotation in 3-space""" - roll : Quantity[float] | None - pitch : Quantity[float] | None - yaw : Quantity[float] | None - - @staticmethod - def deserialise_json(json_data: dict): - roll = None - pitch = None - yaw = None - if "roll" in json_data: - roll = Quantity.deserialise_json(json_data["roll"]) - if "pitch" in json_data: - pitch = Quantity.deserialise_json(json_data["pitch"]) - if "yaw" in json_data: - yaw = Quantity.deserialise_json(json_data["yaw"]) - return Rot3(roll=roll, pitch=pitch, yaw=yaw) - - def serialise_json(self): - data = { - "roll": None, - "pitch": None, - "yaw": None - } - if self.roll is not None: - data["roll"] = self.roll.serialise_json() - if self.pitch is not None: - data["pitch"] = self.pitch.serialise_json() - if self.yaw is not None: - data["yaw"] = self.yaw.serialise_json() - return data - -@dataclass(kw_only=True) -class Detector: - """ - Detector information - """ +from typing import Generic, TypeVar + +from numpy._typing import ArrayLike + +from sasdata.quantities.quantities import Unit, Quantity def __init__(self, target_object: AccessorTarget): @@ -127,560 +44,61 @@ def __init__(self, target_object: AccessorTarget): "slit_length.units", default_unit=units.millimeters) - def summary(self): - return (f"Detector:\n" - f" Name: {self.name}\n" - f" Distance: {self.distance}\n" - f" Offset: {self.offset}\n" - f" Orientation: {self.orientation}\n" - f" Beam center: {self.beam_center}\n" - f" Pixel size: {self.pixel_size}\n" - f" Slit length: {self.slit_length}\n") - - @staticmethod - def deserialise_json(json_data: dict): - name = None - distance = None - offset = None - orientation = None - beam_center = None - pixel_size = None - slit_length = None - if "name" in json_data: - name = json_data["name"] - if "distance" in json_data: - distance = Quantity.deserialise_json(json_data["distance"]) - if "offset" in json_data: - offset = Vec3.deserialise_json(json_data["offset"]) - if "orientation" in json_data: - orientation = Rot3.deserialise_json(json_data["orientation"]) - if "beam_center" in json_data: - beam_center = Vec3.deserialise_json(json_data["beam_center"]) - if "pixel_size" in json_data: - pixel_size = Vec3.deserialise_json(json_data["pixel_size"]) - if "slit_length" in json_data: - slit_length = Quantity.deserialise_json(json_data["slit_length"]) - return Detector( - name=name, - distance=distance, - offset=offset, - orientation=orientation, - beam_center=beam_center, - pixel_size=pixel_size, - slit_length=slit_length - ) - - - def serialise_json(self): - data = { - "name": self.name, - "distance": None, - "offset": None, - "orientation": None, - "beam_center": None, - "pixel_size": None, - "slit_length": None - } - if self.distance is not None: - data["distance"] = self.distance.serialise_json() - if self.offset is not None: - data["offset"] = self.offset.serialise_json() - if self.orientation is not None: - data["orientation"] = self.orientation.serialise_json() - if self.beam_center is not None: - data["beam_center"] = self.beam_center.serialise_json() - if self.pixel_size is not None: - data["pixel_size"] = self.pixel_size.serialise_json() - if self.slit_length is not None: - data["slit_length"] = self.slit_length.serialise_json() - return data - - -class Aperture: +class RawMetaData: + pass - def __init__(self, target_object: AccessorTarget): +class MetaData: + pass - # Name - self.name = StringAccessor(target_object, "name") - # Type - self.type = StringAccessor(target_object, "type") +FieldDataType = TypeVar("FieldDataType") +OutputDataType = TypeVar("OutputDataType") - # Size name - TODO: What is the name of a size - self.size_name = StringAccessor(target_object, "size_name") +class Accessor(Generic[FieldDataType, OutputDataType]): + def __init__(self, target_field: str): + self._target_field = target_field - # Aperture size [Vector] # TODO: Wat!?! - self.size = QuantityAccessor[ArrayLike](target_object, - "size", - "size.units", - default_unit=units.millimeters) + def _raw_values(self) -> FieldDataType: + raise NotImplementedError("not implemented in base class") - # Aperture distance [float] - self.distance = LengthAccessor[float](target_object, - "distance", - "distance.units", - default_unit=units.millimeters) - - - def summary(self): - return (f"Aperture:\n" - f" Name: {self.name}\n" - f" Aperture size: {self.size}\n" - f" Aperture distance: {self.distance}\n") - - @staticmethod - def deserialise_json(json_data: dict): - distance = None - size = None - size_name = None - name = None - type_ = None - if "distance" in json_data: - distance = Quantity.deserialise_json(json_data["distance"]) - if "size" in json_data: - size = Vec3.deserialise_json(json_data["size"]) - if "size_name" in json_data: - size_name = json_data["size_name"] - if "name" in json_data: - name = json_data["name"] - if "type" in json_data: - type_ = json_data["type"] - return Aperture( - distance=distance, size=size, size_name=size_name, name=name, type_=type_ - ) - - def serialise_json(self): - data = { - "distance": None, - "size": None, - "size_name": self.size_name, - "name": self.name, - "type": self.type_ - } - if self.distance is not None: - data["distance"] = self.distance.serialise_json() - if self.size is not None: - data["size"] = self.size.serialise_json() - -class Collimation: - """ - Class to hold collimation information - """ - - def __init__(self, name, length): - - # Name - self.name = name - # Length [float] [mm] - self.length = length - # TODO - parse units properly - - def summary(self): - - #TODO collimation stuff - return ( - f"Collimation:\n" - f" Length: {self.length}\n") - - @staticmethod - def deserialise_json(json_data: dict): - length = None - apertures = [] - if "length" in json_data: - length = Quantity.deserialise_json(json_data["length"]) - if "apertures" in json_data: - apertures = [Aperture.deserialise_json(a) for a in json_data["apertures"]] - - def serialise_json(self): - data = { - "length": None, - "apertures": [a.serialise_json() for a in self.apertures] - } - if self.length is not None: - data["length"] = self.length.serialise_json() - return data - -@dataclass -class BeamSize: - name: str | None - size: Vec3 | None - - @staticmethod - def deserialise_json(json_data: dict): - name = None - size = None - if "name" in json_data: - name = json_data["name"] - if "size" in json_data: - size = Vec3.deserialise_json(json_data["size"]) - return BeamSize(name=name, size=size) - - def serialise_json(self): - data = { - "name": self.name, - "size": None - } - if self.size is not None: - data["size"] = self.size.serialise_json() - return data - - -@dataclass -class Source: - radiation: str - beam_shape: str - beam_size: Optional[BeamSize] - wavelength : Quantity[float] - wavelength_min : Quantity[float] - wavelength_max : Quantity[float] - wavelength_spread : Quantity[float] - - def summary(self) -> str: - if self.radiation is None and self.type.value and self.probe_particle.value: - radiation = f"{self.type.value} {self.probe_particle.value}" - else: - radiation = f"{self.radiation}" - - return ( - f"Source:\n" - f" Radiation: {radiation}\n" - f" Shape: {self.beam_shape}\n" - f" Wavelength: {self.wavelength}\n" - f" Min. Wavelength: {self.wavelength_min}\n" - f" Max. Wavelength: {self.wavelength_max}\n" - f" Wavelength Spread: {self.wavelength_spread}\n" - f" Beam Size: {self.beam_size}\n" - ) - - @staticmethod - def deserialise_json(json_data: dict): - radiation = None - beam_shape = None - beam_size = None - wavelength = None - wavelength_min = None - wavelength_max = None - wavelength_spread = None - if "radiation" in json_data: - radiation = json_data["radiation"] - if "beam_shape" in json_data: - beam_shape = json_data["beam_shape"] - if "beam_size" in json_data: - beam_size = BeamSize.deserialise_json(json_data["beam_size"]) - if "wavelength" in json_data: - wavelength = Quantity.deserialise_json(json_data["wavelength"]) - if "wavelength_min" in json_data: - wavelength_min = Quantity.deserialise_json(json_data["wavelength_min"]) - if "wavelength_max" in json_data: - wavelength_max = Quantity.deserialise_json(json_data["wavelength_max"]) - if "wavelength_spread" in json_data: - wavelength_spread = Quantity.deserialise_json(json_data["wavelength_spread"]) - return Source( - radiation=radiation, - beam_shape=beam_shape, - beam_size=beam_size, - wavelength=wavelength, - wavelength_min=wavelength_min, - wavelength_max=wavelength_max, - wavelength_spread=wavelength_spread - ) - - def serialise_json(self): - data = { - "radiation": self.radiation, - "beam_shape": self.beam_shape, - "beam_size": None, - "wavelength": None, - "wavelength_min": None, - "wavelength_max": None, - "wavelength_spread": None - } - if self.beam_size is not None: - data["beam_size"] = self.beam_size.serialise_json() - if self.wavelength is not None: - data["wavelength"] = self.wavelength.serialise_json() - if self.wavelength_min is not None: - data["wavelength_min"] = self.wavelength_min.serialise_json() - if self.wavelength_max is not None: - data["wavelength_max"] = self.wavelength_max.serialise_json() - if self.wavelength_spread is not None: - data["wavelength_spread"] = self.wavelength_spread.serialise_json() - return data - - -""" -Definitions of radiation types -""" -NEUTRON = 'neutron' -XRAY = 'x-ray' -MUON = 'muon' -ELECTRON = 'electron' - - -class Sample: - """ - Class to hold the sample description - """ - def __init__(self, target_object: AccessorTarget): + @property + def value(self) -> OutputDataType: + raise NotImplementedError("value not implemented in base class") - # Short name for sample - self.name = StringAccessor(target_object, "name") - # ID - - self.sample_id = StringAccessor(target_object, "id") - - # Thickness [float] [mm] - self.thickness = LengthAccessor(target_object, - "thickness", - "thickness.units", - default_unit=units.millimeters) - - # Transmission [float] [fraction] - self.transmission = FloatAccessor(target_object,"transmission") - - # Temperature [float] [No Default] - self.temperature = AbsoluteTemperatureAccessor(target_object, - "temperature", - "temperature.unit", - default_unit=units.kelvin) - # Position [Vector] [mm] - self.position = LengthAccessor[ArrayLike](target_object, - "position", - "position.unit", - default_unit=units.millimeters) - - # Orientation [Vector] [degrees] - self.orientation = AngleAccessor[ArrayLike](target_object, - "orientation", - "orientation.unit", - default_unit=units.degrees) - # Details - self.details = StringAccessor(target_object, "details") - - - # SESANS zacceptance - zacceptance = (0,"") - yacceptance = (0,"") - - def summary(self) -> str: - return (f"Sample:\n" - f" ID: {self.sample_id}\n" - f" Transmission: {self.transmission}\n" - f" Thickness: {self.thickness}\n" - f" Temperature: {self.temperature}\n" - f" Position: {self.position}\n" - f" Orientation: {self.orientation}\n") - - @staticmethod - def deserialise_json(json_data): - name = None - sample_id = None - thickness = None - transmission = None - temperature = None - position = None - orientation = None - details = [] - if "name" in json_data: - name = json_data["name"] - if "sample_id" in json_data: - sample_id = json_data["sample_id"] - if "thickness" in json_data: - thickness = Quantity.deserialise_json(json_data["thickness"]) - if "temperature" in json_data: - temperature = Quantity.deserialise_json(json_data["temperature"]) - if "position" in json_data: - position = Vec3.deserialise_json(json_data["position"]) - if "orientation" in json_data: - orientation = Rot3.deserialise_json(json_data["orientation"]) - return Sample( - name=name, - sample_id=sample_id, - thickness=thickness, - transmission=transmission, - temperature=temperature, - position=position, - orientation=orientation, - details=details - ) - - - def serialise_json(self): - data = { - "name": self.name, - "sample_id": self.sample_id, - "thickness": None, - "transmission": self.transmission, - "temperature": None, - "position": None, - "orientation": None, - "details": self.details - } - if self.thickness is not None: - data["thickness"] = self.thickness.serialise_json() - if self.temperature is not None: - data["temperature"] = self.temperature.serialise_json() - if self.position is not None: - data["position"] = self.position.serialise_json() - if self.orientation is not None: - data["orientation"] = self.orientation.serialise_json() - return data - - -class Process: - """ - Class that holds information about the processes - performed on the data. - """ - def __init__(self, target_object: AccessorTarget): - self.name = StringAccessor(target_object, "name") - self.date = StringAccessor(target_object, "date") - self.description = StringAccessor(target_object, "description") - - #TODO: It seems like these might be lists of strings, this should be checked - - self.term = StringAccessor(target_object, "term") - self.notes = StringAccessor(target_object, "notes") - - def single_line_desc(self): - """ - Return a single line string representing the process - """ - return f"{self.name} {self.date} {self.description}" - - def summary(self): - return (f"Process:\n" - f" Name: {self.name.value}\n" - f" Date: {self.date.value}\n" - f" Description: {self.description.value}\n" - f" Term: {self.term.value}\n" - f" Notes: {self.notes.value}\n" - ) - - @staticmethod - def deserialise_json(json_data: dict): - name = None - date = None - description = None - term = None - if "name" in json_data: - name = json_data["name"] - if "date" in json_data: - date = json_data["date"] - if "description" in json_data: - description = json_data["description"] - if "term" in json_data: - term = json_data["term"] - return Process(name=name, date=date, description=description, term=term) - - def serialise_json(self): - return { - "name": self.name, - "date": self.date, - "description": self.description, - "term": self.term, - } - - -@dataclass -class Instrument: - collimations : list[Collimation] - source : Source - detector : list[Detector] - - def summary(self): - return ( - self.aperture.summary() + - "\n".join([c.summary for c in self.collimations]) + - self.detector.summary() + - self.source.summary()) - - @staticmethod - def deserialize_json(json_data: dict): - collimations = [] - source = None - detector= [] - if "collimations" in json_data: - collimations = [Collimation.deserialise_json(c) for c in json_data["collimations"]] - if "source" in json_data: - source = Source.deserialise_json(json_data["source"]) - if "detector" in json_data: - detector = [Detector.deserialise_json(d) for d in json_data["detector"]] - return Instrument(collimations=collimations, source=source, detector=detector) - - def serialise_json(self): - data = { - "collimations": [c.serialise_json() for c in self.collimations], - "source": None, - "detector": [d.serialise_json() for d in self.detector] - } - if self.source is not None: - data["source"] = self.source.serialise_json() - return data - -@dataclass(kw_only=True) -class Metadata: - def __init__(self, target: AccessorTarget, instrument: Instrument): - self._target = target - - self.instrument = instrument - self.process = Process(target.with_path_prefix("sasprocess|process")) - self.sample = Sample(target.with_path_prefix("sassample|sample")) - self.transmission_spectrum = TransmissionSpectrum(target.with_path_prefix("sastransmission_spectrum|transmission_spectrum")) - - self._title = StringAccessor(target, "title") - self._run = StringAccessor(target, "run") - self._definition = StringAccessor(target, "definition") - - self.title: str = decode_string(self._title.value) - self.run: str = decode_string(self._run.value) - self.definition: str = decode_string(self._definition.value) - title: Optional[str] - run: list[str] - definition: Optional[str] - process: list[str] - sample: Optional[Sample] - instrument: Optional[Instrument] - - def summary(self): - return ( - f" {self.title}, Run: {self.run}\n" + - " " + "="*len(self.title) + - "=======" + - "="*len(self.run) + "\n\n" + - f"Definition: {self.title}\n" + - self.process.summary() + - self.sample.summary() + - (self.instrument.summary() if self.instrument else "")) - - @staticmethod - def deserialize_json(json_data: dict): - title = json_data["title"] - run = json_data["run"] - definition = json_data["definition"] - process = [Process.deserialise_json(p) for p in json_data["process"]] - sample = None - instrument = None - if json_data["sample"] is not None: - sample = Sample.deserialise_json(json_data["sample"]) - if json_data["instrument"] is not None: - instrument = Instrument.deserialize_json(json_data["instrument"]) - return Metadata( - title=title, run=run, definition=definition, process=process, sample=sample, instrument=instrument - ) - - def serialise_json(self): - serialized = { - "instrument": None, - "process": [p.serialise_json() for p in self.process], - "sample": None, - "title": self.title, - "run": self.run, - "definition": self.definition - } if self.sample is not None: - serialized["sample"] = self.sample.serialise_json() - if self.instrument is not None: - serialized["instrument"] = self.instrument.serialise_json() - - return serialized \ No newline at end of file + +class QuantityAccessor(Accessor[ArrayLike, Quantity[ArrayLike]]): + 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: + pass + + def _raw_values(self) -> ArrayLike: + pass + + +class StringAccessor(Accessor[str]): + @property + def value(self) -> str: + return self._raw_values() + + +class LengthAccessor(QuantityAccessor): + @property + def m(self): + return self.value.in_units_of("m") + + +class TimeAccessor(QuantityAccessor): + pass + + +class TemperatureAccessor(QuantityAccessor): + pass + + +class AbsoluteTemperatureAccessor(QuantityAccessor): + pass + diff --git a/sasdata/model_requirements.py b/sasdata/model_requirements.py index 12ad54565..a35759bd2 100644 --- a/sasdata/model_requirements.py +++ b/sasdata/model_requirements.py @@ -2,8 +2,7 @@ import numpy as np -from sasdata.metadata import Metadata -from sasdata.quantities.quantity import Operation +from transforms.operation import Operation @dataclass @@ -12,12 +11,12 @@ class ModellingRequirements: dimensionality: int operation: Operation - def from_qi_transformation(self, data: np.ndarray, metadata: Metadata) -> np.ndarray: - """ Transformation for going from qi to this data""" + + def from_qi_transformation(self, data: np.ndarray) -> np.ndaarray: pass def guess_requirements(abscissae, ordinate) -> ModellingRequirements: - """ Use names of axes and units to guess what kind of processing needs to be done """ \ No newline at end of file + """ Use names of axes and units to guess what kind of processing needs to be done """ diff --git a/sasdata/quantities/quantities.py b/sasdata/quantities/quantities.py new file mode 100644 index 000000000..2a03a5d41 --- /dev/null +++ b/sasdata/quantities/quantities.py @@ -0,0 +1,146 @@ +from typing import Collection, Sequence, TypeVar, Generic +from dataclasses import dataclass + +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, other: "Dimensions"): + + 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, other: "Dimensions"): + + 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) + + +@dataclass +class UnitName: + ascii_name: str + unicode_name: str | None = None + + @property + def best_name(self): + if self.unicode_name is None: + return self.ascii_name + else: + return self.unicode_name + +class Unit: + def __init__(self, + si_scaling_factor: float, + dimensions: Dimensions, + name: UnitName | None = None): + + self.scale = si_scaling_factor + self.dimensions = dimensions + self.name = name + + def _components(self, tokens: Sequence["UnitToken"]): + pass + + def __mul__(self, other: "Unit"): + if not isinstance(other, Unit): + return NotImplemented + + return Unit(self.scale * other.scale, self.dimensions * other.dimensions) + + def __truediv__(self, other: "Unit"): + if not isinstance(other, Unit): + return NotImplemented + + return Unit(self.scale / other.scale, self.dimensions / other.dimensions) + + def __pow__(self, power: int): + if not isinstance(power, int): + return NotImplemented + + return Unit(self.scale**power, self.dimensions**power) + + +QuantityType = TypeVar("QuantityType") +class Quantity(Generic[QuantityType]): + def __init__(self, value: QuantityType, units: Unit): + self.value = value + self.units = units + + def in_units_of(self, units: Unit) -> QuantityType: + pass + +class ExpressionMethod: + pass + + +class SetExpressionMethod(ExpressionMethod): + pass + + +class AnyExpressionMethod(ExpressionMethod): + pass + + +class ForceExpressionMethod(ExpressionMethod): + pass + + +class UnitToken: + def __init__(self, unit: Collection[NamedUnit], method: ExpressionMethod): + pass + +unit_dictionary = { + "Amps": Unit(1, Dimensions(current=1), UnitName("A")), + "Coulombs": Unit(1, Dimensions(current=1, time=1), UnitName("C")) +} + +@dataclass +class Disambiguator: + A: Unit = unit_dictionary["Amps"] + C: Unit = unit_dictionary["Coulombs"] + +def parse_units(unit_string: str, disambiguator: Disambiguator = Disambiguator()) -> Unit: + pass diff --git a/sasdata/transforms/operation.py b/sasdata/transforms/operation.py new file mode 100644 index 000000000..3ff7b0614 --- /dev/null +++ b/sasdata/transforms/operation.py @@ -0,0 +1,19 @@ +import numpy as np +from sasdata.quantities.quantities import Quantity + +class Operation: + """ Sketch of what model post-processing classes might look like """ + + children: list["Operation"] + named_children: dict[str, "Operation"] + + @property + def name(self) -> str: + raise NotImplementedError("No name for transform") + + def evaluate(self) -> Quantity[np.ndarray]: + pass + + def __call__(self, *children, **named_children): + self.children = children + self.named_children = named_children \ No newline at end of file From e9a5d2bc17085ae63393c44cb1cae06a5a7fde9c Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 5 Aug 2024 09:14:59 +0100 Subject: [PATCH 487/675] Some units --- sasdata/data.py | 3 +- sasdata/metadata.py | 33 ++++++++++--- sasdata/quantities/quantities.py | 49 ++++++++++++++++++- sasdata/quantities/units_table.py | 78 +++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 10 deletions(-) create mode 100644 sasdata/quantities/units_table.py diff --git a/sasdata/data.py b/sasdata/data.py index 638bad1e0..fe47552cc 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -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 diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 4042d2cb9..8c0a28258 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -1,4 +1,4 @@ -from typing import Generic, TypeVar +from typing import TypeVar from numpy._typing import ArrayLike @@ -47,14 +47,11 @@ def __init__(self, target_object: AccessorTarget): 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 @@ -72,18 +69,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 @@ -102,3 +110,14 @@ class TemperatureAccessor(QuantityAccessor): class AbsoluteTemperatureAccessor(QuantityAccessor): 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 + diff --git a/sasdata/quantities/quantities.py b/sasdata/quantities/quantities.py index 2a03a5d41..d9b749420 100644 --- a/sasdata/quantities/quantities.py +++ b/sasdata/quantities/quantities.py @@ -1,6 +1,9 @@ from typing import Collection, Sequence, TypeVar, Generic from dataclasses import dataclass +from numpy._typing import ArrayLike + + class Dimensions: """ @@ -58,6 +61,17 @@ def __pow__(self, power: int): self.current * power, self.temperature * power) + def __eq__(self, other: "Dimensions"): + 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 + + @dataclass class UnitName: @@ -102,9 +116,12 @@ def __pow__(self, power: int): return Unit(self.scale**power, self.dimensions**power) + def equivalent(self, other: "Unit"): + return self.dimensions == other.dimensions + -QuantityType = TypeVar("QuantityType") -class Quantity(Generic[QuantityType]): +# QuantityType = TypeVar("QuantityType") +class Quantity[QuantityType]: def __init__(self, value: QuantityType, units: Unit): self.value = value self.units = units @@ -112,6 +129,34 @@ def __init__(self, value: QuantityType, units: Unit): def in_units_of(self, units: Unit) -> QuantityType: pass + def __mul__(self, other: ArrayLike | "Quantity" ) -> "Quantity": + if isinstance(other, Quantity): + pass + + else: + pass + + def __truediv__(self, other: float | "Quantity") -> "Quantity": + if isinstance(other, Quantity): + pass + + else: + pass + + def __rdiv__(self, other: float | "Quantity") -> "Quantity": + if isinstance(other, Quantity): + pass + + else: + pass + def __add__(self, other: "Quantity") -> "Quantity": + if isinstance(other, Quantity): + pass + + def __sub__(self, other: "Quantity") -> "Quantity": + if isinstance(other, Quantity): + pass + class ExpressionMethod: pass diff --git a/sasdata/quantities/units_table.py b/sasdata/quantities/units_table.py new file mode 100644 index 000000000..5a2d6b5c4 --- /dev/null +++ b/sasdata/quantities/units_table.py @@ -0,0 +1,78 @@ +""" +Builds a data file containing details of units +""" + +import numpy as np + +bigger_magnitudes = [ + ("E", None, "exa", 1e18), + ("P", None, "peta", 1e15), + ("T", None, "tera", 1e12), + ("G", None, "giga", 1e9), + ("M", None, "mega", 1e6), + ("k", None, "kilo", 1e3) ] + +smaller_magnitudes = [ + ("m", None, "milli", 1e-3), + ("u", "µ", "micro", 1e-6), + ("n", None, "nano", 1e-9), + ("p", None, "pico", 1e-12), + ("f", None, "femto", 1e-15), + ("a", None, "atto", 1e-18)] + +all_magnitudes = bigger_magnitudes + smaller_magnitudes + +# Length, time, mass, current, temperature +base_si_units = [ + ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, all_magnitudes), + ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, smaller_magnitudes), + ("g", None, "gram", "grams", 1, 0, 0, 1, 0, 0, all_magnitudes), + ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, all_magnitudes), + ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, all_magnitudes) ] + +derived_si_units = [ + ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, all_magnitudes), + ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, all_magnitudes), + ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, all_magnitudes), + ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, all_magnitudes), + ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, all_magnitudes), + ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, all_magnitudes), + ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, all_magnitudes), + ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, all_magnitudes), + ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, all_magnitudes), + ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, all_magnitudes), + ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, all_magnitudes), + ("T", None, "tesla", "tesla", 1, 2, -2, 1, -1, 0, all_magnitudes), + ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, all_magnitudes), + ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, []) +] + +non_si_units = [ + ("A", None, "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), + ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, []), + ("hr", None, "hour", "hours", 360, 0, 1, 0, 0, 0, []), + ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), + ("day", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), + ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), + ("yr", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), + ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, []), + ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, []), + ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []) +] + +all_units = base_si_units + derived_si_units + non_si_units + +encoding = "utf-8" + +with open("unit_data.txt", mode='w', encoding=encoding) as fid: + for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, magnitudes in all_units: + fid.write(f"'{symbol}', '{special_symbol}', '{singular}', '{plural}', ") + fid.write(f"{scale}, {length}, {time}, {mass}, {current}, {temperature}\n") + + for mag_symbol, mag_special_symbol, name, mag_scale in magnitudes: + + combined_symbol = (mag_symbol if mag_special_symbol is None else mag_special_symbol) + \ + (symbol if special_symbol is None else special_symbol) + + fid.write(f"'{mag_symbol}{symbol}', '{combined_symbol}', '{name}{singular}', '{name}{plural}', ") + fid.write(f"{scale * mag_scale}, {length}, {time}, {mass}, {current}, {temperature}\n") From 233f38326563ffdb7541416b5774407f4c081ec1 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 6 Aug 2024 10:23:39 +0100 Subject: [PATCH 488/675] Work towards outline --- sasdata/quantities/quantities.py | 131 ++---------------------------- sasdata/quantities/units_base.py | 118 +++++++++++++++++++++++++++ sasdata/quantities/units_table.py | 85 ++++++++++++++++++- sasdata/quantities/units_tests.py | 2 +- 4 files changed, 209 insertions(+), 127 deletions(-) create mode 100644 sasdata/quantities/units_base.py diff --git a/sasdata/quantities/quantities.py b/sasdata/quantities/quantities.py index d9b749420..56c06c519 100644 --- a/sasdata/quantities/quantities.py +++ b/sasdata/quantities/quantities.py @@ -1,126 +1,12 @@ -from typing import Collection, Sequence, TypeVar, Generic +from typing import Collection, Sequence, TypeVar, Generic, Self from dataclasses import dataclass from numpy._typing import ArrayLike +from sasdata.quantities.units_base import Unit -class Dimensions: - """ +QuantityType = TypeVar("QuantityType") - 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, other: "Dimensions"): - - 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, other: "Dimensions"): - - 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, other: "Dimensions"): - 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 - - - -@dataclass -class UnitName: - ascii_name: str - unicode_name: str | None = None - - @property - def best_name(self): - if self.unicode_name is None: - return self.ascii_name - else: - return self.unicode_name - -class Unit: - def __init__(self, - si_scaling_factor: float, - dimensions: Dimensions, - name: UnitName | None = None): - - self.scale = si_scaling_factor - self.dimensions = dimensions - self.name = name - - def _components(self, tokens: Sequence["UnitToken"]): - pass - - def __mul__(self, other: "Unit"): - if not isinstance(other, Unit): - return NotImplemented - - return Unit(self.scale * other.scale, self.dimensions * other.dimensions) - - def __truediv__(self, other: "Unit"): - if not isinstance(other, Unit): - return NotImplemented - - return Unit(self.scale / other.scale, self.dimensions / other.dimensions) - - def __pow__(self, power: int): - if not isinstance(power, int): - return NotImplemented - - return Unit(self.scale**power, self.dimensions**power) - - def equivalent(self, other: "Unit"): - return self.dimensions == other.dimensions - - -# QuantityType = TypeVar("QuantityType") class Quantity[QuantityType]: def __init__(self, value: QuantityType, units: Unit): self.value = value @@ -129,34 +15,35 @@ def __init__(self, value: QuantityType, units: Unit): def in_units_of(self, units: Unit) -> QuantityType: pass - def __mul__(self, other: ArrayLike | "Quantity" ) -> "Quantity": + def __mul__(self: Self, other: ArrayLike | Self ) -> Self: if isinstance(other, Quantity): pass else: pass - def __truediv__(self, other: float | "Quantity") -> "Quantity": + def __truediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): pass else: pass - def __rdiv__(self, other: float | "Quantity") -> "Quantity": + def __rdiv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): pass else: pass - def __add__(self, other: "Quantity") -> "Quantity": + def __add__(self: Self, other: Self) -> Self: if isinstance(other, Quantity): pass - def __sub__(self, other: "Quantity") -> "Quantity": + def __sub__(self: Self, other: Self) -> Self: if isinstance(other, Quantity): pass + class ExpressionMethod: pass diff --git a/sasdata/quantities/units_base.py b/sasdata/quantities/units_base.py new file mode 100644 index 000000000..d29785cc3 --- /dev/null +++ b/sasdata/quantities/units_base.py @@ -0,0 +1,118 @@ +from dataclasses import dataclass +from typing import Sequence, Self, TypeVar + +import numpy as np + + +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): + return hash((self.length, self.time, self.mass, self.current, self.temperature)) + +class Unit: + def __init__(self, + si_scaling_factor: float, + dimensions: Dimensions): + + self.scale = si_scaling_factor + self.dimensions = dimensions + + 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 diff --git a/sasdata/quantities/units_table.py b/sasdata/quantities/units_table.py index 5a2d6b5c4..ac59e0dcd 100644 --- a/sasdata/quantities/units_table.py +++ b/sasdata/quantities/units_table.py @@ -3,6 +3,7 @@ """ import numpy as np +from collections import defaultdict bigger_magnitudes = [ ("E", None, "exa", 1e18), @@ -64,15 +65,91 @@ encoding = "utf-8" +def write_unit_string(fid, symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature): + fid.write(f"'{symbol}', '{special_symbol}', '{singular}', '{plural}', ") + fid.write(f"{scale}, {length}, {time}, {mass}, {current}, {temperature}\n") + + with open("unit_data.txt", mode='w', encoding=encoding) as fid: for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, magnitudes in all_units: - fid.write(f"'{symbol}', '{special_symbol}', '{singular}', '{plural}', ") - fid.write(f"{scale}, {length}, {time}, {mass}, {current}, {temperature}\n") + + write_unit_string(fid, symbol, special_symbol, singular, plural, + scale, length, time, mass, current, temperature) for mag_symbol, mag_special_symbol, name, mag_scale in magnitudes: combined_symbol = (mag_symbol if mag_special_symbol is None else mag_special_symbol) + \ (symbol if special_symbol is None else special_symbol) - fid.write(f"'{mag_symbol}{symbol}', '{combined_symbol}', '{name}{singular}', '{name}{plural}', ") - fid.write(f"{scale * mag_scale}, {length}, {time}, {mass}, {current}, {temperature}\n") + write_unit_string(fid,f"{mag_symbol}{symbol}", combined_symbol, f"{name}{singular}", + f"{name}{plural}", scale * mag_scale, length, time, mass, current, temperature) + + +def format_name(name: str): + return name.replace(" ", "_") + +header = """ + +Autogenerated file by units_table.py + + + + ******** DO NOT EDIT BY HAND ******** + + + +""" + +with open("units.py", 'w', encoding=encoding) as fid: + + # Write warning header + fid.write('"""'+header+'"""') + + # Write in class definitions + fid.write("\n\n" + "#\n" + "# Included from units_base.py\n" + "#\n\n") + + with open("units_base.py", 'r') as base: + for line in base: + fid.write(line) + + # Write in unit definitions + fid.write("\n\n" + "#\n" + "# Specific units \n" + "#\n\n") + + symbol_lookup = {} + unit_types = defaultdict(list) + + for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, magnitudes in all_units: + + formatted_plural = format_name(plural) + + fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}))\n") + + symbol_lookup[symbol] = formatted_plural + if special_symbol is not None: + symbol_lookup[special_symbol] = formatted_plural + + for mag_symbol, mag_special_symbol, name, mag_scale in magnitudes: + + combined_special_symbol = (mag_symbol if mag_special_symbol is None else mag_special_symbol) + \ + (symbol if special_symbol is None else special_symbol) + + combined_symbol = mag_symbol + symbol + + combined_name = f"{name}{formatted_plural}" + + fid.write(f"{combined_name} = Unit({scale * mag_scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}))\n") + + symbol_lookup[combined_symbol] = combined_name + symbol_lookup[combined_special_symbol] = combined_name + + fid.write("symbol_lookup = {\n") + for k in symbol_lookup: + fid.write(f' "{k}": {symbol_lookup[k]},\n') + fid.write("}\n\n") + diff --git a/sasdata/quantities/units_tests.py b/sasdata/quantities/units_tests.py index 9fea2a6b8..05636153f 100644 --- a/sasdata/quantities/units_tests.py +++ b/sasdata/quantities/units_tests.py @@ -34,7 +34,7 @@ def run_test(self): EqualUnits("Resistance", units.ohms, - units.volts / units.amperes, + units.volts / units.amps, 1e-3/units.millisiemens) From 529bedc354ab9cdb0af2894ed5e2b94903adcfd4 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 6 Aug 2024 14:34:38 +0100 Subject: [PATCH 489/675] Units now available and grouped --- sasdata/quantities/_units_base.py | 250 +-- sasdata/quantities/_units_table.py | 268 +++ sasdata/quantities/quantities.py | 2 +- sasdata/quantities/units.py | 3241 ++++++++-------------------- sasdata/quantities/units_base.py | 118 - sasdata/quantities/units_table.py | 155 -- 6 files changed, 1164 insertions(+), 2870 deletions(-) create mode 100644 sasdata/quantities/_units_table.py delete mode 100644 sasdata/quantities/units_base.py delete mode 100644 sasdata/quantities/units_table.py diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 8f32edec7..d87bb2b9e 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -1,22 +1,17 @@ from dataclasses import dataclass from typing import Sequence, Self, TypeVar -from fractions import Fraction import numpy as np from sasdata.quantities.unicode_superscript import int_as_unicode_superscript -class DimensionError(Exception): - pass class Dimensions: """ - Note that some SI Base units are not useful from the perspective of the sasview project, and make things - behave badly. In particular: moles and angular measures are dimensionless, and candelas are really a weighted - measure of power. + Note that some SI Base units are - We do however track angle and amount, because it's really useful for formatting units + For example, moles and angular measures are dimensionless from this perspective, and candelas are """ def __init__(self, @@ -24,22 +19,13 @@ def __init__(self, time: int = 0, mass: int = 0, current: int = 0, - temperature: int = 0, - moles_hint: int = 0, - angle_hint: int = 0): + temperature: int = 0): self.length = length self.time = time self.mass = mass self.current = current self.temperature = temperature - self.moles_hint = moles_hint - self.angle_hint = angle_hint - - @property - def is_dimensionless(self): - """ Is this dimension dimensionless (ignores moles_hint and angle_hint) """ - return self.length == 0 and self.time == 0 and self.mass == 0 and self.current == 0 and self.temperature == 0 def __mul__(self: Self, other: Self): @@ -51,9 +37,7 @@ def __mul__(self: Self, other: Self): self.time + other.time, self.mass + other.mass, self.current + other.current, - self.temperature + other.temperature, - self.moles_hint + other.moles_hint, - self.angle_hint + other.angle_hint) + self.temperature + other.temperature) def __truediv__(self: Self, other: Self): @@ -65,50 +49,19 @@ def __truediv__(self: Self, other: Self): self.time - other.time, self.mass - other.mass, self.current - other.current, - self.temperature - other.temperature, - self.moles_hint - other.moles_hint, - self.angle_hint - other.angle_hint) + self.temperature - other.temperature) - def __pow__(self, power: int | float): + def __pow__(self, power: int): - if not isinstance(power, (int, float)): + if not isinstance(power, int): return NotImplemented - frac = Fraction(power).limit_denominator(500) # Probably way bigger than needed, 10 would probably be fine - denominator = frac.denominator - numerator = frac.numerator - - # Throw errors if dimension is not a multiple of the denominator - - if self.length % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with length dimensionality {self.length}") - - if self.time % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with time dimensionality {self.time}") - - if self.mass % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with mass dimensionality {self.mass}") - - if self.current % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with current dimensionality {self.current}") - - if self.temperature % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with temperature dimensionality {self.temperature}") - - if self.moles_hint % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with moles hint dimensionality of {self.moles_hint}") - - if self.angle_hint % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with angle hint dimensionality of {self.angle_hint}") - return Dimensions( - (self.length * numerator) // denominator, - (self.time * numerator) // denominator, - (self.mass * numerator) // denominator, - (self.current * numerator) // denominator, - (self.temperature * numerator) // denominator, - (self.moles_hint * numerator) // denominator, - (self.angle_hint * numerator) // denominator) + 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): @@ -116,9 +69,7 @@ def __eq__(self: Self, other: Self): self.time == other.time and self.mass == other.mass and self.current == other.current and - self.temperature == other.temperature and - self.moles_hint == other.moles_hint and - self.angle_hint == other.angle_hint) + self.temperature == other.temperature) return NotImplemented @@ -141,90 +92,57 @@ def __hash__(self): if self.temperature < 0: two_powers += 16 - if self.moles_hint < 0: - two_powers += 32 - - if self.angle_hint < 0: - two_powers += 64 - return 2**two_powers * 3**abs(self.length) * 5**abs(self.time) * \ - 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) * \ - 17**abs(self.moles_hint) * 19**abs(self.angle_hint) + 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) def __repr__(self): - tokens = [] + s = "" for name, size in [ - ("length", self.length), - ("time", self.time), - ("mass", self.mass), - ("current", self.current), - ("temperature", self.temperature), - ("amount", self.moles_hint), - ("angle", self.angle_hint)]: + ("L", self.length), + ("T", self.time), + ("M", self.mass), + ("C", self.current), + ("K", self.temperature)]: if size == 0: pass elif size == 1: - tokens.append(f"{name}") + s += f"{name}" else: - tokens.append(f"{name}{int_as_unicode_superscript(size)}") - - return ' '.join(tokens) - - def si_repr(self): - tokens = [] - for name, size in [ - ("kg", self.mass), - ("m", self.length), - ("s", self.time), - ("A", self.current), - ("K", self.temperature), - ("mol", self.moles_hint)]: - - if size == 0: - pass - elif size == 1: - tokens.append(f"{name}") - else: - tokens.append(f"{name}{int_as_unicode_superscript(size)}") - - match self.angle_hint: - case 0: - pass - case 2: - tokens.append("sr") - case -2: - tokens.append("sr" + int_as_unicode_superscript(-1)) - case _: - tokens.append("rad" + int_as_unicode_superscript(self.angle_hint)) - - return ''.join(tokens) + s += f"{name}{int_as_unicode_superscript(size)}" + return s class Unit: def __init__(self, si_scaling_factor: float, - dimensions: Dimensions): + 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: "Unit"): + 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: "Unit"): + 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: "Unit"): + 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)): @@ -232,111 +150,19 @@ def __rtruediv__(self: Self, other: "Unit"): else: return NotImplemented - def __pow__(self, power: int | float): - if not isinstance(power, int | float): + 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: "Unit"): + def equivalent(self: Self, other: Self): return self.dimensions == other.dimensions - def __eq__(self: Self, other: "Unit"): + def __eq__(self: Self, other: Self): return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 - def si_equivalent(self): - """ Get the SI unit corresponding to this unit""" - return Unit(1, self.dimensions) - - def _format_unit(self, format_process: list["UnitFormatProcessor"]): - for processor in format_process: - pass - - def __repr__(self): - if self.scale == 1: - # We're in SI - return self.dimensions.si_repr() - - else: - return f"Unit[{self.scale}, {self.dimensions}]" - - @staticmethod - def parse(unit_string: str) -> "Unit": - pass -class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): - """ This processor minimises the dimensionality of the unit by multiplying by as many - units of the specified type as needed """ - def __init__(self, unit: Unit): - self.unit = unit - - def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: - pass - -class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): - pass - - -class NamedUnit(Unit): - """ Units, but they have a name, and a symbol - - :si_scaling_factor: Number of these units per SI equivalent - :param dimensions: Dimensions object representing the dimensionality of these units - :param name: Name of unit - string without unicode - :param ascii_symbol: Symbol for unit without unicode - :param symbol: Unicode symbol - """ - def __init__(self, - si_scaling_factor: float, - dimensions: Dimensions, - name: str | None = None, - ascii_symbol: str | None = None, - symbol: str | None = None): - - super().__init__(si_scaling_factor, dimensions) - self.name = name - self.ascii_symbol = ascii_symbol - self.symbol = symbol - - def __repr__(self): - return self.name - -# -# Parsing plan: -# Require unknown amounts of units to be explicitly positive or negative? -# -# - - - -@dataclass -class ProcessedUnitToken: - """ Mid processing representation of formatted units """ - base_string: str - exponent_string: str - latex_exponent_string: str - exponent: int - -class UnitFormatProcessor: - """ Represents a step in the unit processing pipeline""" - def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: - """ This will be called to deal with each processing stage""" - -class RequiredUnitFormatProcessor(UnitFormatProcessor): - """ This unit is required to exist in the formatting """ - def __init__(self, unit: Unit, power: int = 1): - self.unit = unit - self.power = power - def apply(self, scale, dimensions) -> tuple[float, Dimensions, ProcessedUnitToken]: - new_scale = scale / (self.unit.scale * self.power) - new_dimensions = self.unit.dimensions / (dimensions**self.power) - token = ProcessedUnitToken(self.unit, self.power) - - return new_scale, new_dimensions, token - class UnitGroup: - """ A group of units that all have the same dimensionality """ - def __init__(self, name: str, units: list[NamedUnit]): + def __init__(self, name: str, units: list[Unit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) - diff --git a/sasdata/quantities/_units_table.py b/sasdata/quantities/_units_table.py new file mode 100644 index 000000000..9b88cab42 --- /dev/null +++ b/sasdata/quantities/_units_table.py @@ -0,0 +1,268 @@ +""" +Builds a data file containing details of units +""" + +import numpy as np +from collections import defaultdict +from _units_base import Dimensions, Unit + +bigger_magnitudes = [ + ("E", None, "exa", 1e18), + ("P", None, "peta", 1e15), + ("T", None, "tera", 1e12), + ("G", None, "giga", 1e9), + ("M", None, "mega", 1e6), + ("k", None, "kilo", 1e3) ] + +smaller_magnitudes = [ + ("m", None, "milli", 1e-3), + ("u", "µ", "micro", 1e-6), + ("n", None, "nano", 1e-9), + ("p", None, "pico", 1e-12), + ("f", None, "femto", 1e-15), + ("a", None, "atto", 1e-18)] + +all_magnitudes = bigger_magnitudes + smaller_magnitudes + +# Length, time, mass, current, temperature +base_si_units = [ + ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, all_magnitudes), + ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, smaller_magnitudes), + ("g", None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, all_magnitudes), + ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, all_magnitudes), + ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, all_magnitudes) ] + +derived_si_units = [ + ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, all_magnitudes), + ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, all_magnitudes), + ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, all_magnitudes), + ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, all_magnitudes), + ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, all_magnitudes), + ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, all_magnitudes), + ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, all_magnitudes), + ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, all_magnitudes), + ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, all_magnitudes), + ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, all_magnitudes), + ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, all_magnitudes), + ("T", None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, all_magnitudes), + ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, all_magnitudes), + ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, []) +] + +non_si_units = [ + ("A", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), + ("Ang", None, "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), + ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, []), + ("hr", None, "hour", "hours", 360, 0, 1, 0, 0, 0, []), + ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), + ("day", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), + ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), + ("yr", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), + ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, []), + ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, []), + ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []) +] + + + +all_units = base_si_units + derived_si_units + non_si_units + +encoding = "utf-8" + +def format_name(name: str): + return name.lower().replace(" ", "_") + +header = """ + +Autogenerated file by _units_table.py + + + + ******** DO NOT EDIT BY HAND ******** + + + +""" + +with open("units.py", 'w', encoding=encoding) as fid: + + # Write warning header + fid.write('"""'+header+'"""') + + # Write in class definitions + fid.write("\n\n" + "#\n" + "# Included from _units_base.py\n" + "#\n\n") + + with open("_units_base.py", 'r') as base: + for line in base: + fid.write(line) + + # Write in unit definitions + fid.write("\n\n" + "#\n" + "# Specific units \n" + "#\n\n") + + symbol_lookup = {} + unit_types_temp = defaultdict(list) # Keep track of unit types + unit_types = defaultdict(list) + + for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, magnitudes in all_units: + + formatted_plural = format_name(plural) + formatted_singular = format_name(singular) + + dimensions = Dimensions(length, time, mass, current, temperature) + fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature})," + f"name='{formatted_plural}'," + f"ascii_symbol='{symbol}'," + f"symbol='{symbol if special_symbol is None else special_symbol}')\n") + + symbol_lookup[symbol] = formatted_plural + if special_symbol is not None: + symbol_lookup[special_symbol] = formatted_plural + + unit_types_temp[hash(dimensions)].append( + (symbol, special_symbol, formatted_singular, formatted_plural, scale, dimensions)) + + unit_types[hash(dimensions)].append(formatted_plural) + + for mag_symbol, mag_special_symbol, name, mag_scale in magnitudes: + + # Work out the combined symbol, accounts for unicode or not + combined_special_symbol = (mag_symbol if mag_special_symbol is None else mag_special_symbol) + \ + (symbol if special_symbol is None else special_symbol) + + combined_symbol = mag_symbol + symbol + + # Combined unit name + combined_name_singular = f"{name}{formatted_singular}" + combined_name_plural = f"{name}{formatted_plural}" + + combined_scale = scale * mag_scale + + # Units + dimensions = Dimensions(length, time, mass, current, temperature) + fid.write(f"{combined_name_plural} = Unit({combined_scale}, " + f"Dimensions({length}, {time}, {mass}, {current}, {temperature})," + f"name='{combined_name_plural}'," + f"ascii_symbol='{combined_symbol}'," + f"symbol='{combined_special_symbol}')\n") + + symbol_lookup[combined_symbol] = combined_name_plural + symbol_lookup[combined_special_symbol] = combined_name_plural + + unit_types_temp[hash(dimensions)].append( + (combined_symbol, combined_special_symbol, combined_name_singular, + combined_name_plural, combined_scale, dimensions)) + + unit_types[hash(dimensions)].append(combined_name_plural) + + # + # Higher dimensioned types + # + + length_units = unit_types_temp[hash(Dimensions(length=1))] + time_units = unit_types_temp[hash(Dimensions(time=1))] + + # Length based + for symbol, special_symbol, singular, plural, scale, _ in length_units: + for prefix, power, name, unicode_suffix in [ + ("square_", 2, plural, '²'), + ("cubic_", 3, plural, '³'), + ("per_", -1, singular, '⁻¹'), + ("per_square_", -2, singular,'⁻²'), + ("per_cubic_", -3, singular,'⁻³')]: + + dimensions = Dimensions(length=power) + unit_name = prefix + name + unit_special_symbol = (symbol if special_symbol is None else special_symbol) + unicode_suffix + unit_symbol = symbol + f"^{power}" + fid.write(f"{unit_name} = Unit({scale**power}, Dimensions({power}, 0, 0, 0, 0), " + f"name='{unit_name}', " + f"ascii_symbol='{unit_symbol}', " + f"symbol='{unit_special_symbol}')\n") + + unit_types[hash(dimensions)].append(unit_name) + + # Speed and acceleration + for length_symbol, length_special_symbol, _, length_name, length_scale, _ in length_units: + for time_symbol, time_special_symbol, time_name, _, time_scale, _ in time_units: + speed_name = length_name + "_per_" + time_name + accel_name = length_name + "_per_square_" + time_name + + speed_dimensions = Dimensions(length=1, time=-1) + accel_dimensions = Dimensions(length=1, time=-2) + + fid.write(f"{speed_name} " + f"= Unit({length_scale / time_scale}, " + f"Dimensions(1, -1, 0, 0, 0), " + f"name='{speed_name}', " + f"ascii_symbol='{length_symbol}/{time_symbol}', " + f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") + + fid.write(f"{accel_name} = Unit({length_scale / time_scale}, " + f"Dimensions(1, -2, 0, 0, 0), " + f"name='{accel_name}', " + f"ascii_symbol='{length_symbol}/{time_symbol}^2', " + f"symbol='{length_special_symbol}{time_special_symbol}⁻²')\n") + + unit_types[hash(speed_dimensions)].append(speed_name) + unit_types[hash(accel_dimensions)].append(accel_name) + + # + # Write out the symbol lookup table + # + fid.write("\n#\n# Lookup table from symbols to units\n#\n\n") + fid.write("symbol_lookup = {\n") + for k in symbol_lookup: + fid.write(f' "{k}": {symbol_lookup[k]},\n') + fid.write("}\n\n") + + # + # Collections of units by type + # + + dimension_names = [ + ("length", Dimensions(length=1)), + ("area", Dimensions(length=2)), + ("volume", Dimensions(length=3)), + ("inverse_length", Dimensions(length=-1)), + ("inverse_area", Dimensions(length=-2)), + ("inverse_volume", Dimensions(length=-3)), + ("time", Dimensions(time=1)), + ("rate", Dimensions(time=-1)), + ("speed", Dimensions(length=1, time=-1)), + ("acceleration", Dimensions(length=1, time=-2)), + ("force", Dimensions(1, -2, 1, 0, 0)), + ("pressure", Dimensions(-1, -2, 1, 0, 0)), + ("energy", Dimensions(2, -2, 1, 0, 0)), + ("power", Dimensions(2, -3, 1, 0, 0)), + ("charge", Dimensions(0, 1, 0, 1, 0)), + ("potential", Dimensions(2, -3, 1, -1, 0)), + ("resistance", Dimensions(2, -3, 1, -2, 0)), + ("capacitance", Dimensions(-2, 4, -1, 2, 0)), + ("conductance", Dimensions(-2, 3, -1, 2, 0)), + ("magnetic_flux", Dimensions(2, -2, 1, -1, 0)), + ("magnetic_flux_density", Dimensions(0, -2, 1, -1, 0)), + ("inductance", Dimensions(2, -2, 1, -2, 0)), + ("temperature", Dimensions(temperature=1)) + ] + + fid.write("\n#\n# Units by type \n#\n\n") + + for dimension_name, dimensions in dimension_names: + + print(dimensions, hash(dimensions)) + + fid.write(f"\n" + f"{dimension_name} = UnitGroup(\n" + f" name = '{dimension_name}', \n" + f" units = [\n") + + for unit_name in unit_types[hash(dimensions)]: + fid.write(" " + unit_name + ",\n") + + fid.write("])\n") \ No newline at end of file diff --git a/sasdata/quantities/quantities.py b/sasdata/quantities/quantities.py index 56c06c519..7b41f8f6d 100644 --- a/sasdata/quantities/quantities.py +++ b/sasdata/quantities/quantities.py @@ -3,7 +3,7 @@ from numpy._typing import ArrayLike -from sasdata.quantities.units_base import Unit +from sasdata.quantities.units import Unit QuantityType = TypeVar("QuantityType") diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 834bb2436..6105e27f5 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -1,79 +1,11 @@ """ -This file is autogenerated! - -Do not edit by hand, instead edit the files that build it (_build_tables.py, _units_base.py) - - - - -DDDDDDDDDDDDD NNNNNNNN NNNNNNNN tttt -D::::::::::::DDD N:::::::N N::::::N ttt:::t -D:::::::::::::::DD N::::::::N N::::::N t:::::t -DDD:::::DDDDD:::::D N:::::::::N N::::::N t:::::t - D:::::D D:::::D ooooooooooo N::::::::::N N::::::N ooooooooooo ttttttt:::::ttttttt - D:::::D D:::::D oo:::::::::::oo N:::::::::::N N::::::N oo:::::::::::oo t:::::::::::::::::t - D:::::D D:::::Do:::::::::::::::o N:::::::N::::N N::::::No:::::::::::::::ot:::::::::::::::::t - D:::::D D:::::Do:::::ooooo:::::o N::::::N N::::N N::::::No:::::ooooo:::::otttttt:::::::tttttt - D:::::D D:::::Do::::o o::::o N::::::N N::::N:::::::No::::o o::::o t:::::t - D:::::D D:::::Do::::o o::::o N::::::N N:::::::::::No::::o o::::o t:::::t - D:::::D D:::::Do::::o o::::o N::::::N N::::::::::No::::o o::::o t:::::t - D:::::D D:::::D o::::o o::::o N::::::N N:::::::::No::::o o::::o t:::::t tttttt -DDD:::::DDDDD:::::D o:::::ooooo:::::o N::::::N N::::::::No:::::ooooo:::::o t::::::tttt:::::t -D:::::::::::::::DD o:::::::::::::::o N::::::N N:::::::No:::::::::::::::o tt::::::::::::::t -D::::::::::::DDD oo:::::::::::oo N::::::N N::::::N oo:::::::::::oo tt:::::::::::tt -DDDDDDDDDDDDD ooooooooooo NNNNNNNN NNNNNNN ooooooooooo ttttttttttt - - - - - - - - - dddddddd -EEEEEEEEEEEEEEEEEEEEEE d::::::d iiii tttt BBBBBBBBBBBBBBBBB -E::::::::::::::::::::E d::::::d i::::i ttt:::t B::::::::::::::::B -E::::::::::::::::::::E d::::::d iiii t:::::t B::::::BBBBBB:::::B -EE::::::EEEEEEEEE::::E d:::::d t:::::t BB:::::B B:::::B - E:::::E EEEEEE ddddddddd:::::d iiiiiiittttttt:::::ttttttt B::::B B:::::Byyyyyyy yyyyyyy - E:::::E dd::::::::::::::d i:::::it:::::::::::::::::t B::::B B:::::B y:::::y y:::::y - E::::::EEEEEEEEEE d::::::::::::::::d i::::it:::::::::::::::::t B::::BBBBBB:::::B y:::::y y:::::y - E:::::::::::::::E d:::::::ddddd:::::d i::::itttttt:::::::tttttt B:::::::::::::BB y:::::y y:::::y - E:::::::::::::::E d::::::d d:::::d i::::i t:::::t B::::BBBBBB:::::B y:::::y y:::::y - E::::::EEEEEEEEEE d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y y:::::y - E:::::E d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y:::::y - E:::::E EEEEEEd:::::d d:::::d i::::i t:::::t tttttt B::::B B:::::B y:::::::::y -EE::::::EEEEEEEE:::::Ed::::::ddddd::::::ddi::::::i t::::::tttt:::::t BB:::::BBBBBB::::::B y:::::::y -E::::::::::::::::::::E d:::::::::::::::::di::::::i tt::::::::::::::t B:::::::::::::::::B y:::::y -E::::::::::::::::::::E d:::::::::ddd::::di::::::i tt:::::::::::tt B::::::::::::::::B y:::::y -EEEEEEEEEEEEEEEEEEEEEE ddddddddd dddddiiiiiiii ttttttttttt BBBBBBBBBBBBBBBBB y:::::y - y:::::y - y:::::y - y:::::y - y:::::y - yyyyyyy - - - - dddddddd -HHHHHHHHH HHHHHHHHH d::::::d -H:::::::H H:::::::H d::::::d -H:::::::H H:::::::H d::::::d -HH::::::H H::::::HH d:::::d - H:::::H H:::::H aaaaaaaaaaaaa nnnn nnnnnnnn ddddddddd:::::d - H:::::H H:::::H a::::::::::::a n:::nn::::::::nn dd::::::::::::::d - H::::::HHHHH::::::H aaaaaaaaa:::::an::::::::::::::nn d::::::::::::::::d - H:::::::::::::::::H a::::ann:::::::::::::::nd:::::::ddddd:::::d - H:::::::::::::::::H aaaaaaa:::::a n:::::nnnn:::::nd::::::d d:::::d - H::::::HHHHH::::::H aa::::::::::::a n::::n n::::nd:::::d d:::::d - H:::::H H:::::H a::::aaaa::::::a n::::n n::::nd:::::d d:::::d - H:::::H H:::::H a::::a a:::::a n::::n n::::nd:::::d d:::::d -HH::::::H H::::::HHa::::a a:::::a n::::n n::::nd::::::ddddd::::::dd -H:::::::H H:::::::Ha:::::aaaa::::::a n::::n n::::n d:::::::::::::::::d -H:::::::H H:::::::H a::::::::::aa:::a n::::n n::::n d:::::::::ddd::::d -HHHHHHHHH HHHHHHHHH aaaaaaaaaa aaaa nnnnnn nnnnnn ddddddddd ddddd - +Autogenerated file by _units_table.py + + + + ******** DO NOT EDIT BY HAND ******** + """ @@ -84,23 +16,18 @@ from dataclasses import dataclass from typing import Sequence, Self, TypeVar -from fractions import Fraction import numpy as np from sasdata.quantities.unicode_superscript import int_as_unicode_superscript -class DimensionError(Exception): - pass class Dimensions: """ - Note that some SI Base units are not useful from the perspecive of the sasview project, and make things - behave badly. In particular: moles and angular measures are dimensionless, and candelas are really a weighted - measure of power. + Note that some SI Base units are - We do however track angle and amount, because its really useful for formatting units + For example, moles and angular measures are dimensionless from this perspective, and candelas are """ def __init__(self, @@ -108,22 +35,13 @@ def __init__(self, time: int = 0, mass: int = 0, current: int = 0, - temperature: int = 0, - moles_hint: int = 0, - angle_hint: int = 0): + temperature: int = 0): self.length = length self.time = time self.mass = mass self.current = current self.temperature = temperature - self.moles_hint = moles_hint - self.angle_hint = angle_hint - - @property - def is_dimensionless(self): - """ Is this dimension dimensionless (ignores moles_hint and angle_hint) """ - return self.length == 0 and self.time == 0 and self.mass == 0 and self.current == 0 and self.temperature == 0 def __mul__(self: Self, other: Self): @@ -135,9 +53,7 @@ def __mul__(self: Self, other: Self): self.time + other.time, self.mass + other.mass, self.current + other.current, - self.temperature + other.temperature, - self.moles_hint + other.moles_hint, - self.angle_hint + other.angle_hint) + self.temperature + other.temperature) def __truediv__(self: Self, other: Self): @@ -149,50 +65,19 @@ def __truediv__(self: Self, other: Self): self.time - other.time, self.mass - other.mass, self.current - other.current, - self.temperature - other.temperature, - self.moles_hint - other.moles_hint, - self.angle_hint - other.angle_hint) + self.temperature - other.temperature) - def __pow__(self, power: int | float): + def __pow__(self, power: int): - if not isinstance(power, (int, float)): + if not isinstance(power, int): return NotImplemented - frac = Fraction(power).limit_denominator(500) # Probably way bigger than needed, 10 would probably be fine - denominator = frac.denominator - numerator = frac.numerator - - # Throw errors if dimension is not a multiple of the denominator - - if self.length % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with length dimensionality {self.length}") - - if self.time % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with time dimensionality {self.time}") - - if self.mass % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with mass dimensionality {self.mass}") - - if self.current % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with current dimensionality {self.current}") - - if self.temperature % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with temperature dimensionality {self.temperature}") - - if self.moles_hint % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with moles hint dimensionality of {self.moles_hint}") - - if self.angle_hint % denominator != 0: - raise DimensionError(f"Cannot apply power of {frac} to unit with angle hint dimensionality of {self.angle_hint}") - return Dimensions( - (self.length * numerator) // denominator, - (self.time * numerator) // denominator, - (self.mass * numerator) // denominator, - (self.current * numerator) // denominator, - (self.temperature * numerator) // denominator, - (self.moles_hint * numerator) // denominator, - (self.angle_hint * numerator) // denominator) + 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): @@ -200,9 +85,7 @@ def __eq__(self: Self, other: Self): self.time == other.time and self.mass == other.mass and self.current == other.current and - self.temperature == other.temperature and - self.moles_hint == other.moles_hint and - self.angle_hint == other.angle_hint) + self.temperature == other.temperature) return NotImplemented @@ -225,90 +108,57 @@ def __hash__(self): if self.temperature < 0: two_powers += 16 - if self.moles_hint < 0: - two_powers += 32 - - if self.angle_hint < 0: - two_powers += 64 - return 2**two_powers * 3**abs(self.length) * 5**abs(self.time) * \ - 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) * \ - 17**abs(self.moles_hint) * 19**abs(self.angle_hint) + 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) def __repr__(self): - tokens = [] - for name, size in [ - ("length", self.length), - ("time", self.time), - ("mass", self.mass), - ("current", self.current), - ("temperature", self.temperature), - ("amount", self.moles_hint), - ("angle", self.angle_hint)]: - - if size == 0: - pass - elif size == 1: - tokens.append(f"{name}") - else: - tokens.append(f"{name}{int_as_unicode_superscript(size)}") - - return ' '.join(tokens) - - def si_repr(self): - tokens = [] + s = "" for name, size in [ - ("kg", self.mass), - ("m", self.length), - ("s", self.time), - ("A", self.current), - ("K", self.temperature), - ("mol", self.moles_hint)]: + ("L", self.length), + ("T", self.time), + ("M", self.mass), + ("C", self.current), + ("K", self.temperature)]: if size == 0: pass elif size == 1: - tokens.append(f"{name}") + s += f"{name}" else: - tokens.append(f"{name}{int_as_unicode_superscript(size)}") - - match self.angle_hint: - case 0: - pass - case 2: - tokens.append("sr") - case -2: - tokens.append("sr" + int_as_unicode_superscript(-1)) - case _: - tokens.append("rad" + int_as_unicode_superscript(self.angle_hint)) - - return ''.join(tokens) + s += f"{name}{int_as_unicode_superscript(size)}" + return s class Unit: def __init__(self, si_scaling_factor: float, - dimensions: Dimensions): + 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: "Unit"): + 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: "Unit"): + 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: "Unit"): + 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)): @@ -316,1391 +166,733 @@ def __rtruediv__(self: Self, other: "Unit"): else: return NotImplemented - def __pow__(self, power: int | float): - if not isinstance(power, int | float): + 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: "Unit"): + def equivalent(self: Self, other: Self): return self.dimensions == other.dimensions - def __eq__(self: Self, other: "Unit"): + def __eq__(self: Self, other: Self): return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 - def si_equivalent(self): - """ Get the SI unit corresponding to this unit""" - return Unit(1, self.dimensions) - - def _format_unit(self, format_process: list["UnitFormatProcessor"]): - for processor in format_process: - pass - - def __repr__(self): - if self.scale == 1: - # We're in SI - return self.dimensions.si_repr() - - else: - return f"Unit[{self.scale}, {self.dimensions}]" - - @staticmethod - def parse(unit_string: str) -> "Unit": - pass - -class NamedUnit(Unit): - """ Units, but they have a name, and a symbol - - :si_scaling_factor: Number of these units per SI equivalent - :param dimensions: Dimensions object representing the dimensionality of these units - :param name: Name of unit - string without unicode - :param ascii_symbol: Symbol for unit without unicode - :param symbol: Unicode symbol - """ - def __init__(self, - si_scaling_factor: float, - dimensions: Dimensions, - name: str | None = None, - ascii_symbol: str | None = None, - symbol: str | None = None): - - super().__init__(si_scaling_factor, dimensions) - self.name = name - self.ascii_symbol = ascii_symbol - self.symbol = symbol - - def __repr__(self): - return self.name - -# -# Parsing plan: -# Require unknown amounts of units to be explicitly positive or negative? -# -# - - - -@dataclass -class ProcessedUnitToken: - """ Mid processing representation of formatted units """ - base_string: str - exponent_string: str - latex_exponent_string: str - exponent: int - -class UnitFormatProcessor: - """ Represents a step in the unit processing pipeline""" - def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: - """ This will be called to deal with each processing stage""" - -class RequiredUnitFormatProcessor(UnitFormatProcessor): - """ This unit is required to exist in the formatting """ - def __init__(self, unit: Unit, power: int = 1): - self.unit = unit - self.power = power - def apply(self, scale, dimensions) -> tuple[float, Dimensions, ProcessedUnitToken]: - new_scale = scale / (self.unit.scale * self.power) - new_dimensions = self.unit.dimensions / (dimensions**self.power) - token = ProcessedUnitToken(self.unit, self.power) - - return new_scale, new_dimensions, token -class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): - """ This processor minimises the dimensionality of the unit by multiplying by as many - units of the specified type as needed """ - def __init__(self, unit: Unit): - self.unit = unit - - def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: - pass - -class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): - pass - class UnitGroup: - """ A group of units that all have the same dimensionality """ - def __init__(self, name: str, units: list[NamedUnit]): + def __init__(self, name: str, units: list[Unit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) - # # Specific units # -meters = NamedUnit(1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') -exameters = NamedUnit(1e+18, Dimensions(1, 0, 0, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') -petameters = NamedUnit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') -terameters = NamedUnit(1000000000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') -gigameters = NamedUnit(1000000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='gigameters',ascii_symbol='Gm',symbol='Gm') -megameters = NamedUnit(1000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='megameters',ascii_symbol='Mm',symbol='Mm') -kilometers = NamedUnit(1000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='kilometers',ascii_symbol='km',symbol='km') -millimeters = NamedUnit(0.001, Dimensions(1, 0, 0, 0, 0, 0, 0),name='millimeters',ascii_symbol='mm',symbol='mm') -micrometers = NamedUnit(1e-06, Dimensions(1, 0, 0, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',symbol='µm') -nanometers = NamedUnit(1e-09, Dimensions(1, 0, 0, 0, 0, 0, 0),name='nanometers',ascii_symbol='nm',symbol='nm') -picometers = NamedUnit(1e-12, Dimensions(1, 0, 0, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') -femtometers = NamedUnit(1e-15, Dimensions(1, 0, 0, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') -attometers = NamedUnit(1e-18, Dimensions(1, 0, 0, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') -decimeters = NamedUnit(0.1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='decimeters',ascii_symbol='dm',symbol='dm') -centimeters = NamedUnit(0.01, Dimensions(1, 0, 0, 0, 0, 0, 0),name='centimeters',ascii_symbol='cm',symbol='cm') -seconds = NamedUnit(1, Dimensions(0, 1, 0, 0, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') -milliseconds = NamedUnit(0.001, Dimensions(0, 1, 0, 0, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') -microseconds = NamedUnit(1e-06, Dimensions(0, 1, 0, 0, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') -nanoseconds = NamedUnit(1e-09, Dimensions(0, 1, 0, 0, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') -picoseconds = NamedUnit(1e-12, Dimensions(0, 1, 0, 0, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') -femtoseconds = NamedUnit(1e-15, Dimensions(0, 1, 0, 0, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') -attoseconds = NamedUnit(1e-18, Dimensions(0, 1, 0, 0, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') -grams = NamedUnit(0.001, Dimensions(0, 0, 1, 0, 0, 0, 0),name='grams',ascii_symbol='g',symbol='g') -exagrams = NamedUnit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') -petagrams = NamedUnit(1000000000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') -teragrams = NamedUnit(1000000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') -gigagrams = NamedUnit(1000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='gigagrams',ascii_symbol='Gg',symbol='Gg') -megagrams = NamedUnit(1000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='megagrams',ascii_symbol='Mg',symbol='Mg') -kilograms = NamedUnit(1.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='kilograms',ascii_symbol='kg',symbol='kg') -milligrams = NamedUnit(1e-06, Dimensions(0, 0, 1, 0, 0, 0, 0),name='milligrams',ascii_symbol='mg',symbol='mg') -micrograms = NamedUnit(1e-09, Dimensions(0, 0, 1, 0, 0, 0, 0),name='micrograms',ascii_symbol='ug',symbol='µg') -nanograms = NamedUnit(1.0000000000000002e-12, Dimensions(0, 0, 1, 0, 0, 0, 0),name='nanograms',ascii_symbol='ng',symbol='ng') -picograms = NamedUnit(1e-15, Dimensions(0, 0, 1, 0, 0, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') -femtograms = NamedUnit(1e-18, Dimensions(0, 0, 1, 0, 0, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') -attograms = NamedUnit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') -amperes = NamedUnit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amperes',ascii_symbol='A',symbol='A') -exaamperes = NamedUnit(1e+18, Dimensions(0, 0, 0, 1, 0, 0, 0),name='exaamperes',ascii_symbol='EA',symbol='EA') -petaamperes = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='petaamperes',ascii_symbol='PA',symbol='PA') -teraamperes = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='teraamperes',ascii_symbol='TA',symbol='TA') -gigaamperes = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='gigaamperes',ascii_symbol='GA',symbol='GA') -megaamperes = NamedUnit(1000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='megaamperes',ascii_symbol='MA',symbol='MA') -kiloamperes = NamedUnit(1000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='kiloamperes',ascii_symbol='kA',symbol='kA') -milliamperes = NamedUnit(0.001, Dimensions(0, 0, 0, 1, 0, 0, 0),name='milliamperes',ascii_symbol='mA',symbol='mA') -microamperes = NamedUnit(1e-06, Dimensions(0, 0, 0, 1, 0, 0, 0),name='microamperes',ascii_symbol='uA',symbol='µA') -nanoamperes = NamedUnit(1e-09, Dimensions(0, 0, 0, 1, 0, 0, 0),name='nanoamperes',ascii_symbol='nA',symbol='nA') -picoamperes = NamedUnit(1e-12, Dimensions(0, 0, 0, 1, 0, 0, 0),name='picoamperes',ascii_symbol='pA',symbol='pA') -femtoamperes = NamedUnit(1e-15, Dimensions(0, 0, 0, 1, 0, 0, 0),name='femtoamperes',ascii_symbol='fA',symbol='fA') -attoamperes = NamedUnit(1e-18, Dimensions(0, 0, 0, 1, 0, 0, 0),name='attoamperes',ascii_symbol='aA',symbol='aA') -kelvin = NamedUnit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kelvin',ascii_symbol='K',symbol='K') -exakelvin = NamedUnit(1e+18, Dimensions(0, 0, 0, 0, 1, 0, 0),name='exakelvin',ascii_symbol='EK',symbol='EK') -petakelvin = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='petakelvin',ascii_symbol='PK',symbol='PK') -terakelvin = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='terakelvin',ascii_symbol='TK',symbol='TK') -gigakelvin = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='gigakelvin',ascii_symbol='GK',symbol='GK') -megakelvin = NamedUnit(1000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='megakelvin',ascii_symbol='MK',symbol='MK') -kilokelvin = NamedUnit(1000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kilokelvin',ascii_symbol='kK',symbol='kK') -millikelvin = NamedUnit(0.001, Dimensions(0, 0, 0, 0, 1, 0, 0),name='millikelvin',ascii_symbol='mK',symbol='mK') -microkelvin = NamedUnit(1e-06, Dimensions(0, 0, 0, 0, 1, 0, 0),name='microkelvin',ascii_symbol='uK',symbol='µK') -nanokelvin = NamedUnit(1e-09, Dimensions(0, 0, 0, 0, 1, 0, 0),name='nanokelvin',ascii_symbol='nK',symbol='nK') -picokelvin = NamedUnit(1e-12, Dimensions(0, 0, 0, 0, 1, 0, 0),name='picokelvin',ascii_symbol='pK',symbol='pK') -femtokelvin = NamedUnit(1e-15, Dimensions(0, 0, 0, 0, 1, 0, 0),name='femtokelvin',ascii_symbol='fK',symbol='fK') -attokelvin = NamedUnit(1e-18, Dimensions(0, 0, 0, 0, 1, 0, 0),name='attokelvin',ascii_symbol='aK',symbol='aK') -hertz = NamedUnit(1, Dimensions(0, -1, 0, 0, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') -exahertz = NamedUnit(1e+18, Dimensions(0, -1, 0, 0, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') -petahertz = NamedUnit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') -terahertz = NamedUnit(1000000000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') -gigahertz = NamedUnit(1000000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='gigahertz',ascii_symbol='GHz',symbol='GHz') -megahertz = NamedUnit(1000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='megahertz',ascii_symbol='MHz',symbol='MHz') -kilohertz = NamedUnit(1000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='kilohertz',ascii_symbol='kHz',symbol='kHz') -millihertz = NamedUnit(0.001, Dimensions(0, -1, 0, 0, 0, 0, 0),name='millihertz',ascii_symbol='mHz',symbol='mHz') -microhertz = NamedUnit(1e-06, Dimensions(0, -1, 0, 0, 0, 0, 0),name='microhertz',ascii_symbol='uHz',symbol='µHz') -nanohertz = NamedUnit(1e-09, Dimensions(0, -1, 0, 0, 0, 0, 0),name='nanohertz',ascii_symbol='nHz',symbol='nHz') -picohertz = NamedUnit(1e-12, Dimensions(0, -1, 0, 0, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') -femtohertz = NamedUnit(1e-15, Dimensions(0, -1, 0, 0, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') -attohertz = NamedUnit(1e-18, Dimensions(0, -1, 0, 0, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') -newtons = NamedUnit(1, Dimensions(1, -2, 1, 0, 0, 0, 0),name='newtons',ascii_symbol='N',symbol='N') -exanewtons = NamedUnit(1e+18, Dimensions(1, -2, 1, 0, 0, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') -petanewtons = NamedUnit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') -teranewtons = NamedUnit(1000000000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') -giganewtons = NamedUnit(1000000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='giganewtons',ascii_symbol='GN',symbol='GN') -meganewtons = NamedUnit(1000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='meganewtons',ascii_symbol='MN',symbol='MN') -kilonewtons = NamedUnit(1000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kilonewtons',ascii_symbol='kN',symbol='kN') -millinewtons = NamedUnit(0.001, Dimensions(1, -2, 1, 0, 0, 0, 0),name='millinewtons',ascii_symbol='mN',symbol='mN') -micronewtons = NamedUnit(1e-06, Dimensions(1, -2, 1, 0, 0, 0, 0),name='micronewtons',ascii_symbol='uN',symbol='µN') -nanonewtons = NamedUnit(1e-09, Dimensions(1, -2, 1, 0, 0, 0, 0),name='nanonewtons',ascii_symbol='nN',symbol='nN') -piconewtons = NamedUnit(1e-12, Dimensions(1, -2, 1, 0, 0, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') -femtonewtons = NamedUnit(1e-15, Dimensions(1, -2, 1, 0, 0, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') -attonewtons = NamedUnit(1e-18, Dimensions(1, -2, 1, 0, 0, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') -pascals = NamedUnit(1, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') -exapascals = NamedUnit(1e+18, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') -petapascals = NamedUnit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') -terapascals = NamedUnit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') -gigapascals = NamedUnit(1000000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='gigapascals',ascii_symbol='GPa',symbol='GPa') -megapascals = NamedUnit(1000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='megapascals',ascii_symbol='MPa',symbol='MPa') -kilopascals = NamedUnit(1000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='kilopascals',ascii_symbol='kPa',symbol='kPa') -millipascals = NamedUnit(0.001, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='millipascals',ascii_symbol='mPa',symbol='mPa') -micropascals = NamedUnit(1e-06, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='micropascals',ascii_symbol='uPa',symbol='µPa') -nanopascals = NamedUnit(1e-09, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='nanopascals',ascii_symbol='nPa',symbol='nPa') -picopascals = NamedUnit(1e-12, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') -femtopascals = NamedUnit(1e-15, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') -attopascals = NamedUnit(1e-18, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') -joules = NamedUnit(1, Dimensions(2, -2, 1, 0, 0, 0, 0),name='joules',ascii_symbol='J',symbol='J') -exajoules = NamedUnit(1e+18, Dimensions(2, -2, 1, 0, 0, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') -petajoules = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') -terajoules = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') -gigajoules = NamedUnit(1000000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='gigajoules',ascii_symbol='GJ',symbol='GJ') -megajoules = NamedUnit(1000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='megajoules',ascii_symbol='MJ',symbol='MJ') -kilojoules = NamedUnit(1000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='kilojoules',ascii_symbol='kJ',symbol='kJ') -millijoules = NamedUnit(0.001, Dimensions(2, -2, 1, 0, 0, 0, 0),name='millijoules',ascii_symbol='mJ',symbol='mJ') -microjoules = NamedUnit(1e-06, Dimensions(2, -2, 1, 0, 0, 0, 0),name='microjoules',ascii_symbol='uJ',symbol='µJ') -nanojoules = NamedUnit(1e-09, Dimensions(2, -2, 1, 0, 0, 0, 0),name='nanojoules',ascii_symbol='nJ',symbol='nJ') -picojoules = NamedUnit(1e-12, Dimensions(2, -2, 1, 0, 0, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') -femtojoules = NamedUnit(1e-15, Dimensions(2, -2, 1, 0, 0, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') -attojoules = NamedUnit(1e-18, Dimensions(2, -2, 1, 0, 0, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') -watts = NamedUnit(1, Dimensions(2, -3, 1, 0, 0, 0, 0),name='watts',ascii_symbol='W',symbol='W') -exawatts = NamedUnit(1e+18, Dimensions(2, -3, 1, 0, 0, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') -petawatts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') -terawatts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') -gigawatts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='gigawatts',ascii_symbol='GW',symbol='GW') -megawatts = NamedUnit(1000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='megawatts',ascii_symbol='MW',symbol='MW') -kilowatts = NamedUnit(1000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='kilowatts',ascii_symbol='kW',symbol='kW') -milliwatts = NamedUnit(0.001, Dimensions(2, -3, 1, 0, 0, 0, 0),name='milliwatts',ascii_symbol='mW',symbol='mW') -microwatts = NamedUnit(1e-06, Dimensions(2, -3, 1, 0, 0, 0, 0),name='microwatts',ascii_symbol='uW',symbol='µW') -nanowatts = NamedUnit(1e-09, Dimensions(2, -3, 1, 0, 0, 0, 0),name='nanowatts',ascii_symbol='nW',symbol='nW') -picowatts = NamedUnit(1e-12, Dimensions(2, -3, 1, 0, 0, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') -femtowatts = NamedUnit(1e-15, Dimensions(2, -3, 1, 0, 0, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') -attowatts = NamedUnit(1e-18, Dimensions(2, -3, 1, 0, 0, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') -coulombs = NamedUnit(1, Dimensions(0, 1, 0, 1, 0, 0, 0),name='coulombs',ascii_symbol='C',symbol='C') -exacoulombs = NamedUnit(1e+18, Dimensions(0, 1, 0, 1, 0, 0, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') -petacoulombs = NamedUnit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') -teracoulombs = NamedUnit(1000000000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') -gigacoulombs = NamedUnit(1000000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='gigacoulombs',ascii_symbol='GC',symbol='GC') -megacoulombs = NamedUnit(1000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='megacoulombs',ascii_symbol='MC',symbol='MC') -kilocoulombs = NamedUnit(1000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='kilocoulombs',ascii_symbol='kC',symbol='kC') -millicoulombs = NamedUnit(0.001, Dimensions(0, 1, 0, 1, 0, 0, 0),name='millicoulombs',ascii_symbol='mC',symbol='mC') -microcoulombs = NamedUnit(1e-06, Dimensions(0, 1, 0, 1, 0, 0, 0),name='microcoulombs',ascii_symbol='uC',symbol='µC') -nanocoulombs = NamedUnit(1e-09, Dimensions(0, 1, 0, 1, 0, 0, 0),name='nanocoulombs',ascii_symbol='nC',symbol='nC') -picocoulombs = NamedUnit(1e-12, Dimensions(0, 1, 0, 1, 0, 0, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') -femtocoulombs = NamedUnit(1e-15, Dimensions(0, 1, 0, 1, 0, 0, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') -attocoulombs = NamedUnit(1e-18, Dimensions(0, 1, 0, 1, 0, 0, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') -volts = NamedUnit(1, Dimensions(2, -3, 1, -1, 0, 0, 0),name='volts',ascii_symbol='V',symbol='V') -exavolts = NamedUnit(1e+18, Dimensions(2, -3, 1, -1, 0, 0, 0),name='exavolts',ascii_symbol='EV',symbol='EV') -petavolts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='petavolts',ascii_symbol='PV',symbol='PV') -teravolts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='teravolts',ascii_symbol='TV',symbol='TV') -gigavolts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='gigavolts',ascii_symbol='GV',symbol='GV') -megavolts = NamedUnit(1000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='megavolts',ascii_symbol='MV',symbol='MV') -kilovolts = NamedUnit(1000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='kilovolts',ascii_symbol='kV',symbol='kV') -millivolts = NamedUnit(0.001, Dimensions(2, -3, 1, -1, 0, 0, 0),name='millivolts',ascii_symbol='mV',symbol='mV') -microvolts = NamedUnit(1e-06, Dimensions(2, -3, 1, -1, 0, 0, 0),name='microvolts',ascii_symbol='uV',symbol='µV') -nanovolts = NamedUnit(1e-09, Dimensions(2, -3, 1, -1, 0, 0, 0),name='nanovolts',ascii_symbol='nV',symbol='nV') -picovolts = NamedUnit(1e-12, Dimensions(2, -3, 1, -1, 0, 0, 0),name='picovolts',ascii_symbol='pV',symbol='pV') -femtovolts = NamedUnit(1e-15, Dimensions(2, -3, 1, -1, 0, 0, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') -attovolts = NamedUnit(1e-18, Dimensions(2, -3, 1, -1, 0, 0, 0),name='attovolts',ascii_symbol='aV',symbol='aV') -ohms = NamedUnit(1, Dimensions(2, -3, 1, -2, 0, 0, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') -exaohms = NamedUnit(1e+18, Dimensions(2, -3, 1, -2, 0, 0, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') -petaohms = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') -teraohms = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') -gigaohms = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='gigaohms',ascii_symbol='GOhm',symbol='GΩ') -megaohms = NamedUnit(1000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='megaohms',ascii_symbol='MOhm',symbol='MΩ') -kiloohms = NamedUnit(1000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='kiloohms',ascii_symbol='kOhm',symbol='kΩ') -milliohms = NamedUnit(0.001, Dimensions(2, -3, 1, -2, 0, 0, 0),name='milliohms',ascii_symbol='mOhm',symbol='mΩ') -microohms = NamedUnit(1e-06, Dimensions(2, -3, 1, -2, 0, 0, 0),name='microohms',ascii_symbol='uOhm',symbol='µΩ') -nanoohms = NamedUnit(1e-09, Dimensions(2, -3, 1, -2, 0, 0, 0),name='nanoohms',ascii_symbol='nOhm',symbol='nΩ') -picoohms = NamedUnit(1e-12, Dimensions(2, -3, 1, -2, 0, 0, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') -femtoohms = NamedUnit(1e-15, Dimensions(2, -3, 1, -2, 0, 0, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') -attoohms = NamedUnit(1e-18, Dimensions(2, -3, 1, -2, 0, 0, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') -farads = NamedUnit(1, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='farads',ascii_symbol='F',symbol='F') -exafarads = NamedUnit(1e+18, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='exafarads',ascii_symbol='EF',symbol='EF') -petafarads = NamedUnit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='petafarads',ascii_symbol='PF',symbol='PF') -terafarads = NamedUnit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='terafarads',ascii_symbol='TF',symbol='TF') -gigafarads = NamedUnit(1000000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='gigafarads',ascii_symbol='GF',symbol='GF') -megafarads = NamedUnit(1000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='megafarads',ascii_symbol='MF',symbol='MF') -kilofarads = NamedUnit(1000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='kilofarads',ascii_symbol='kF',symbol='kF') -millifarads = NamedUnit(0.001, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='millifarads',ascii_symbol='mF',symbol='mF') -microfarads = NamedUnit(1e-06, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='microfarads',ascii_symbol='uF',symbol='µF') -nanofarads = NamedUnit(1e-09, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='nanofarads',ascii_symbol='nF',symbol='nF') -picofarads = NamedUnit(1e-12, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='picofarads',ascii_symbol='pF',symbol='pF') -femtofarads = NamedUnit(1e-15, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') -attofarads = NamedUnit(1e-18, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='attofarads',ascii_symbol='aF',symbol='aF') -siemens = NamedUnit(1, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='siemens',ascii_symbol='S',symbol='S') -exasiemens = NamedUnit(1e+18, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') -petasiemens = NamedUnit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') -terasiemens = NamedUnit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') -gigasiemens = NamedUnit(1000000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='gigasiemens',ascii_symbol='GS',symbol='GS') -megasiemens = NamedUnit(1000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='megasiemens',ascii_symbol='MS',symbol='MS') -kilosiemens = NamedUnit(1000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='kilosiemens',ascii_symbol='kS',symbol='kS') -millisiemens = NamedUnit(0.001, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='millisiemens',ascii_symbol='mS',symbol='mS') -microsiemens = NamedUnit(1e-06, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='microsiemens',ascii_symbol='uS',symbol='µS') -nanosiemens = NamedUnit(1e-09, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='nanosiemens',ascii_symbol='nS',symbol='nS') -picosiemens = NamedUnit(1e-12, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') -femtosiemens = NamedUnit(1e-15, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') -attosiemens = NamedUnit(1e-18, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') -webers = NamedUnit(1, Dimensions(2, -2, 1, -1, 0, 0, 0),name='webers',ascii_symbol='Wb',symbol='Wb') -exawebers = NamedUnit(1e+18, Dimensions(2, -2, 1, -1, 0, 0, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') -petawebers = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') -terawebers = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') -gigawebers = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='gigawebers',ascii_symbol='GWb',symbol='GWb') -megawebers = NamedUnit(1000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='megawebers',ascii_symbol='MWb',symbol='MWb') -kilowebers = NamedUnit(1000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='kilowebers',ascii_symbol='kWb',symbol='kWb') -milliwebers = NamedUnit(0.001, Dimensions(2, -2, 1, -1, 0, 0, 0),name='milliwebers',ascii_symbol='mWb',symbol='mWb') -microwebers = NamedUnit(1e-06, Dimensions(2, -2, 1, -1, 0, 0, 0),name='microwebers',ascii_symbol='uWb',symbol='µWb') -nanowebers = NamedUnit(1e-09, Dimensions(2, -2, 1, -1, 0, 0, 0),name='nanowebers',ascii_symbol='nWb',symbol='nWb') -picowebers = NamedUnit(1e-12, Dimensions(2, -2, 1, -1, 0, 0, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') -femtowebers = NamedUnit(1e-15, Dimensions(2, -2, 1, -1, 0, 0, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') -attowebers = NamedUnit(1e-18, Dimensions(2, -2, 1, -1, 0, 0, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') -tesla = NamedUnit(1, Dimensions(0, -2, 1, -1, 0, 0, 0),name='tesla',ascii_symbol='T',symbol='T') -exatesla = NamedUnit(1e+18, Dimensions(0, -2, 1, -1, 0, 0, 0),name='exatesla',ascii_symbol='ET',symbol='ET') -petatesla = NamedUnit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='petatesla',ascii_symbol='PT',symbol='PT') -teratesla = NamedUnit(1000000000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='teratesla',ascii_symbol='TT',symbol='TT') -gigatesla = NamedUnit(1000000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='gigatesla',ascii_symbol='GT',symbol='GT') -megatesla = NamedUnit(1000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='megatesla',ascii_symbol='MT',symbol='MT') -kilotesla = NamedUnit(1000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='kilotesla',ascii_symbol='kT',symbol='kT') -millitesla = NamedUnit(0.001, Dimensions(0, -2, 1, -1, 0, 0, 0),name='millitesla',ascii_symbol='mT',symbol='mT') -microtesla = NamedUnit(1e-06, Dimensions(0, -2, 1, -1, 0, 0, 0),name='microtesla',ascii_symbol='uT',symbol='µT') -nanotesla = NamedUnit(1e-09, Dimensions(0, -2, 1, -1, 0, 0, 0),name='nanotesla',ascii_symbol='nT',symbol='nT') -picotesla = NamedUnit(1e-12, Dimensions(0, -2, 1, -1, 0, 0, 0),name='picotesla',ascii_symbol='pT',symbol='pT') -femtotesla = NamedUnit(1e-15, Dimensions(0, -2, 1, -1, 0, 0, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') -attotesla = NamedUnit(1e-18, Dimensions(0, -2, 1, -1, 0, 0, 0),name='attotesla',ascii_symbol='aT',symbol='aT') -henry = NamedUnit(1, Dimensions(2, -2, 1, -2, 0, 0, 0),name='henry',ascii_symbol='H',symbol='H') -exahenry = NamedUnit(1e+18, Dimensions(2, -2, 1, -2, 0, 0, 0),name='exahenry',ascii_symbol='EH',symbol='EH') -petahenry = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='petahenry',ascii_symbol='PH',symbol='PH') -terahenry = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='terahenry',ascii_symbol='TH',symbol='TH') -gigahenry = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='gigahenry',ascii_symbol='GH',symbol='GH') -megahenry = NamedUnit(1000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='megahenry',ascii_symbol='MH',symbol='MH') -kilohenry = NamedUnit(1000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='kilohenry',ascii_symbol='kH',symbol='kH') -millihenry = NamedUnit(0.001, Dimensions(2, -2, 1, -2, 0, 0, 0),name='millihenry',ascii_symbol='mH',symbol='mH') -microhenry = NamedUnit(1e-06, Dimensions(2, -2, 1, -2, 0, 0, 0),name='microhenry',ascii_symbol='uH',symbol='µH') -nanohenry = NamedUnit(1e-09, Dimensions(2, -2, 1, -2, 0, 0, 0),name='nanohenry',ascii_symbol='nH',symbol='nH') -picohenry = NamedUnit(1e-12, Dimensions(2, -2, 1, -2, 0, 0, 0),name='picohenry',ascii_symbol='pH',symbol='pH') -femtohenry = NamedUnit(1e-15, Dimensions(2, -2, 1, -2, 0, 0, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') -attohenry = NamedUnit(1e-18, Dimensions(2, -2, 1, -2, 0, 0, 0),name='attohenry',ascii_symbol='aH',symbol='aH') -angstroms = NamedUnit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') -minutes = NamedUnit(60, Dimensions(0, 1, 0, 0, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') -hours = NamedUnit(360, Dimensions(0, 1, 0, 0, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') -days = NamedUnit(8640, Dimensions(0, 1, 0, 0, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') -years = NamedUnit(3155695.2, Dimensions(0, 1, 0, 0, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') -degrees = NamedUnit(57.29577951308232, Dimensions(0, 0, 0, 0, 0, 0, 1),name='degrees',ascii_symbol='deg',symbol='deg') -radians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 1),name='radians',ascii_symbol='rad',symbol='rad') -stradians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') -litres = NamedUnit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') -electronvolts = NamedUnit(1.602176634e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') -exaelectronvolts = NamedUnit(0.1602176634, Dimensions(2, -2, 1, 0, 0, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') -petaelectronvolts = NamedUnit(0.0001602176634, Dimensions(2, -2, 1, 0, 0, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') -teraelectronvolts = NamedUnit(1.602176634e-07, Dimensions(2, -2, 1, 0, 0, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') -gigaelectronvolts = NamedUnit(1.6021766339999998e-10, Dimensions(2, -2, 1, 0, 0, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') -megaelectronvolts = NamedUnit(1.6021766339999998e-13, Dimensions(2, -2, 1, 0, 0, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') -kiloelectronvolts = NamedUnit(1.602176634e-16, Dimensions(2, -2, 1, 0, 0, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') -millielectronvolts = NamedUnit(1.6021766339999998e-22, Dimensions(2, -2, 1, 0, 0, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') -microelectronvolts = NamedUnit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') -nanoelectronvolts = NamedUnit(1.602176634e-28, Dimensions(2, -2, 1, 0, 0, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') -picoelectronvolts = NamedUnit(1.6021766339999998e-31, Dimensions(2, -2, 1, 0, 0, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') -femtoelectronvolts = NamedUnit(1.602176634e-34, Dimensions(2, -2, 1, 0, 0, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') -attoelectronvolts = NamedUnit(1.602176634e-37, Dimensions(2, -2, 1, 0, 0, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') -atomic_mass_units = NamedUnit(1.660538921e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') -moles = NamedUnit(6.02214076e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') -millimoles = NamedUnit(6.02214076e+20, Dimensions(0, 0, 0, 0, 0, 1, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') -micromoles = NamedUnit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0, 1, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') -nanomoles = NamedUnit(602214076000000.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') -picomoles = NamedUnit(602214076000.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') -femtomoles = NamedUnit(602214076.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') -attomoles = NamedUnit(602214.076, Dimensions(0, 0, 0, 0, 0, 1, 0),name='attomoles',ascii_symbol='amol',symbol='amol') -kg_force = NamedUnit(9.80665, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kg_force',ascii_symbol='kgForce',symbol='kgForce') -degrees_celsius = NamedUnit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') -miles = NamedUnit(1609.344, Dimensions(1, 0, 0, 0, 0, 0, 0),name='miles',ascii_symbol='miles',symbol='miles') -yards = NamedUnit(0.9144000000000001, Dimensions(1, 0, 0, 0, 0, 0, 0),name='yards',ascii_symbol='yrd',symbol='yrd') -feet = NamedUnit(0.3048, Dimensions(1, 0, 0, 0, 0, 0, 0),name='feet',ascii_symbol='ft',symbol='ft') -inches = NamedUnit(0.0254, Dimensions(1, 0, 0, 0, 0, 0, 0),name='inches',ascii_symbol='in',symbol='in') -pounds = NamedUnit(0.45359237, Dimensions(0, 0, 1, 0, 0, 0, 0),name='pounds',ascii_symbol='lb',symbol='lb') -pounds_force = NamedUnit(4.448222, Dimensions(1, -2, 1, 0, 0, 0, 0),name='pounds_force',ascii_symbol='lbf',symbol='lbf') -ounces = NamedUnit(0.028349523125, Dimensions(0, 0, 1, 0, 0, 0, 0),name='ounces',ascii_symbol='oz',symbol='oz') -pounds_force_per_square_inch = NamedUnit(6894.757889515779, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pounds_force_per_square_inch',ascii_symbol='psi',symbol='psi') -none = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') -percent = NamedUnit(0.01, Dimensions(0, 0, 0, 0, 0, 0, 0),name='percent',ascii_symbol='percent',symbol='%') -square_meters = NamedUnit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') -cubic_meters = NamedUnit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') -per_meter = NamedUnit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') -per_square_meter = NamedUnit(1.0, Dimensions(length=-2), name='per_square_meter', ascii_symbol='m^-2', symbol='m⁻²') -per_cubic_meter = NamedUnit(1.0, Dimensions(length=-3), name='per_cubic_meter', ascii_symbol='m^-3', symbol='m⁻³') -square_exameters = NamedUnit(1e+36, Dimensions(length=2), name='square_exameters', ascii_symbol='Em^2', symbol='Em²') -cubic_exameters = NamedUnit(1e+54, Dimensions(length=3), name='cubic_exameters', ascii_symbol='Em^3', symbol='Em³') -per_exameter = NamedUnit(1e-18, Dimensions(length=-1), name='per_exameter', ascii_symbol='Em^-1', symbol='Em⁻¹') -per_square_exameter = NamedUnit(1e-36, Dimensions(length=-2), name='per_square_exameter', ascii_symbol='Em^-2', symbol='Em⁻²') -per_cubic_exameter = NamedUnit(1e-54, Dimensions(length=-3), name='per_cubic_exameter', ascii_symbol='Em^-3', symbol='Em⁻³') -square_petameters = NamedUnit(1e+30, Dimensions(length=2), name='square_petameters', ascii_symbol='Pm^2', symbol='Pm²') -cubic_petameters = NamedUnit(1e+45, Dimensions(length=3), name='cubic_petameters', ascii_symbol='Pm^3', symbol='Pm³') -per_petameter = NamedUnit(1e-15, Dimensions(length=-1), name='per_petameter', ascii_symbol='Pm^-1', symbol='Pm⁻¹') -per_square_petameter = NamedUnit(1e-30, Dimensions(length=-2), name='per_square_petameter', ascii_symbol='Pm^-2', symbol='Pm⁻²') -per_cubic_petameter = NamedUnit(1e-45, Dimensions(length=-3), name='per_cubic_petameter', ascii_symbol='Pm^-3', symbol='Pm⁻³') -square_terameters = NamedUnit(1e+24, Dimensions(length=2), name='square_terameters', ascii_symbol='Tm^2', symbol='Tm²') -cubic_terameters = NamedUnit(1e+36, Dimensions(length=3), name='cubic_terameters', ascii_symbol='Tm^3', symbol='Tm³') -per_terameter = NamedUnit(1e-12, Dimensions(length=-1), name='per_terameter', ascii_symbol='Tm^-1', symbol='Tm⁻¹') -per_square_terameter = NamedUnit(1e-24, Dimensions(length=-2), name='per_square_terameter', ascii_symbol='Tm^-2', symbol='Tm⁻²') -per_cubic_terameter = NamedUnit(1e-36, Dimensions(length=-3), name='per_cubic_terameter', ascii_symbol='Tm^-3', symbol='Tm⁻³') -square_gigameters = NamedUnit(1e+18, Dimensions(length=2), name='square_gigameters', ascii_symbol='Gm^2', symbol='Gm²') -cubic_gigameters = NamedUnit(1e+27, Dimensions(length=3), name='cubic_gigameters', ascii_symbol='Gm^3', symbol='Gm³') -per_gigameter = NamedUnit(1e-09, Dimensions(length=-1), name='per_gigameter', ascii_symbol='Gm^-1', symbol='Gm⁻¹') -per_square_gigameter = NamedUnit(1e-18, Dimensions(length=-2), name='per_square_gigameter', ascii_symbol='Gm^-2', symbol='Gm⁻²') -per_cubic_gigameter = NamedUnit(1e-27, Dimensions(length=-3), name='per_cubic_gigameter', ascii_symbol='Gm^-3', symbol='Gm⁻³') -square_megameters = NamedUnit(1000000000000.0, Dimensions(length=2), name='square_megameters', ascii_symbol='Mm^2', symbol='Mm²') -cubic_megameters = NamedUnit(1e+18, Dimensions(length=3), name='cubic_megameters', ascii_symbol='Mm^3', symbol='Mm³') -per_megameter = NamedUnit(1e-06, Dimensions(length=-1), name='per_megameter', ascii_symbol='Mm^-1', symbol='Mm⁻¹') -per_square_megameter = NamedUnit(1e-12, Dimensions(length=-2), name='per_square_megameter', ascii_symbol='Mm^-2', symbol='Mm⁻²') -per_cubic_megameter = NamedUnit(1e-18, Dimensions(length=-3), name='per_cubic_megameter', ascii_symbol='Mm^-3', symbol='Mm⁻³') -square_kilometers = NamedUnit(1000000.0, Dimensions(length=2), name='square_kilometers', ascii_symbol='km^2', symbol='km²') -cubic_kilometers = NamedUnit(1000000000.0, Dimensions(length=3), name='cubic_kilometers', ascii_symbol='km^3', symbol='km³') -per_kilometer = NamedUnit(0.001, Dimensions(length=-1), name='per_kilometer', ascii_symbol='km^-1', symbol='km⁻¹') -per_square_kilometer = NamedUnit(1e-06, Dimensions(length=-2), name='per_square_kilometer', ascii_symbol='km^-2', symbol='km⁻²') -per_cubic_kilometer = NamedUnit(1e-09, Dimensions(length=-3), name='per_cubic_kilometer', ascii_symbol='km^-3', symbol='km⁻³') -square_millimeters = NamedUnit(1e-06, Dimensions(length=2), name='square_millimeters', ascii_symbol='mm^2', symbol='mm²') -cubic_millimeters = NamedUnit(1e-09, Dimensions(length=3), name='cubic_millimeters', ascii_symbol='mm^3', symbol='mm³') -per_millimeter = NamedUnit(1000.0, Dimensions(length=-1), name='per_millimeter', ascii_symbol='mm^-1', symbol='mm⁻¹') -per_square_millimeter = NamedUnit(1000000.0, Dimensions(length=-2), name='per_square_millimeter', ascii_symbol='mm^-2', symbol='mm⁻²') -per_cubic_millimeter = NamedUnit(999999999.9999999, Dimensions(length=-3), name='per_cubic_millimeter', ascii_symbol='mm^-3', symbol='mm⁻³') -square_micrometers = NamedUnit(1e-12, Dimensions(length=2), name='square_micrometers', ascii_symbol='um^2', symbol='µm²') -cubic_micrometers = NamedUnit(9.999999999999999e-19, Dimensions(length=3), name='cubic_micrometers', ascii_symbol='um^3', symbol='µm³') -per_micrometer = NamedUnit(1000000.0, Dimensions(length=-1), name='per_micrometer', ascii_symbol='um^-1', symbol='µm⁻¹') -per_square_micrometer = NamedUnit(1000000000000.0001, Dimensions(length=-2), name='per_square_micrometer', ascii_symbol='um^-2', symbol='µm⁻²') -per_cubic_micrometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3), name='per_cubic_micrometer', ascii_symbol='um^-3', symbol='µm⁻³') -square_nanometers = NamedUnit(1e-18, Dimensions(length=2), name='square_nanometers', ascii_symbol='nm^2', symbol='nm²') -cubic_nanometers = NamedUnit(1.0000000000000002e-27, Dimensions(length=3), name='cubic_nanometers', ascii_symbol='nm^3', symbol='nm³') -per_nanometer = NamedUnit(999999999.9999999, Dimensions(length=-1), name='per_nanometer', ascii_symbol='nm^-1', symbol='nm⁻¹') -per_square_nanometer = NamedUnit(9.999999999999999e+17, Dimensions(length=-2), name='per_square_nanometer', ascii_symbol='nm^-2', symbol='nm⁻²') -per_cubic_nanometer = NamedUnit(9.999999999999999e+26, Dimensions(length=-3), name='per_cubic_nanometer', ascii_symbol='nm^-3', symbol='nm⁻³') -square_picometers = NamedUnit(1e-24, Dimensions(length=2), name='square_picometers', ascii_symbol='pm^2', symbol='pm²') -cubic_picometers = NamedUnit(1e-36, Dimensions(length=3), name='cubic_picometers', ascii_symbol='pm^3', symbol='pm³') -per_picometer = NamedUnit(1000000000000.0, Dimensions(length=-1), name='per_picometer', ascii_symbol='pm^-1', symbol='pm⁻¹') -per_square_picometer = NamedUnit(1e+24, Dimensions(length=-2), name='per_square_picometer', ascii_symbol='pm^-2', symbol='pm⁻²') -per_cubic_picometer = NamedUnit(1e+36, Dimensions(length=-3), name='per_cubic_picometer', ascii_symbol='pm^-3', symbol='pm⁻³') -square_femtometers = NamedUnit(1e-30, Dimensions(length=2), name='square_femtometers', ascii_symbol='fm^2', symbol='fm²') -cubic_femtometers = NamedUnit(1.0000000000000003e-45, Dimensions(length=3), name='cubic_femtometers', ascii_symbol='fm^3', symbol='fm³') -per_femtometer = NamedUnit(999999999999999.9, Dimensions(length=-1), name='per_femtometer', ascii_symbol='fm^-1', symbol='fm⁻¹') -per_square_femtometer = NamedUnit(9.999999999999999e+29, Dimensions(length=-2), name='per_square_femtometer', ascii_symbol='fm^-2', symbol='fm⁻²') -per_cubic_femtometer = NamedUnit(9.999999999999998e+44, Dimensions(length=-3), name='per_cubic_femtometer', ascii_symbol='fm^-3', symbol='fm⁻³') -square_attometers = NamedUnit(1.0000000000000001e-36, Dimensions(length=2), name='square_attometers', ascii_symbol='am^2', symbol='am²') -cubic_attometers = NamedUnit(1.0000000000000002e-54, Dimensions(length=3), name='cubic_attometers', ascii_symbol='am^3', symbol='am³') -per_attometer = NamedUnit(9.999999999999999e+17, Dimensions(length=-1), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') -per_square_attometer = NamedUnit(9.999999999999999e+35, Dimensions(length=-2), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') -per_cubic_attometer = NamedUnit(9.999999999999997e+53, Dimensions(length=-3), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') -square_decimeters = NamedUnit(0.010000000000000002, Dimensions(length=2), name='square_decimeters', ascii_symbol='dm^2', symbol='dm²') -cubic_decimeters = NamedUnit(0.0010000000000000002, Dimensions(length=3), name='cubic_decimeters', ascii_symbol='dm^3', symbol='dm³') -per_decimeter = NamedUnit(10.0, Dimensions(length=-1), name='per_decimeter', ascii_symbol='dm^-1', symbol='dm⁻¹') -per_square_decimeter = NamedUnit(99.99999999999999, Dimensions(length=-2), name='per_square_decimeter', ascii_symbol='dm^-2', symbol='dm⁻²') -per_cubic_decimeter = NamedUnit(999.9999999999999, Dimensions(length=-3), name='per_cubic_decimeter', ascii_symbol='dm^-3', symbol='dm⁻³') -square_centimeters = NamedUnit(0.0001, Dimensions(length=2), name='square_centimeters', ascii_symbol='cm^2', symbol='cm²') -cubic_centimeters = NamedUnit(1.0000000000000002e-06, Dimensions(length=3), name='cubic_centimeters', ascii_symbol='cm^3', symbol='cm³') -per_centimeter = NamedUnit(100.0, Dimensions(length=-1), name='per_centimeter', ascii_symbol='cm^-1', symbol='cm⁻¹') -per_square_centimeter = NamedUnit(10000.0, Dimensions(length=-2), name='per_square_centimeter', ascii_symbol='cm^-2', symbol='cm⁻²') -per_cubic_centimeter = NamedUnit(999999.9999999999, Dimensions(length=-3), name='per_cubic_centimeter', ascii_symbol='cm^-3', symbol='cm⁻³') -square_angstroms = NamedUnit(1.0000000000000001e-20, Dimensions(length=2), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ų') -cubic_angstroms = NamedUnit(1e-30, Dimensions(length=3), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='ų') -per_angstrom = NamedUnit(10000000000.0, Dimensions(length=-1), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Å⁻¹') -per_square_angstrom = NamedUnit(1e+20, Dimensions(length=-2), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Å⁻²') -per_cubic_angstrom = NamedUnit(9.999999999999999e+29, Dimensions(length=-3), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Å⁻³') -square_miles = NamedUnit(2589988.110336, Dimensions(length=2), name='square_miles', ascii_symbol='miles^2', symbol='miles²') -cubic_miles = NamedUnit(4168181825.44058, Dimensions(length=3), name='cubic_miles', ascii_symbol='miles^3', symbol='miles³') -per_mile = NamedUnit(0.0006213711922373339, Dimensions(length=-1), name='per_mile', ascii_symbol='miles^-1', symbol='miles⁻¹') -per_square_mile = NamedUnit(3.861021585424458e-07, Dimensions(length=-2), name='per_square_mile', ascii_symbol='miles^-2', symbol='miles⁻²') -per_cubic_mile = NamedUnit(2.399127585789277e-10, Dimensions(length=-3), name='per_cubic_mile', ascii_symbol='miles^-3', symbol='miles⁻³') -square_yards = NamedUnit(0.8361273600000002, Dimensions(length=2), name='square_yards', ascii_symbol='yrd^2', symbol='yrd²') -cubic_yards = NamedUnit(0.7645548579840002, Dimensions(length=3), name='cubic_yards', ascii_symbol='yrd^3', symbol='yrd³') -per_yard = NamedUnit(1.0936132983377076, Dimensions(length=-1), name='per_yard', ascii_symbol='yrd^-1', symbol='yrd⁻¹') -per_square_yard = NamedUnit(1.19599004630108, Dimensions(length=-2), name='per_square_yard', ascii_symbol='yrd^-2', symbol='yrd⁻²') -per_cubic_yard = NamedUnit(1.3079506193143917, Dimensions(length=-3), name='per_cubic_yard', ascii_symbol='yrd^-3', symbol='yrd⁻³') -square_feet = NamedUnit(0.09290304, Dimensions(length=2), name='square_feet', ascii_symbol='ft^2', symbol='ft²') -cubic_feet = NamedUnit(0.028316846592000004, Dimensions(length=3), name='cubic_feet', ascii_symbol='ft^3', symbol='ft³') -per_foot = NamedUnit(3.280839895013123, Dimensions(length=-1), name='per_foot', ascii_symbol='ft^-1', symbol='ft⁻¹') -per_square_foot = NamedUnit(10.763910416709722, Dimensions(length=-2), name='per_square_foot', ascii_symbol='ft^-2', symbol='ft⁻²') -per_cubic_foot = NamedUnit(35.314666721488585, Dimensions(length=-3), name='per_cubic_foot', ascii_symbol='ft^-3', symbol='ft⁻³') -square_inches = NamedUnit(0.00064516, Dimensions(length=2), name='square_inches', ascii_symbol='in^2', symbol='in²') -cubic_inches = NamedUnit(1.6387064e-05, Dimensions(length=3), name='cubic_inches', ascii_symbol='in^3', symbol='in³') -per_inch = NamedUnit(39.37007874015748, Dimensions(length=-1), name='per_inch', ascii_symbol='in^-1', symbol='in⁻¹') -per_square_inch = NamedUnit(1550.0031000062002, Dimensions(length=-2), name='per_square_inch', ascii_symbol='in^-2', symbol='in⁻²') -per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3), name='per_cubic_inch', ascii_symbol='in^-3', symbol='in⁻³') -meters_per_second = NamedUnit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='ms⁻¹') -meters_per_square_second = NamedUnit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='ms⁻²') -meters_per_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='mms⁻¹') -meters_per_square_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='mms⁻²') -meters_per_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='mµs⁻¹') -meters_per_square_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='mµs⁻²') -meters_per_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='mns⁻¹') -meters_per_square_nanosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='mns⁻²') -meters_per_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='mps⁻¹') -meters_per_square_picosecond = NamedUnit(1.0000000000000001e+24, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='mps⁻²') -meters_per_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='mfs⁻¹') -meters_per_square_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='mfs⁻²') -meters_per_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='mas⁻¹') -meters_per_square_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='mas⁻²') -meters_per_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='mmin⁻¹') -meters_per_square_minute = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='mmin⁻²') -meters_per_hour = NamedUnit(0.002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='mh⁻¹') -meters_per_square_hour = NamedUnit(7.71604938271605e-06, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='mh⁻²') -meters_per_day = NamedUnit(0.00011574074074074075, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='md⁻¹') -meters_per_square_day = NamedUnit(1.3395919067215363e-08, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='md⁻²') -meters_per_year = NamedUnit(3.168873850681143e-07, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='my⁻¹') -meters_per_square_year = NamedUnit(1.0041761481530735e-13, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='my⁻²') -exameters_per_second = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='Ems⁻¹') -exameters_per_square_second = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='Ems⁻²') -exameters_per_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') -exameters_per_square_millisecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') -exameters_per_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') -exameters_per_square_microsecond = NamedUnit(1e+30, Dimensions(length=1, time=-2), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') -exameters_per_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') -exameters_per_square_nanosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') -exameters_per_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-1), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') -exameters_per_square_picosecond = NamedUnit(1e+42, Dimensions(length=1, time=-2), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') -exameters_per_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') -exameters_per_square_femtosecond = NamedUnit(9.999999999999999e+47, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') -exameters_per_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-1), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') -exameters_per_square_attosecond = NamedUnit(9.999999999999999e+53, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') -exameters_per_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='Emmin⁻¹') -exameters_per_square_minute = NamedUnit(277777777777777.78, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='Emmin⁻²') -exameters_per_hour = NamedUnit(2777777777777778.0, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='Emh⁻¹') -exameters_per_square_hour = NamedUnit(7716049382716.05, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='Emh⁻²') -exameters_per_day = NamedUnit(115740740740740.73, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='Emd⁻¹') -exameters_per_square_day = NamedUnit(13395919067.215364, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='Emd⁻²') -exameters_per_year = NamedUnit(316887385068.1143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='Emy⁻¹') -exameters_per_square_year = NamedUnit(100417.61481530734, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='Emy⁻²') -petameters_per_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='Pms⁻¹') -petameters_per_square_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='Pms⁻²') -petameters_per_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') -petameters_per_square_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') -petameters_per_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') -petameters_per_square_microsecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') -petameters_per_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') -petameters_per_square_nanosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') -petameters_per_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-1), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') -petameters_per_square_picosecond = NamedUnit(1.0000000000000001e+39, Dimensions(length=1, time=-2), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') -petameters_per_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') -petameters_per_square_femtosecond = NamedUnit(1e+45, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') -petameters_per_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') -petameters_per_square_attosecond = NamedUnit(9.999999999999998e+50, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') -petameters_per_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='Pmmin⁻¹') -petameters_per_square_minute = NamedUnit(277777777777.7778, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='Pmmin⁻²') -petameters_per_hour = NamedUnit(2777777777777.778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='Pmh⁻¹') -petameters_per_square_hour = NamedUnit(7716049382.716049, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='Pmh⁻²') -petameters_per_day = NamedUnit(115740740740.74074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='Pmd⁻¹') -petameters_per_square_day = NamedUnit(13395919.067215364, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='Pmd⁻²') -petameters_per_year = NamedUnit(316887385.0681143, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='Pmy⁻¹') -petameters_per_square_year = NamedUnit(100.41761481530735, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='Pmy⁻²') -terameters_per_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='Tms⁻¹') -terameters_per_square_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='Tms⁻²') -terameters_per_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') -terameters_per_square_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') -terameters_per_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') -terameters_per_square_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') -terameters_per_nanosecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') -terameters_per_square_nanosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') -terameters_per_picosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') -terameters_per_square_picosecond = NamedUnit(1e+36, Dimensions(length=1, time=-2), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') -terameters_per_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') -terameters_per_square_femtosecond = NamedUnit(9.999999999999999e+41, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') -terameters_per_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') -terameters_per_square_attosecond = NamedUnit(9.999999999999999e+47, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') -terameters_per_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='Tmmin⁻¹') -terameters_per_square_minute = NamedUnit(277777777.7777778, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='Tmmin⁻²') -terameters_per_hour = NamedUnit(2777777777.7777777, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='Tmh⁻¹') -terameters_per_square_hour = NamedUnit(7716049.382716049, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='Tmh⁻²') -terameters_per_day = NamedUnit(115740740.74074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='Tmd⁻¹') -terameters_per_square_day = NamedUnit(13395.919067215364, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='Tmd⁻²') -terameters_per_year = NamedUnit(316887.38506811426, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='Tmy⁻¹') -terameters_per_square_year = NamedUnit(0.10041761481530735, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='Tmy⁻²') -gigameters_per_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='Gms⁻¹') -gigameters_per_square_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='Gms⁻²') -gigameters_per_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') -gigameters_per_square_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') -gigameters_per_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') -gigameters_per_square_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') -gigameters_per_nanosecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') -gigameters_per_square_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') -gigameters_per_picosecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') -gigameters_per_square_picosecond = NamedUnit(1.0000000000000001e+33, Dimensions(length=1, time=-2), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') -gigameters_per_femtosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') -gigameters_per_square_femtosecond = NamedUnit(1e+39, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') -gigameters_per_attosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') -gigameters_per_square_attosecond = NamedUnit(1e+45, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') -gigameters_per_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='Gmmin⁻¹') -gigameters_per_square_minute = NamedUnit(277777.77777777775, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='Gmmin⁻²') -gigameters_per_hour = NamedUnit(2777777.777777778, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='Gmh⁻¹') -gigameters_per_square_hour = NamedUnit(7716.049382716049, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='Gmh⁻²') -gigameters_per_day = NamedUnit(115740.74074074074, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='Gmd⁻¹') -gigameters_per_square_day = NamedUnit(13.395919067215363, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='Gmd⁻²') -gigameters_per_year = NamedUnit(316.88738506811427, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='Gmy⁻¹') -gigameters_per_square_year = NamedUnit(0.00010041761481530735, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='Gmy⁻²') -megameters_per_second = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='Mms⁻¹') -megameters_per_square_second = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='Mms⁻²') -megameters_per_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') -megameters_per_square_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') -megameters_per_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') -megameters_per_square_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') -megameters_per_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') -megameters_per_square_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') -megameters_per_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') -megameters_per_square_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-2), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') -megameters_per_femtosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') -megameters_per_square_femtosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') -megameters_per_attosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') -megameters_per_square_attosecond = NamedUnit(9.999999999999999e+41, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') -megameters_per_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='Mmmin⁻¹') -megameters_per_square_minute = NamedUnit(277.77777777777777, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='Mmmin⁻²') -megameters_per_hour = NamedUnit(2777.777777777778, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='Mmh⁻¹') -megameters_per_square_hour = NamedUnit(7.716049382716049, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='Mmh⁻²') -megameters_per_day = NamedUnit(115.74074074074075, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='Mmd⁻¹') -megameters_per_square_day = NamedUnit(0.013395919067215363, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='Mmd⁻²') -megameters_per_year = NamedUnit(0.3168873850681143, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='Mmy⁻¹') -megameters_per_square_year = NamedUnit(1.0041761481530735e-07, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='Mmy⁻²') -kilometers_per_second = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kms⁻¹') -kilometers_per_square_second = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kms⁻²') -kilometers_per_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') -kilometers_per_square_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') -kilometers_per_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') -kilometers_per_square_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') -kilometers_per_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') -kilometers_per_square_nanosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') -kilometers_per_picosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') -kilometers_per_square_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') -kilometers_per_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') -kilometers_per_square_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') -kilometers_per_attosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') -kilometers_per_square_attosecond = NamedUnit(1e+39, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') -kilometers_per_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmmin⁻¹') -kilometers_per_square_minute = NamedUnit(0.2777777777777778, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmmin⁻²') -kilometers_per_hour = NamedUnit(2.7777777777777777, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmh⁻¹') -kilometers_per_square_hour = NamedUnit(0.007716049382716049, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmh⁻²') -kilometers_per_day = NamedUnit(0.11574074074074074, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmd⁻¹') -kilometers_per_square_day = NamedUnit(1.3395919067215363e-05, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmd⁻²') -kilometers_per_year = NamedUnit(0.0003168873850681143, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmy⁻¹') -kilometers_per_square_year = NamedUnit(1.0041761481530735e-10, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmy⁻²') -millimeters_per_second = NamedUnit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mms⁻¹') -millimeters_per_square_second = NamedUnit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mms⁻²') -millimeters_per_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') -millimeters_per_square_millisecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') -millimeters_per_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') -millimeters_per_square_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') -millimeters_per_nanosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') -millimeters_per_square_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') -millimeters_per_picosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') -millimeters_per_square_picosecond = NamedUnit(1.0000000000000001e+21, Dimensions(length=1, time=-2), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') -millimeters_per_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') -millimeters_per_square_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') -millimeters_per_attosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') -millimeters_per_square_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') -millimeters_per_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmmin⁻¹') -millimeters_per_square_minute = NamedUnit(2.7777777777777776e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmmin⁻²') -millimeters_per_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmh⁻¹') -millimeters_per_square_hour = NamedUnit(7.71604938271605e-09, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmh⁻²') -millimeters_per_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmd⁻¹') -millimeters_per_square_day = NamedUnit(1.3395919067215364e-11, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmd⁻²') -millimeters_per_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmy⁻¹') -millimeters_per_square_year = NamedUnit(1.0041761481530735e-16, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmy⁻²') -micrometers_per_second = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µms⁻¹') -micrometers_per_square_second = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µms⁻²') -micrometers_per_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') -micrometers_per_square_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') -micrometers_per_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') -micrometers_per_square_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') -micrometers_per_nanosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-1), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') -micrometers_per_square_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-2), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') -micrometers_per_picosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') -micrometers_per_square_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') -micrometers_per_femtosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') -micrometers_per_square_femtosecond = NamedUnit(9.999999999999998e+23, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') -micrometers_per_attosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') -micrometers_per_square_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') -micrometers_per_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmmin⁻¹') -micrometers_per_square_minute = NamedUnit(2.7777777777777777e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmmin⁻²') -micrometers_per_hour = NamedUnit(2.7777777777777776e-09, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmh⁻¹') -micrometers_per_square_hour = NamedUnit(7.716049382716049e-12, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmh⁻²') -micrometers_per_day = NamedUnit(1.1574074074074074e-10, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmd⁻¹') -micrometers_per_square_day = NamedUnit(1.3395919067215363e-14, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmd⁻²') -micrometers_per_year = NamedUnit(3.168873850681143e-13, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmy⁻¹') -micrometers_per_square_year = NamedUnit(1.0041761481530734e-19, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmy⁻²') -nanometers_per_second = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nms⁻¹') -nanometers_per_square_second = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nms⁻²') -nanometers_per_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') -nanometers_per_square_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') -nanometers_per_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') -nanometers_per_square_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') -nanometers_per_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') -nanometers_per_square_nanosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') -nanometers_per_picosecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') -nanometers_per_square_picosecond = NamedUnit(1000000000000000.1, Dimensions(length=1, time=-2), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') -nanometers_per_femtosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') -nanometers_per_square_femtosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') -nanometers_per_attosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') -nanometers_per_square_attosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') -nanometers_per_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmmin⁻¹') -nanometers_per_square_minute = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmmin⁻²') -nanometers_per_hour = NamedUnit(2.777777777777778e-12, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmh⁻¹') -nanometers_per_square_hour = NamedUnit(7.71604938271605e-15, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmh⁻²') -nanometers_per_day = NamedUnit(1.1574074074074076e-13, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmd⁻¹') -nanometers_per_square_day = NamedUnit(1.3395919067215365e-17, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmd⁻²') -nanometers_per_year = NamedUnit(3.1688738506811433e-16, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmy⁻¹') -nanometers_per_square_year = NamedUnit(1.0041761481530736e-22, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmy⁻²') -picometers_per_second = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pms⁻¹') -picometers_per_square_second = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pms⁻²') -picometers_per_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') -picometers_per_square_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') -picometers_per_microsecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') -picometers_per_square_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') -picometers_per_nanosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') -picometers_per_square_nanosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-2), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') -picometers_per_picosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') -picometers_per_square_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') -picometers_per_femtosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-1), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') -picometers_per_square_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') -picometers_per_attosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-1), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') -picometers_per_square_attosecond = NamedUnit(9.999999999999998e+23, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') -picometers_per_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmmin⁻¹') -picometers_per_square_minute = NamedUnit(2.7777777777777775e-16, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmmin⁻²') -picometers_per_hour = NamedUnit(2.7777777777777776e-15, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmh⁻¹') -picometers_per_square_hour = NamedUnit(7.716049382716049e-18, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmh⁻²') -picometers_per_day = NamedUnit(1.1574074074074073e-16, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmd⁻¹') -picometers_per_square_day = NamedUnit(1.3395919067215364e-20, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmd⁻²') -picometers_per_year = NamedUnit(3.168873850681143e-19, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmy⁻¹') -picometers_per_square_year = NamedUnit(1.0041761481530734e-25, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmy⁻²') -femtometers_per_second = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fms⁻¹') -femtometers_per_square_second = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fms⁻²') -femtometers_per_millisecond = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') -femtometers_per_square_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') -femtometers_per_microsecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') -femtometers_per_square_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') -femtometers_per_nanosecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') -femtometers_per_square_nanosecond = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') -femtometers_per_picosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') -femtometers_per_square_picosecond = NamedUnit(1000000000.0000001, Dimensions(length=1, time=-2), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') -femtometers_per_femtosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') -femtometers_per_square_femtosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') -femtometers_per_attosecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') -femtometers_per_square_attosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') -femtometers_per_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmmin⁻¹') -femtometers_per_square_minute = NamedUnit(2.777777777777778e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmmin⁻²') -femtometers_per_hour = NamedUnit(2.777777777777778e-18, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmh⁻¹') -femtometers_per_square_hour = NamedUnit(7.71604938271605e-21, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmh⁻²') -femtometers_per_day = NamedUnit(1.1574074074074075e-19, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmd⁻¹') -femtometers_per_square_day = NamedUnit(1.3395919067215363e-23, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmd⁻²') -femtometers_per_year = NamedUnit(3.168873850681143e-22, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmy⁻¹') -femtometers_per_square_year = NamedUnit(1.0041761481530735e-28, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmy⁻²') -attometers_per_second = NamedUnit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='ams⁻¹') -attometers_per_square_second = NamedUnit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='ams⁻²') -attometers_per_millisecond = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') -attometers_per_square_millisecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') -attometers_per_microsecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-1), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') -attometers_per_square_microsecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-2), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') -attometers_per_nanosecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') -attometers_per_square_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') -attometers_per_picosecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-1), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') -attometers_per_square_picosecond = NamedUnit(1000000.0000000001, Dimensions(length=1, time=-2), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') -attometers_per_femtosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') -attometers_per_square_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') -attometers_per_attosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') -attometers_per_square_attosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') -attometers_per_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='ammin⁻¹') -attometers_per_square_minute = NamedUnit(2.777777777777778e-22, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='ammin⁻²') -attometers_per_hour = NamedUnit(2.7777777777777778e-21, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amh⁻¹') -attometers_per_square_hour = NamedUnit(7.71604938271605e-24, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amh⁻²') -attometers_per_day = NamedUnit(1.1574074074074074e-22, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amd⁻¹') -attometers_per_square_day = NamedUnit(1.3395919067215363e-26, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amd⁻²') -attometers_per_year = NamedUnit(3.1688738506811433e-25, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amy⁻¹') -attometers_per_square_year = NamedUnit(1.0041761481530734e-31, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amy⁻²') -decimeters_per_second = NamedUnit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dms⁻¹') -decimeters_per_square_second = NamedUnit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dms⁻²') -decimeters_per_millisecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='decimeters_per_millisecond', ascii_symbol='dm/ms', symbol='dmms⁻¹') -decimeters_per_square_millisecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') -decimeters_per_microsecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-1), name='decimeters_per_microsecond', ascii_symbol='dm/us', symbol='dmµs⁻¹') -decimeters_per_square_microsecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_microsecond', ascii_symbol='dm/us^2', symbol='dmµs⁻²') -decimeters_per_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='decimeters_per_nanosecond', ascii_symbol='dm/ns', symbol='dmns⁻¹') -decimeters_per_square_nanosecond = NamedUnit(1e+17, Dimensions(length=1, time=-2), name='decimeters_per_square_nanosecond', ascii_symbol='dm/ns^2', symbol='dmns⁻²') -decimeters_per_picosecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_picosecond', ascii_symbol='dm/ps', symbol='dmps⁻¹') -decimeters_per_square_picosecond = NamedUnit(1.0000000000000001e+23, Dimensions(length=1, time=-2), name='decimeters_per_square_picosecond', ascii_symbol='dm/ps^2', symbol='dmps⁻²') -decimeters_per_femtosecond = NamedUnit(100000000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_femtosecond', ascii_symbol='dm/fs', symbol='dmfs⁻¹') -decimeters_per_square_femtosecond = NamedUnit(1e+29, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') -decimeters_per_attosecond = NamedUnit(1e+17, Dimensions(length=1, time=-1), name='decimeters_per_attosecond', ascii_symbol='dm/as', symbol='dmas⁻¹') -decimeters_per_square_attosecond = NamedUnit(1e+35, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') -decimeters_per_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmmin⁻¹') -decimeters_per_square_minute = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmmin⁻²') -decimeters_per_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmh⁻¹') -decimeters_per_square_hour = NamedUnit(7.71604938271605e-07, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmh⁻²') -decimeters_per_day = NamedUnit(1.1574074074074075e-05, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmd⁻¹') -decimeters_per_square_day = NamedUnit(1.3395919067215364e-09, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmd⁻²') -decimeters_per_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmy⁻¹') -decimeters_per_square_year = NamedUnit(1.0041761481530735e-14, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmy⁻²') -centimeters_per_second = NamedUnit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cms⁻¹') -centimeters_per_square_second = NamedUnit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cms⁻²') -centimeters_per_millisecond = NamedUnit(10.0, Dimensions(length=1, time=-1), name='centimeters_per_millisecond', ascii_symbol='cm/ms', symbol='cmms⁻¹') -centimeters_per_square_millisecond = NamedUnit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') -centimeters_per_microsecond = NamedUnit(10000.0, Dimensions(length=1, time=-1), name='centimeters_per_microsecond', ascii_symbol='cm/us', symbol='cmµs⁻¹') -centimeters_per_square_microsecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_microsecond', ascii_symbol='cm/us^2', symbol='cmµs⁻²') -centimeters_per_nanosecond = NamedUnit(10000000.0, Dimensions(length=1, time=-1), name='centimeters_per_nanosecond', ascii_symbol='cm/ns', symbol='cmns⁻¹') -centimeters_per_square_nanosecond = NamedUnit(1e+16, Dimensions(length=1, time=-2), name='centimeters_per_square_nanosecond', ascii_symbol='cm/ns^2', symbol='cmns⁻²') -centimeters_per_picosecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_picosecond', ascii_symbol='cm/ps', symbol='cmps⁻¹') -centimeters_per_square_picosecond = NamedUnit(1e+22, Dimensions(length=1, time=-2), name='centimeters_per_square_picosecond', ascii_symbol='cm/ps^2', symbol='cmps⁻²') -centimeters_per_femtosecond = NamedUnit(10000000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_femtosecond', ascii_symbol='cm/fs', symbol='cmfs⁻¹') -centimeters_per_square_femtosecond = NamedUnit(1e+28, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') -centimeters_per_attosecond = NamedUnit(1e+16, Dimensions(length=1, time=-1), name='centimeters_per_attosecond', ascii_symbol='cm/as', symbol='cmas⁻¹') -centimeters_per_square_attosecond = NamedUnit(1e+34, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') -centimeters_per_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmmin⁻¹') -centimeters_per_square_minute = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmmin⁻²') -centimeters_per_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmh⁻¹') -centimeters_per_square_hour = NamedUnit(7.71604938271605e-08, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmh⁻²') -centimeters_per_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmd⁻¹') -centimeters_per_square_day = NamedUnit(1.3395919067215363e-10, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmd⁻²') -centimeters_per_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmy⁻¹') -centimeters_per_square_year = NamedUnit(1.0041761481530735e-15, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmy⁻²') -angstroms_per_second = NamedUnit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='Ås⁻¹') -angstroms_per_square_second = NamedUnit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='Ås⁻²') -angstroms_per_millisecond = NamedUnit(1e-07, Dimensions(length=1, time=-1), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') -angstroms_per_square_millisecond = NamedUnit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') -angstroms_per_microsecond = NamedUnit(0.0001, Dimensions(length=1, time=-1), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') -angstroms_per_square_microsecond = NamedUnit(100.0, Dimensions(length=1, time=-2), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') -angstroms_per_nanosecond = NamedUnit(0.09999999999999999, Dimensions(length=1, time=-1), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') -angstroms_per_square_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') -angstroms_per_picosecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') -angstroms_per_square_picosecond = NamedUnit(100000000000000.02, Dimensions(length=1, time=-2), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') -angstroms_per_femtosecond = NamedUnit(100000.0, Dimensions(length=1, time=-1), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') -angstroms_per_square_femtosecond = NamedUnit(1e+20, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') -angstroms_per_attosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') -angstroms_per_square_attosecond = NamedUnit(9.999999999999999e+25, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') -angstroms_per_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='Åmin⁻¹') -angstroms_per_square_minute = NamedUnit(2.7777777777777778e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='Åmin⁻²') -angstroms_per_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='Åh⁻¹') -angstroms_per_square_hour = NamedUnit(7.716049382716049e-16, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='Åh⁻²') -angstroms_per_day = NamedUnit(1.1574074074074074e-14, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='Åd⁻¹') -angstroms_per_square_day = NamedUnit(1.3395919067215363e-18, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='Åd⁻²') -angstroms_per_year = NamedUnit(3.168873850681143e-17, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='Åy⁻¹') -angstroms_per_square_year = NamedUnit(1.0041761481530734e-23, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='Åy⁻²') -miles_per_second = NamedUnit(1609.344, Dimensions(length=1, time=-1), name='miles_per_second', ascii_symbol='miles/s', symbol='miless⁻¹') -miles_per_square_second = NamedUnit(1609.344, Dimensions(length=1, time=-2), name='miles_per_square_second', ascii_symbol='miles/s^2', symbol='miless⁻²') -miles_per_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-1), name='miles_per_millisecond', ascii_symbol='miles/ms', symbol='milesms⁻¹') -miles_per_square_millisecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-2), name='miles_per_square_millisecond', ascii_symbol='miles/ms^2', symbol='milesms⁻²') -miles_per_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-1), name='miles_per_microsecond', ascii_symbol='miles/us', symbol='milesµs⁻¹') -miles_per_square_microsecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-2), name='miles_per_square_microsecond', ascii_symbol='miles/us^2', symbol='milesµs⁻²') -miles_per_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-1), name='miles_per_nanosecond', ascii_symbol='miles/ns', symbol='milesns⁻¹') -miles_per_square_nanosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-2), name='miles_per_square_nanosecond', ascii_symbol='miles/ns^2', symbol='milesns⁻²') -miles_per_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-1), name='miles_per_picosecond', ascii_symbol='miles/ps', symbol='milesps⁻¹') -miles_per_square_picosecond = NamedUnit(1.609344e+27, Dimensions(length=1, time=-2), name='miles_per_square_picosecond', ascii_symbol='miles/ps^2', symbol='milesps⁻²') -miles_per_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-1), name='miles_per_femtosecond', ascii_symbol='miles/fs', symbol='milesfs⁻¹') -miles_per_square_femtosecond = NamedUnit(1.609344e+33, Dimensions(length=1, time=-2), name='miles_per_square_femtosecond', ascii_symbol='miles/fs^2', symbol='milesfs⁻²') -miles_per_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-1), name='miles_per_attosecond', ascii_symbol='miles/as', symbol='milesas⁻¹') -miles_per_square_attosecond = NamedUnit(1.609344e+39, Dimensions(length=1, time=-2), name='miles_per_square_attosecond', ascii_symbol='miles/as^2', symbol='milesas⁻²') -miles_per_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-1), name='miles_per_minute', ascii_symbol='miles/min', symbol='milesmin⁻¹') -miles_per_square_minute = NamedUnit(0.44704, Dimensions(length=1, time=-2), name='miles_per_square_minute', ascii_symbol='miles/min^2', symbol='milesmin⁻²') -miles_per_hour = NamedUnit(4.4704, Dimensions(length=1, time=-1), name='miles_per_hour', ascii_symbol='miles/h', symbol='milesh⁻¹') -miles_per_square_hour = NamedUnit(0.012417777777777778, Dimensions(length=1, time=-2), name='miles_per_square_hour', ascii_symbol='miles/h^2', symbol='milesh⁻²') -miles_per_day = NamedUnit(0.18626666666666666, Dimensions(length=1, time=-1), name='miles_per_day', ascii_symbol='miles/d', symbol='milesd⁻¹') -miles_per_square_day = NamedUnit(2.1558641975308643e-05, Dimensions(length=1, time=-2), name='miles_per_square_day', ascii_symbol='miles/d^2', symbol='milesd⁻²') -miles_per_year = NamedUnit(0.0005099808118350593, Dimensions(length=1, time=-1), name='miles_per_year', ascii_symbol='miles/y', symbol='milesy⁻¹') -miles_per_square_year = NamedUnit(1.61606485897326e-10, Dimensions(length=1, time=-2), name='miles_per_square_year', ascii_symbol='miles/y^2', symbol='milesy⁻²') -yards_per_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-1), name='yards_per_second', ascii_symbol='yrd/s', symbol='yrds⁻¹') -yards_per_square_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-2), name='yards_per_square_second', ascii_symbol='yrd/s^2', symbol='yrds⁻²') -yards_per_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-1), name='yards_per_millisecond', ascii_symbol='yrd/ms', symbol='yrdms⁻¹') -yards_per_square_millisecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-2), name='yards_per_square_millisecond', ascii_symbol='yrd/ms^2', symbol='yrdms⁻²') -yards_per_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-1), name='yards_per_microsecond', ascii_symbol='yrd/us', symbol='yrdµs⁻¹') -yards_per_square_microsecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-2), name='yards_per_square_microsecond', ascii_symbol='yrd/us^2', symbol='yrdµs⁻²') -yards_per_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-1), name='yards_per_nanosecond', ascii_symbol='yrd/ns', symbol='yrdns⁻¹') -yards_per_square_nanosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-2), name='yards_per_square_nanosecond', ascii_symbol='yrd/ns^2', symbol='yrdns⁻²') -yards_per_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-1), name='yards_per_picosecond', ascii_symbol='yrd/ps', symbol='yrdps⁻¹') -yards_per_square_picosecond = NamedUnit(9.144000000000002e+23, Dimensions(length=1, time=-2), name='yards_per_square_picosecond', ascii_symbol='yrd/ps^2', symbol='yrdps⁻²') -yards_per_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-1), name='yards_per_femtosecond', ascii_symbol='yrd/fs', symbol='yrdfs⁻¹') -yards_per_square_femtosecond = NamedUnit(9.144e+29, Dimensions(length=1, time=-2), name='yards_per_square_femtosecond', ascii_symbol='yrd/fs^2', symbol='yrdfs⁻²') -yards_per_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-1), name='yards_per_attosecond', ascii_symbol='yrd/as', symbol='yrdas⁻¹') -yards_per_square_attosecond = NamedUnit(9.144e+35, Dimensions(length=1, time=-2), name='yards_per_square_attosecond', ascii_symbol='yrd/as^2', symbol='yrdas⁻²') -yards_per_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-1), name='yards_per_minute', ascii_symbol='yrd/min', symbol='yrdmin⁻¹') -yards_per_square_minute = NamedUnit(0.00025400000000000005, Dimensions(length=1, time=-2), name='yards_per_square_minute', ascii_symbol='yrd/min^2', symbol='yrdmin⁻²') -yards_per_hour = NamedUnit(0.00254, Dimensions(length=1, time=-1), name='yards_per_hour', ascii_symbol='yrd/h', symbol='yrdh⁻¹') -yards_per_square_hour = NamedUnit(7.055555555555557e-06, Dimensions(length=1, time=-2), name='yards_per_square_hour', ascii_symbol='yrd/h^2', symbol='yrdh⁻²') -yards_per_day = NamedUnit(0.00010583333333333335, Dimensions(length=1, time=-1), name='yards_per_day', ascii_symbol='yrd/d', symbol='yrdd⁻¹') -yards_per_square_day = NamedUnit(1.224922839506173e-08, Dimensions(length=1, time=-2), name='yards_per_square_day', ascii_symbol='yrd/d^2', symbol='yrdd⁻²') -yards_per_year = NamedUnit(2.897618249062837e-07, Dimensions(length=1, time=-1), name='yards_per_year', ascii_symbol='yrd/y', symbol='yrdy⁻¹') -yards_per_square_year = NamedUnit(9.182186698711705e-14, Dimensions(length=1, time=-2), name='yards_per_square_year', ascii_symbol='yrd/y^2', symbol='yrdy⁻²') -feet_per_second = NamedUnit(0.3048, Dimensions(length=1, time=-1), name='feet_per_second', ascii_symbol='ft/s', symbol='fts⁻¹') -feet_per_square_second = NamedUnit(0.3048, Dimensions(length=1, time=-2), name='feet_per_square_second', ascii_symbol='ft/s^2', symbol='fts⁻²') -feet_per_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-1), name='feet_per_millisecond', ascii_symbol='ft/ms', symbol='ftms⁻¹') -feet_per_square_millisecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-2), name='feet_per_square_millisecond', ascii_symbol='ft/ms^2', symbol='ftms⁻²') -feet_per_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-1), name='feet_per_microsecond', ascii_symbol='ft/us', symbol='ftµs⁻¹') -feet_per_square_microsecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-2), name='feet_per_square_microsecond', ascii_symbol='ft/us^2', symbol='ftµs⁻²') -feet_per_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-1), name='feet_per_nanosecond', ascii_symbol='ft/ns', symbol='ftns⁻¹') -feet_per_square_nanosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-2), name='feet_per_square_nanosecond', ascii_symbol='ft/ns^2', symbol='ftns⁻²') -feet_per_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-1), name='feet_per_picosecond', ascii_symbol='ft/ps', symbol='ftps⁻¹') -feet_per_square_picosecond = NamedUnit(3.048e+23, Dimensions(length=1, time=-2), name='feet_per_square_picosecond', ascii_symbol='ft/ps^2', symbol='ftps⁻²') -feet_per_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-1), name='feet_per_femtosecond', ascii_symbol='ft/fs', symbol='ftfs⁻¹') -feet_per_square_femtosecond = NamedUnit(3.048e+29, Dimensions(length=1, time=-2), name='feet_per_square_femtosecond', ascii_symbol='ft/fs^2', symbol='ftfs⁻²') -feet_per_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-1), name='feet_per_attosecond', ascii_symbol='ft/as', symbol='ftas⁻¹') -feet_per_square_attosecond = NamedUnit(3.0479999999999997e+35, Dimensions(length=1, time=-2), name='feet_per_square_attosecond', ascii_symbol='ft/as^2', symbol='ftas⁻²') -feet_per_minute = NamedUnit(0.00508, Dimensions(length=1, time=-1), name='feet_per_minute', ascii_symbol='ft/min', symbol='ftmin⁻¹') -feet_per_square_minute = NamedUnit(8.466666666666667e-05, Dimensions(length=1, time=-2), name='feet_per_square_minute', ascii_symbol='ft/min^2', symbol='ftmin⁻²') -feet_per_hour = NamedUnit(0.0008466666666666667, Dimensions(length=1, time=-1), name='feet_per_hour', ascii_symbol='ft/h', symbol='fth⁻¹') -feet_per_square_hour = NamedUnit(2.351851851851852e-06, Dimensions(length=1, time=-2), name='feet_per_square_hour', ascii_symbol='ft/h^2', symbol='fth⁻²') -feet_per_day = NamedUnit(3.527777777777778e-05, Dimensions(length=1, time=-1), name='feet_per_day', ascii_symbol='ft/d', symbol='ftd⁻¹') -feet_per_square_day = NamedUnit(4.083076131687243e-09, Dimensions(length=1, time=-2), name='feet_per_square_day', ascii_symbol='ft/d^2', symbol='ftd⁻²') -feet_per_year = NamedUnit(9.658727496876123e-08, Dimensions(length=1, time=-1), name='feet_per_year', ascii_symbol='ft/y', symbol='fty⁻¹') -feet_per_square_year = NamedUnit(3.060728899570568e-14, Dimensions(length=1, time=-2), name='feet_per_square_year', ascii_symbol='ft/y^2', symbol='fty⁻²') -inches_per_second = NamedUnit(0.0254, Dimensions(length=1, time=-1), name='inches_per_second', ascii_symbol='in/s', symbol='ins⁻¹') -inches_per_square_second = NamedUnit(0.0254, Dimensions(length=1, time=-2), name='inches_per_square_second', ascii_symbol='in/s^2', symbol='ins⁻²') -inches_per_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-1), name='inches_per_millisecond', ascii_symbol='in/ms', symbol='inms⁻¹') -inches_per_square_millisecond = NamedUnit(25400.0, Dimensions(length=1, time=-2), name='inches_per_square_millisecond', ascii_symbol='in/ms^2', symbol='inms⁻²') -inches_per_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-1), name='inches_per_microsecond', ascii_symbol='in/us', symbol='inµs⁻¹') -inches_per_square_microsecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-2), name='inches_per_square_microsecond', ascii_symbol='in/us^2', symbol='inµs⁻²') -inches_per_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-1), name='inches_per_nanosecond', ascii_symbol='in/ns', symbol='inns⁻¹') -inches_per_square_nanosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-2), name='inches_per_square_nanosecond', ascii_symbol='in/ns^2', symbol='inns⁻²') -inches_per_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-1), name='inches_per_picosecond', ascii_symbol='in/ps', symbol='inps⁻¹') -inches_per_square_picosecond = NamedUnit(2.54e+22, Dimensions(length=1, time=-2), name='inches_per_square_picosecond', ascii_symbol='in/ps^2', symbol='inps⁻²') -inches_per_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-1), name='inches_per_femtosecond', ascii_symbol='in/fs', symbol='infs⁻¹') -inches_per_square_femtosecond = NamedUnit(2.54e+28, Dimensions(length=1, time=-2), name='inches_per_square_femtosecond', ascii_symbol='in/fs^2', symbol='infs⁻²') -inches_per_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-1), name='inches_per_attosecond', ascii_symbol='in/as', symbol='inas⁻¹') -inches_per_square_attosecond = NamedUnit(2.5399999999999998e+34, Dimensions(length=1, time=-2), name='inches_per_square_attosecond', ascii_symbol='in/as^2', symbol='inas⁻²') -inches_per_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-1), name='inches_per_minute', ascii_symbol='in/min', symbol='inmin⁻¹') -inches_per_square_minute = NamedUnit(7.055555555555555e-06, Dimensions(length=1, time=-2), name='inches_per_square_minute', ascii_symbol='in/min^2', symbol='inmin⁻²') -inches_per_hour = NamedUnit(7.055555555555556e-05, Dimensions(length=1, time=-1), name='inches_per_hour', ascii_symbol='in/h', symbol='inh⁻¹') -inches_per_square_hour = NamedUnit(1.9598765432098765e-07, Dimensions(length=1, time=-2), name='inches_per_square_hour', ascii_symbol='in/h^2', symbol='inh⁻²') -inches_per_day = NamedUnit(2.9398148148148147e-06, Dimensions(length=1, time=-1), name='inches_per_day', ascii_symbol='in/d', symbol='ind⁻¹') -inches_per_square_day = NamedUnit(3.4025634430727023e-10, Dimensions(length=1, time=-2), name='inches_per_square_day', ascii_symbol='in/d^2', symbol='ind⁻²') -inches_per_year = NamedUnit(8.048939580730103e-09, Dimensions(length=1, time=-1), name='inches_per_year', ascii_symbol='in/y', symbol='iny⁻¹') -inches_per_square_year = NamedUnit(2.5506074163088065e-15, Dimensions(length=1, time=-2), name='inches_per_square_year', ascii_symbol='in/y^2', symbol='iny⁻²') -grams_per_cubic_meter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='gm⁻³') -exagrams_per_cubic_meter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='Egm⁻³') -petagrams_per_cubic_meter = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='Pgm⁻³') -teragrams_per_cubic_meter = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='Tgm⁻³') -gigagrams_per_cubic_meter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='Ggm⁻³') -megagrams_per_cubic_meter = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='Mgm⁻³') -kilograms_per_cubic_meter = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgm⁻³') -milligrams_per_cubic_meter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgm⁻³') -micrograms_per_cubic_meter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgm⁻³') -nanograms_per_cubic_meter = NamedUnit(1.0000000000000002e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngm⁻³') -picograms_per_cubic_meter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgm⁻³') -femtograms_per_cubic_meter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgm⁻³') -attograms_per_cubic_meter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agm⁻³') -atomic_mass_units_per_cubic_meter = NamedUnit(1.660538921e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='aum⁻³') -pounds_per_cubic_meter = NamedUnit(0.45359237, Dimensions(length=-3, mass=1), name='pounds_per_cubic_meter', ascii_symbol='lb m^-3', symbol='lbm⁻³') -ounces_per_cubic_meter = NamedUnit(0.028349523125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_meter', ascii_symbol='oz m^-3', symbol='ozm⁻³') -grams_per_cubic_exameter = NamedUnit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='gEm⁻³') -exagrams_per_cubic_exameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') -petagrams_per_cubic_exameter = NamedUnit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') -teragrams_per_cubic_exameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') -gigagrams_per_cubic_exameter = NamedUnit(1e-48, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_exameter', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') -megagrams_per_cubic_exameter = NamedUnit(9.999999999999999e-52, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_exameter', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') -kilograms_per_cubic_exameter = NamedUnit(9.999999999999999e-55, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_exameter', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') -milligrams_per_cubic_exameter = NamedUnit(9.999999999999998e-61, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_exameter', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') -micrograms_per_cubic_exameter = NamedUnit(9.999999999999999e-64, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_exameter', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') -nanograms_per_cubic_exameter = NamedUnit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_exameter', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') -picograms_per_cubic_exameter = NamedUnit(1e-69, Dimensions(length=-3, mass=1), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') -femtograms_per_cubic_exameter = NamedUnit(1e-72, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') -attograms_per_cubic_exameter = NamedUnit(1e-75, Dimensions(length=-3, mass=1), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -atomic_mass_units_per_cubic_exameter = NamedUnit(1.6605389209999996e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='auEm⁻³') -pounds_per_cubic_exameter = NamedUnit(4.5359237e-55, Dimensions(length=-3, mass=1), name='pounds_per_cubic_exameter', ascii_symbol='lb Em^-3', symbol='lbEm⁻³') -ounces_per_cubic_exameter = NamedUnit(2.8349523125e-56, Dimensions(length=-3, mass=1), name='ounces_per_cubic_exameter', ascii_symbol='oz Em^-3', symbol='ozEm⁻³') -grams_per_cubic_petameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='gPm⁻³') -exagrams_per_cubic_petameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') -petagrams_per_cubic_petameter = NamedUnit(1e-33, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') -teragrams_per_cubic_petameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') -gigagrams_per_cubic_petameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_petameter', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') -megagrams_per_cubic_petameter = NamedUnit(1e-42, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_petameter', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') -kilograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-45, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_petameter', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') -milligrams_per_cubic_petameter = NamedUnit(1e-51, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_petameter', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') -micrograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-54, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_petameter', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') -nanograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-57, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_petameter', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') -picograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-60, Dimensions(length=-3, mass=1), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') -femtograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-63, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') -attograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -atomic_mass_units_per_cubic_petameter = NamedUnit(1.660538921e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='auPm⁻³') -pounds_per_cubic_petameter = NamedUnit(4.5359237000000005e-46, Dimensions(length=-3, mass=1), name='pounds_per_cubic_petameter', ascii_symbol='lb Pm^-3', symbol='lbPm⁻³') -ounces_per_cubic_petameter = NamedUnit(2.8349523125000003e-47, Dimensions(length=-3, mass=1), name='ounces_per_cubic_petameter', ascii_symbol='oz Pm^-3', symbol='ozPm⁻³') -grams_per_cubic_terameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='gTm⁻³') -exagrams_per_cubic_terameter = NamedUnit(1e-21, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') -petagrams_per_cubic_terameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') -teragrams_per_cubic_terameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') -gigagrams_per_cubic_terameter = NamedUnit(9.999999999999999e-31, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_terameter', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') -megagrams_per_cubic_terameter = NamedUnit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_terameter', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') -kilograms_per_cubic_terameter = NamedUnit(1e-36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_terameter', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') -milligrams_per_cubic_terameter = NamedUnit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_terameter', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') -micrograms_per_cubic_terameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_terameter', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') -nanograms_per_cubic_terameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_terameter', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') -picograms_per_cubic_terameter = NamedUnit(1e-51, Dimensions(length=-3, mass=1), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') -femtograms_per_cubic_terameter = NamedUnit(1e-54, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') -attograms_per_cubic_terameter = NamedUnit(1.0000000000000001e-57, Dimensions(length=-3, mass=1), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -atomic_mass_units_per_cubic_terameter = NamedUnit(1.6605389209999997e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='auTm⁻³') -pounds_per_cubic_terameter = NamedUnit(4.5359237e-37, Dimensions(length=-3, mass=1), name='pounds_per_cubic_terameter', ascii_symbol='lb Tm^-3', symbol='lbTm⁻³') -ounces_per_cubic_terameter = NamedUnit(2.8349523125e-38, Dimensions(length=-3, mass=1), name='ounces_per_cubic_terameter', ascii_symbol='oz Tm^-3', symbol='ozTm⁻³') -grams_per_cubic_gigameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='gGm⁻³') -exagrams_per_cubic_gigameter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') -petagrams_per_cubic_gigameter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') -teragrams_per_cubic_gigameter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') -gigagrams_per_cubic_gigameter = NamedUnit(1e-21, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_gigameter', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') -megagrams_per_cubic_gigameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_gigameter', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') -kilograms_per_cubic_gigameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_gigameter', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') -milligrams_per_cubic_gigameter = NamedUnit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_gigameter', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') -micrograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_gigameter', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') -nanograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_gigameter', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') -picograms_per_cubic_gigameter = NamedUnit(1e-42, Dimensions(length=-3, mass=1), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') -femtograms_per_cubic_gigameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') -attograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -atomic_mass_units_per_cubic_gigameter = NamedUnit(1.660538921e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='auGm⁻³') -pounds_per_cubic_gigameter = NamedUnit(4.5359237e-28, Dimensions(length=-3, mass=1), name='pounds_per_cubic_gigameter', ascii_symbol='lb Gm^-3', symbol='lbGm⁻³') -ounces_per_cubic_gigameter = NamedUnit(2.8349523125e-29, Dimensions(length=-3, mass=1), name='ounces_per_cubic_gigameter', ascii_symbol='oz Gm^-3', symbol='ozGm⁻³') -grams_per_cubic_megameter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='gMm⁻³') -exagrams_per_cubic_megameter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') -petagrams_per_cubic_megameter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') -teragrams_per_cubic_megameter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') -gigagrams_per_cubic_megameter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_megameter', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') -megagrams_per_cubic_megameter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_megameter', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') -kilograms_per_cubic_megameter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_megameter', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') -milligrams_per_cubic_megameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_megameter', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') -micrograms_per_cubic_megameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_megameter', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') -nanograms_per_cubic_megameter = NamedUnit(1.0000000000000003e-30, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_megameter', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') -picograms_per_cubic_megameter = NamedUnit(1e-33, Dimensions(length=-3, mass=1), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') -femtograms_per_cubic_megameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') -attograms_per_cubic_megameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -atomic_mass_units_per_cubic_megameter = NamedUnit(1.6605389209999997e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='auMm⁻³') -pounds_per_cubic_megameter = NamedUnit(4.535923700000001e-19, Dimensions(length=-3, mass=1), name='pounds_per_cubic_megameter', ascii_symbol='lb Mm^-3', symbol='lbMm⁻³') -ounces_per_cubic_megameter = NamedUnit(2.8349523125000004e-20, Dimensions(length=-3, mass=1), name='ounces_per_cubic_megameter', ascii_symbol='oz Mm^-3', symbol='ozMm⁻³') -grams_per_cubic_kilometer = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='gkm⁻³') -exagrams_per_cubic_kilometer = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') -petagrams_per_cubic_kilometer = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') -teragrams_per_cubic_kilometer = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') -gigagrams_per_cubic_kilometer = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_kilometer', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') -megagrams_per_cubic_kilometer = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_kilometer', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') -kilograms_per_cubic_kilometer = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_kilometer', ascii_symbol='kg km^-3', symbol='kgkm⁻³') -milligrams_per_cubic_kilometer = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_kilometer', ascii_symbol='mg km^-3', symbol='mgkm⁻³') -micrograms_per_cubic_kilometer = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_kilometer', ascii_symbol='ug km^-3', symbol='µgkm⁻³') -nanograms_per_cubic_kilometer = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_kilometer', ascii_symbol='ng km^-3', symbol='ngkm⁻³') -picograms_per_cubic_kilometer = NamedUnit(1.0000000000000001e-24, Dimensions(length=-3, mass=1), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') -femtograms_per_cubic_kilometer = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') -attograms_per_cubic_kilometer = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') -atomic_mass_units_per_cubic_kilometer = NamedUnit(1.6605389209999997e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='aukm⁻³') -pounds_per_cubic_kilometer = NamedUnit(4.5359237000000004e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_kilometer', ascii_symbol='lb km^-3', symbol='lbkm⁻³') -ounces_per_cubic_kilometer = NamedUnit(2.8349523125000003e-11, Dimensions(length=-3, mass=1), name='ounces_per_cubic_kilometer', ascii_symbol='oz km^-3', symbol='ozkm⁻³') -grams_per_cubic_millimeter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='gmm⁻³') -exagrams_per_cubic_millimeter = NamedUnit(1e+24, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') -petagrams_per_cubic_millimeter = NamedUnit(1e+21, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') -teragrams_per_cubic_millimeter = NamedUnit(1e+18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') -gigagrams_per_cubic_millimeter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_millimeter', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') -megagrams_per_cubic_millimeter = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_millimeter', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') -kilograms_per_cubic_millimeter = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_millimeter', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') -milligrams_per_cubic_millimeter = NamedUnit(999.9999999999999, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_millimeter', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') -micrograms_per_cubic_millimeter = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_millimeter', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') -nanograms_per_cubic_millimeter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_millimeter', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') -picograms_per_cubic_millimeter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') -femtograms_per_cubic_millimeter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') -attograms_per_cubic_millimeter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -atomic_mass_units_per_cubic_millimeter = NamedUnit(1.6605389209999997e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='aumm⁻³') -pounds_per_cubic_millimeter = NamedUnit(453592370.0, Dimensions(length=-3, mass=1), name='pounds_per_cubic_millimeter', ascii_symbol='lb mm^-3', symbol='lbmm⁻³') -ounces_per_cubic_millimeter = NamedUnit(28349523.125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_millimeter', ascii_symbol='oz mm^-3', symbol='ozmm⁻³') -grams_per_cubic_micrometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='gµm⁻³') -exagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') -petagrams_per_cubic_micrometer = NamedUnit(1.0000000000000002e+30, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') -teragrams_per_cubic_micrometer = NamedUnit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') -gigagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+24, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_micrometer', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') -megagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_micrometer', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') -kilograms_per_cubic_micrometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_micrometer', ascii_symbol='kg um^-3', symbol='kgµm⁻³') -milligrams_per_cubic_micrometer = NamedUnit(1000000000000.0001, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_micrometer', ascii_symbol='mg um^-3', symbol='mgµm⁻³') -micrograms_per_cubic_micrometer = NamedUnit(1000000000.0000002, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_micrometer', ascii_symbol='ug um^-3', symbol='µgµm⁻³') -nanograms_per_cubic_micrometer = NamedUnit(1000000.0000000003, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_micrometer', ascii_symbol='ng um^-3', symbol='ngµm⁻³') -picograms_per_cubic_micrometer = NamedUnit(1000.0000000000002, Dimensions(length=-3, mass=1), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') -femtograms_per_cubic_micrometer = NamedUnit(1.0000000000000002, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') -attograms_per_cubic_micrometer = NamedUnit(0.0010000000000000002, Dimensions(length=-3, mass=1), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') -atomic_mass_units_per_cubic_micrometer = NamedUnit(1.660538921e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='auµm⁻³') -pounds_per_cubic_micrometer = NamedUnit(4.5359237000000006e+17, Dimensions(length=-3, mass=1), name='pounds_per_cubic_micrometer', ascii_symbol='lb um^-3', symbol='lbµm⁻³') -ounces_per_cubic_micrometer = NamedUnit(2.8349523125000004e+16, Dimensions(length=-3, mass=1), name='ounces_per_cubic_micrometer', ascii_symbol='oz um^-3', symbol='ozµm⁻³') -grams_per_cubic_nanometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='gnm⁻³') -exagrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') -petagrams_per_cubic_nanometer = NamedUnit(9.999999999999998e+38, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') -teragrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') -gigagrams_per_cubic_nanometer = NamedUnit(9.999999999999998e+32, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_nanometer', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') -megagrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_nanometer', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') -kilograms_per_cubic_nanometer = NamedUnit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_nanometer', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') -milligrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+20, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_nanometer', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') -micrograms_per_cubic_nanometer = NamedUnit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_nanometer', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') -nanograms_per_cubic_nanometer = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_nanometer', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') -picograms_per_cubic_nanometer = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') -femtograms_per_cubic_nanometer = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') -attograms_per_cubic_nanometer = NamedUnit(999999.9999999999, Dimensions(length=-3, mass=1), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -atomic_mass_units_per_cubic_nanometer = NamedUnit(1.6605389209999994, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='aunm⁻³') -pounds_per_cubic_nanometer = NamedUnit(4.535923699999999e+26, Dimensions(length=-3, mass=1), name='pounds_per_cubic_nanometer', ascii_symbol='lb nm^-3', symbol='lbnm⁻³') -ounces_per_cubic_nanometer = NamedUnit(2.8349523124999993e+25, Dimensions(length=-3, mass=1), name='ounces_per_cubic_nanometer', ascii_symbol='oz nm^-3', symbol='oznm⁻³') -grams_per_cubic_picometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='gpm⁻³') -exagrams_per_cubic_picometer = NamedUnit(1e+51, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') -petagrams_per_cubic_picometer = NamedUnit(1e+48, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') -teragrams_per_cubic_picometer = NamedUnit(1.0000000000000001e+45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') -gigagrams_per_cubic_picometer = NamedUnit(1e+42, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_picometer', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') -megagrams_per_cubic_picometer = NamedUnit(1.0000000000000001e+39, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_picometer', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') -kilograms_per_cubic_picometer = NamedUnit(1e+36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_picometer', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') -milligrams_per_cubic_picometer = NamedUnit(1e+30, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_picometer', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') -micrograms_per_cubic_picometer = NamedUnit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_picometer', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') -nanograms_per_cubic_picometer = NamedUnit(1.0000000000000003e+24, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_picometer', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') -picograms_per_cubic_picometer = NamedUnit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') -femtograms_per_cubic_picometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') -attograms_per_cubic_picometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -atomic_mass_units_per_cubic_picometer = NamedUnit(1660538921.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='aupm⁻³') -pounds_per_cubic_picometer = NamedUnit(4.5359237000000005e+35, Dimensions(length=-3, mass=1), name='pounds_per_cubic_picometer', ascii_symbol='lb pm^-3', symbol='lbpm⁻³') -ounces_per_cubic_picometer = NamedUnit(2.8349523125000003e+34, Dimensions(length=-3, mass=1), name='ounces_per_cubic_picometer', ascii_symbol='oz pm^-3', symbol='ozpm⁻³') -grams_per_cubic_femtometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='gfm⁻³') -exagrams_per_cubic_femtometer = NamedUnit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') -petagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+56, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') -teragrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+53, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') -gigagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+50, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_femtometer', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') -megagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_femtometer', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') -kilograms_per_cubic_femtometer = NamedUnit(9.999999999999998e+44, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_femtometer', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') -milligrams_per_cubic_femtometer = NamedUnit(9.999999999999996e+38, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_femtometer', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') -micrograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_femtometer', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') -nanograms_per_cubic_femtometer = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_femtometer', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') -picograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') -femtograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') -attograms_per_cubic_femtometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -atomic_mass_units_per_cubic_femtometer = NamedUnit(1.6605389209999992e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='aufm⁻³') -pounds_per_cubic_femtometer = NamedUnit(4.5359236999999985e+44, Dimensions(length=-3, mass=1), name='pounds_per_cubic_femtometer', ascii_symbol='lb fm^-3', symbol='lbfm⁻³') -ounces_per_cubic_femtometer = NamedUnit(2.834952312499999e+43, Dimensions(length=-3, mass=1), name='ounces_per_cubic_femtometer', ascii_symbol='oz fm^-3', symbol='ozfm⁻³') -grams_per_cubic_attometer = NamedUnit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='gam⁻³') -exagrams_per_cubic_attometer = NamedUnit(9.999999999999999e+68, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') -petagrams_per_cubic_attometer = NamedUnit(9.999999999999998e+65, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') -teragrams_per_cubic_attometer = NamedUnit(9.999999999999999e+62, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') -gigagrams_per_cubic_attometer = NamedUnit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_attometer', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') -megagrams_per_cubic_attometer = NamedUnit(9.999999999999999e+56, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_attometer', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') -kilograms_per_cubic_attometer = NamedUnit(9.999999999999999e+53, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_attometer', ascii_symbol='kg am^-3', symbol='kgam⁻³') -milligrams_per_cubic_attometer = NamedUnit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_attometer', ascii_symbol='mg am^-3', symbol='mgam⁻³') -micrograms_per_cubic_attometer = NamedUnit(1e+45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_attometer', ascii_symbol='ug am^-3', symbol='µgam⁻³') -nanograms_per_cubic_attometer = NamedUnit(1e+42, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_attometer', ascii_symbol='ng am^-3', symbol='ngam⁻³') -picograms_per_cubic_attometer = NamedUnit(1e+39, Dimensions(length=-3, mass=1), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') -femtograms_per_cubic_attometer = NamedUnit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') -attograms_per_cubic_attometer = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') -atomic_mass_units_per_cubic_attometer = NamedUnit(1.6605389209999997e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='auam⁻³') -pounds_per_cubic_attometer = NamedUnit(4.5359237e+53, Dimensions(length=-3, mass=1), name='pounds_per_cubic_attometer', ascii_symbol='lb am^-3', symbol='lbam⁻³') -ounces_per_cubic_attometer = NamedUnit(2.8349523125e+52, Dimensions(length=-3, mass=1), name='ounces_per_cubic_attometer', ascii_symbol='oz am^-3', symbol='ozam⁻³') -grams_per_cubic_decimeter = NamedUnit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='gdm⁻³') -exagrams_per_cubic_decimeter = NamedUnit(9.999999999999997e+17, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_decimeter', ascii_symbol='Eg dm^-3', symbol='Egdm⁻³') -petagrams_per_cubic_decimeter = NamedUnit(999999999999999.8, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_decimeter', ascii_symbol='Pg dm^-3', symbol='Pgdm⁻³') -teragrams_per_cubic_decimeter = NamedUnit(999999999999.9998, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_decimeter', ascii_symbol='Tg dm^-3', symbol='Tgdm⁻³') -gigagrams_per_cubic_decimeter = NamedUnit(999999999.9999998, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_decimeter', ascii_symbol='Gg dm^-3', symbol='Ggdm⁻³') -megagrams_per_cubic_decimeter = NamedUnit(999999.9999999998, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_decimeter', ascii_symbol='Mg dm^-3', symbol='Mgdm⁻³') -kilograms_per_cubic_decimeter = NamedUnit(999.9999999999998, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_decimeter', ascii_symbol='kg dm^-3', symbol='kgdm⁻³') -milligrams_per_cubic_decimeter = NamedUnit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_decimeter', ascii_symbol='mg dm^-3', symbol='mgdm⁻³') -micrograms_per_cubic_decimeter = NamedUnit(9.999999999999997e-07, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_decimeter', ascii_symbol='ug dm^-3', symbol='µgdm⁻³') -nanograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_decimeter', ascii_symbol='ng dm^-3', symbol='ngdm⁻³') -picograms_per_cubic_decimeter = NamedUnit(9.999999999999998e-13, Dimensions(length=-3, mass=1), name='picograms_per_cubic_decimeter', ascii_symbol='pg dm^-3', symbol='pgdm⁻³') -femtograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_decimeter', ascii_symbol='fg dm^-3', symbol='fgdm⁻³') -attograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-19, Dimensions(length=-3, mass=1), name='attograms_per_cubic_decimeter', ascii_symbol='ag dm^-3', symbol='agdm⁻³') -atomic_mass_units_per_cubic_decimeter = NamedUnit(1.6605389209999993e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='audm⁻³') -pounds_per_cubic_decimeter = NamedUnit(453.5923699999999, Dimensions(length=-3, mass=1), name='pounds_per_cubic_decimeter', ascii_symbol='lb dm^-3', symbol='lbdm⁻³') -ounces_per_cubic_decimeter = NamedUnit(28.349523124999994, Dimensions(length=-3, mass=1), name='ounces_per_cubic_decimeter', ascii_symbol='oz dm^-3', symbol='ozdm⁻³') -grams_per_cubic_centimeter = NamedUnit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='gcm⁻³') -exagrams_per_cubic_centimeter = NamedUnit(9.999999999999999e+20, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_centimeter', ascii_symbol='Eg cm^-3', symbol='Egcm⁻³') -petagrams_per_cubic_centimeter = NamedUnit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_centimeter', ascii_symbol='Pg cm^-3', symbol='Pgcm⁻³') -teragrams_per_cubic_centimeter = NamedUnit(999999999999999.9, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_centimeter', ascii_symbol='Tg cm^-3', symbol='Tgcm⁻³') -gigagrams_per_cubic_centimeter = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_centimeter', ascii_symbol='Gg cm^-3', symbol='Ggcm⁻³') -megagrams_per_cubic_centimeter = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_centimeter', ascii_symbol='Mg cm^-3', symbol='Mgcm⁻³') -kilograms_per_cubic_centimeter = NamedUnit(999999.9999999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_centimeter', ascii_symbol='kg cm^-3', symbol='kgcm⁻³') -milligrams_per_cubic_centimeter = NamedUnit(0.9999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_centimeter', ascii_symbol='mg cm^-3', symbol='mgcm⁻³') -micrograms_per_cubic_centimeter = NamedUnit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_centimeter', ascii_symbol='ug cm^-3', symbol='µgcm⁻³') -nanograms_per_cubic_centimeter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_centimeter', ascii_symbol='ng cm^-3', symbol='ngcm⁻³') -picograms_per_cubic_centimeter = NamedUnit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='picograms_per_cubic_centimeter', ascii_symbol='pg cm^-3', symbol='pgcm⁻³') -femtograms_per_cubic_centimeter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_centimeter', ascii_symbol='fg cm^-3', symbol='fgcm⁻³') -attograms_per_cubic_centimeter = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='attograms_per_cubic_centimeter', ascii_symbol='ag cm^-3', symbol='agcm⁻³') -atomic_mass_units_per_cubic_centimeter = NamedUnit(1.6605389209999996e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='aucm⁻³') -pounds_per_cubic_centimeter = NamedUnit(453592.36999999994, Dimensions(length=-3, mass=1), name='pounds_per_cubic_centimeter', ascii_symbol='lb cm^-3', symbol='lbcm⁻³') -ounces_per_cubic_centimeter = NamedUnit(28349.523124999996, Dimensions(length=-3, mass=1), name='ounces_per_cubic_centimeter', ascii_symbol='oz cm^-3', symbol='ozcm⁻³') -grams_per_cubic_angstrom = NamedUnit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='gÅ⁻³') -exagrams_per_cubic_angstrom = NamedUnit(1e+45, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') -petagrams_per_cubic_angstrom = NamedUnit(9.999999999999999e+41, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') -teragrams_per_cubic_angstrom = NamedUnit(1e+39, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') -gigagrams_per_cubic_angstrom = NamedUnit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_angstrom', ascii_symbol='Gg Ang^-3', symbol='GgÅ⁻³') -megagrams_per_cubic_angstrom = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_angstrom', ascii_symbol='Mg Ang^-3', symbol='MgÅ⁻³') -kilograms_per_cubic_angstrom = NamedUnit(9.999999999999999e+29, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_angstrom', ascii_symbol='kg Ang^-3', symbol='kgÅ⁻³') -milligrams_per_cubic_angstrom = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_angstrom', ascii_symbol='mg Ang^-3', symbol='mgÅ⁻³') -micrograms_per_cubic_angstrom = NamedUnit(1e+21, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_angstrom', ascii_symbol='ug Ang^-3', symbol='µgÅ⁻³') -nanograms_per_cubic_angstrom = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_angstrom', ascii_symbol='ng Ang^-3', symbol='ngÅ⁻³') -picograms_per_cubic_angstrom = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') -femtograms_per_cubic_angstrom = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') -attograms_per_cubic_angstrom = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') -atomic_mass_units_per_cubic_angstrom = NamedUnit(1660.5389209999996, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='auÅ⁻³') -pounds_per_cubic_angstrom = NamedUnit(4.5359237e+29, Dimensions(length=-3, mass=1), name='pounds_per_cubic_angstrom', ascii_symbol='lb Ang^-3', symbol='lbÅ⁻³') -ounces_per_cubic_angstrom = NamedUnit(2.8349523125e+28, Dimensions(length=-3, mass=1), name='ounces_per_cubic_angstrom', ascii_symbol='oz Ang^-3', symbol='ozÅ⁻³') -grams_per_cubic_mile = NamedUnit(2.399127585789277e-13, Dimensions(length=-3, mass=1), name='grams_per_cubic_mile', ascii_symbol='g miles^-3', symbol='gmiles⁻³') -exagrams_per_cubic_mile = NamedUnit(239912.7585789277, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_mile', ascii_symbol='Eg miles^-3', symbol='Egmiles⁻³') -petagrams_per_cubic_mile = NamedUnit(239.9127585789277, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_mile', ascii_symbol='Pg miles^-3', symbol='Pgmiles⁻³') -teragrams_per_cubic_mile = NamedUnit(0.2399127585789277, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_mile', ascii_symbol='Tg miles^-3', symbol='Tgmiles⁻³') -gigagrams_per_cubic_mile = NamedUnit(0.0002399127585789277, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_mile', ascii_symbol='Gg miles^-3', symbol='Ggmiles⁻³') -megagrams_per_cubic_mile = NamedUnit(2.399127585789277e-07, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_mile', ascii_symbol='Mg miles^-3', symbol='Mgmiles⁻³') -kilograms_per_cubic_mile = NamedUnit(2.399127585789277e-10, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_mile', ascii_symbol='kg miles^-3', symbol='kgmiles⁻³') -milligrams_per_cubic_mile = NamedUnit(2.399127585789277e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_mile', ascii_symbol='mg miles^-3', symbol='mgmiles⁻³') -micrograms_per_cubic_mile = NamedUnit(2.3991275857892774e-19, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_mile', ascii_symbol='ug miles^-3', symbol='µgmiles⁻³') -nanograms_per_cubic_mile = NamedUnit(2.3991275857892774e-22, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_mile', ascii_symbol='ng miles^-3', symbol='ngmiles⁻³') -picograms_per_cubic_mile = NamedUnit(2.399127585789277e-25, Dimensions(length=-3, mass=1), name='picograms_per_cubic_mile', ascii_symbol='pg miles^-3', symbol='pgmiles⁻³') -femtograms_per_cubic_mile = NamedUnit(2.3991275857892772e-28, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_mile', ascii_symbol='fg miles^-3', symbol='fgmiles⁻³') -attograms_per_cubic_mile = NamedUnit(2.399127585789277e-31, Dimensions(length=-3, mass=1), name='attograms_per_cubic_mile', ascii_symbol='ag miles^-3', symbol='agmiles⁻³') -atomic_mass_units_per_cubic_mile = NamedUnit(3.98384473264786e-37, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_mile', ascii_symbol='au miles^-3', symbol='aumiles⁻³') -pounds_per_cubic_mile = NamedUnit(1.0882259675705365e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_mile', ascii_symbol='lb miles^-3', symbol='lbmiles⁻³') -ounces_per_cubic_mile = NamedUnit(6.801412297315853e-12, Dimensions(length=-3, mass=1), name='ounces_per_cubic_mile', ascii_symbol='oz miles^-3', symbol='ozmiles⁻³') -grams_per_cubic_yard = NamedUnit(0.0013079506193143919, Dimensions(length=-3, mass=1), name='grams_per_cubic_yard', ascii_symbol='g yrd^-3', symbol='gyrd⁻³') -exagrams_per_cubic_yard = NamedUnit(1307950619314391.8, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_yard', ascii_symbol='Eg yrd^-3', symbol='Egyrd⁻³') -petagrams_per_cubic_yard = NamedUnit(1307950619314.3918, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_yard', ascii_symbol='Pg yrd^-3', symbol='Pgyrd⁻³') -teragrams_per_cubic_yard = NamedUnit(1307950619.3143919, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_yard', ascii_symbol='Tg yrd^-3', symbol='Tgyrd⁻³') -gigagrams_per_cubic_yard = NamedUnit(1307950.6193143919, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_yard', ascii_symbol='Gg yrd^-3', symbol='Ggyrd⁻³') -megagrams_per_cubic_yard = NamedUnit(1307.9506193143918, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_yard', ascii_symbol='Mg yrd^-3', symbol='Mgyrd⁻³') -kilograms_per_cubic_yard = NamedUnit(1.3079506193143917, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_yard', ascii_symbol='kg yrd^-3', symbol='kgyrd⁻³') -milligrams_per_cubic_yard = NamedUnit(1.3079506193143917e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_yard', ascii_symbol='mg yrd^-3', symbol='mgyrd⁻³') -micrograms_per_cubic_yard = NamedUnit(1.3079506193143919e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_yard', ascii_symbol='ug yrd^-3', symbol='µgyrd⁻³') -nanograms_per_cubic_yard = NamedUnit(1.307950619314392e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_yard', ascii_symbol='ng yrd^-3', symbol='ngyrd⁻³') -picograms_per_cubic_yard = NamedUnit(1.3079506193143919e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_yard', ascii_symbol='pg yrd^-3', symbol='pgyrd⁻³') -femtograms_per_cubic_yard = NamedUnit(1.3079506193143918e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_yard', ascii_symbol='fg yrd^-3', symbol='fgyrd⁻³') -attograms_per_cubic_yard = NamedUnit(1.307950619314392e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_yard', ascii_symbol='ag yrd^-3', symbol='agyrd⁻³') -atomic_mass_units_per_cubic_yard = NamedUnit(2.1719029101176016e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_yard', ascii_symbol='au yrd^-3', symbol='auyrd⁻³') -pounds_per_cubic_yard = NamedUnit(0.5932764212577828, Dimensions(length=-3, mass=1), name='pounds_per_cubic_yard', ascii_symbol='lb yrd^-3', symbol='lbyrd⁻³') -ounces_per_cubic_yard = NamedUnit(0.037079776328611425, Dimensions(length=-3, mass=1), name='ounces_per_cubic_yard', ascii_symbol='oz yrd^-3', symbol='ozyrd⁻³') -grams_per_cubic_foot = NamedUnit(0.035314666721488586, Dimensions(length=-3, mass=1), name='grams_per_cubic_foot', ascii_symbol='g ft^-3', symbol='gft⁻³') -exagrams_per_cubic_foot = NamedUnit(3.5314666721488584e+16, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_foot', ascii_symbol='Eg ft^-3', symbol='Egft⁻³') -petagrams_per_cubic_foot = NamedUnit(35314666721488.586, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_foot', ascii_symbol='Pg ft^-3', symbol='Pgft⁻³') -teragrams_per_cubic_foot = NamedUnit(35314666721.48859, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_foot', ascii_symbol='Tg ft^-3', symbol='Tgft⁻³') -gigagrams_per_cubic_foot = NamedUnit(35314666.72148859, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_foot', ascii_symbol='Gg ft^-3', symbol='Ggft⁻³') -megagrams_per_cubic_foot = NamedUnit(35314.66672148858, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_foot', ascii_symbol='Mg ft^-3', symbol='Mgft⁻³') -kilograms_per_cubic_foot = NamedUnit(35.314666721488585, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_foot', ascii_symbol='kg ft^-3', symbol='kgft⁻³') -milligrams_per_cubic_foot = NamedUnit(3.5314666721488586e-05, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_foot', ascii_symbol='mg ft^-3', symbol='mgft⁻³') -micrograms_per_cubic_foot = NamedUnit(3.5314666721488584e-08, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_foot', ascii_symbol='ug ft^-3', symbol='µgft⁻³') -nanograms_per_cubic_foot = NamedUnit(3.531466672148859e-11, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_foot', ascii_symbol='ng ft^-3', symbol='ngft⁻³') -picograms_per_cubic_foot = NamedUnit(3.531466672148859e-14, Dimensions(length=-3, mass=1), name='picograms_per_cubic_foot', ascii_symbol='pg ft^-3', symbol='pgft⁻³') -femtograms_per_cubic_foot = NamedUnit(3.5314666721488585e-17, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_foot', ascii_symbol='fg ft^-3', symbol='fgft⁻³') -attograms_per_cubic_foot = NamedUnit(3.531466672148859e-20, Dimensions(length=-3, mass=1), name='attograms_per_cubic_foot', ascii_symbol='ag ft^-3', symbol='agft⁻³') -atomic_mass_units_per_cubic_foot = NamedUnit(5.864137857317526e-26, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_foot', ascii_symbol='au ft^-3', symbol='auft⁻³') -pounds_per_cubic_foot = NamedUnit(16.018463373960138, Dimensions(length=-3, mass=1), name='pounds_per_cubic_foot', ascii_symbol='lb ft^-3', symbol='lbft⁻³') -ounces_per_cubic_foot = NamedUnit(1.0011539608725086, Dimensions(length=-3, mass=1), name='ounces_per_cubic_foot', ascii_symbol='oz ft^-3', symbol='ozft⁻³') -grams_per_cubic_inch = NamedUnit(61.02374409473229, Dimensions(length=-3, mass=1), name='grams_per_cubic_inch', ascii_symbol='g in^-3', symbol='gin⁻³') -exagrams_per_cubic_inch = NamedUnit(6.102374409473229e+19, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_inch', ascii_symbol='Eg in^-3', symbol='Egin⁻³') -petagrams_per_cubic_inch = NamedUnit(6.102374409473229e+16, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_inch', ascii_symbol='Pg in^-3', symbol='Pgin⁻³') -teragrams_per_cubic_inch = NamedUnit(61023744094732.29, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_inch', ascii_symbol='Tg in^-3', symbol='Tgin⁻³') -gigagrams_per_cubic_inch = NamedUnit(61023744094.732285, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_inch', ascii_symbol='Gg in^-3', symbol='Ggin⁻³') -megagrams_per_cubic_inch = NamedUnit(61023744.094732285, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_inch', ascii_symbol='Mg in^-3', symbol='Mgin⁻³') -kilograms_per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_inch', ascii_symbol='kg in^-3', symbol='kgin⁻³') -milligrams_per_cubic_inch = NamedUnit(0.06102374409473228, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_inch', ascii_symbol='mg in^-3', symbol='mgin⁻³') -micrograms_per_cubic_inch = NamedUnit(6.102374409473229e-05, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_inch', ascii_symbol='ug in^-3', symbol='µgin⁻³') -nanograms_per_cubic_inch = NamedUnit(6.10237440947323e-08, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_inch', ascii_symbol='ng in^-3', symbol='ngin⁻³') -picograms_per_cubic_inch = NamedUnit(6.102374409473229e-11, Dimensions(length=-3, mass=1), name='picograms_per_cubic_inch', ascii_symbol='pg in^-3', symbol='pgin⁻³') -femtograms_per_cubic_inch = NamedUnit(6.10237440947323e-14, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_inch', ascii_symbol='fg in^-3', symbol='fgin⁻³') -attograms_per_cubic_inch = NamedUnit(6.10237440947323e-17, Dimensions(length=-3, mass=1), name='attograms_per_cubic_inch', ascii_symbol='ag in^-3', symbol='agin⁻³') -atomic_mass_units_per_cubic_inch = NamedUnit(1.0133230217444687e-22, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_inch', ascii_symbol='au in^-3', symbol='auin⁻³') -pounds_per_cubic_inch = NamedUnit(27679.904710203125, Dimensions(length=-3, mass=1), name='pounds_per_cubic_inch', ascii_symbol='lb in^-3', symbol='lbin⁻³') -ounces_per_cubic_inch = NamedUnit(1729.9940443876953, Dimensions(length=-3, mass=1), name='ounces_per_cubic_inch', ascii_symbol='oz in^-3', symbol='ozin⁻³') -moles_per_cubic_meter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='molm⁻³') -millimoles_per_cubic_meter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolm⁻³') -micromoles_per_cubic_meter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolm⁻³') -nanomoles_per_cubic_meter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolm⁻³') -picomoles_per_cubic_meter = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolm⁻³') -femtomoles_per_cubic_meter = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolm⁻³') -attomoles_per_cubic_meter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolm⁻³') -moles_per_cubic_exameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='molEm⁻³') -millimoles_per_cubic_exameter = NamedUnit(6.02214076e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') -micromoles_per_cubic_exameter = NamedUnit(6.02214076e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') -nanomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') -picomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') -femtomoles_per_cubic_exameter = NamedUnit(6.02214076e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') -attomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') -moles_per_cubic_petameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='molPm⁻³') -millimoles_per_cubic_petameter = NamedUnit(6.0221407600000005e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') -micromoles_per_cubic_petameter = NamedUnit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') -nanomoles_per_cubic_petameter = NamedUnit(6.02214076e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') -picomoles_per_cubic_petameter = NamedUnit(6.0221407600000005e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') -femtomoles_per_cubic_petameter = NamedUnit(6.022140760000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') -attomoles_per_cubic_petameter = NamedUnit(6.022140760000001e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') -moles_per_cubic_terameter = NamedUnit(6.022140759999999e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='molTm⁻³') -millimoles_per_cubic_terameter = NamedUnit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') -micromoles_per_cubic_terameter = NamedUnit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') -nanomoles_per_cubic_terameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') -picomoles_per_cubic_terameter = NamedUnit(6.02214076e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') -femtomoles_per_cubic_terameter = NamedUnit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') -attomoles_per_cubic_terameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') -moles_per_cubic_gigameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='molGm⁻³') -millimoles_per_cubic_gigameter = NamedUnit(6.022140760000001e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') -micromoles_per_cubic_gigameter = NamedUnit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') -nanomoles_per_cubic_gigameter = NamedUnit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') -picomoles_per_cubic_gigameter = NamedUnit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') -femtomoles_per_cubic_gigameter = NamedUnit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') -attomoles_per_cubic_gigameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') -moles_per_cubic_megameter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='molMm⁻³') -millimoles_per_cubic_megameter = NamedUnit(602.214076, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') -micromoles_per_cubic_megameter = NamedUnit(0.602214076, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') -nanomoles_per_cubic_megameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') -picomoles_per_cubic_megameter = NamedUnit(6.02214076e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') -femtomoles_per_cubic_megameter = NamedUnit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') -attomoles_per_cubic_megameter = NamedUnit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') -moles_per_cubic_kilometer = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='molkm⁻³') -millimoles_per_cubic_kilometer = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') -micromoles_per_cubic_kilometer = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') -nanomoles_per_cubic_kilometer = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') -picomoles_per_cubic_kilometer = NamedUnit(602.214076, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') -femtomoles_per_cubic_kilometer = NamedUnit(0.602214076, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') -attomoles_per_cubic_kilometer = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') -moles_per_cubic_millimeter = NamedUnit(6.0221407599999995e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='molmm⁻³') -millimoles_per_cubic_millimeter = NamedUnit(6.02214076e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') -micromoles_per_cubic_millimeter = NamedUnit(6.0221407599999996e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') -nanomoles_per_cubic_millimeter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') -picomoles_per_cubic_millimeter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') -femtomoles_per_cubic_millimeter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') -attomoles_per_cubic_millimeter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') -moles_per_cubic_micrometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='molµm⁻³') -millimoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') -micromoles_per_cubic_micrometer = NamedUnit(6.0221407600000004e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') -nanomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') -picomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') -femtomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') -attomoles_per_cubic_micrometer = NamedUnit(6.0221407600000005e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') -moles_per_cubic_nanometer = NamedUnit(6.022140759999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='molnm⁻³') -millimoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') -micromoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') -nanomoles_per_cubic_nanometer = NamedUnit(6.022140759999998e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') -picomoles_per_cubic_nanometer = NamedUnit(6.0221407599999985e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') -femtomoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') -attomoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') -moles_per_cubic_picometer = NamedUnit(6.0221407600000005e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='molpm⁻³') -millimoles_per_cubic_picometer = NamedUnit(6.0221407600000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') -micromoles_per_cubic_picometer = NamedUnit(6.022140760000001e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') -nanomoles_per_cubic_picometer = NamedUnit(6.0221407600000005e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') -picomoles_per_cubic_picometer = NamedUnit(6.02214076e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') -femtomoles_per_cubic_picometer = NamedUnit(6.022140760000001e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') -attomoles_per_cubic_picometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') -moles_per_cubic_femtometer = NamedUnit(6.022140759999998e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='molfm⁻³') -millimoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') -micromoles_per_cubic_femtometer = NamedUnit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') -nanomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') -picomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') -femtomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') -attomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') -moles_per_cubic_attometer = NamedUnit(6.022140759999998e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='molam⁻³') -millimoles_per_cubic_attometer = NamedUnit(6.022140759999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') -micromoles_per_cubic_attometer = NamedUnit(6.022140759999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') -nanomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') -picomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') -femtomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') -attomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') -moles_per_cubic_decimeter = NamedUnit(6.022140759999998e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='moldm⁻³') -millimoles_per_cubic_decimeter = NamedUnit(6.0221407599999985e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') -micromoles_per_cubic_decimeter = NamedUnit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') -nanomoles_per_cubic_decimeter = NamedUnit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') -picomoles_per_cubic_decimeter = NamedUnit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') -femtomoles_per_cubic_decimeter = NamedUnit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') -attomoles_per_cubic_decimeter = NamedUnit(602214075.9999999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') -moles_per_cubic_centimeter = NamedUnit(6.022140759999999e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='molcm⁻³') -millimoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') -micromoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') -nanomoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') -picomoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') -femtomoles_per_cubic_centimeter = NamedUnit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') -attomoles_per_cubic_centimeter = NamedUnit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') -moles_per_cubic_angstrom = NamedUnit(6.022140759999999e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='molÅ⁻³') -millimoles_per_cubic_angstrom = NamedUnit(6.02214076e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') -micromoles_per_cubic_angstrom = NamedUnit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') -nanomoles_per_cubic_angstrom = NamedUnit(6.02214076e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') -picomoles_per_cubic_angstrom = NamedUnit(6.02214076e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') -femtomoles_per_cubic_angstrom = NamedUnit(6.022140759999999e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') -attomoles_per_cubic_angstrom = NamedUnit(6.02214076e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') -moles_per_cubic_mile = NamedUnit(144478840228220.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_mile', ascii_symbol='mol miles^-3', symbol='molmiles⁻³') -millimoles_per_cubic_mile = NamedUnit(144478840228.22003, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_mile', ascii_symbol='mmol miles^-3', symbol='mmolmiles⁻³') -micromoles_per_cubic_mile = NamedUnit(144478840.22822002, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_mile', ascii_symbol='umol miles^-3', symbol='µmolmiles⁻³') -nanomoles_per_cubic_mile = NamedUnit(144478.84022822, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_mile', ascii_symbol='nmol miles^-3', symbol='nmolmiles⁻³') -picomoles_per_cubic_mile = NamedUnit(144.47884022822, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_mile', ascii_symbol='pmol miles^-3', symbol='pmolmiles⁻³') -femtomoles_per_cubic_mile = NamedUnit(0.14447884022822002, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_mile', ascii_symbol='fmol miles^-3', symbol='fmolmiles⁻³') -attomoles_per_cubic_mile = NamedUnit(0.00014447884022822003, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_mile', ascii_symbol='amol miles^-3', symbol='amolmiles⁻³') -moles_per_cubic_yard = NamedUnit(7.876662736640442e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_yard', ascii_symbol='mol yrd^-3', symbol='molyrd⁻³') -millimoles_per_cubic_yard = NamedUnit(7.876662736640442e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_yard', ascii_symbol='mmol yrd^-3', symbol='mmolyrd⁻³') -micromoles_per_cubic_yard = NamedUnit(7.876662736640442e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_yard', ascii_symbol='umol yrd^-3', symbol='µmolyrd⁻³') -nanomoles_per_cubic_yard = NamedUnit(787666273664044.2, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_yard', ascii_symbol='nmol yrd^-3', symbol='nmolyrd⁻³') -picomoles_per_cubic_yard = NamedUnit(787666273664.0442, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_yard', ascii_symbol='pmol yrd^-3', symbol='pmolyrd⁻³') -femtomoles_per_cubic_yard = NamedUnit(787666273.6640443, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_yard', ascii_symbol='fmol yrd^-3', symbol='fmolyrd⁻³') -attomoles_per_cubic_yard = NamedUnit(787666.2736640442, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_yard', ascii_symbol='amol yrd^-3', symbol='amolyrd⁻³') -moles_per_cubic_foot = NamedUnit(2.1266989388929195e+25, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_foot', ascii_symbol='mol ft^-3', symbol='molft⁻³') -millimoles_per_cubic_foot = NamedUnit(2.1266989388929197e+22, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_foot', ascii_symbol='mmol ft^-3', symbol='mmolft⁻³') -micromoles_per_cubic_foot = NamedUnit(2.1266989388929196e+19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_foot', ascii_symbol='umol ft^-3', symbol='µmolft⁻³') -nanomoles_per_cubic_foot = NamedUnit(2.1266989388929196e+16, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_foot', ascii_symbol='nmol ft^-3', symbol='nmolft⁻³') -picomoles_per_cubic_foot = NamedUnit(21266989388929.2, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_foot', ascii_symbol='pmol ft^-3', symbol='pmolft⁻³') -femtomoles_per_cubic_foot = NamedUnit(21266989388.9292, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_foot', ascii_symbol='fmol ft^-3', symbol='fmolft⁻³') -attomoles_per_cubic_foot = NamedUnit(21266989.388929196, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_foot', ascii_symbol='amol ft^-3', symbol='amolft⁻³') -moles_per_cubic_inch = NamedUnit(3.6749357664069658e+28, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_inch', ascii_symbol='mol in^-3', symbol='molin⁻³') -millimoles_per_cubic_inch = NamedUnit(3.674935766406966e+25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_inch', ascii_symbol='mmol in^-3', symbol='mmolin⁻³') -micromoles_per_cubic_inch = NamedUnit(3.674935766406966e+22, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_inch', ascii_symbol='umol in^-3', symbol='µmolin⁻³') -nanomoles_per_cubic_inch = NamedUnit(3.674935766406966e+19, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_inch', ascii_symbol='nmol in^-3', symbol='nmolin⁻³') -picomoles_per_cubic_inch = NamedUnit(3.674935766406966e+16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_inch', ascii_symbol='pmol in^-3', symbol='pmolin⁻³') -femtomoles_per_cubic_inch = NamedUnit(36749357664069.664, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_inch', ascii_symbol='fmol in^-3', symbol='fmolin⁻³') -attomoles_per_cubic_inch = NamedUnit(36749357664.069664, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_inch', ascii_symbol='amol in^-3', symbol='amolin⁻³') +meters = Unit(1, Dimensions(1, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') +exameters = Unit(1e+18, Dimensions(1, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') +petameters = Unit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') +terameters = Unit(1000000000000.0, Dimensions(1, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') +gigameters = Unit(1000000000.0, Dimensions(1, 0, 0, 0, 0),name='gigameters',ascii_symbol='Gm',symbol='Gm') +megameters = Unit(1000000.0, Dimensions(1, 0, 0, 0, 0),name='megameters',ascii_symbol='Mm',symbol='Mm') +kilometers = Unit(1000.0, Dimensions(1, 0, 0, 0, 0),name='kilometers',ascii_symbol='km',symbol='km') +millimeters = Unit(0.001, Dimensions(1, 0, 0, 0, 0),name='millimeters',ascii_symbol='mm',symbol='mm') +micrometers = Unit(1e-06, Dimensions(1, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',symbol='µm') +nanometers = Unit(1e-09, Dimensions(1, 0, 0, 0, 0),name='nanometers',ascii_symbol='nm',symbol='nm') +picometers = Unit(1e-12, Dimensions(1, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') +femtometers = Unit(1e-15, Dimensions(1, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') +attometers = Unit(1e-18, Dimensions(1, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') +seconds = Unit(1, Dimensions(0, 1, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') +milliseconds = Unit(0.001, Dimensions(0, 1, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') +microseconds = Unit(1e-06, Dimensions(0, 1, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') +nanoseconds = Unit(1e-09, Dimensions(0, 1, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') +picoseconds = Unit(1e-12, Dimensions(0, 1, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') +femtoseconds = Unit(1e-15, Dimensions(0, 1, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') +attoseconds = Unit(1e-18, Dimensions(0, 1, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') +grams = Unit(0.001, Dimensions(0, 0, 1, 0, 0),name='grams',ascii_symbol='g',symbol='g') +exagrams = Unit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') +petagrams = Unit(1000000000000.0, Dimensions(0, 0, 1, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') +teragrams = Unit(1000000000.0, Dimensions(0, 0, 1, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') +gigagrams = Unit(1000000.0, Dimensions(0, 0, 1, 0, 0),name='gigagrams',ascii_symbol='Gg',symbol='Gg') +megagrams = Unit(1000.0, Dimensions(0, 0, 1, 0, 0),name='megagrams',ascii_symbol='Mg',symbol='Mg') +kilograms = Unit(1.0, Dimensions(0, 0, 1, 0, 0),name='kilograms',ascii_symbol='kg',symbol='kg') +milligrams = Unit(1e-06, Dimensions(0, 0, 1, 0, 0),name='milligrams',ascii_symbol='mg',symbol='mg') +micrograms = Unit(1e-09, Dimensions(0, 0, 1, 0, 0),name='micrograms',ascii_symbol='ug',symbol='µg') +nanograms = Unit(1.0000000000000002e-12, Dimensions(0, 0, 1, 0, 0),name='nanograms',ascii_symbol='ng',symbol='ng') +picograms = Unit(1e-15, Dimensions(0, 0, 1, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') +femtograms = Unit(1e-18, Dimensions(0, 0, 1, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') +attograms = Unit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') +amps = Unit(1, Dimensions(0, 0, 0, 1, 0),name='amps',ascii_symbol='A',symbol='A') +exaamps = Unit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamps',ascii_symbol='EA',symbol='EA') +petaamps = Unit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamps',ascii_symbol='PA',symbol='PA') +teraamps = Unit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamps',ascii_symbol='TA',symbol='TA') +gigaamps = Unit(1000000000.0, Dimensions(0, 0, 0, 1, 0),name='gigaamps',ascii_symbol='GA',symbol='GA') +megaamps = Unit(1000000.0, Dimensions(0, 0, 0, 1, 0),name='megaamps',ascii_symbol='MA',symbol='MA') +kiloamps = Unit(1000.0, Dimensions(0, 0, 0, 1, 0),name='kiloamps',ascii_symbol='kA',symbol='kA') +milliamps = Unit(0.001, Dimensions(0, 0, 0, 1, 0),name='milliamps',ascii_symbol='mA',symbol='mA') +microamps = Unit(1e-06, Dimensions(0, 0, 0, 1, 0),name='microamps',ascii_symbol='uA',symbol='µA') +nanoamps = Unit(1e-09, Dimensions(0, 0, 0, 1, 0),name='nanoamps',ascii_symbol='nA',symbol='nA') +picoamps = Unit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamps',ascii_symbol='pA',symbol='pA') +femtoamps = Unit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamps',ascii_symbol='fA',symbol='fA') +attoamps = Unit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamps',ascii_symbol='aA',symbol='aA') +kelvin = Unit(1, Dimensions(0, 0, 0, 0, 1),name='kelvin',ascii_symbol='K',symbol='K') +exakelvin = Unit(1e+18, Dimensions(0, 0, 0, 0, 1),name='exakelvin',ascii_symbol='EK',symbol='EK') +petakelvin = Unit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1),name='petakelvin',ascii_symbol='PK',symbol='PK') +terakelvin = Unit(1000000000000.0, Dimensions(0, 0, 0, 0, 1),name='terakelvin',ascii_symbol='TK',symbol='TK') +gigakelvin = Unit(1000000000.0, Dimensions(0, 0, 0, 0, 1),name='gigakelvin',ascii_symbol='GK',symbol='GK') +megakelvin = Unit(1000000.0, Dimensions(0, 0, 0, 0, 1),name='megakelvin',ascii_symbol='MK',symbol='MK') +kilokelvin = Unit(1000.0, Dimensions(0, 0, 0, 0, 1),name='kilokelvin',ascii_symbol='kK',symbol='kK') +millikelvin = Unit(0.001, Dimensions(0, 0, 0, 0, 1),name='millikelvin',ascii_symbol='mK',symbol='mK') +microkelvin = Unit(1e-06, Dimensions(0, 0, 0, 0, 1),name='microkelvin',ascii_symbol='uK',symbol='µK') +nanokelvin = Unit(1e-09, Dimensions(0, 0, 0, 0, 1),name='nanokelvin',ascii_symbol='nK',symbol='nK') +picokelvin = Unit(1e-12, Dimensions(0, 0, 0, 0, 1),name='picokelvin',ascii_symbol='pK',symbol='pK') +femtokelvin = Unit(1e-15, Dimensions(0, 0, 0, 0, 1),name='femtokelvin',ascii_symbol='fK',symbol='fK') +attokelvin = Unit(1e-18, Dimensions(0, 0, 0, 0, 1),name='attokelvin',ascii_symbol='aK',symbol='aK') +hertz = Unit(1, Dimensions(0, -1, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') +exahertz = Unit(1e+18, Dimensions(0, -1, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') +petahertz = Unit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') +terahertz = Unit(1000000000000.0, Dimensions(0, -1, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') +gigahertz = Unit(1000000000.0, Dimensions(0, -1, 0, 0, 0),name='gigahertz',ascii_symbol='GHz',symbol='GHz') +megahertz = Unit(1000000.0, Dimensions(0, -1, 0, 0, 0),name='megahertz',ascii_symbol='MHz',symbol='MHz') +kilohertz = Unit(1000.0, Dimensions(0, -1, 0, 0, 0),name='kilohertz',ascii_symbol='kHz',symbol='kHz') +millihertz = Unit(0.001, Dimensions(0, -1, 0, 0, 0),name='millihertz',ascii_symbol='mHz',symbol='mHz') +microhertz = Unit(1e-06, Dimensions(0, -1, 0, 0, 0),name='microhertz',ascii_symbol='uHz',symbol='µHz') +nanohertz = Unit(1e-09, Dimensions(0, -1, 0, 0, 0),name='nanohertz',ascii_symbol='nHz',symbol='nHz') +picohertz = Unit(1e-12, Dimensions(0, -1, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') +femtohertz = Unit(1e-15, Dimensions(0, -1, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') +attohertz = Unit(1e-18, Dimensions(0, -1, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') +newtons = Unit(1, Dimensions(1, -2, 1, 0, 0),name='newtons',ascii_symbol='N',symbol='N') +exanewtons = Unit(1e+18, Dimensions(1, -2, 1, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') +petanewtons = Unit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') +teranewtons = Unit(1000000000000.0, Dimensions(1, -2, 1, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') +giganewtons = Unit(1000000000.0, Dimensions(1, -2, 1, 0, 0),name='giganewtons',ascii_symbol='GN',symbol='GN') +meganewtons = Unit(1000000.0, Dimensions(1, -2, 1, 0, 0),name='meganewtons',ascii_symbol='MN',symbol='MN') +kilonewtons = Unit(1000.0, Dimensions(1, -2, 1, 0, 0),name='kilonewtons',ascii_symbol='kN',symbol='kN') +millinewtons = Unit(0.001, Dimensions(1, -2, 1, 0, 0),name='millinewtons',ascii_symbol='mN',symbol='mN') +micronewtons = Unit(1e-06, Dimensions(1, -2, 1, 0, 0),name='micronewtons',ascii_symbol='uN',symbol='µN') +nanonewtons = Unit(1e-09, Dimensions(1, -2, 1, 0, 0),name='nanonewtons',ascii_symbol='nN',symbol='nN') +piconewtons = Unit(1e-12, Dimensions(1, -2, 1, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') +femtonewtons = Unit(1e-15, Dimensions(1, -2, 1, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') +attonewtons = Unit(1e-18, Dimensions(1, -2, 1, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') +pascals = Unit(1, Dimensions(-1, -2, 1, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') +exapascals = Unit(1e+18, Dimensions(-1, -2, 1, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') +petapascals = Unit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') +terapascals = Unit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') +gigapascals = Unit(1000000000.0, Dimensions(-1, -2, 1, 0, 0),name='gigapascals',ascii_symbol='GPa',symbol='GPa') +megapascals = Unit(1000000.0, Dimensions(-1, -2, 1, 0, 0),name='megapascals',ascii_symbol='MPa',symbol='MPa') +kilopascals = Unit(1000.0, Dimensions(-1, -2, 1, 0, 0),name='kilopascals',ascii_symbol='kPa',symbol='kPa') +millipascals = Unit(0.001, Dimensions(-1, -2, 1, 0, 0),name='millipascals',ascii_symbol='mPa',symbol='mPa') +micropascals = Unit(1e-06, Dimensions(-1, -2, 1, 0, 0),name='micropascals',ascii_symbol='uPa',symbol='µPa') +nanopascals = Unit(1e-09, Dimensions(-1, -2, 1, 0, 0),name='nanopascals',ascii_symbol='nPa',symbol='nPa') +picopascals = Unit(1e-12, Dimensions(-1, -2, 1, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') +femtopascals = Unit(1e-15, Dimensions(-1, -2, 1, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') +attopascals = Unit(1e-18, Dimensions(-1, -2, 1, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') +joules = Unit(1, Dimensions(2, -2, 1, 0, 0),name='joules',ascii_symbol='J',symbol='J') +exajoules = Unit(1e+18, Dimensions(2, -2, 1, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') +petajoules = Unit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') +terajoules = Unit(1000000000000.0, Dimensions(2, -2, 1, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') +gigajoules = Unit(1000000000.0, Dimensions(2, -2, 1, 0, 0),name='gigajoules',ascii_symbol='GJ',symbol='GJ') +megajoules = Unit(1000000.0, Dimensions(2, -2, 1, 0, 0),name='megajoules',ascii_symbol='MJ',symbol='MJ') +kilojoules = Unit(1000.0, Dimensions(2, -2, 1, 0, 0),name='kilojoules',ascii_symbol='kJ',symbol='kJ') +millijoules = Unit(0.001, Dimensions(2, -2, 1, 0, 0),name='millijoules',ascii_symbol='mJ',symbol='mJ') +microjoules = Unit(1e-06, Dimensions(2, -2, 1, 0, 0),name='microjoules',ascii_symbol='uJ',symbol='µJ') +nanojoules = Unit(1e-09, Dimensions(2, -2, 1, 0, 0),name='nanojoules',ascii_symbol='nJ',symbol='nJ') +picojoules = Unit(1e-12, Dimensions(2, -2, 1, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') +femtojoules = Unit(1e-15, Dimensions(2, -2, 1, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') +attojoules = Unit(1e-18, Dimensions(2, -2, 1, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') +watts = Unit(1, Dimensions(2, -3, 1, 0, 0),name='watts',ascii_symbol='W',symbol='W') +exawatts = Unit(1e+18, Dimensions(2, -3, 1, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') +petawatts = Unit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') +terawatts = Unit(1000000000000.0, Dimensions(2, -3, 1, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') +gigawatts = Unit(1000000000.0, Dimensions(2, -3, 1, 0, 0),name='gigawatts',ascii_symbol='GW',symbol='GW') +megawatts = Unit(1000000.0, Dimensions(2, -3, 1, 0, 0),name='megawatts',ascii_symbol='MW',symbol='MW') +kilowatts = Unit(1000.0, Dimensions(2, -3, 1, 0, 0),name='kilowatts',ascii_symbol='kW',symbol='kW') +milliwatts = Unit(0.001, Dimensions(2, -3, 1, 0, 0),name='milliwatts',ascii_symbol='mW',symbol='mW') +microwatts = Unit(1e-06, Dimensions(2, -3, 1, 0, 0),name='microwatts',ascii_symbol='uW',symbol='µW') +nanowatts = Unit(1e-09, Dimensions(2, -3, 1, 0, 0),name='nanowatts',ascii_symbol='nW',symbol='nW') +picowatts = Unit(1e-12, Dimensions(2, -3, 1, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') +femtowatts = Unit(1e-15, Dimensions(2, -3, 1, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') +attowatts = Unit(1e-18, Dimensions(2, -3, 1, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') +coulombs = Unit(1, Dimensions(0, 1, 0, 1, 0),name='coulombs',ascii_symbol='C',symbol='C') +exacoulombs = Unit(1e+18, Dimensions(0, 1, 0, 1, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') +petacoulombs = Unit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') +teracoulombs = Unit(1000000000000.0, Dimensions(0, 1, 0, 1, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') +gigacoulombs = Unit(1000000000.0, Dimensions(0, 1, 0, 1, 0),name='gigacoulombs',ascii_symbol='GC',symbol='GC') +megacoulombs = Unit(1000000.0, Dimensions(0, 1, 0, 1, 0),name='megacoulombs',ascii_symbol='MC',symbol='MC') +kilocoulombs = Unit(1000.0, Dimensions(0, 1, 0, 1, 0),name='kilocoulombs',ascii_symbol='kC',symbol='kC') +millicoulombs = Unit(0.001, Dimensions(0, 1, 0, 1, 0),name='millicoulombs',ascii_symbol='mC',symbol='mC') +microcoulombs = Unit(1e-06, Dimensions(0, 1, 0, 1, 0),name='microcoulombs',ascii_symbol='uC',symbol='µC') +nanocoulombs = Unit(1e-09, Dimensions(0, 1, 0, 1, 0),name='nanocoulombs',ascii_symbol='nC',symbol='nC') +picocoulombs = Unit(1e-12, Dimensions(0, 1, 0, 1, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') +femtocoulombs = Unit(1e-15, Dimensions(0, 1, 0, 1, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') +attocoulombs = Unit(1e-18, Dimensions(0, 1, 0, 1, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') +volts = Unit(1, Dimensions(2, -3, 1, -1, 0),name='volts',ascii_symbol='V',symbol='V') +exavolts = Unit(1e+18, Dimensions(2, -3, 1, -1, 0),name='exavolts',ascii_symbol='EV',symbol='EV') +petavolts = Unit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0),name='petavolts',ascii_symbol='PV',symbol='PV') +teravolts = Unit(1000000000000.0, Dimensions(2, -3, 1, -1, 0),name='teravolts',ascii_symbol='TV',symbol='TV') +gigavolts = Unit(1000000000.0, Dimensions(2, -3, 1, -1, 0),name='gigavolts',ascii_symbol='GV',symbol='GV') +megavolts = Unit(1000000.0, Dimensions(2, -3, 1, -1, 0),name='megavolts',ascii_symbol='MV',symbol='MV') +kilovolts = Unit(1000.0, Dimensions(2, -3, 1, -1, 0),name='kilovolts',ascii_symbol='kV',symbol='kV') +millivolts = Unit(0.001, Dimensions(2, -3, 1, -1, 0),name='millivolts',ascii_symbol='mV',symbol='mV') +microvolts = Unit(1e-06, Dimensions(2, -3, 1, -1, 0),name='microvolts',ascii_symbol='uV',symbol='µV') +nanovolts = Unit(1e-09, Dimensions(2, -3, 1, -1, 0),name='nanovolts',ascii_symbol='nV',symbol='nV') +picovolts = Unit(1e-12, Dimensions(2, -3, 1, -1, 0),name='picovolts',ascii_symbol='pV',symbol='pV') +femtovolts = Unit(1e-15, Dimensions(2, -3, 1, -1, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') +attovolts = Unit(1e-18, Dimensions(2, -3, 1, -1, 0),name='attovolts',ascii_symbol='aV',symbol='aV') +ohms = Unit(1, Dimensions(2, -3, 1, -2, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') +exaohms = Unit(1e+18, Dimensions(2, -3, 1, -2, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') +petaohms = Unit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') +teraohms = Unit(1000000000000.0, Dimensions(2, -3, 1, -2, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') +gigaohms = Unit(1000000000.0, Dimensions(2, -3, 1, -2, 0),name='gigaohms',ascii_symbol='GOhm',symbol='GΩ') +megaohms = Unit(1000000.0, Dimensions(2, -3, 1, -2, 0),name='megaohms',ascii_symbol='MOhm',symbol='MΩ') +kiloohms = Unit(1000.0, Dimensions(2, -3, 1, -2, 0),name='kiloohms',ascii_symbol='kOhm',symbol='kΩ') +milliohms = Unit(0.001, Dimensions(2, -3, 1, -2, 0),name='milliohms',ascii_symbol='mOhm',symbol='mΩ') +microohms = Unit(1e-06, Dimensions(2, -3, 1, -2, 0),name='microohms',ascii_symbol='uOhm',symbol='µΩ') +nanoohms = Unit(1e-09, Dimensions(2, -3, 1, -2, 0),name='nanoohms',ascii_symbol='nOhm',symbol='nΩ') +picoohms = Unit(1e-12, Dimensions(2, -3, 1, -2, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') +femtoohms = Unit(1e-15, Dimensions(2, -3, 1, -2, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') +attoohms = Unit(1e-18, Dimensions(2, -3, 1, -2, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') +farads = Unit(1, Dimensions(-2, 4, -1, 2, 0),name='farads',ascii_symbol='F',symbol='F') +exafarads = Unit(1e+18, Dimensions(-2, 4, -1, 2, 0),name='exafarads',ascii_symbol='EF',symbol='EF') +petafarads = Unit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='petafarads',ascii_symbol='PF',symbol='PF') +terafarads = Unit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='terafarads',ascii_symbol='TF',symbol='TF') +gigafarads = Unit(1000000000.0, Dimensions(-2, 4, -1, 2, 0),name='gigafarads',ascii_symbol='GF',symbol='GF') +megafarads = Unit(1000000.0, Dimensions(-2, 4, -1, 2, 0),name='megafarads',ascii_symbol='MF',symbol='MF') +kilofarads = Unit(1000.0, Dimensions(-2, 4, -1, 2, 0),name='kilofarads',ascii_symbol='kF',symbol='kF') +millifarads = Unit(0.001, Dimensions(-2, 4, -1, 2, 0),name='millifarads',ascii_symbol='mF',symbol='mF') +microfarads = Unit(1e-06, Dimensions(-2, 4, -1, 2, 0),name='microfarads',ascii_symbol='uF',symbol='µF') +nanofarads = Unit(1e-09, Dimensions(-2, 4, -1, 2, 0),name='nanofarads',ascii_symbol='nF',symbol='nF') +picofarads = Unit(1e-12, Dimensions(-2, 4, -1, 2, 0),name='picofarads',ascii_symbol='pF',symbol='pF') +femtofarads = Unit(1e-15, Dimensions(-2, 4, -1, 2, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') +attofarads = Unit(1e-18, Dimensions(-2, 4, -1, 2, 0),name='attofarads',ascii_symbol='aF',symbol='aF') +siemens = Unit(1, Dimensions(-2, 3, -1, 2, 0),name='siemens',ascii_symbol='S',symbol='S') +exasiemens = Unit(1e+18, Dimensions(-2, 3, -1, 2, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') +petasiemens = Unit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') +terasiemens = Unit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') +gigasiemens = Unit(1000000000.0, Dimensions(-2, 3, -1, 2, 0),name='gigasiemens',ascii_symbol='GS',symbol='GS') +megasiemens = Unit(1000000.0, Dimensions(-2, 3, -1, 2, 0),name='megasiemens',ascii_symbol='MS',symbol='MS') +kilosiemens = Unit(1000.0, Dimensions(-2, 3, -1, 2, 0),name='kilosiemens',ascii_symbol='kS',symbol='kS') +millisiemens = Unit(0.001, Dimensions(-2, 3, -1, 2, 0),name='millisiemens',ascii_symbol='mS',symbol='mS') +microsiemens = Unit(1e-06, Dimensions(-2, 3, -1, 2, 0),name='microsiemens',ascii_symbol='uS',symbol='µS') +nanosiemens = Unit(1e-09, Dimensions(-2, 3, -1, 2, 0),name='nanosiemens',ascii_symbol='nS',symbol='nS') +picosiemens = Unit(1e-12, Dimensions(-2, 3, -1, 2, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') +femtosiemens = Unit(1e-15, Dimensions(-2, 3, -1, 2, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') +attosiemens = Unit(1e-18, Dimensions(-2, 3, -1, 2, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') +webers = Unit(1, Dimensions(2, -2, 1, -1, 0),name='webers',ascii_symbol='Wb',symbol='Wb') +exawebers = Unit(1e+18, Dimensions(2, -2, 1, -1, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') +petawebers = Unit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') +terawebers = Unit(1000000000000.0, Dimensions(2, -2, 1, -1, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') +gigawebers = Unit(1000000000.0, Dimensions(2, -2, 1, -1, 0),name='gigawebers',ascii_symbol='GWb',symbol='GWb') +megawebers = Unit(1000000.0, Dimensions(2, -2, 1, -1, 0),name='megawebers',ascii_symbol='MWb',symbol='MWb') +kilowebers = Unit(1000.0, Dimensions(2, -2, 1, -1, 0),name='kilowebers',ascii_symbol='kWb',symbol='kWb') +milliwebers = Unit(0.001, Dimensions(2, -2, 1, -1, 0),name='milliwebers',ascii_symbol='mWb',symbol='mWb') +microwebers = Unit(1e-06, Dimensions(2, -2, 1, -1, 0),name='microwebers',ascii_symbol='uWb',symbol='µWb') +nanowebers = Unit(1e-09, Dimensions(2, -2, 1, -1, 0),name='nanowebers',ascii_symbol='nWb',symbol='nWb') +picowebers = Unit(1e-12, Dimensions(2, -2, 1, -1, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') +femtowebers = Unit(1e-15, Dimensions(2, -2, 1, -1, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') +attowebers = Unit(1e-18, Dimensions(2, -2, 1, -1, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') +tesla = Unit(1, Dimensions(0, -2, 1, -1, 0),name='tesla',ascii_symbol='T',symbol='T') +exatesla = Unit(1e+18, Dimensions(0, -2, 1, -1, 0),name='exatesla',ascii_symbol='ET',symbol='ET') +petatesla = Unit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0),name='petatesla',ascii_symbol='PT',symbol='PT') +teratesla = Unit(1000000000000.0, Dimensions(0, -2, 1, -1, 0),name='teratesla',ascii_symbol='TT',symbol='TT') +gigatesla = Unit(1000000000.0, Dimensions(0, -2, 1, -1, 0),name='gigatesla',ascii_symbol='GT',symbol='GT') +megatesla = Unit(1000000.0, Dimensions(0, -2, 1, -1, 0),name='megatesla',ascii_symbol='MT',symbol='MT') +kilotesla = Unit(1000.0, Dimensions(0, -2, 1, -1, 0),name='kilotesla',ascii_symbol='kT',symbol='kT') +millitesla = Unit(0.001, Dimensions(0, -2, 1, -1, 0),name='millitesla',ascii_symbol='mT',symbol='mT') +microtesla = Unit(1e-06, Dimensions(0, -2, 1, -1, 0),name='microtesla',ascii_symbol='uT',symbol='µT') +nanotesla = Unit(1e-09, Dimensions(0, -2, 1, -1, 0),name='nanotesla',ascii_symbol='nT',symbol='nT') +picotesla = Unit(1e-12, Dimensions(0, -2, 1, -1, 0),name='picotesla',ascii_symbol='pT',symbol='pT') +femtotesla = Unit(1e-15, Dimensions(0, -2, 1, -1, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') +attotesla = Unit(1e-18, Dimensions(0, -2, 1, -1, 0),name='attotesla',ascii_symbol='aT',symbol='aT') +henry = Unit(1, Dimensions(2, -2, 1, -2, 0),name='henry',ascii_symbol='H',symbol='H') +exahenry = Unit(1e+18, Dimensions(2, -2, 1, -2, 0),name='exahenry',ascii_symbol='EH',symbol='EH') +petahenry = Unit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0),name='petahenry',ascii_symbol='PH',symbol='PH') +terahenry = Unit(1000000000000.0, Dimensions(2, -2, 1, -2, 0),name='terahenry',ascii_symbol='TH',symbol='TH') +gigahenry = Unit(1000000000.0, Dimensions(2, -2, 1, -2, 0),name='gigahenry',ascii_symbol='GH',symbol='GH') +megahenry = Unit(1000000.0, Dimensions(2, -2, 1, -2, 0),name='megahenry',ascii_symbol='MH',symbol='MH') +kilohenry = Unit(1000.0, Dimensions(2, -2, 1, -2, 0),name='kilohenry',ascii_symbol='kH',symbol='kH') +millihenry = Unit(0.001, Dimensions(2, -2, 1, -2, 0),name='millihenry',ascii_symbol='mH',symbol='mH') +microhenry = Unit(1e-06, Dimensions(2, -2, 1, -2, 0),name='microhenry',ascii_symbol='uH',symbol='µH') +nanohenry = Unit(1e-09, Dimensions(2, -2, 1, -2, 0),name='nanohenry',ascii_symbol='nH',symbol='nH') +picohenry = Unit(1e-12, Dimensions(2, -2, 1, -2, 0),name='picohenry',ascii_symbol='pH',symbol='pH') +femtohenry = Unit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') +attohenry = Unit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') +degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1),name='degrees_celsius',ascii_symbol='C',symbol='C') +angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0),name='angstroms',ascii_symbol='A',symbol='Å') +angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Ang') +minutes = Unit(60, Dimensions(0, 1, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') +hours = Unit(360, Dimensions(0, 1, 0, 0, 0),name='hours',ascii_symbol='hr',symbol='hr') +days = Unit(8640, Dimensions(0, 1, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') +days = Unit(8640, Dimensions(0, 1, 0, 0, 0),name='days',ascii_symbol='day',symbol='day') +years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') +years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0),name='years',ascii_symbol='yr',symbol='yr') +degrees = Unit(57.29577951308232, Dimensions(0, 0, 0, 0, 0),name='degrees',ascii_symbol='deg',symbol='deg') +radians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='radians',ascii_symbol='rad',symbol='rad') +stradians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='stradians',ascii_symbol='sr',symbol='sr') +square_meters = Unit(1, Dimensions(2, 0, 0, 0, 0), name='square_meters', ascii_symbol='m^2', symbol='m²') +cubic_meters = Unit(1, Dimensions(3, 0, 0, 0, 0), name='cubic_meters', ascii_symbol='m^3', symbol='m³') +per_meter = Unit(1.0, Dimensions(-1, 0, 0, 0, 0), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') +per_square_meter = Unit(1.0, Dimensions(-2, 0, 0, 0, 0), name='per_square_meter', ascii_symbol='m^-2', symbol='m⁻²') +per_cubic_meter = Unit(1.0, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_meter', ascii_symbol='m^-3', symbol='m⁻³') +square_exameters = Unit(1e+36, Dimensions(2, 0, 0, 0, 0), name='square_exameters', ascii_symbol='Em^2', symbol='Em²') +cubic_exameters = Unit(1e+54, Dimensions(3, 0, 0, 0, 0), name='cubic_exameters', ascii_symbol='Em^3', symbol='Em³') +per_exameter = Unit(1e-18, Dimensions(-1, 0, 0, 0, 0), name='per_exameter', ascii_symbol='Em^-1', symbol='Em⁻¹') +per_square_exameter = Unit(1e-36, Dimensions(-2, 0, 0, 0, 0), name='per_square_exameter', ascii_symbol='Em^-2', symbol='Em⁻²') +per_cubic_exameter = Unit(1e-54, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_exameter', ascii_symbol='Em^-3', symbol='Em⁻³') +square_petameters = Unit(1e+30, Dimensions(2, 0, 0, 0, 0), name='square_petameters', ascii_symbol='Pm^2', symbol='Pm²') +cubic_petameters = Unit(1e+45, Dimensions(3, 0, 0, 0, 0), name='cubic_petameters', ascii_symbol='Pm^3', symbol='Pm³') +per_petameter = Unit(1e-15, Dimensions(-1, 0, 0, 0, 0), name='per_petameter', ascii_symbol='Pm^-1', symbol='Pm⁻¹') +per_square_petameter = Unit(1e-30, Dimensions(-2, 0, 0, 0, 0), name='per_square_petameter', ascii_symbol='Pm^-2', symbol='Pm⁻²') +per_cubic_petameter = Unit(1e-45, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_petameter', ascii_symbol='Pm^-3', symbol='Pm⁻³') +square_terameters = Unit(1e+24, Dimensions(2, 0, 0, 0, 0), name='square_terameters', ascii_symbol='Tm^2', symbol='Tm²') +cubic_terameters = Unit(1e+36, Dimensions(3, 0, 0, 0, 0), name='cubic_terameters', ascii_symbol='Tm^3', symbol='Tm³') +per_terameter = Unit(1e-12, Dimensions(-1, 0, 0, 0, 0), name='per_terameter', ascii_symbol='Tm^-1', symbol='Tm⁻¹') +per_square_terameter = Unit(1e-24, Dimensions(-2, 0, 0, 0, 0), name='per_square_terameter', ascii_symbol='Tm^-2', symbol='Tm⁻²') +per_cubic_terameter = Unit(1e-36, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_terameter', ascii_symbol='Tm^-3', symbol='Tm⁻³') +square_gigameters = Unit(1e+18, Dimensions(2, 0, 0, 0, 0), name='square_gigameters', ascii_symbol='Gm^2', symbol='Gm²') +cubic_gigameters = Unit(1e+27, Dimensions(3, 0, 0, 0, 0), name='cubic_gigameters', ascii_symbol='Gm^3', symbol='Gm³') +per_gigameter = Unit(1e-09, Dimensions(-1, 0, 0, 0, 0), name='per_gigameter', ascii_symbol='Gm^-1', symbol='Gm⁻¹') +per_square_gigameter = Unit(1e-18, Dimensions(-2, 0, 0, 0, 0), name='per_square_gigameter', ascii_symbol='Gm^-2', symbol='Gm⁻²') +per_cubic_gigameter = Unit(1e-27, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_gigameter', ascii_symbol='Gm^-3', symbol='Gm⁻³') +square_megameters = Unit(1000000000000.0, Dimensions(2, 0, 0, 0, 0), name='square_megameters', ascii_symbol='Mm^2', symbol='Mm²') +cubic_megameters = Unit(1e+18, Dimensions(3, 0, 0, 0, 0), name='cubic_megameters', ascii_symbol='Mm^3', symbol='Mm³') +per_megameter = Unit(1e-06, Dimensions(-1, 0, 0, 0, 0), name='per_megameter', ascii_symbol='Mm^-1', symbol='Mm⁻¹') +per_square_megameter = Unit(1e-12, Dimensions(-2, 0, 0, 0, 0), name='per_square_megameter', ascii_symbol='Mm^-2', symbol='Mm⁻²') +per_cubic_megameter = Unit(1e-18, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_megameter', ascii_symbol='Mm^-3', symbol='Mm⁻³') +square_kilometers = Unit(1000000.0, Dimensions(2, 0, 0, 0, 0), name='square_kilometers', ascii_symbol='km^2', symbol='km²') +cubic_kilometers = Unit(1000000000.0, Dimensions(3, 0, 0, 0, 0), name='cubic_kilometers', ascii_symbol='km^3', symbol='km³') +per_kilometer = Unit(0.001, Dimensions(-1, 0, 0, 0, 0), name='per_kilometer', ascii_symbol='km^-1', symbol='km⁻¹') +per_square_kilometer = Unit(1e-06, Dimensions(-2, 0, 0, 0, 0), name='per_square_kilometer', ascii_symbol='km^-2', symbol='km⁻²') +per_cubic_kilometer = Unit(1e-09, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_kilometer', ascii_symbol='km^-3', symbol='km⁻³') +square_millimeters = Unit(1e-06, Dimensions(2, 0, 0, 0, 0), name='square_millimeters', ascii_symbol='mm^2', symbol='mm²') +cubic_millimeters = Unit(1e-09, Dimensions(3, 0, 0, 0, 0), name='cubic_millimeters', ascii_symbol='mm^3', symbol='mm³') +per_millimeter = Unit(1000.0, Dimensions(-1, 0, 0, 0, 0), name='per_millimeter', ascii_symbol='mm^-1', symbol='mm⁻¹') +per_square_millimeter = Unit(1000000.0, Dimensions(-2, 0, 0, 0, 0), name='per_square_millimeter', ascii_symbol='mm^-2', symbol='mm⁻²') +per_cubic_millimeter = Unit(999999999.9999999, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_millimeter', ascii_symbol='mm^-3', symbol='mm⁻³') +square_micrometers = Unit(1e-12, Dimensions(2, 0, 0, 0, 0), name='square_micrometers', ascii_symbol='um^2', symbol='µm²') +cubic_micrometers = Unit(9.999999999999999e-19, Dimensions(3, 0, 0, 0, 0), name='cubic_micrometers', ascii_symbol='um^3', symbol='µm³') +per_micrometer = Unit(1000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_micrometer', ascii_symbol='um^-1', symbol='µm⁻¹') +per_square_micrometer = Unit(1000000000000.0001, Dimensions(-2, 0, 0, 0, 0), name='per_square_micrometer', ascii_symbol='um^-2', symbol='µm⁻²') +per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_micrometer', ascii_symbol='um^-3', symbol='µm⁻³') +square_nanometers = Unit(1e-18, Dimensions(2, 0, 0, 0, 0), name='square_nanometers', ascii_symbol='nm^2', symbol='nm²') +cubic_nanometers = Unit(1.0000000000000002e-27, Dimensions(3, 0, 0, 0, 0), name='cubic_nanometers', ascii_symbol='nm^3', symbol='nm³') +per_nanometer = Unit(999999999.9999999, Dimensions(-1, 0, 0, 0, 0), name='per_nanometer', ascii_symbol='nm^-1', symbol='nm⁻¹') +per_square_nanometer = Unit(9.999999999999999e+17, Dimensions(-2, 0, 0, 0, 0), name='per_square_nanometer', ascii_symbol='nm^-2', symbol='nm⁻²') +per_cubic_nanometer = Unit(9.999999999999999e+26, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_nanometer', ascii_symbol='nm^-3', symbol='nm⁻³') +square_picometers = Unit(1e-24, Dimensions(2, 0, 0, 0, 0), name='square_picometers', ascii_symbol='pm^2', symbol='pm²') +cubic_picometers = Unit(1e-36, Dimensions(3, 0, 0, 0, 0), name='cubic_picometers', ascii_symbol='pm^3', symbol='pm³') +per_picometer = Unit(1000000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_picometer', ascii_symbol='pm^-1', symbol='pm⁻¹') +per_square_picometer = Unit(1e+24, Dimensions(-2, 0, 0, 0, 0), name='per_square_picometer', ascii_symbol='pm^-2', symbol='pm⁻²') +per_cubic_picometer = Unit(1e+36, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_picometer', ascii_symbol='pm^-3', symbol='pm⁻³') +square_femtometers = Unit(1e-30, Dimensions(2, 0, 0, 0, 0), name='square_femtometers', ascii_symbol='fm^2', symbol='fm²') +cubic_femtometers = Unit(1.0000000000000003e-45, Dimensions(3, 0, 0, 0, 0), name='cubic_femtometers', ascii_symbol='fm^3', symbol='fm³') +per_femtometer = Unit(999999999999999.9, Dimensions(-1, 0, 0, 0, 0), name='per_femtometer', ascii_symbol='fm^-1', symbol='fm⁻¹') +per_square_femtometer = Unit(9.999999999999999e+29, Dimensions(-2, 0, 0, 0, 0), name='per_square_femtometer', ascii_symbol='fm^-2', symbol='fm⁻²') +per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_femtometer', ascii_symbol='fm^-3', symbol='fm⁻³') +square_attometers = Unit(1.0000000000000001e-36, Dimensions(2, 0, 0, 0, 0), name='square_attometers', ascii_symbol='am^2', symbol='am²') +cubic_attometers = Unit(1.0000000000000002e-54, Dimensions(3, 0, 0, 0, 0), name='cubic_attometers', ascii_symbol='am^3', symbol='am³') +per_attometer = Unit(9.999999999999999e+17, Dimensions(-1, 0, 0, 0, 0), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') +per_square_attometer = Unit(9.999999999999999e+35, Dimensions(-2, 0, 0, 0, 0), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') +per_cubic_attometer = Unit(9.999999999999997e+53, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') +square_angstroms = Unit(1.0000000000000001e-20, Dimensions(2, 0, 0, 0, 0), name='square_angstroms', ascii_symbol='A^2', symbol='Ų') +cubic_angstroms = Unit(1e-30, Dimensions(3, 0, 0, 0, 0), name='cubic_angstroms', ascii_symbol='A^3', symbol='ų') +per_angstrom = Unit(10000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_angstrom', ascii_symbol='A^-1', symbol='Å⁻¹') +per_square_angstrom = Unit(1e+20, Dimensions(-2, 0, 0, 0, 0), name='per_square_angstrom', ascii_symbol='A^-2', symbol='Å⁻²') +per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_angstrom', ascii_symbol='A^-3', symbol='Å⁻³') +square_angstroms = Unit(1.0000000000000001e-20, Dimensions(2, 0, 0, 0, 0), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ang²') +cubic_angstroms = Unit(1e-30, Dimensions(3, 0, 0, 0, 0), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='Ang³') +per_angstrom = Unit(10000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Ang⁻¹') +per_square_angstrom = Unit(1e+20, Dimensions(-2, 0, 0, 0, 0), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Ang⁻²') +per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Ang⁻³') +meters_per_second = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') +meters_per_square_second = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') +meters_per_millisecond = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') +meters_per_square_millisecond = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') +meters_per_microsecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') +meters_per_square_microsecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') +meters_per_nanosecond = Unit(999999999.9999999, Dimensions(1, -1, 0, 0, 0), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') +meters_per_square_nanosecond = Unit(999999999.9999999, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') +meters_per_picosecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') +meters_per_square_picosecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') +meters_per_femtosecond = Unit(999999999999999.9, Dimensions(1, -1, 0, 0, 0), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') +meters_per_square_femtosecond = Unit(999999999999999.9, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') +meters_per_attosecond = Unit(9.999999999999999e+17, Dimensions(1, -1, 0, 0, 0), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') +meters_per_square_attosecond = Unit(9.999999999999999e+17, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') +meters_per_minute = Unit(0.016666666666666666, Dimensions(1, -1, 0, 0, 0), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') +meters_per_square_minute = Unit(0.016666666666666666, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') +meters_per_hour = Unit(0.002777777777777778, Dimensions(1, -1, 0, 0, 0), name='meters_per_hour', ascii_symbol='m/hr', symbol='NoneNone⁻¹') +meters_per_square_hour = Unit(0.002777777777777778, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_hour', ascii_symbol='m/hr^2', symbol='NoneNone⁻²') +meters_per_day = Unit(0.00011574074074074075, Dimensions(1, -1, 0, 0, 0), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') +meters_per_square_day = Unit(0.00011574074074074075, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') +meters_per_day = Unit(0.00011574074074074075, Dimensions(1, -1, 0, 0, 0), name='meters_per_day', ascii_symbol='m/day', symbol='NoneNone⁻¹') +meters_per_square_day = Unit(0.00011574074074074075, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_day', ascii_symbol='m/day^2', symbol='NoneNone⁻²') +meters_per_year = Unit(3.168873850681143e-07, Dimensions(1, -1, 0, 0, 0), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') +meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') +meters_per_year = Unit(3.168873850681143e-07, Dimensions(1, -1, 0, 0, 0), name='meters_per_year', ascii_symbol='m/yr', symbol='NoneNone⁻¹') +meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_year', ascii_symbol='m/yr^2', symbol='NoneNone⁻²') +exameters_per_second = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') +exameters_per_square_second = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') +exameters_per_millisecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') +exameters_per_square_millisecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') +exameters_per_microsecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') +exameters_per_square_microsecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') +exameters_per_nanosecond = Unit(9.999999999999999e+26, Dimensions(1, -1, 0, 0, 0), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') +exameters_per_square_nanosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') +exameters_per_picosecond = Unit(1e+30, Dimensions(1, -1, 0, 0, 0), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') +exameters_per_square_picosecond = Unit(1e+30, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') +exameters_per_femtosecond = Unit(1e+33, Dimensions(1, -1, 0, 0, 0), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') +exameters_per_square_femtosecond = Unit(1e+33, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') +exameters_per_attosecond = Unit(9.999999999999999e+35, Dimensions(1, -1, 0, 0, 0), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') +exameters_per_square_attosecond = Unit(9.999999999999999e+35, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') +exameters_per_minute = Unit(1.6666666666666666e+16, Dimensions(1, -1, 0, 0, 0), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') +exameters_per_square_minute = Unit(1.6666666666666666e+16, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') +exameters_per_hour = Unit(2777777777777778.0, Dimensions(1, -1, 0, 0, 0), name='exameters_per_hour', ascii_symbol='Em/hr', symbol='EmNone⁻¹') +exameters_per_square_hour = Unit(2777777777777778.0, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_hour', ascii_symbol='Em/hr^2', symbol='EmNone⁻²') +exameters_per_day = Unit(115740740740740.73, Dimensions(1, -1, 0, 0, 0), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') +exameters_per_square_day = Unit(115740740740740.73, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') +exameters_per_day = Unit(115740740740740.73, Dimensions(1, -1, 0, 0, 0), name='exameters_per_day', ascii_symbol='Em/day', symbol='EmNone⁻¹') +exameters_per_square_day = Unit(115740740740740.73, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_day', ascii_symbol='Em/day^2', symbol='EmNone⁻²') +exameters_per_year = Unit(316887385068.1143, Dimensions(1, -1, 0, 0, 0), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') +exameters_per_square_year = Unit(316887385068.1143, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') +exameters_per_year = Unit(316887385068.1143, Dimensions(1, -1, 0, 0, 0), name='exameters_per_year', ascii_symbol='Em/yr', symbol='EmNone⁻¹') +exameters_per_square_year = Unit(316887385068.1143, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_year', ascii_symbol='Em/yr^2', symbol='EmNone⁻²') +petameters_per_second = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') +petameters_per_square_second = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') +petameters_per_millisecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') +petameters_per_square_millisecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') +petameters_per_microsecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') +petameters_per_square_microsecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') +petameters_per_nanosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') +petameters_per_square_nanosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') +petameters_per_picosecond = Unit(1e+27, Dimensions(1, -1, 0, 0, 0), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') +petameters_per_square_picosecond = Unit(1e+27, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') +petameters_per_femtosecond = Unit(9.999999999999999e+29, Dimensions(1, -1, 0, 0, 0), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') +petameters_per_square_femtosecond = Unit(9.999999999999999e+29, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') +petameters_per_attosecond = Unit(1e+33, Dimensions(1, -1, 0, 0, 0), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') +petameters_per_square_attosecond = Unit(1e+33, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') +petameters_per_minute = Unit(16666666666666.666, Dimensions(1, -1, 0, 0, 0), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') +petameters_per_square_minute = Unit(16666666666666.666, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') +petameters_per_hour = Unit(2777777777777.778, Dimensions(1, -1, 0, 0, 0), name='petameters_per_hour', ascii_symbol='Pm/hr', symbol='PmNone⁻¹') +petameters_per_square_hour = Unit(2777777777777.778, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_hour', ascii_symbol='Pm/hr^2', symbol='PmNone⁻²') +petameters_per_day = Unit(115740740740.74074, Dimensions(1, -1, 0, 0, 0), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') +petameters_per_square_day = Unit(115740740740.74074, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') +petameters_per_day = Unit(115740740740.74074, Dimensions(1, -1, 0, 0, 0), name='petameters_per_day', ascii_symbol='Pm/day', symbol='PmNone⁻¹') +petameters_per_square_day = Unit(115740740740.74074, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_day', ascii_symbol='Pm/day^2', symbol='PmNone⁻²') +petameters_per_year = Unit(316887385.0681143, Dimensions(1, -1, 0, 0, 0), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') +petameters_per_square_year = Unit(316887385.0681143, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') +petameters_per_year = Unit(316887385.0681143, Dimensions(1, -1, 0, 0, 0), name='petameters_per_year', ascii_symbol='Pm/yr', symbol='PmNone⁻¹') +petameters_per_square_year = Unit(316887385.0681143, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_year', ascii_symbol='Pm/yr^2', symbol='PmNone⁻²') +terameters_per_second = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') +terameters_per_square_second = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') +terameters_per_millisecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') +terameters_per_square_millisecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') +terameters_per_microsecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') +terameters_per_square_microsecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') +terameters_per_nanosecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') +terameters_per_square_nanosecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') +terameters_per_picosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') +terameters_per_square_picosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') +terameters_per_femtosecond = Unit(9.999999999999999e+26, Dimensions(1, -1, 0, 0, 0), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') +terameters_per_square_femtosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') +terameters_per_attosecond = Unit(9.999999999999999e+29, Dimensions(1, -1, 0, 0, 0), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') +terameters_per_square_attosecond = Unit(9.999999999999999e+29, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') +terameters_per_minute = Unit(16666666666.666666, Dimensions(1, -1, 0, 0, 0), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') +terameters_per_square_minute = Unit(16666666666.666666, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') +terameters_per_hour = Unit(2777777777.7777777, Dimensions(1, -1, 0, 0, 0), name='terameters_per_hour', ascii_symbol='Tm/hr', symbol='TmNone⁻¹') +terameters_per_square_hour = Unit(2777777777.7777777, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_hour', ascii_symbol='Tm/hr^2', symbol='TmNone⁻²') +terameters_per_day = Unit(115740740.74074075, Dimensions(1, -1, 0, 0, 0), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') +terameters_per_square_day = Unit(115740740.74074075, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') +terameters_per_day = Unit(115740740.74074075, Dimensions(1, -1, 0, 0, 0), name='terameters_per_day', ascii_symbol='Tm/day', symbol='TmNone⁻¹') +terameters_per_square_day = Unit(115740740.74074075, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_day', ascii_symbol='Tm/day^2', symbol='TmNone⁻²') +terameters_per_year = Unit(316887.38506811426, Dimensions(1, -1, 0, 0, 0), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') +terameters_per_square_year = Unit(316887.38506811426, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') +terameters_per_year = Unit(316887.38506811426, Dimensions(1, -1, 0, 0, 0), name='terameters_per_year', ascii_symbol='Tm/yr', symbol='TmNone⁻¹') +terameters_per_square_year = Unit(316887.38506811426, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_year', ascii_symbol='Tm/yr^2', symbol='TmNone⁻²') +gigameters_per_second = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') +gigameters_per_square_second = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') +gigameters_per_millisecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') +gigameters_per_square_millisecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') +gigameters_per_microsecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') +gigameters_per_square_microsecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') +gigameters_per_nanosecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') +gigameters_per_square_nanosecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') +gigameters_per_picosecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') +gigameters_per_square_picosecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') +gigameters_per_femtosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') +gigameters_per_square_femtosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') +gigameters_per_attosecond = Unit(9.999999999999999e+26, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') +gigameters_per_square_attosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') +gigameters_per_minute = Unit(16666666.666666666, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') +gigameters_per_square_minute = Unit(16666666.666666666, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') +gigameters_per_hour = Unit(2777777.777777778, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_hour', ascii_symbol='Gm/hr', symbol='GmNone⁻¹') +gigameters_per_square_hour = Unit(2777777.777777778, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_hour', ascii_symbol='Gm/hr^2', symbol='GmNone⁻²') +gigameters_per_day = Unit(115740.74074074074, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') +gigameters_per_square_day = Unit(115740.74074074074, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') +gigameters_per_day = Unit(115740.74074074074, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_day', ascii_symbol='Gm/day', symbol='GmNone⁻¹') +gigameters_per_square_day = Unit(115740.74074074074, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_day', ascii_symbol='Gm/day^2', symbol='GmNone⁻²') +gigameters_per_year = Unit(316.88738506811427, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') +gigameters_per_square_year = Unit(316.88738506811427, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') +gigameters_per_year = Unit(316.88738506811427, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_year', ascii_symbol='Gm/yr', symbol='GmNone⁻¹') +gigameters_per_square_year = Unit(316.88738506811427, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_year', ascii_symbol='Gm/yr^2', symbol='GmNone⁻²') +megameters_per_second = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') +megameters_per_square_second = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') +megameters_per_millisecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') +megameters_per_square_millisecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') +megameters_per_microsecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') +megameters_per_square_microsecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') +megameters_per_nanosecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') +megameters_per_square_nanosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') +megameters_per_picosecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') +megameters_per_square_picosecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') +megameters_per_femtosecond = Unit(9.999999999999999e+20, Dimensions(1, -1, 0, 0, 0), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') +megameters_per_square_femtosecond = Unit(9.999999999999999e+20, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') +megameters_per_attosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') +megameters_per_square_attosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') +megameters_per_minute = Unit(16666.666666666668, Dimensions(1, -1, 0, 0, 0), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') +megameters_per_square_minute = Unit(16666.666666666668, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') +megameters_per_hour = Unit(2777.777777777778, Dimensions(1, -1, 0, 0, 0), name='megameters_per_hour', ascii_symbol='Mm/hr', symbol='MmNone⁻¹') +megameters_per_square_hour = Unit(2777.777777777778, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_hour', ascii_symbol='Mm/hr^2', symbol='MmNone⁻²') +megameters_per_day = Unit(115.74074074074075, Dimensions(1, -1, 0, 0, 0), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') +megameters_per_square_day = Unit(115.74074074074075, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') +megameters_per_day = Unit(115.74074074074075, Dimensions(1, -1, 0, 0, 0), name='megameters_per_day', ascii_symbol='Mm/day', symbol='MmNone⁻¹') +megameters_per_square_day = Unit(115.74074074074075, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_day', ascii_symbol='Mm/day^2', symbol='MmNone⁻²') +megameters_per_year = Unit(0.3168873850681143, Dimensions(1, -1, 0, 0, 0), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') +megameters_per_square_year = Unit(0.3168873850681143, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') +megameters_per_year = Unit(0.3168873850681143, Dimensions(1, -1, 0, 0, 0), name='megameters_per_year', ascii_symbol='Mm/yr', symbol='MmNone⁻¹') +megameters_per_square_year = Unit(0.3168873850681143, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_year', ascii_symbol='Mm/yr^2', symbol='MmNone⁻²') +kilometers_per_second = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') +kilometers_per_square_second = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') +kilometers_per_millisecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') +kilometers_per_square_millisecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') +kilometers_per_microsecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') +kilometers_per_square_microsecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') +kilometers_per_nanosecond = Unit(999999999999.9999, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') +kilometers_per_square_nanosecond = Unit(999999999999.9999, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') +kilometers_per_picosecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') +kilometers_per_square_picosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') +kilometers_per_femtosecond = Unit(9.999999999999999e+17, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') +kilometers_per_square_femtosecond = Unit(9.999999999999999e+17, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') +kilometers_per_attosecond = Unit(9.999999999999999e+20, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') +kilometers_per_square_attosecond = Unit(9.999999999999999e+20, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') +kilometers_per_minute = Unit(16.666666666666668, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') +kilometers_per_square_minute = Unit(16.666666666666668, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') +kilometers_per_hour = Unit(2.7777777777777777, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_hour', ascii_symbol='km/hr', symbol='kmNone⁻¹') +kilometers_per_square_hour = Unit(2.7777777777777777, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_hour', ascii_symbol='km/hr^2', symbol='kmNone⁻²') +kilometers_per_day = Unit(0.11574074074074074, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') +kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') +kilometers_per_day = Unit(0.11574074074074074, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_day', ascii_symbol='km/day', symbol='kmNone⁻¹') +kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_day', ascii_symbol='km/day^2', symbol='kmNone⁻²') +kilometers_per_year = Unit(0.0003168873850681143, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') +kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') +kilometers_per_year = Unit(0.0003168873850681143, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_year', ascii_symbol='km/yr', symbol='kmNone⁻¹') +kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_year', ascii_symbol='km/yr^2', symbol='kmNone⁻²') +millimeters_per_second = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') +millimeters_per_square_second = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') +millimeters_per_millisecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') +millimeters_per_square_millisecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') +millimeters_per_microsecond = Unit(1000.0000000000001, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') +millimeters_per_square_microsecond = Unit(1000.0000000000001, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') +millimeters_per_nanosecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') +millimeters_per_square_nanosecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') +millimeters_per_picosecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') +millimeters_per_square_picosecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') +millimeters_per_femtosecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') +millimeters_per_square_femtosecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') +millimeters_per_attosecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') +millimeters_per_square_attosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') +millimeters_per_minute = Unit(1.6666666666666667e-05, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') +millimeters_per_square_minute = Unit(1.6666666666666667e-05, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') +millimeters_per_hour = Unit(2.777777777777778e-06, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_hour', ascii_symbol='mm/hr', symbol='mmNone⁻¹') +millimeters_per_square_hour = Unit(2.777777777777778e-06, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_hour', ascii_symbol='mm/hr^2', symbol='mmNone⁻²') +millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') +millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') +millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_day', ascii_symbol='mm/day', symbol='mmNone⁻¹') +millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_day', ascii_symbol='mm/day^2', symbol='mmNone⁻²') +millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') +millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') +millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_year', ascii_symbol='mm/yr', symbol='mmNone⁻¹') +millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_year', ascii_symbol='mm/yr^2', symbol='mmNone⁻²') +micrometers_per_second = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') +micrometers_per_square_second = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') +micrometers_per_millisecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') +micrometers_per_square_millisecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') +micrometers_per_microsecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') +micrometers_per_square_microsecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') +micrometers_per_nanosecond = Unit(999.9999999999999, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') +micrometers_per_square_nanosecond = Unit(999.9999999999999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') +micrometers_per_picosecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') +micrometers_per_square_picosecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') +micrometers_per_femtosecond = Unit(999999999.9999999, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') +micrometers_per_square_femtosecond = Unit(999999999.9999999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') +micrometers_per_attosecond = Unit(999999999999.9999, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') +micrometers_per_square_attosecond = Unit(999999999999.9999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') +micrometers_per_minute = Unit(1.6666666666666667e-08, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') +micrometers_per_square_minute = Unit(1.6666666666666667e-08, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') +micrometers_per_hour = Unit(2.7777777777777776e-09, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_hour', ascii_symbol='um/hr', symbol='µmNone⁻¹') +micrometers_per_square_hour = Unit(2.7777777777777776e-09, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_hour', ascii_symbol='um/hr^2', symbol='µmNone⁻²') +micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') +micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') +micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_day', ascii_symbol='um/day', symbol='µmNone⁻¹') +micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_day', ascii_symbol='um/day^2', symbol='µmNone⁻²') +micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') +micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') +micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_year', ascii_symbol='um/yr', symbol='µmNone⁻¹') +micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_year', ascii_symbol='um/yr^2', symbol='µmNone⁻²') +nanometers_per_second = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') +nanometers_per_square_second = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') +nanometers_per_millisecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') +nanometers_per_square_millisecond = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') +nanometers_per_microsecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') +nanometers_per_square_microsecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') +nanometers_per_nanosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') +nanometers_per_square_nanosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') +nanometers_per_picosecond = Unit(1000.0000000000001, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') +nanometers_per_square_picosecond = Unit(1000.0000000000001, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') +nanometers_per_femtosecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') +nanometers_per_square_femtosecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') +nanometers_per_attosecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') +nanometers_per_square_attosecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') +nanometers_per_minute = Unit(1.6666666666666667e-11, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') +nanometers_per_square_minute = Unit(1.6666666666666667e-11, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') +nanometers_per_hour = Unit(2.777777777777778e-12, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_hour', ascii_symbol='nm/hr', symbol='nmNone⁻¹') +nanometers_per_square_hour = Unit(2.777777777777778e-12, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_hour', ascii_symbol='nm/hr^2', symbol='nmNone⁻²') +nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') +nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') +nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_day', ascii_symbol='nm/day', symbol='nmNone⁻¹') +nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_day', ascii_symbol='nm/day^2', symbol='nmNone⁻²') +nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') +nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') +nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_year', ascii_symbol='nm/yr', symbol='nmNone⁻¹') +nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_year', ascii_symbol='nm/yr^2', symbol='nmNone⁻²') +picometers_per_second = Unit(1e-12, Dimensions(1, -1, 0, 0, 0), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') +picometers_per_square_second = Unit(1e-12, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') +picometers_per_millisecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') +picometers_per_square_millisecond = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') +picometers_per_microsecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') +picometers_per_square_microsecond = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') +picometers_per_nanosecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') +picometers_per_square_nanosecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') +picometers_per_picosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') +picometers_per_square_picosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') +picometers_per_femtosecond = Unit(999.9999999999999, Dimensions(1, -1, 0, 0, 0), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') +picometers_per_square_femtosecond = Unit(999.9999999999999, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') +picometers_per_attosecond = Unit(999999.9999999999, Dimensions(1, -1, 0, 0, 0), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') +picometers_per_square_attosecond = Unit(999999.9999999999, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') +picometers_per_minute = Unit(1.6666666666666667e-14, Dimensions(1, -1, 0, 0, 0), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') +picometers_per_square_minute = Unit(1.6666666666666667e-14, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') +picometers_per_hour = Unit(2.7777777777777776e-15, Dimensions(1, -1, 0, 0, 0), name='picometers_per_hour', ascii_symbol='pm/hr', symbol='pmNone⁻¹') +picometers_per_square_hour = Unit(2.7777777777777776e-15, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_hour', ascii_symbol='pm/hr^2', symbol='pmNone⁻²') +picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(1, -1, 0, 0, 0), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') +picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') +picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(1, -1, 0, 0, 0), name='picometers_per_day', ascii_symbol='pm/day', symbol='pmNone⁻¹') +picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_day', ascii_symbol='pm/day^2', symbol='pmNone⁻²') +picometers_per_year = Unit(3.168873850681143e-19, Dimensions(1, -1, 0, 0, 0), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') +picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') +picometers_per_year = Unit(3.168873850681143e-19, Dimensions(1, -1, 0, 0, 0), name='picometers_per_year', ascii_symbol='pm/yr', symbol='pmNone⁻¹') +picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_year', ascii_symbol='pm/yr^2', symbol='pmNone⁻²') +femtometers_per_second = Unit(1e-15, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') +femtometers_per_square_second = Unit(1e-15, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') +femtometers_per_millisecond = Unit(1e-12, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') +femtometers_per_square_millisecond = Unit(1e-12, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') +femtometers_per_microsecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') +femtometers_per_square_microsecond = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') +femtometers_per_nanosecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') +femtometers_per_square_nanosecond = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') +femtometers_per_picosecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') +femtometers_per_square_picosecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') +femtometers_per_femtosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') +femtometers_per_square_femtosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') +femtometers_per_attosecond = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') +femtometers_per_square_attosecond = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') +femtometers_per_minute = Unit(1.6666666666666667e-17, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') +femtometers_per_square_minute = Unit(1.6666666666666667e-17, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') +femtometers_per_hour = Unit(2.777777777777778e-18, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_hour', ascii_symbol='fm/hr', symbol='fmNone⁻¹') +femtometers_per_square_hour = Unit(2.777777777777778e-18, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_hour', ascii_symbol='fm/hr^2', symbol='fmNone⁻²') +femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') +femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') +femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_day', ascii_symbol='fm/day', symbol='fmNone⁻¹') +femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_day', ascii_symbol='fm/day^2', symbol='fmNone⁻²') +femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') +femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') +femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_year', ascii_symbol='fm/yr', symbol='fmNone⁻¹') +femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_year', ascii_symbol='fm/yr^2', symbol='fmNone⁻²') +attometers_per_second = Unit(1e-18, Dimensions(1, -1, 0, 0, 0), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') +attometers_per_square_second = Unit(1e-18, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') +attometers_per_millisecond = Unit(1e-15, Dimensions(1, -1, 0, 0, 0), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') +attometers_per_square_millisecond = Unit(1e-15, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') +attometers_per_microsecond = Unit(1.0000000000000002e-12, Dimensions(1, -1, 0, 0, 0), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') +attometers_per_square_microsecond = Unit(1.0000000000000002e-12, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') +attometers_per_nanosecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') +attometers_per_square_nanosecond = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') +attometers_per_picosecond = Unit(1.0000000000000002e-06, Dimensions(1, -1, 0, 0, 0), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') +attometers_per_square_picosecond = Unit(1.0000000000000002e-06, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') +attometers_per_femtosecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') +attometers_per_square_femtosecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') +attometers_per_attosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') +attometers_per_square_attosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') +attometers_per_minute = Unit(1.6666666666666668e-20, Dimensions(1, -1, 0, 0, 0), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') +attometers_per_square_minute = Unit(1.6666666666666668e-20, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') +attometers_per_hour = Unit(2.7777777777777778e-21, Dimensions(1, -1, 0, 0, 0), name='attometers_per_hour', ascii_symbol='am/hr', symbol='amNone⁻¹') +attometers_per_square_hour = Unit(2.7777777777777778e-21, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_hour', ascii_symbol='am/hr^2', symbol='amNone⁻²') +attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(1, -1, 0, 0, 0), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') +attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') +attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(1, -1, 0, 0, 0), name='attometers_per_day', ascii_symbol='am/day', symbol='amNone⁻¹') +attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_day', ascii_symbol='am/day^2', symbol='amNone⁻²') +attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(1, -1, 0, 0, 0), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') +attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') +attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(1, -1, 0, 0, 0), name='attometers_per_year', ascii_symbol='am/yr', symbol='amNone⁻¹') +attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_year', ascii_symbol='am/yr^2', symbol='amNone⁻²') +angstroms_per_second = Unit(1e-10, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_second', ascii_symbol='A/s', symbol='ÅNone⁻¹') +angstroms_per_square_second = Unit(1e-10, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_second', ascii_symbol='A/s^2', symbol='ÅNone⁻²') +angstroms_per_millisecond = Unit(1e-07, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_millisecond', ascii_symbol='A/ms', symbol='Åms⁻¹') +angstroms_per_square_millisecond = Unit(1e-07, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_millisecond', ascii_symbol='A/ms^2', symbol='Åms⁻²') +angstroms_per_microsecond = Unit(0.0001, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_microsecond', ascii_symbol='A/us', symbol='ŵs⁻¹') +angstroms_per_square_microsecond = Unit(0.0001, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_microsecond', ascii_symbol='A/us^2', symbol='ŵs⁻²') +angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_nanosecond', ascii_symbol='A/ns', symbol='Åns⁻¹') +angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_nanosecond', ascii_symbol='A/ns^2', symbol='Åns⁻²') +angstroms_per_picosecond = Unit(100.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_picosecond', ascii_symbol='A/ps', symbol='Åps⁻¹') +angstroms_per_square_picosecond = Unit(100.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_picosecond', ascii_symbol='A/ps^2', symbol='Åps⁻²') +angstroms_per_femtosecond = Unit(100000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_femtosecond', ascii_symbol='A/fs', symbol='Åfs⁻¹') +angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_femtosecond', ascii_symbol='A/fs^2', symbol='Åfs⁻²') +angstroms_per_attosecond = Unit(100000000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_attosecond', ascii_symbol='A/as', symbol='Åas⁻¹') +angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_attosecond', ascii_symbol='A/as^2', symbol='Åas⁻²') +angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_minute', ascii_symbol='A/min', symbol='ÅNone⁻¹') +angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_minute', ascii_symbol='A/min^2', symbol='ÅNone⁻²') +angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_hour', ascii_symbol='A/hr', symbol='ÅNone⁻¹') +angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_hour', ascii_symbol='A/hr^2', symbol='ÅNone⁻²') +angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='A/d', symbol='ÅNone⁻¹') +angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='A/d^2', symbol='ÅNone⁻²') +angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='A/day', symbol='ÅNone⁻¹') +angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='A/day^2', symbol='ÅNone⁻²') +angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='A/y', symbol='ÅNone⁻¹') +angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='A/y^2', symbol='ÅNone⁻²') +angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='A/yr', symbol='ÅNone⁻¹') +angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='A/yr^2', symbol='ÅNone⁻²') +angstroms_per_second = Unit(1e-10, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='NoneNone⁻¹') +angstroms_per_square_second = Unit(1e-10, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='NoneNone⁻²') +angstroms_per_millisecond = Unit(1e-07, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Nonems⁻¹') +angstroms_per_square_millisecond = Unit(1e-07, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Nonems⁻²') +angstroms_per_microsecond = Unit(0.0001, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='Noneµs⁻¹') +angstroms_per_square_microsecond = Unit(0.0001, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='Noneµs⁻²') +angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Nonens⁻¹') +angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Nonens⁻²') +angstroms_per_picosecond = Unit(100.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Noneps⁻¹') +angstroms_per_square_picosecond = Unit(100.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Noneps⁻²') +angstroms_per_femtosecond = Unit(100000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Nonefs⁻¹') +angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Nonefs⁻²') +angstroms_per_attosecond = Unit(100000000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Noneas⁻¹') +angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Noneas⁻²') +angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='NoneNone⁻¹') +angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='NoneNone⁻²') +angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_hour', ascii_symbol='Ang/hr', symbol='NoneNone⁻¹') +angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_hour', ascii_symbol='Ang/hr^2', symbol='NoneNone⁻²') +angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='NoneNone⁻¹') +angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='NoneNone⁻²') +angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='Ang/day', symbol='NoneNone⁻¹') +angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='Ang/day^2', symbol='NoneNone⁻²') +angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='NoneNone⁻¹') +angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='NoneNone⁻²') +angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/yr', symbol='NoneNone⁻¹') +angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/yr^2', symbol='NoneNone⁻²') # # Lookup table from symbols to units @@ -1721,8 +913,6 @@ def __init__(self, name: str, units: list[NamedUnit]): "pm": picometers, "fm": femtometers, "am": attometers, - "dm": decimeters, - "cm": centimeters, "s": seconds, "ms": milliseconds, "us": microseconds, @@ -1746,19 +936,19 @@ def __init__(self, name: str, units: list[NamedUnit]): "fg": femtograms, "ag": attograms, "A": angstroms, - "EA": exaamperes, - "PA": petaamperes, - "TA": teraamperes, - "GA": gigaamperes, - "MA": megaamperes, - "kA": kiloamperes, - "mA": milliamperes, - "uA": microamperes, - "µA": microamperes, - "nA": nanoamperes, - "pA": picoamperes, - "fA": femtoamperes, - "aA": attoamperes, + "EA": exaamps, + "PA": petaamps, + "TA": teraamps, + "GA": gigaamps, + "MA": megaamps, + "kA": kiloamps, + "mA": milliamps, + "uA": microamps, + "µA": microamps, + "nA": nanoamps, + "pA": picoamps, + "fA": femtoamps, + "aA": attoamps, "K": kelvin, "EK": exakelvin, "PK": petakelvin, @@ -1843,7 +1033,7 @@ def __init__(self, name: str, units: list[NamedUnit]): "pW": picowatts, "fW": femtowatts, "aW": attowatts, - "C": kelvin, + "C": degrees_celsius, "EC": exacoulombs, "PC": petacoulombs, "TC": teracoulombs, @@ -1967,71 +1157,17 @@ def __init__(self, name: str, units: list[NamedUnit]): "pH": picohenry, "fH": femtohenry, "aH": attohenry, - "Ang": angstroms, "Å": angstroms, + "Ang": angstroms, "min": minutes, - "h": hours, + "hr": hours, "d": days, + "day": days, "y": years, + "yr": years, "deg": degrees, "rad": radians, "sr": stradians, - "l": litres, - "eV": electronvolts, - "EeV": exaelectronvolts, - "PeV": petaelectronvolts, - "TeV": teraelectronvolts, - "GeV": gigaelectronvolts, - "MeV": megaelectronvolts, - "keV": kiloelectronvolts, - "meV": millielectronvolts, - "ueV": microelectronvolts, - "µeV": microelectronvolts, - "neV": nanoelectronvolts, - "peV": picoelectronvolts, - "feV": femtoelectronvolts, - "aeV": attoelectronvolts, - "au": atomic_mass_units, - "mol": moles, - "mmol": millimoles, - "umol": micromoles, - "µmol": micromoles, - "nmol": nanomoles, - "pmol": picomoles, - "fmol": femtomoles, - "amol": attomoles, - "kgForce": kg_force, - "miles": miles, - "yrd": yards, - "ft": feet, - "in": inches, - "lb": pounds, - "lbf": pounds_force, - "oz": ounces, - "psi": pounds_force_per_square_inch, - "percent": percent, - "%": percent, - "Amps": amperes, - "amps": amperes, - "Coulombs": degrees_celsius, - "coulombs": degrees_celsius, - "yr": years, - "year": years, - "day": days, - "hr": hours, - "hour": hours, - "amu": atomic_mass_units, - "degr": degrees, - "Deg": degrees, - "degrees": degrees, - "Degrees": degrees, - "Counts": none, - "counts": none, - "cnts": none, - "Cnts": none, - "a.u.": none, - "fraction": none, - "Fraction": none, } @@ -2056,13 +1192,8 @@ def __init__(self, name: str, units: list[NamedUnit]): picometers, femtometers, attometers, - decimeters, - centimeters, angstroms, - miles, - yards, - feet, - inches, + angstroms, ]) area = UnitGroup( @@ -2081,19 +1212,13 @@ def __init__(self, name: str, units: list[NamedUnit]): square_picometers, square_femtometers, square_attometers, - square_decimeters, - square_centimeters, square_angstroms, - square_miles, - square_yards, - square_feet, - square_inches, + square_angstroms, ]) volume = UnitGroup( name = 'volume', units = [ - litres, cubic_meters, cubic_exameters, cubic_petameters, @@ -2107,13 +1232,8 @@ def __init__(self, name: str, units: list[NamedUnit]): cubic_picometers, cubic_femtometers, cubic_attometers, - cubic_decimeters, - cubic_centimeters, cubic_angstroms, - cubic_miles, - cubic_yards, - cubic_feet, - cubic_inches, + cubic_angstroms, ]) inverse_length = UnitGroup( @@ -2132,13 +1252,8 @@ def __init__(self, name: str, units: list[NamedUnit]): per_picometer, per_femtometer, per_attometer, - per_decimeter, - per_centimeter, per_angstrom, - per_mile, - per_yard, - per_foot, - per_inch, + per_angstrom, ]) inverse_area = UnitGroup( @@ -2157,13 +1272,8 @@ def __init__(self, name: str, units: list[NamedUnit]): per_square_picometer, per_square_femtometer, per_square_attometer, - per_square_decimeter, - per_square_centimeter, per_square_angstrom, - per_square_mile, - per_square_yard, - per_square_foot, - per_square_inch, + per_square_angstrom, ]) inverse_volume = UnitGroup( @@ -2182,13 +1292,8 @@ def __init__(self, name: str, units: list[NamedUnit]): per_cubic_picometer, per_cubic_femtometer, per_cubic_attometer, - per_cubic_decimeter, - per_cubic_centimeter, per_cubic_angstrom, - per_cubic_mile, - per_cubic_yard, - per_cubic_foot, - per_cubic_inch, + per_cubic_angstrom, ]) time = UnitGroup( @@ -2204,6 +1309,8 @@ def __init__(self, name: str, units: list[NamedUnit]): minutes, hours, days, + days, + years, years, ]) @@ -2239,6 +1346,8 @@ def __init__(self, name: str, units: list[NamedUnit]): meters_per_minute, meters_per_hour, meters_per_day, + meters_per_day, + meters_per_year, meters_per_year, exameters_per_second, exameters_per_millisecond, @@ -2250,6 +1359,8 @@ def __init__(self, name: str, units: list[NamedUnit]): exameters_per_minute, exameters_per_hour, exameters_per_day, + exameters_per_day, + exameters_per_year, exameters_per_year, petameters_per_second, petameters_per_millisecond, @@ -2261,6 +1372,8 @@ def __init__(self, name: str, units: list[NamedUnit]): petameters_per_minute, petameters_per_hour, petameters_per_day, + petameters_per_day, + petameters_per_year, petameters_per_year, terameters_per_second, terameters_per_millisecond, @@ -2272,6 +1385,8 @@ def __init__(self, name: str, units: list[NamedUnit]): terameters_per_minute, terameters_per_hour, terameters_per_day, + terameters_per_day, + terameters_per_year, terameters_per_year, gigameters_per_second, gigameters_per_millisecond, @@ -2283,6 +1398,8 @@ def __init__(self, name: str, units: list[NamedUnit]): gigameters_per_minute, gigameters_per_hour, gigameters_per_day, + gigameters_per_day, + gigameters_per_year, gigameters_per_year, megameters_per_second, megameters_per_millisecond, @@ -2294,6 +1411,8 @@ def __init__(self, name: str, units: list[NamedUnit]): megameters_per_minute, megameters_per_hour, megameters_per_day, + megameters_per_day, + megameters_per_year, megameters_per_year, kilometers_per_second, kilometers_per_millisecond, @@ -2305,6 +1424,8 @@ def __init__(self, name: str, units: list[NamedUnit]): kilometers_per_minute, kilometers_per_hour, kilometers_per_day, + kilometers_per_day, + kilometers_per_year, kilometers_per_year, millimeters_per_second, millimeters_per_millisecond, @@ -2316,6 +1437,8 @@ def __init__(self, name: str, units: list[NamedUnit]): millimeters_per_minute, millimeters_per_hour, millimeters_per_day, + millimeters_per_day, + millimeters_per_year, millimeters_per_year, micrometers_per_second, micrometers_per_millisecond, @@ -2327,6 +1450,8 @@ def __init__(self, name: str, units: list[NamedUnit]): micrometers_per_minute, micrometers_per_hour, micrometers_per_day, + micrometers_per_day, + micrometers_per_year, micrometers_per_year, nanometers_per_second, nanometers_per_millisecond, @@ -2338,6 +1463,8 @@ def __init__(self, name: str, units: list[NamedUnit]): nanometers_per_minute, nanometers_per_hour, nanometers_per_day, + nanometers_per_day, + nanometers_per_year, nanometers_per_year, picometers_per_second, picometers_per_millisecond, @@ -2349,6 +1476,8 @@ def __init__(self, name: str, units: list[NamedUnit]): picometers_per_minute, picometers_per_hour, picometers_per_day, + picometers_per_day, + picometers_per_year, picometers_per_year, femtometers_per_second, femtometers_per_millisecond, @@ -2360,6 +1489,8 @@ def __init__(self, name: str, units: list[NamedUnit]): femtometers_per_minute, femtometers_per_hour, femtometers_per_day, + femtometers_per_day, + femtometers_per_year, femtometers_per_year, attometers_per_second, attometers_per_millisecond, @@ -2371,29 +1502,9 @@ def __init__(self, name: str, units: list[NamedUnit]): attometers_per_minute, attometers_per_hour, attometers_per_day, + attometers_per_day, + attometers_per_year, attometers_per_year, - decimeters_per_second, - decimeters_per_millisecond, - decimeters_per_microsecond, - decimeters_per_nanosecond, - decimeters_per_picosecond, - decimeters_per_femtosecond, - decimeters_per_attosecond, - decimeters_per_minute, - decimeters_per_hour, - decimeters_per_day, - decimeters_per_year, - centimeters_per_second, - centimeters_per_millisecond, - centimeters_per_microsecond, - centimeters_per_nanosecond, - centimeters_per_picosecond, - centimeters_per_femtosecond, - centimeters_per_attosecond, - centimeters_per_minute, - centimeters_per_hour, - centimeters_per_day, - centimeters_per_year, angstroms_per_second, angstroms_per_millisecond, angstroms_per_microsecond, @@ -2404,51 +1515,22 @@ def __init__(self, name: str, units: list[NamedUnit]): angstroms_per_minute, angstroms_per_hour, angstroms_per_day, + angstroms_per_day, + angstroms_per_year, + angstroms_per_year, + angstroms_per_second, + angstroms_per_millisecond, + angstroms_per_microsecond, + angstroms_per_nanosecond, + angstroms_per_picosecond, + angstroms_per_femtosecond, + angstroms_per_attosecond, + angstroms_per_minute, + angstroms_per_hour, + angstroms_per_day, + angstroms_per_day, + angstroms_per_year, angstroms_per_year, - miles_per_second, - miles_per_millisecond, - miles_per_microsecond, - miles_per_nanosecond, - miles_per_picosecond, - miles_per_femtosecond, - miles_per_attosecond, - miles_per_minute, - miles_per_hour, - miles_per_day, - miles_per_year, - yards_per_second, - yards_per_millisecond, - yards_per_microsecond, - yards_per_nanosecond, - yards_per_picosecond, - yards_per_femtosecond, - yards_per_attosecond, - yards_per_minute, - yards_per_hour, - yards_per_day, - yards_per_year, - feet_per_second, - feet_per_millisecond, - feet_per_microsecond, - feet_per_nanosecond, - feet_per_picosecond, - feet_per_femtosecond, - feet_per_attosecond, - feet_per_minute, - feet_per_hour, - feet_per_day, - feet_per_year, - inches_per_second, - inches_per_millisecond, - inches_per_microsecond, - inches_per_nanosecond, - inches_per_picosecond, - inches_per_femtosecond, - inches_per_attosecond, - inches_per_minute, - inches_per_hour, - inches_per_day, - inches_per_year, ]) acceleration = UnitGroup( @@ -2464,6 +1546,8 @@ def __init__(self, name: str, units: list[NamedUnit]): meters_per_square_minute, meters_per_square_hour, meters_per_square_day, + meters_per_square_day, + meters_per_square_year, meters_per_square_year, exameters_per_square_second, exameters_per_square_millisecond, @@ -2475,6 +1559,8 @@ def __init__(self, name: str, units: list[NamedUnit]): exameters_per_square_minute, exameters_per_square_hour, exameters_per_square_day, + exameters_per_square_day, + exameters_per_square_year, exameters_per_square_year, petameters_per_square_second, petameters_per_square_millisecond, @@ -2486,6 +1572,8 @@ def __init__(self, name: str, units: list[NamedUnit]): petameters_per_square_minute, petameters_per_square_hour, petameters_per_square_day, + petameters_per_square_day, + petameters_per_square_year, petameters_per_square_year, terameters_per_square_second, terameters_per_square_millisecond, @@ -2497,6 +1585,8 @@ def __init__(self, name: str, units: list[NamedUnit]): terameters_per_square_minute, terameters_per_square_hour, terameters_per_square_day, + terameters_per_square_day, + terameters_per_square_year, terameters_per_square_year, gigameters_per_square_second, gigameters_per_square_millisecond, @@ -2508,6 +1598,8 @@ def __init__(self, name: str, units: list[NamedUnit]): gigameters_per_square_minute, gigameters_per_square_hour, gigameters_per_square_day, + gigameters_per_square_day, + gigameters_per_square_year, gigameters_per_square_year, megameters_per_square_second, megameters_per_square_millisecond, @@ -2519,6 +1611,8 @@ def __init__(self, name: str, units: list[NamedUnit]): megameters_per_square_minute, megameters_per_square_hour, megameters_per_square_day, + megameters_per_square_day, + megameters_per_square_year, megameters_per_square_year, kilometers_per_square_second, kilometers_per_square_millisecond, @@ -2530,6 +1624,8 @@ def __init__(self, name: str, units: list[NamedUnit]): kilometers_per_square_minute, kilometers_per_square_hour, kilometers_per_square_day, + kilometers_per_square_day, + kilometers_per_square_year, kilometers_per_square_year, millimeters_per_square_second, millimeters_per_square_millisecond, @@ -2541,6 +1637,8 @@ def __init__(self, name: str, units: list[NamedUnit]): millimeters_per_square_minute, millimeters_per_square_hour, millimeters_per_square_day, + millimeters_per_square_day, + millimeters_per_square_year, millimeters_per_square_year, micrometers_per_square_second, micrometers_per_square_millisecond, @@ -2552,6 +1650,8 @@ def __init__(self, name: str, units: list[NamedUnit]): micrometers_per_square_minute, micrometers_per_square_hour, micrometers_per_square_day, + micrometers_per_square_day, + micrometers_per_square_year, micrometers_per_square_year, nanometers_per_square_second, nanometers_per_square_millisecond, @@ -2563,6 +1663,8 @@ def __init__(self, name: str, units: list[NamedUnit]): nanometers_per_square_minute, nanometers_per_square_hour, nanometers_per_square_day, + nanometers_per_square_day, + nanometers_per_square_year, nanometers_per_square_year, picometers_per_square_second, picometers_per_square_millisecond, @@ -2574,6 +1676,8 @@ def __init__(self, name: str, units: list[NamedUnit]): picometers_per_square_minute, picometers_per_square_hour, picometers_per_square_day, + picometers_per_square_day, + picometers_per_square_year, picometers_per_square_year, femtometers_per_square_second, femtometers_per_square_millisecond, @@ -2585,6 +1689,8 @@ def __init__(self, name: str, units: list[NamedUnit]): femtometers_per_square_minute, femtometers_per_square_hour, femtometers_per_square_day, + femtometers_per_square_day, + femtometers_per_square_year, femtometers_per_square_year, attometers_per_square_second, attometers_per_square_millisecond, @@ -2596,29 +1702,9 @@ def __init__(self, name: str, units: list[NamedUnit]): attometers_per_square_minute, attometers_per_square_hour, attometers_per_square_day, + attometers_per_square_day, + attometers_per_square_year, attometers_per_square_year, - decimeters_per_square_second, - decimeters_per_square_millisecond, - decimeters_per_square_microsecond, - decimeters_per_square_nanosecond, - decimeters_per_square_picosecond, - decimeters_per_square_femtosecond, - decimeters_per_square_attosecond, - decimeters_per_square_minute, - decimeters_per_square_hour, - decimeters_per_square_day, - decimeters_per_square_year, - centimeters_per_square_second, - centimeters_per_square_millisecond, - centimeters_per_square_microsecond, - centimeters_per_square_nanosecond, - centimeters_per_square_picosecond, - centimeters_per_square_femtosecond, - centimeters_per_square_attosecond, - centimeters_per_square_minute, - centimeters_per_square_hour, - centimeters_per_square_day, - centimeters_per_square_year, angstroms_per_square_second, angstroms_per_square_millisecond, angstroms_per_square_microsecond, @@ -2629,376 +1715,22 @@ def __init__(self, name: str, units: list[NamedUnit]): angstroms_per_square_minute, angstroms_per_square_hour, angstroms_per_square_day, + angstroms_per_square_day, + angstroms_per_square_year, + angstroms_per_square_year, + angstroms_per_square_second, + angstroms_per_square_millisecond, + angstroms_per_square_microsecond, + angstroms_per_square_nanosecond, + angstroms_per_square_picosecond, + angstroms_per_square_femtosecond, + angstroms_per_square_attosecond, + angstroms_per_square_minute, + angstroms_per_square_hour, + angstroms_per_square_day, + angstroms_per_square_day, + angstroms_per_square_year, angstroms_per_square_year, - miles_per_square_second, - miles_per_square_millisecond, - miles_per_square_microsecond, - miles_per_square_nanosecond, - miles_per_square_picosecond, - miles_per_square_femtosecond, - miles_per_square_attosecond, - miles_per_square_minute, - miles_per_square_hour, - miles_per_square_day, - miles_per_square_year, - yards_per_square_second, - yards_per_square_millisecond, - yards_per_square_microsecond, - yards_per_square_nanosecond, - yards_per_square_picosecond, - yards_per_square_femtosecond, - yards_per_square_attosecond, - yards_per_square_minute, - yards_per_square_hour, - yards_per_square_day, - yards_per_square_year, - feet_per_square_second, - feet_per_square_millisecond, - feet_per_square_microsecond, - feet_per_square_nanosecond, - feet_per_square_picosecond, - feet_per_square_femtosecond, - feet_per_square_attosecond, - feet_per_square_minute, - feet_per_square_hour, - feet_per_square_day, - feet_per_square_year, - inches_per_square_second, - inches_per_square_millisecond, - inches_per_square_microsecond, - inches_per_square_nanosecond, - inches_per_square_picosecond, - inches_per_square_femtosecond, - inches_per_square_attosecond, - inches_per_square_minute, - inches_per_square_hour, - inches_per_square_day, - inches_per_square_year, -]) - -density = UnitGroup( - name = 'density', - units = [ - grams_per_cubic_meter, - exagrams_per_cubic_meter, - petagrams_per_cubic_meter, - teragrams_per_cubic_meter, - gigagrams_per_cubic_meter, - megagrams_per_cubic_meter, - kilograms_per_cubic_meter, - milligrams_per_cubic_meter, - micrograms_per_cubic_meter, - nanograms_per_cubic_meter, - picograms_per_cubic_meter, - femtograms_per_cubic_meter, - attograms_per_cubic_meter, - atomic_mass_units_per_cubic_meter, - pounds_per_cubic_meter, - ounces_per_cubic_meter, - grams_per_cubic_exameter, - exagrams_per_cubic_exameter, - petagrams_per_cubic_exameter, - teragrams_per_cubic_exameter, - gigagrams_per_cubic_exameter, - megagrams_per_cubic_exameter, - kilograms_per_cubic_exameter, - milligrams_per_cubic_exameter, - micrograms_per_cubic_exameter, - nanograms_per_cubic_exameter, - picograms_per_cubic_exameter, - femtograms_per_cubic_exameter, - attograms_per_cubic_exameter, - atomic_mass_units_per_cubic_exameter, - pounds_per_cubic_exameter, - ounces_per_cubic_exameter, - grams_per_cubic_petameter, - exagrams_per_cubic_petameter, - petagrams_per_cubic_petameter, - teragrams_per_cubic_petameter, - gigagrams_per_cubic_petameter, - megagrams_per_cubic_petameter, - kilograms_per_cubic_petameter, - milligrams_per_cubic_petameter, - micrograms_per_cubic_petameter, - nanograms_per_cubic_petameter, - picograms_per_cubic_petameter, - femtograms_per_cubic_petameter, - attograms_per_cubic_petameter, - atomic_mass_units_per_cubic_petameter, - pounds_per_cubic_petameter, - ounces_per_cubic_petameter, - grams_per_cubic_terameter, - exagrams_per_cubic_terameter, - petagrams_per_cubic_terameter, - teragrams_per_cubic_terameter, - gigagrams_per_cubic_terameter, - megagrams_per_cubic_terameter, - kilograms_per_cubic_terameter, - milligrams_per_cubic_terameter, - micrograms_per_cubic_terameter, - nanograms_per_cubic_terameter, - picograms_per_cubic_terameter, - femtograms_per_cubic_terameter, - attograms_per_cubic_terameter, - atomic_mass_units_per_cubic_terameter, - pounds_per_cubic_terameter, - ounces_per_cubic_terameter, - grams_per_cubic_gigameter, - exagrams_per_cubic_gigameter, - petagrams_per_cubic_gigameter, - teragrams_per_cubic_gigameter, - gigagrams_per_cubic_gigameter, - megagrams_per_cubic_gigameter, - kilograms_per_cubic_gigameter, - milligrams_per_cubic_gigameter, - micrograms_per_cubic_gigameter, - nanograms_per_cubic_gigameter, - picograms_per_cubic_gigameter, - femtograms_per_cubic_gigameter, - attograms_per_cubic_gigameter, - atomic_mass_units_per_cubic_gigameter, - pounds_per_cubic_gigameter, - ounces_per_cubic_gigameter, - grams_per_cubic_megameter, - exagrams_per_cubic_megameter, - petagrams_per_cubic_megameter, - teragrams_per_cubic_megameter, - gigagrams_per_cubic_megameter, - megagrams_per_cubic_megameter, - kilograms_per_cubic_megameter, - milligrams_per_cubic_megameter, - micrograms_per_cubic_megameter, - nanograms_per_cubic_megameter, - picograms_per_cubic_megameter, - femtograms_per_cubic_megameter, - attograms_per_cubic_megameter, - atomic_mass_units_per_cubic_megameter, - pounds_per_cubic_megameter, - ounces_per_cubic_megameter, - grams_per_cubic_kilometer, - exagrams_per_cubic_kilometer, - petagrams_per_cubic_kilometer, - teragrams_per_cubic_kilometer, - gigagrams_per_cubic_kilometer, - megagrams_per_cubic_kilometer, - kilograms_per_cubic_kilometer, - milligrams_per_cubic_kilometer, - micrograms_per_cubic_kilometer, - nanograms_per_cubic_kilometer, - picograms_per_cubic_kilometer, - femtograms_per_cubic_kilometer, - attograms_per_cubic_kilometer, - atomic_mass_units_per_cubic_kilometer, - pounds_per_cubic_kilometer, - ounces_per_cubic_kilometer, - grams_per_cubic_millimeter, - exagrams_per_cubic_millimeter, - petagrams_per_cubic_millimeter, - teragrams_per_cubic_millimeter, - gigagrams_per_cubic_millimeter, - megagrams_per_cubic_millimeter, - kilograms_per_cubic_millimeter, - milligrams_per_cubic_millimeter, - micrograms_per_cubic_millimeter, - nanograms_per_cubic_millimeter, - picograms_per_cubic_millimeter, - femtograms_per_cubic_millimeter, - attograms_per_cubic_millimeter, - atomic_mass_units_per_cubic_millimeter, - pounds_per_cubic_millimeter, - ounces_per_cubic_millimeter, - grams_per_cubic_micrometer, - exagrams_per_cubic_micrometer, - petagrams_per_cubic_micrometer, - teragrams_per_cubic_micrometer, - gigagrams_per_cubic_micrometer, - megagrams_per_cubic_micrometer, - kilograms_per_cubic_micrometer, - milligrams_per_cubic_micrometer, - micrograms_per_cubic_micrometer, - nanograms_per_cubic_micrometer, - picograms_per_cubic_micrometer, - femtograms_per_cubic_micrometer, - attograms_per_cubic_micrometer, - atomic_mass_units_per_cubic_micrometer, - pounds_per_cubic_micrometer, - ounces_per_cubic_micrometer, - grams_per_cubic_nanometer, - exagrams_per_cubic_nanometer, - petagrams_per_cubic_nanometer, - teragrams_per_cubic_nanometer, - gigagrams_per_cubic_nanometer, - megagrams_per_cubic_nanometer, - kilograms_per_cubic_nanometer, - milligrams_per_cubic_nanometer, - micrograms_per_cubic_nanometer, - nanograms_per_cubic_nanometer, - picograms_per_cubic_nanometer, - femtograms_per_cubic_nanometer, - attograms_per_cubic_nanometer, - atomic_mass_units_per_cubic_nanometer, - pounds_per_cubic_nanometer, - ounces_per_cubic_nanometer, - grams_per_cubic_picometer, - exagrams_per_cubic_picometer, - petagrams_per_cubic_picometer, - teragrams_per_cubic_picometer, - gigagrams_per_cubic_picometer, - megagrams_per_cubic_picometer, - kilograms_per_cubic_picometer, - milligrams_per_cubic_picometer, - micrograms_per_cubic_picometer, - nanograms_per_cubic_picometer, - picograms_per_cubic_picometer, - femtograms_per_cubic_picometer, - attograms_per_cubic_picometer, - atomic_mass_units_per_cubic_picometer, - pounds_per_cubic_picometer, - ounces_per_cubic_picometer, - grams_per_cubic_femtometer, - exagrams_per_cubic_femtometer, - petagrams_per_cubic_femtometer, - teragrams_per_cubic_femtometer, - gigagrams_per_cubic_femtometer, - megagrams_per_cubic_femtometer, - kilograms_per_cubic_femtometer, - milligrams_per_cubic_femtometer, - micrograms_per_cubic_femtometer, - nanograms_per_cubic_femtometer, - picograms_per_cubic_femtometer, - femtograms_per_cubic_femtometer, - attograms_per_cubic_femtometer, - atomic_mass_units_per_cubic_femtometer, - pounds_per_cubic_femtometer, - ounces_per_cubic_femtometer, - grams_per_cubic_attometer, - exagrams_per_cubic_attometer, - petagrams_per_cubic_attometer, - teragrams_per_cubic_attometer, - gigagrams_per_cubic_attometer, - megagrams_per_cubic_attometer, - kilograms_per_cubic_attometer, - milligrams_per_cubic_attometer, - micrograms_per_cubic_attometer, - nanograms_per_cubic_attometer, - picograms_per_cubic_attometer, - femtograms_per_cubic_attometer, - attograms_per_cubic_attometer, - atomic_mass_units_per_cubic_attometer, - pounds_per_cubic_attometer, - ounces_per_cubic_attometer, - grams_per_cubic_decimeter, - exagrams_per_cubic_decimeter, - petagrams_per_cubic_decimeter, - teragrams_per_cubic_decimeter, - gigagrams_per_cubic_decimeter, - megagrams_per_cubic_decimeter, - kilograms_per_cubic_decimeter, - milligrams_per_cubic_decimeter, - micrograms_per_cubic_decimeter, - nanograms_per_cubic_decimeter, - picograms_per_cubic_decimeter, - femtograms_per_cubic_decimeter, - attograms_per_cubic_decimeter, - atomic_mass_units_per_cubic_decimeter, - pounds_per_cubic_decimeter, - ounces_per_cubic_decimeter, - grams_per_cubic_centimeter, - exagrams_per_cubic_centimeter, - petagrams_per_cubic_centimeter, - teragrams_per_cubic_centimeter, - gigagrams_per_cubic_centimeter, - megagrams_per_cubic_centimeter, - kilograms_per_cubic_centimeter, - milligrams_per_cubic_centimeter, - micrograms_per_cubic_centimeter, - nanograms_per_cubic_centimeter, - picograms_per_cubic_centimeter, - femtograms_per_cubic_centimeter, - attograms_per_cubic_centimeter, - atomic_mass_units_per_cubic_centimeter, - pounds_per_cubic_centimeter, - ounces_per_cubic_centimeter, - grams_per_cubic_angstrom, - exagrams_per_cubic_angstrom, - petagrams_per_cubic_angstrom, - teragrams_per_cubic_angstrom, - gigagrams_per_cubic_angstrom, - megagrams_per_cubic_angstrom, - kilograms_per_cubic_angstrom, - milligrams_per_cubic_angstrom, - micrograms_per_cubic_angstrom, - nanograms_per_cubic_angstrom, - picograms_per_cubic_angstrom, - femtograms_per_cubic_angstrom, - attograms_per_cubic_angstrom, - atomic_mass_units_per_cubic_angstrom, - pounds_per_cubic_angstrom, - ounces_per_cubic_angstrom, - grams_per_cubic_mile, - exagrams_per_cubic_mile, - petagrams_per_cubic_mile, - teragrams_per_cubic_mile, - gigagrams_per_cubic_mile, - megagrams_per_cubic_mile, - kilograms_per_cubic_mile, - milligrams_per_cubic_mile, - micrograms_per_cubic_mile, - nanograms_per_cubic_mile, - picograms_per_cubic_mile, - femtograms_per_cubic_mile, - attograms_per_cubic_mile, - atomic_mass_units_per_cubic_mile, - pounds_per_cubic_mile, - ounces_per_cubic_mile, - grams_per_cubic_yard, - exagrams_per_cubic_yard, - petagrams_per_cubic_yard, - teragrams_per_cubic_yard, - gigagrams_per_cubic_yard, - megagrams_per_cubic_yard, - kilograms_per_cubic_yard, - milligrams_per_cubic_yard, - micrograms_per_cubic_yard, - nanograms_per_cubic_yard, - picograms_per_cubic_yard, - femtograms_per_cubic_yard, - attograms_per_cubic_yard, - atomic_mass_units_per_cubic_yard, - pounds_per_cubic_yard, - ounces_per_cubic_yard, - grams_per_cubic_foot, - exagrams_per_cubic_foot, - petagrams_per_cubic_foot, - teragrams_per_cubic_foot, - gigagrams_per_cubic_foot, - megagrams_per_cubic_foot, - kilograms_per_cubic_foot, - milligrams_per_cubic_foot, - micrograms_per_cubic_foot, - nanograms_per_cubic_foot, - picograms_per_cubic_foot, - femtograms_per_cubic_foot, - attograms_per_cubic_foot, - atomic_mass_units_per_cubic_foot, - pounds_per_cubic_foot, - ounces_per_cubic_foot, - grams_per_cubic_inch, - exagrams_per_cubic_inch, - petagrams_per_cubic_inch, - teragrams_per_cubic_inch, - gigagrams_per_cubic_inch, - megagrams_per_cubic_inch, - kilograms_per_cubic_inch, - milligrams_per_cubic_inch, - micrograms_per_cubic_inch, - nanograms_per_cubic_inch, - picograms_per_cubic_inch, - femtograms_per_cubic_inch, - attograms_per_cubic_inch, - atomic_mass_units_per_cubic_inch, - pounds_per_cubic_inch, - ounces_per_cubic_inch, ]) force = UnitGroup( @@ -3017,8 +1749,6 @@ def __init__(self, name: str, units: list[NamedUnit]): piconewtons, femtonewtons, attonewtons, - kg_force, - pounds_force, ]) pressure = UnitGroup( @@ -3037,7 +1767,6 @@ def __init__(self, name: str, units: list[NamedUnit]): picopascals, femtopascals, attopascals, - pounds_force_per_square_inch, ]) energy = UnitGroup( @@ -3056,19 +1785,6 @@ def __init__(self, name: str, units: list[NamedUnit]): picojoules, femtojoules, attojoules, - electronvolts, - exaelectronvolts, - petaelectronvolts, - teraelectronvolts, - gigaelectronvolts, - megaelectronvolts, - kiloelectronvolts, - millielectronvolts, - microelectronvolts, - nanoelectronvolts, - picoelectronvolts, - femtoelectronvolts, - attoelectronvolts, ]) power = UnitGroup( @@ -3251,246 +1967,3 @@ def __init__(self, name: str, units: list[NamedUnit]): attokelvin, degrees_celsius, ]) - -dimensionless = UnitGroup( - name = 'dimensionless', - units = [ - none, - percent, -]) - -angle = UnitGroup( - name = 'angle', - units = [ - degrees, - radians, -]) - -solid_angle = UnitGroup( - name = 'solid_angle', - units = [ - stradians, -]) - -amount = UnitGroup( - name = 'amount', - units = [ - moles, - millimoles, - micromoles, - nanomoles, - picomoles, - femtomoles, - attomoles, -]) - -concentration = UnitGroup( - name = 'concentration', - units = [ - moles_per_cubic_meter, - millimoles_per_cubic_meter, - micromoles_per_cubic_meter, - nanomoles_per_cubic_meter, - picomoles_per_cubic_meter, - femtomoles_per_cubic_meter, - attomoles_per_cubic_meter, - moles_per_cubic_exameter, - millimoles_per_cubic_exameter, - micromoles_per_cubic_exameter, - nanomoles_per_cubic_exameter, - picomoles_per_cubic_exameter, - femtomoles_per_cubic_exameter, - attomoles_per_cubic_exameter, - moles_per_cubic_petameter, - millimoles_per_cubic_petameter, - micromoles_per_cubic_petameter, - nanomoles_per_cubic_petameter, - picomoles_per_cubic_petameter, - femtomoles_per_cubic_petameter, - attomoles_per_cubic_petameter, - moles_per_cubic_terameter, - millimoles_per_cubic_terameter, - micromoles_per_cubic_terameter, - nanomoles_per_cubic_terameter, - picomoles_per_cubic_terameter, - femtomoles_per_cubic_terameter, - attomoles_per_cubic_terameter, - moles_per_cubic_gigameter, - millimoles_per_cubic_gigameter, - micromoles_per_cubic_gigameter, - nanomoles_per_cubic_gigameter, - picomoles_per_cubic_gigameter, - femtomoles_per_cubic_gigameter, - attomoles_per_cubic_gigameter, - moles_per_cubic_megameter, - millimoles_per_cubic_megameter, - micromoles_per_cubic_megameter, - nanomoles_per_cubic_megameter, - picomoles_per_cubic_megameter, - femtomoles_per_cubic_megameter, - attomoles_per_cubic_megameter, - moles_per_cubic_kilometer, - millimoles_per_cubic_kilometer, - micromoles_per_cubic_kilometer, - nanomoles_per_cubic_kilometer, - picomoles_per_cubic_kilometer, - femtomoles_per_cubic_kilometer, - attomoles_per_cubic_kilometer, - moles_per_cubic_millimeter, - millimoles_per_cubic_millimeter, - micromoles_per_cubic_millimeter, - nanomoles_per_cubic_millimeter, - picomoles_per_cubic_millimeter, - femtomoles_per_cubic_millimeter, - attomoles_per_cubic_millimeter, - moles_per_cubic_micrometer, - millimoles_per_cubic_micrometer, - micromoles_per_cubic_micrometer, - nanomoles_per_cubic_micrometer, - picomoles_per_cubic_micrometer, - femtomoles_per_cubic_micrometer, - attomoles_per_cubic_micrometer, - moles_per_cubic_nanometer, - millimoles_per_cubic_nanometer, - micromoles_per_cubic_nanometer, - nanomoles_per_cubic_nanometer, - picomoles_per_cubic_nanometer, - femtomoles_per_cubic_nanometer, - attomoles_per_cubic_nanometer, - moles_per_cubic_picometer, - millimoles_per_cubic_picometer, - micromoles_per_cubic_picometer, - nanomoles_per_cubic_picometer, - picomoles_per_cubic_picometer, - femtomoles_per_cubic_picometer, - attomoles_per_cubic_picometer, - moles_per_cubic_femtometer, - millimoles_per_cubic_femtometer, - micromoles_per_cubic_femtometer, - nanomoles_per_cubic_femtometer, - picomoles_per_cubic_femtometer, - femtomoles_per_cubic_femtometer, - attomoles_per_cubic_femtometer, - moles_per_cubic_attometer, - millimoles_per_cubic_attometer, - micromoles_per_cubic_attometer, - nanomoles_per_cubic_attometer, - picomoles_per_cubic_attometer, - femtomoles_per_cubic_attometer, - attomoles_per_cubic_attometer, - moles_per_cubic_decimeter, - millimoles_per_cubic_decimeter, - micromoles_per_cubic_decimeter, - nanomoles_per_cubic_decimeter, - picomoles_per_cubic_decimeter, - femtomoles_per_cubic_decimeter, - attomoles_per_cubic_decimeter, - moles_per_cubic_centimeter, - millimoles_per_cubic_centimeter, - micromoles_per_cubic_centimeter, - nanomoles_per_cubic_centimeter, - picomoles_per_cubic_centimeter, - femtomoles_per_cubic_centimeter, - attomoles_per_cubic_centimeter, - moles_per_cubic_angstrom, - millimoles_per_cubic_angstrom, - micromoles_per_cubic_angstrom, - nanomoles_per_cubic_angstrom, - picomoles_per_cubic_angstrom, - femtomoles_per_cubic_angstrom, - attomoles_per_cubic_angstrom, - moles_per_cubic_mile, - millimoles_per_cubic_mile, - micromoles_per_cubic_mile, - nanomoles_per_cubic_mile, - picomoles_per_cubic_mile, - femtomoles_per_cubic_mile, - attomoles_per_cubic_mile, - moles_per_cubic_yard, - millimoles_per_cubic_yard, - micromoles_per_cubic_yard, - nanomoles_per_cubic_yard, - picomoles_per_cubic_yard, - femtomoles_per_cubic_yard, - attomoles_per_cubic_yard, - moles_per_cubic_foot, - millimoles_per_cubic_foot, - micromoles_per_cubic_foot, - nanomoles_per_cubic_foot, - picomoles_per_cubic_foot, - femtomoles_per_cubic_foot, - attomoles_per_cubic_foot, - moles_per_cubic_inch, - millimoles_per_cubic_inch, - micromoles_per_cubic_inch, - nanomoles_per_cubic_inch, - picomoles_per_cubic_inch, - femtomoles_per_cubic_inch, - attomoles_per_cubic_inch, -]) - - -unit_group_names = [ - 'length', - 'area', - 'volume', - 'inverse_length', - 'inverse_area', - 'inverse_volume', - 'time', - 'rate', - 'speed', - 'acceleration', - 'density', - 'force', - 'pressure', - 'energy', - 'power', - 'charge', - 'potential', - 'resistance', - 'capacitance', - 'conductance', - 'magnetic_flux', - 'magnetic_flux_density', - 'inductance', - 'temperature', - 'dimensionless', - 'angle', - 'solid_angle', - 'amount', - 'concentration', -] - -unit_groups = { - 'length': length, - 'area': area, - 'volume': volume, - 'inverse_length': inverse_length, - 'inverse_area': inverse_area, - 'inverse_volume': inverse_volume, - 'time': time, - 'rate': rate, - 'speed': speed, - 'acceleration': acceleration, - 'density': density, - 'force': force, - 'pressure': pressure, - 'energy': energy, - 'power': power, - 'charge': charge, - 'potential': potential, - 'resistance': resistance, - 'capacitance': capacitance, - 'conductance': conductance, - 'magnetic_flux': magnetic_flux, - 'magnetic_flux_density': magnetic_flux_density, - 'inductance': inductance, - 'temperature': temperature, - 'dimensionless': dimensionless, - 'angle': angle, - 'solid_angle': solid_angle, - 'amount': amount, - 'concentration': concentration, -} - diff --git a/sasdata/quantities/units_base.py b/sasdata/quantities/units_base.py deleted file mode 100644 index d29785cc3..000000000 --- a/sasdata/quantities/units_base.py +++ /dev/null @@ -1,118 +0,0 @@ -from dataclasses import dataclass -from typing import Sequence, Self, TypeVar - -import numpy as np - - -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): - return hash((self.length, self.time, self.mass, self.current, self.temperature)) - -class Unit: - def __init__(self, - si_scaling_factor: float, - dimensions: Dimensions): - - self.scale = si_scaling_factor - self.dimensions = dimensions - - 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 diff --git a/sasdata/quantities/units_table.py b/sasdata/quantities/units_table.py deleted file mode 100644 index ac59e0dcd..000000000 --- a/sasdata/quantities/units_table.py +++ /dev/null @@ -1,155 +0,0 @@ -""" -Builds a data file containing details of units -""" - -import numpy as np -from collections import defaultdict - -bigger_magnitudes = [ - ("E", None, "exa", 1e18), - ("P", None, "peta", 1e15), - ("T", None, "tera", 1e12), - ("G", None, "giga", 1e9), - ("M", None, "mega", 1e6), - ("k", None, "kilo", 1e3) ] - -smaller_magnitudes = [ - ("m", None, "milli", 1e-3), - ("u", "µ", "micro", 1e-6), - ("n", None, "nano", 1e-9), - ("p", None, "pico", 1e-12), - ("f", None, "femto", 1e-15), - ("a", None, "atto", 1e-18)] - -all_magnitudes = bigger_magnitudes + smaller_magnitudes - -# Length, time, mass, current, temperature -base_si_units = [ - ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, all_magnitudes), - ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, smaller_magnitudes), - ("g", None, "gram", "grams", 1, 0, 0, 1, 0, 0, all_magnitudes), - ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, all_magnitudes), - ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, all_magnitudes) ] - -derived_si_units = [ - ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, all_magnitudes), - ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, all_magnitudes), - ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, all_magnitudes), - ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, all_magnitudes), - ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, all_magnitudes), - ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, all_magnitudes), - ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, all_magnitudes), - ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, all_magnitudes), - ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, all_magnitudes), - ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, all_magnitudes), - ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, all_magnitudes), - ("T", None, "tesla", "tesla", 1, 2, -2, 1, -1, 0, all_magnitudes), - ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, all_magnitudes), - ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, []) -] - -non_si_units = [ - ("A", None, "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), - ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, []), - ("hr", None, "hour", "hours", 360, 0, 1, 0, 0, 0, []), - ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), - ("day", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), - ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), - ("yr", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), - ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, []), - ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, []), - ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []) -] - -all_units = base_si_units + derived_si_units + non_si_units - -encoding = "utf-8" - -def write_unit_string(fid, symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature): - fid.write(f"'{symbol}', '{special_symbol}', '{singular}', '{plural}', ") - fid.write(f"{scale}, {length}, {time}, {mass}, {current}, {temperature}\n") - - -with open("unit_data.txt", mode='w', encoding=encoding) as fid: - for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, magnitudes in all_units: - - write_unit_string(fid, symbol, special_symbol, singular, plural, - scale, length, time, mass, current, temperature) - - for mag_symbol, mag_special_symbol, name, mag_scale in magnitudes: - - combined_symbol = (mag_symbol if mag_special_symbol is None else mag_special_symbol) + \ - (symbol if special_symbol is None else special_symbol) - - write_unit_string(fid,f"{mag_symbol}{symbol}", combined_symbol, f"{name}{singular}", - f"{name}{plural}", scale * mag_scale, length, time, mass, current, temperature) - - -def format_name(name: str): - return name.replace(" ", "_") - -header = """ - -Autogenerated file by units_table.py - - - - ******** DO NOT EDIT BY HAND ******** - - - -""" - -with open("units.py", 'w', encoding=encoding) as fid: - - # Write warning header - fid.write('"""'+header+'"""') - - # Write in class definitions - fid.write("\n\n" - "#\n" - "# Included from units_base.py\n" - "#\n\n") - - with open("units_base.py", 'r') as base: - for line in base: - fid.write(line) - - # Write in unit definitions - fid.write("\n\n" - "#\n" - "# Specific units \n" - "#\n\n") - - symbol_lookup = {} - unit_types = defaultdict(list) - - for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, magnitudes in all_units: - - formatted_plural = format_name(plural) - - fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}))\n") - - symbol_lookup[symbol] = formatted_plural - if special_symbol is not None: - symbol_lookup[special_symbol] = formatted_plural - - for mag_symbol, mag_special_symbol, name, mag_scale in magnitudes: - - combined_special_symbol = (mag_symbol if mag_special_symbol is None else mag_special_symbol) + \ - (symbol if special_symbol is None else special_symbol) - - combined_symbol = mag_symbol + symbol - - combined_name = f"{name}{formatted_plural}" - - fid.write(f"{combined_name} = Unit({scale * mag_scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}))\n") - - symbol_lookup[combined_symbol] = combined_name - symbol_lookup[combined_special_symbol] = combined_name - - fid.write("symbol_lookup = {\n") - for k in symbol_lookup: - fid.write(f' "{k}": {symbol_lookup[k]},\n') - fid.write("}\n\n") - From a7a77c547c11dcef351b33ac8cce297db45e0329 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 6 Aug 2024 14:57:06 +0100 Subject: [PATCH 490/675] More units --- sasdata/dataset_types.py | 35 ++--- sasdata/quantities/_units_table.py | 25 +++- sasdata/quantities/units.py | 211 +++++++++++++++++++++++++++++ 3 files changed, 253 insertions(+), 18 deletions(-) diff --git a/sasdata/dataset_types.py b/sasdata/dataset_types.py index 27848b3e2..407affcfc 100644 --- a/sasdata/dataset_types.py +++ b/sasdata/dataset_types.py @@ -2,6 +2,8 @@ from dataclasses import dataclass +import sasdata.quantities.units as units + # # VERY ROUGH DRAFT - FOR PROTOTYPING PURPOSES # @@ -54,22 +56,23 @@ class DatasetType: # # The unit options should only be those compatible with the field # -default_units = { - "Q": "1/A", - "I": "1/cm", - "Qx": "1/A", - "Qy": "1/A", - "Qz": "1/A", - "dI": "1/A", - "dQ": "1/A", - "dQx": "1/A", - "dQy": "1/A", - "dQz": "1/A", - "z": "A", - "G": "", - "shaddow": "", - "temperature": "K", - "magnetic field": "T" + +unit_kinds = { + "Q": units.inverse_length, + "I": units.inverse_length, + "Qx": units.inverse_length, + "Qy": units.inverse_length, + "Qz": units.inverse_length, + "dI": units.inverse_length, + "dQ": units.inverse_length, + "dQx": units.inverse_length, + "dQy": units.inverse_length, + "dQz": units.inverse_length, + "z": units.length, + "G": units.area, + "shaddow": units.dimensionless, + "temperature": units.temperature, + "magnetic field": units.magnetic_flux_density } # diff --git a/sasdata/quantities/_units_table.py b/sasdata/quantities/_units_table.py index 9b88cab42..62e38138a 100644 --- a/sasdata/quantities/_units_table.py +++ b/sasdata/quantities/_units_table.py @@ -60,7 +60,8 @@ ("yr", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, []), ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, []), - ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []) + ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []), + ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, []) ] @@ -166,6 +167,7 @@ def format_name(name: str): length_units = unit_types_temp[hash(Dimensions(length=1))] time_units = unit_types_temp[hash(Dimensions(time=1))] + mass_units = unit_types_temp[hash(Dimensions(mass=1))] # Length based for symbol, special_symbol, singular, plural, scale, _ in length_units: @@ -212,6 +214,23 @@ def format_name(name: str): unit_types[hash(speed_dimensions)].append(speed_name) unit_types[hash(accel_dimensions)].append(accel_name) + # Density + for length_symbol, length_special_symbol, _, length_name, length_scale, _ in length_units: + for mass_symbol, mass_special_symbol, mass_name, _, mass_scale, _ in mass_units: + + name = length_name + "_per_cubic_" + mass_name + + dimensions = Dimensions(length=-3, time=1) + + fid.write(f"{speed_name} " + f"= Unit({mass_scale / length_scale**3}, " + f"Dimensions(-3, 1, 0, 0, 0), " + f"name='{name}', " + f"ascii_symbol='{mass_symbol} {length_symbol}^-3', " + f"symbol='{mass_special_symbol}{length_special_symbol}⁻³')\n") + + unit_types[hash(dimensions)].append(name) + # # Write out the symbol lookup table # @@ -236,6 +255,7 @@ def format_name(name: str): ("rate", Dimensions(time=-1)), ("speed", Dimensions(length=1, time=-1)), ("acceleration", Dimensions(length=1, time=-2)), + ("density", Dimensions(length=-3, mass=1)), ("force", Dimensions(1, -2, 1, 0, 0)), ("pressure", Dimensions(-1, -2, 1, 0, 0)), ("energy", Dimensions(2, -2, 1, 0, 0)), @@ -248,7 +268,8 @@ def format_name(name: str): ("magnetic_flux", Dimensions(2, -2, 1, -1, 0)), ("magnetic_flux_density", Dimensions(0, -2, 1, -1, 0)), ("inductance", Dimensions(2, -2, 1, -2, 0)), - ("temperature", Dimensions(temperature=1)) + ("temperature", Dimensions(temperature=1)), + ("dimensionless", Dimensions()) ] fid.write("\n#\n# Units by type \n#\n\n") diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 6105e27f5..530ce41bb 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -428,6 +428,7 @@ def __init__(self, name: str, units: list[Unit]): degrees = Unit(57.29577951308232, Dimensions(0, 0, 0, 0, 0),name='degrees',ascii_symbol='deg',symbol='deg') radians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='radians',ascii_symbol='rad',symbol='rad') stradians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='stradians',ascii_symbol='sr',symbol='sr') +none = Unit(1, Dimensions(0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') square_meters = Unit(1, Dimensions(2, 0, 0, 0, 0), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = Unit(1, Dimensions(3, 0, 0, 0, 0), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = Unit(1.0, Dimensions(-1, 0, 0, 0, 0), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -893,6 +894,201 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='NoneNone⁻²') angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/yr', symbol='NoneNone⁻¹') angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/yr^2', symbol='NoneNone⁻²') +angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_gram', ascii_symbol='g m^-3', symbol='NoneNone⁻³') +angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_exagram', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') +angstroms_per_year = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_petagram', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') +angstroms_per_year = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_teragram', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') +angstroms_per_year = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_gigagram', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') +angstroms_per_year = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_megagram', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') +angstroms_per_year = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_kilogram', ascii_symbol='kg m^-3', symbol='kgNone⁻³') +angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_milligram', ascii_symbol='mg m^-3', symbol='mgNone⁻³') +angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_microgram', ascii_symbol='ug m^-3', symbol='µgNone⁻³') +angstroms_per_year = Unit(1.0000000000000002e-12, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_nanogram', ascii_symbol='ng m^-3', symbol='ngNone⁻³') +angstroms_per_year = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_picogram', ascii_symbol='pg m^-3', symbol='pgNone⁻³') +angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_femtogram', ascii_symbol='fg m^-3', symbol='fgNone⁻³') +angstroms_per_year = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_attogram', ascii_symbol='ag m^-3', symbol='agNone⁻³') +angstroms_per_year = Unit(1e-57, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_gram', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') +angstroms_per_year = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_exagram', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') +angstroms_per_year = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_petagram', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') +angstroms_per_year = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_teragram', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') +angstroms_per_year = Unit(1e-48, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_gigagram', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') +angstroms_per_year = Unit(9.999999999999999e-52, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_megagram', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') +angstroms_per_year = Unit(9.999999999999999e-55, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_kilogram', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') +angstroms_per_year = Unit(9.999999999999998e-61, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_milligram', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') +angstroms_per_year = Unit(9.999999999999999e-64, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_microgram', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_nanogram', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') +angstroms_per_year = Unit(1e-69, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_picogram', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') +angstroms_per_year = Unit(1e-72, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_femtogram', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') +angstroms_per_year = Unit(1e-75, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_attogram', ascii_symbol='ag Em^-3', symbol='agEm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_gram', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') +angstroms_per_year = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_exagram', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') +angstroms_per_year = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_petagram', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_teragram', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_gigagram', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') +angstroms_per_year = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_megagram', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-45, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_kilogram', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') +angstroms_per_year = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_milligram', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') +angstroms_per_year = Unit(1.0000000000000002e-54, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_microgram', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') +angstroms_per_year = Unit(1.0000000000000002e-57, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_nanogram', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-60, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_picogram', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') +angstroms_per_year = Unit(1.0000000000000002e-63, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_femtogram', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_attogram', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') +angstroms_per_year = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_gram', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') +angstroms_per_year = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_exagram', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') +angstroms_per_year = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_petagram', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') +angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_teragram', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') +angstroms_per_year = Unit(9.999999999999999e-31, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_gigagram', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') +angstroms_per_year = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_megagram', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') +angstroms_per_year = Unit(1e-36, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_kilogram', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') +angstroms_per_year = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_milligram', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') +angstroms_per_year = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_microgram', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_nanogram', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') +angstroms_per_year = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_picogram', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') +angstroms_per_year = Unit(1e-54, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_femtogram', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-57, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_attogram', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') +angstroms_per_year = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_gram', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') +angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_exagram', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') +angstroms_per_year = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_petagram', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') +angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_teragram', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') +angstroms_per_year = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_gigagram', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') +angstroms_per_year = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_megagram', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') +angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_kilogram', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') +angstroms_per_year = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_milligram', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_microgram', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_nanogram', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') +angstroms_per_year = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_picogram', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') +angstroms_per_year = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_femtogram', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_attogram', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_gram', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') +angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_exagram', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') +angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_petagram', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') +angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_teragram', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') +angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_gigagram', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') +angstroms_per_year = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_megagram', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') +angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_kilogram', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') +angstroms_per_year = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_milligram', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') +angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_microgram', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') +angstroms_per_year = Unit(1.0000000000000003e-30, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_nanogram', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') +angstroms_per_year = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_picogram', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_femtogram', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_attogram', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') +angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_gram', ascii_symbol='g km^-3', symbol='Nonekm⁻³') +angstroms_per_year = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_exagram', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') +angstroms_per_year = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_petagram', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') +angstroms_per_year = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_teragram', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') +angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_gigagram', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') +angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_megagram', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') +angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_kilogram', ascii_symbol='kg km^-3', symbol='kgkm⁻³') +angstroms_per_year = Unit(9.999999999999999e-16, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_milligram', ascii_symbol='mg km^-3', symbol='mgkm⁻³') +angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_microgram', ascii_symbol='ug km^-3', symbol='µgkm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_nanogram', ascii_symbol='ng km^-3', symbol='ngkm⁻³') +angstroms_per_year = Unit(1.0000000000000001e-24, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_picogram', ascii_symbol='pg km^-3', symbol='pgkm⁻³') +angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_femtogram', ascii_symbol='fg km^-3', symbol='fgkm⁻³') +angstroms_per_year = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_attogram', ascii_symbol='ag km^-3', symbol='agkm⁻³') +angstroms_per_year = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_gram', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') +angstroms_per_year = Unit(1e+24, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_exagram', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') +angstroms_per_year = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_petagram', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') +angstroms_per_year = Unit(1e+18, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_teragram', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') +angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_gigagram', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') +angstroms_per_year = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_megagram', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') +angstroms_per_year = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_kilogram', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') +angstroms_per_year = Unit(999.9999999999999, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_milligram', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') +angstroms_per_year = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_microgram', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') +angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_nanogram', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') +angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_picogram', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') +angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_femtogram', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') +angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_attogram', ascii_symbol='ag mm^-3', symbol='agmm⁻³') +angstroms_per_year = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_gram', ascii_symbol='g um^-3', symbol='Noneµm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_exagram', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') +angstroms_per_year = Unit(1.0000000000000002e+30, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_petagram', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') +angstroms_per_year = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_teragram', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+24, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_gigagram', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_megagram', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_kilogram', ascii_symbol='kg um^-3', symbol='kgµm⁻³') +angstroms_per_year = Unit(1000000000000.0001, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_milligram', ascii_symbol='mg um^-3', symbol='mgµm⁻³') +angstroms_per_year = Unit(1000000000.0000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_microgram', ascii_symbol='ug um^-3', symbol='µgµm⁻³') +angstroms_per_year = Unit(1000000.0000000003, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_nanogram', ascii_symbol='ng um^-3', symbol='ngµm⁻³') +angstroms_per_year = Unit(1000.0000000000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_picogram', ascii_symbol='pg um^-3', symbol='pgµm⁻³') +angstroms_per_year = Unit(1.0000000000000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_femtogram', ascii_symbol='fg um^-3', symbol='fgµm⁻³') +angstroms_per_year = Unit(0.0010000000000000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_attogram', ascii_symbol='ag um^-3', symbol='agµm⁻³') +angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_gram', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') +angstroms_per_year = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_exagram', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') +angstroms_per_year = Unit(9.999999999999998e+38, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_petagram', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') +angstroms_per_year = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_teragram', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') +angstroms_per_year = Unit(9.999999999999998e+32, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_gigagram', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') +angstroms_per_year = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_megagram', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') +angstroms_per_year = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_kilogram', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') +angstroms_per_year = Unit(9.999999999999997e+20, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_milligram', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') +angstroms_per_year = Unit(9.999999999999999e+17, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_microgram', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') +angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_nanogram', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') +angstroms_per_year = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_picogram', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') +angstroms_per_year = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_femtogram', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') +angstroms_per_year = Unit(999999.9999999999, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_attogram', ascii_symbol='ag nm^-3', symbol='agnm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_gram', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') +angstroms_per_year = Unit(1e+51, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_exagram', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') +angstroms_per_year = Unit(1e+48, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_petagram', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+45, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_teragram', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') +angstroms_per_year = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_gigagram', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+39, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_megagram', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') +angstroms_per_year = Unit(1e+36, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_kilogram', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') +angstroms_per_year = Unit(1e+30, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_milligram', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') +angstroms_per_year = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_microgram', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') +angstroms_per_year = Unit(1.0000000000000003e+24, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_nanogram', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_picogram', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') +angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_femtogram', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') +angstroms_per_year = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_attogram', ascii_symbol='ag pm^-3', symbol='agpm⁻³') +angstroms_per_year = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_gram', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') +angstroms_per_year = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_exagram', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+56, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_petagram', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+53, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_teragram', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+50, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_gigagram', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_megagram', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') +angstroms_per_year = Unit(9.999999999999998e+44, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_kilogram', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') +angstroms_per_year = Unit(9.999999999999996e+38, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_milligram', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_microgram', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') +angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_nanogram', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_picogram', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') +angstroms_per_year = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_femtogram', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') +angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_attogram', ascii_symbol='ag fm^-3', symbol='agfm⁻³') +angstroms_per_year = Unit(9.999999999999998e+50, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_gram', ascii_symbol='g am^-3', symbol='Noneam⁻³') +angstroms_per_year = Unit(9.999999999999999e+68, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_exagram', ascii_symbol='Eg am^-3', symbol='Egam⁻³') +angstroms_per_year = Unit(9.999999999999998e+65, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_petagram', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') +angstroms_per_year = Unit(9.999999999999999e+62, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_teragram', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') +angstroms_per_year = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_gigagram', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') +angstroms_per_year = Unit(9.999999999999999e+56, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_megagram', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') +angstroms_per_year = Unit(9.999999999999999e+53, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_kilogram', ascii_symbol='kg am^-3', symbol='kgam⁻³') +angstroms_per_year = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_milligram', ascii_symbol='mg am^-3', symbol='mgam⁻³') +angstroms_per_year = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_microgram', ascii_symbol='ug am^-3', symbol='µgam⁻³') +angstroms_per_year = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_nanogram', ascii_symbol='ng am^-3', symbol='ngam⁻³') +angstroms_per_year = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_picogram', ascii_symbol='pg am^-3', symbol='pgam⁻³') +angstroms_per_year = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_femtogram', ascii_symbol='fg am^-3', symbol='fgam⁻³') +angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_attogram', ascii_symbol='ag am^-3', symbol='agam⁻³') +angstroms_per_year = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gram', ascii_symbol='g A^-3', symbol='NoneÅ⁻³') +angstroms_per_year = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_exagram', ascii_symbol='Eg A^-3', symbol='EgÅ⁻³') +angstroms_per_year = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_petagram', ascii_symbol='Pg A^-3', symbol='PgÅ⁻³') +angstroms_per_year = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_teragram', ascii_symbol='Tg A^-3', symbol='TgÅ⁻³') +angstroms_per_year = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gigagram', ascii_symbol='Gg A^-3', symbol='GgÅ⁻³') +angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_megagram', ascii_symbol='Mg A^-3', symbol='MgÅ⁻³') +angstroms_per_year = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_kilogram', ascii_symbol='kg A^-3', symbol='kgÅ⁻³') +angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_milligram', ascii_symbol='mg A^-3', symbol='mgÅ⁻³') +angstroms_per_year = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_microgram', ascii_symbol='ug A^-3', symbol='µgÅ⁻³') +angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_nanogram', ascii_symbol='ng A^-3', symbol='ngÅ⁻³') +angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_picogram', ascii_symbol='pg A^-3', symbol='pgÅ⁻³') +angstroms_per_year = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_femtogram', ascii_symbol='fg A^-3', symbol='fgÅ⁻³') +angstroms_per_year = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_attogram', ascii_symbol='ag A^-3', symbol='agÅ⁻³') +angstroms_per_year = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gram', ascii_symbol='g Ang^-3', symbol='NoneNone⁻³') +angstroms_per_year = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_exagram', ascii_symbol='Eg Ang^-3', symbol='EgNone⁻³') +angstroms_per_year = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_petagram', ascii_symbol='Pg Ang^-3', symbol='PgNone⁻³') +angstroms_per_year = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_teragram', ascii_symbol='Tg Ang^-3', symbol='TgNone⁻³') +angstroms_per_year = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gigagram', ascii_symbol='Gg Ang^-3', symbol='GgNone⁻³') +angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_megagram', ascii_symbol='Mg Ang^-3', symbol='MgNone⁻³') +angstroms_per_year = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_kilogram', ascii_symbol='kg Ang^-3', symbol='kgNone⁻³') +angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_milligram', ascii_symbol='mg Ang^-3', symbol='mgNone⁻³') +angstroms_per_year = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_microgram', ascii_symbol='ug Ang^-3', symbol='µgNone⁻³') +angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_nanogram', ascii_symbol='ng Ang^-3', symbol='ngNone⁻³') +angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_picogram', ascii_symbol='pg Ang^-3', symbol='pgNone⁻³') +angstroms_per_year = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_femtogram', ascii_symbol='fg Ang^-3', symbol='fgNone⁻³') +angstroms_per_year = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_attogram', ascii_symbol='ag Ang^-3', symbol='agNone⁻³') # # Lookup table from symbols to units @@ -1168,6 +1364,7 @@ def __init__(self, name: str, units: list[Unit]): "deg": degrees, "rad": radians, "sr": stradians, + "none": none, } @@ -1733,6 +1930,11 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_square_year, ]) +density = UnitGroup( + name = 'density', + units = [ +]) + force = UnitGroup( name = 'force', units = [ @@ -1967,3 +2169,12 @@ def __init__(self, name: str, units: list[Unit]): attokelvin, degrees_celsius, ]) + +dimensionless = UnitGroup( + name = 'dimensionless', + units = [ + degrees, + radians, + stradians, + none, +]) From bb489d24e36a771e68867ca52bbca8a5adf93545 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 6 Aug 2024 14:59:50 +0100 Subject: [PATCH 491/675] one d in shadow --- sasdata/dataset_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/dataset_types.py b/sasdata/dataset_types.py index 407affcfc..c7d2f5927 100644 --- a/sasdata/dataset_types.py +++ b/sasdata/dataset_types.py @@ -70,7 +70,7 @@ class DatasetType: "dQz": units.inverse_length, "z": units.length, "G": units.area, - "shaddow": units.dimensionless, + "shadow": units.dimensionless, "temperature": units.temperature, "magnetic field": units.magnetic_flux_density } From 7619dbca61b7850c75006450c78c7a32fcb204da Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 6 Aug 2024 15:35:36 +0100 Subject: [PATCH 492/675] Fixed density units --- sasdata/quantities/_units_table.py | 4 +- sasdata/quantities/units.py | 390 ++++++++++++++--------------- 2 files changed, 197 insertions(+), 197 deletions(-) diff --git a/sasdata/quantities/_units_table.py b/sasdata/quantities/_units_table.py index 62e38138a..27165bde8 100644 --- a/sasdata/quantities/_units_table.py +++ b/sasdata/quantities/_units_table.py @@ -218,11 +218,11 @@ def format_name(name: str): for length_symbol, length_special_symbol, _, length_name, length_scale, _ in length_units: for mass_symbol, mass_special_symbol, mass_name, _, mass_scale, _ in mass_units: - name = length_name + "_per_cubic_" + mass_name + name = mass_name + "_per_cubic_" + length_name dimensions = Dimensions(length=-3, time=1) - fid.write(f"{speed_name} " + fid.write(f"{name} " f"= Unit({mass_scale / length_scale**3}, " f"Dimensions(-3, 1, 0, 0, 0), " f"name='{name}', " diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 530ce41bb..60517eca8 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -894,201 +894,201 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='NoneNone⁻²') angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/yr', symbol='NoneNone⁻¹') angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/yr^2', symbol='NoneNone⁻²') -angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_gram', ascii_symbol='g m^-3', symbol='NoneNone⁻³') -angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_exagram', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') -angstroms_per_year = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_petagram', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') -angstroms_per_year = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_teragram', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') -angstroms_per_year = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_gigagram', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') -angstroms_per_year = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_megagram', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') -angstroms_per_year = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_kilogram', ascii_symbol='kg m^-3', symbol='kgNone⁻³') -angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_milligram', ascii_symbol='mg m^-3', symbol='mgNone⁻³') -angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_microgram', ascii_symbol='ug m^-3', symbol='µgNone⁻³') -angstroms_per_year = Unit(1.0000000000000002e-12, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_nanogram', ascii_symbol='ng m^-3', symbol='ngNone⁻³') -angstroms_per_year = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_picogram', ascii_symbol='pg m^-3', symbol='pgNone⁻³') -angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_femtogram', ascii_symbol='fg m^-3', symbol='fgNone⁻³') -angstroms_per_year = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='meters_per_cubic_attogram', ascii_symbol='ag m^-3', symbol='agNone⁻³') -angstroms_per_year = Unit(1e-57, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_gram', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') -angstroms_per_year = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_exagram', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') -angstroms_per_year = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_petagram', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') -angstroms_per_year = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_teragram', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') -angstroms_per_year = Unit(1e-48, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_gigagram', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') -angstroms_per_year = Unit(9.999999999999999e-52, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_megagram', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') -angstroms_per_year = Unit(9.999999999999999e-55, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_kilogram', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') -angstroms_per_year = Unit(9.999999999999998e-61, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_milligram', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') -angstroms_per_year = Unit(9.999999999999999e-64, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_microgram', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_nanogram', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') -angstroms_per_year = Unit(1e-69, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_picogram', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') -angstroms_per_year = Unit(1e-72, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_femtogram', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') -angstroms_per_year = Unit(1e-75, Dimensions(-3, 1, 0, 0, 0), name='exameters_per_cubic_attogram', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_gram', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') -angstroms_per_year = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_exagram', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') -angstroms_per_year = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_petagram', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_teragram', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_gigagram', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') -angstroms_per_year = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_megagram', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-45, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_kilogram', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') -angstroms_per_year = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_milligram', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') -angstroms_per_year = Unit(1.0000000000000002e-54, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_microgram', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') -angstroms_per_year = Unit(1.0000000000000002e-57, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_nanogram', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-60, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_picogram', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') -angstroms_per_year = Unit(1.0000000000000002e-63, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_femtogram', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='petameters_per_cubic_attogram', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -angstroms_per_year = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_gram', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') -angstroms_per_year = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_exagram', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') -angstroms_per_year = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_petagram', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') -angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_teragram', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') -angstroms_per_year = Unit(9.999999999999999e-31, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_gigagram', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') -angstroms_per_year = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_megagram', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') -angstroms_per_year = Unit(1e-36, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_kilogram', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') -angstroms_per_year = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_milligram', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') -angstroms_per_year = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_microgram', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_nanogram', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') -angstroms_per_year = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_picogram', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') -angstroms_per_year = Unit(1e-54, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_femtogram', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-57, Dimensions(-3, 1, 0, 0, 0), name='terameters_per_cubic_attogram', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -angstroms_per_year = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_gram', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') -angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_exagram', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') -angstroms_per_year = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_petagram', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') -angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_teragram', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') -angstroms_per_year = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_gigagram', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') -angstroms_per_year = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_megagram', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') -angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_kilogram', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') -angstroms_per_year = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_milligram', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_microgram', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_nanogram', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') -angstroms_per_year = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_picogram', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') -angstroms_per_year = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_femtogram', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='gigameters_per_cubic_attogram', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_gram', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') -angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_exagram', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') -angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_petagram', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') -angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_teragram', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') -angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_gigagram', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') -angstroms_per_year = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_megagram', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') -angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_kilogram', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') -angstroms_per_year = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_milligram', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') -angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_microgram', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') -angstroms_per_year = Unit(1.0000000000000003e-30, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_nanogram', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') -angstroms_per_year = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_picogram', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_femtogram', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='megameters_per_cubic_attogram', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_gram', ascii_symbol='g km^-3', symbol='Nonekm⁻³') -angstroms_per_year = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_exagram', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') -angstroms_per_year = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_petagram', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') -angstroms_per_year = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_teragram', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') -angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_gigagram', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') -angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_megagram', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') -angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_kilogram', ascii_symbol='kg km^-3', symbol='kgkm⁻³') -angstroms_per_year = Unit(9.999999999999999e-16, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_milligram', ascii_symbol='mg km^-3', symbol='mgkm⁻³') -angstroms_per_year = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_microgram', ascii_symbol='ug km^-3', symbol='µgkm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_nanogram', ascii_symbol='ng km^-3', symbol='ngkm⁻³') -angstroms_per_year = Unit(1.0000000000000001e-24, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_picogram', ascii_symbol='pg km^-3', symbol='pgkm⁻³') -angstroms_per_year = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_femtogram', ascii_symbol='fg km^-3', symbol='fgkm⁻³') -angstroms_per_year = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='kilometers_per_cubic_attogram', ascii_symbol='ag km^-3', symbol='agkm⁻³') -angstroms_per_year = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_gram', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') -angstroms_per_year = Unit(1e+24, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_exagram', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') -angstroms_per_year = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_petagram', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') -angstroms_per_year = Unit(1e+18, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_teragram', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') -angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_gigagram', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') -angstroms_per_year = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_megagram', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') -angstroms_per_year = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_kilogram', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') -angstroms_per_year = Unit(999.9999999999999, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_milligram', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') -angstroms_per_year = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_microgram', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') -angstroms_per_year = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_nanogram', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') -angstroms_per_year = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_picogram', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') -angstroms_per_year = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_femtogram', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') -angstroms_per_year = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='millimeters_per_cubic_attogram', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -angstroms_per_year = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_gram', ascii_symbol='g um^-3', symbol='Noneµm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_exagram', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') -angstroms_per_year = Unit(1.0000000000000002e+30, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_petagram', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') -angstroms_per_year = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_teragram', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+24, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_gigagram', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_megagram', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_kilogram', ascii_symbol='kg um^-3', symbol='kgµm⁻³') -angstroms_per_year = Unit(1000000000000.0001, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_milligram', ascii_symbol='mg um^-3', symbol='mgµm⁻³') -angstroms_per_year = Unit(1000000000.0000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_microgram', ascii_symbol='ug um^-3', symbol='µgµm⁻³') -angstroms_per_year = Unit(1000000.0000000003, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_nanogram', ascii_symbol='ng um^-3', symbol='ngµm⁻³') -angstroms_per_year = Unit(1000.0000000000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_picogram', ascii_symbol='pg um^-3', symbol='pgµm⁻³') -angstroms_per_year = Unit(1.0000000000000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_femtogram', ascii_symbol='fg um^-3', symbol='fgµm⁻³') -angstroms_per_year = Unit(0.0010000000000000002, Dimensions(-3, 1, 0, 0, 0), name='micrometers_per_cubic_attogram', ascii_symbol='ag um^-3', symbol='agµm⁻³') -angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_gram', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') -angstroms_per_year = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_exagram', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') -angstroms_per_year = Unit(9.999999999999998e+38, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_petagram', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') -angstroms_per_year = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_teragram', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') -angstroms_per_year = Unit(9.999999999999998e+32, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_gigagram', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') -angstroms_per_year = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_megagram', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') -angstroms_per_year = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_kilogram', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') -angstroms_per_year = Unit(9.999999999999997e+20, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_milligram', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') -angstroms_per_year = Unit(9.999999999999999e+17, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_microgram', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') -angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_nanogram', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') -angstroms_per_year = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_picogram', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') -angstroms_per_year = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_femtogram', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') -angstroms_per_year = Unit(999999.9999999999, Dimensions(-3, 1, 0, 0, 0), name='nanometers_per_cubic_attogram', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_gram', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') -angstroms_per_year = Unit(1e+51, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_exagram', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') -angstroms_per_year = Unit(1e+48, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_petagram', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+45, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_teragram', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') -angstroms_per_year = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_gigagram', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+39, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_megagram', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') -angstroms_per_year = Unit(1e+36, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_kilogram', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') -angstroms_per_year = Unit(1e+30, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_milligram', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') -angstroms_per_year = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_microgram', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') -angstroms_per_year = Unit(1.0000000000000003e+24, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_nanogram', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_picogram', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') -angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_femtogram', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') -angstroms_per_year = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='picometers_per_cubic_attogram', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -angstroms_per_year = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_gram', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') -angstroms_per_year = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_exagram', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+56, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_petagram', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+53, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_teragram', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+50, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_gigagram', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_megagram', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') -angstroms_per_year = Unit(9.999999999999998e+44, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_kilogram', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') -angstroms_per_year = Unit(9.999999999999996e+38, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_milligram', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_microgram', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') -angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_nanogram', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_picogram', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') -angstroms_per_year = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_femtogram', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') -angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='femtometers_per_cubic_attogram', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -angstroms_per_year = Unit(9.999999999999998e+50, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_gram', ascii_symbol='g am^-3', symbol='Noneam⁻³') -angstroms_per_year = Unit(9.999999999999999e+68, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_exagram', ascii_symbol='Eg am^-3', symbol='Egam⁻³') -angstroms_per_year = Unit(9.999999999999998e+65, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_petagram', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') -angstroms_per_year = Unit(9.999999999999999e+62, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_teragram', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') -angstroms_per_year = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_gigagram', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') -angstroms_per_year = Unit(9.999999999999999e+56, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_megagram', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') -angstroms_per_year = Unit(9.999999999999999e+53, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_kilogram', ascii_symbol='kg am^-3', symbol='kgam⁻³') -angstroms_per_year = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_milligram', ascii_symbol='mg am^-3', symbol='mgam⁻³') -angstroms_per_year = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_microgram', ascii_symbol='ug am^-3', symbol='µgam⁻³') -angstroms_per_year = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_nanogram', ascii_symbol='ng am^-3', symbol='ngam⁻³') -angstroms_per_year = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_picogram', ascii_symbol='pg am^-3', symbol='pgam⁻³') -angstroms_per_year = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_femtogram', ascii_symbol='fg am^-3', symbol='fgam⁻³') -angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='attometers_per_cubic_attogram', ascii_symbol='ag am^-3', symbol='agam⁻³') -angstroms_per_year = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gram', ascii_symbol='g A^-3', symbol='NoneÅ⁻³') -angstroms_per_year = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_exagram', ascii_symbol='Eg A^-3', symbol='EgÅ⁻³') -angstroms_per_year = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_petagram', ascii_symbol='Pg A^-3', symbol='PgÅ⁻³') -angstroms_per_year = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_teragram', ascii_symbol='Tg A^-3', symbol='TgÅ⁻³') -angstroms_per_year = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gigagram', ascii_symbol='Gg A^-3', symbol='GgÅ⁻³') -angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_megagram', ascii_symbol='Mg A^-3', symbol='MgÅ⁻³') -angstroms_per_year = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_kilogram', ascii_symbol='kg A^-3', symbol='kgÅ⁻³') -angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_milligram', ascii_symbol='mg A^-3', symbol='mgÅ⁻³') -angstroms_per_year = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_microgram', ascii_symbol='ug A^-3', symbol='µgÅ⁻³') -angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_nanogram', ascii_symbol='ng A^-3', symbol='ngÅ⁻³') -angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_picogram', ascii_symbol='pg A^-3', symbol='pgÅ⁻³') -angstroms_per_year = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_femtogram', ascii_symbol='fg A^-3', symbol='fgÅ⁻³') -angstroms_per_year = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_attogram', ascii_symbol='ag A^-3', symbol='agÅ⁻³') -angstroms_per_year = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gram', ascii_symbol='g Ang^-3', symbol='NoneNone⁻³') -angstroms_per_year = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_exagram', ascii_symbol='Eg Ang^-3', symbol='EgNone⁻³') -angstroms_per_year = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_petagram', ascii_symbol='Pg Ang^-3', symbol='PgNone⁻³') -angstroms_per_year = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_teragram', ascii_symbol='Tg Ang^-3', symbol='TgNone⁻³') -angstroms_per_year = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_gigagram', ascii_symbol='Gg Ang^-3', symbol='GgNone⁻³') -angstroms_per_year = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_megagram', ascii_symbol='Mg Ang^-3', symbol='MgNone⁻³') -angstroms_per_year = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_kilogram', ascii_symbol='kg Ang^-3', symbol='kgNone⁻³') -angstroms_per_year = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_milligram', ascii_symbol='mg Ang^-3', symbol='mgNone⁻³') -angstroms_per_year = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_microgram', ascii_symbol='ug Ang^-3', symbol='µgNone⁻³') -angstroms_per_year = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_nanogram', ascii_symbol='ng Ang^-3', symbol='ngNone⁻³') -angstroms_per_year = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_picogram', ascii_symbol='pg Ang^-3', symbol='pgNone⁻³') -angstroms_per_year = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_femtogram', ascii_symbol='fg Ang^-3', symbol='fgNone⁻³') -angstroms_per_year = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='angstroms_per_cubic_attogram', ascii_symbol='ag Ang^-3', symbol='agNone⁻³') +gram_per_cubic_meters = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_meters', ascii_symbol='g m^-3', symbol='NoneNone⁻³') +exagram_per_cubic_meters = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_meters', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') +petagram_per_cubic_meters = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_meters', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') +teragram_per_cubic_meters = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_meters', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') +gigagram_per_cubic_meters = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_meters', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') +megagram_per_cubic_meters = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_meters', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') +kilogram_per_cubic_meters = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_meters', ascii_symbol='kg m^-3', symbol='kgNone⁻³') +milligram_per_cubic_meters = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_meters', ascii_symbol='mg m^-3', symbol='mgNone⁻³') +microgram_per_cubic_meters = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_meters', ascii_symbol='ug m^-3', symbol='µgNone⁻³') +nanogram_per_cubic_meters = Unit(1.0000000000000002e-12, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_meters', ascii_symbol='ng m^-3', symbol='ngNone⁻³') +picogram_per_cubic_meters = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_meters', ascii_symbol='pg m^-3', symbol='pgNone⁻³') +femtogram_per_cubic_meters = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_meters', ascii_symbol='fg m^-3', symbol='fgNone⁻³') +attogram_per_cubic_meters = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_meters', ascii_symbol='ag m^-3', symbol='agNone⁻³') +gram_per_cubic_exameters = Unit(1e-57, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_exameters', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') +exagram_per_cubic_exameters = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_exameters', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') +petagram_per_cubic_exameters = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_exameters', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') +teragram_per_cubic_exameters = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_exameters', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') +gigagram_per_cubic_exameters = Unit(1e-48, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_exameters', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') +megagram_per_cubic_exameters = Unit(9.999999999999999e-52, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_exameters', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') +kilogram_per_cubic_exameters = Unit(9.999999999999999e-55, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_exameters', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') +milligram_per_cubic_exameters = Unit(9.999999999999998e-61, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_exameters', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') +microgram_per_cubic_exameters = Unit(9.999999999999999e-64, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_exameters', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') +nanogram_per_cubic_exameters = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_exameters', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') +picogram_per_cubic_exameters = Unit(1e-69, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_exameters', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') +femtogram_per_cubic_exameters = Unit(1e-72, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_exameters', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') +attogram_per_cubic_exameters = Unit(1e-75, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_exameters', ascii_symbol='ag Em^-3', symbol='agEm⁻³') +gram_per_cubic_petameters = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_petameters', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') +exagram_per_cubic_petameters = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_petameters', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') +petagram_per_cubic_petameters = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_petameters', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') +teragram_per_cubic_petameters = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_petameters', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') +gigagram_per_cubic_petameters = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_petameters', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') +megagram_per_cubic_petameters = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_petameters', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') +kilogram_per_cubic_petameters = Unit(1.0000000000000001e-45, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_petameters', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') +milligram_per_cubic_petameters = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_petameters', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') +microgram_per_cubic_petameters = Unit(1.0000000000000002e-54, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_petameters', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') +nanogram_per_cubic_petameters = Unit(1.0000000000000002e-57, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_petameters', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') +picogram_per_cubic_petameters = Unit(1.0000000000000001e-60, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_petameters', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') +femtogram_per_cubic_petameters = Unit(1.0000000000000002e-63, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_petameters', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') +attogram_per_cubic_petameters = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_petameters', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') +gram_per_cubic_terameters = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_terameters', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') +exagram_per_cubic_terameters = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_terameters', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') +petagram_per_cubic_terameters = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_terameters', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') +teragram_per_cubic_terameters = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_terameters', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') +gigagram_per_cubic_terameters = Unit(9.999999999999999e-31, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_terameters', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') +megagram_per_cubic_terameters = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_terameters', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') +kilogram_per_cubic_terameters = Unit(1e-36, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_terameters', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') +milligram_per_cubic_terameters = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_terameters', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') +microgram_per_cubic_terameters = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_terameters', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') +nanogram_per_cubic_terameters = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_terameters', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') +picogram_per_cubic_terameters = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_terameters', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') +femtogram_per_cubic_terameters = Unit(1e-54, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_terameters', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') +attogram_per_cubic_terameters = Unit(1.0000000000000001e-57, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_terameters', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') +gram_per_cubic_gigameters = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_gigameters', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') +exagram_per_cubic_gigameters = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_gigameters', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') +petagram_per_cubic_gigameters = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_gigameters', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') +teragram_per_cubic_gigameters = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_gigameters', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') +gigagram_per_cubic_gigameters = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_gigameters', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') +megagram_per_cubic_gigameters = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_gigameters', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') +kilogram_per_cubic_gigameters = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_gigameters', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') +milligram_per_cubic_gigameters = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_gigameters', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') +microgram_per_cubic_gigameters = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_gigameters', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') +nanogram_per_cubic_gigameters = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_gigameters', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') +picogram_per_cubic_gigameters = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_gigameters', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') +femtogram_per_cubic_gigameters = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_gigameters', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') +attogram_per_cubic_gigameters = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_gigameters', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') +gram_per_cubic_megameters = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_megameters', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') +exagram_per_cubic_megameters = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_megameters', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') +petagram_per_cubic_megameters = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_megameters', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') +teragram_per_cubic_megameters = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_megameters', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') +gigagram_per_cubic_megameters = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_megameters', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') +megagram_per_cubic_megameters = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_megameters', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') +kilogram_per_cubic_megameters = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_megameters', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') +milligram_per_cubic_megameters = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_megameters', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') +microgram_per_cubic_megameters = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_megameters', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') +nanogram_per_cubic_megameters = Unit(1.0000000000000003e-30, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_megameters', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') +picogram_per_cubic_megameters = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_megameters', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') +femtogram_per_cubic_megameters = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_megameters', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') +attogram_per_cubic_megameters = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_megameters', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') +gram_per_cubic_kilometers = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_kilometers', ascii_symbol='g km^-3', symbol='Nonekm⁻³') +exagram_per_cubic_kilometers = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_kilometers', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') +petagram_per_cubic_kilometers = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_kilometers', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') +teragram_per_cubic_kilometers = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_kilometers', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') +gigagram_per_cubic_kilometers = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_kilometers', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') +megagram_per_cubic_kilometers = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_kilometers', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') +kilogram_per_cubic_kilometers = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_kilometers', ascii_symbol='kg km^-3', symbol='kgkm⁻³') +milligram_per_cubic_kilometers = Unit(9.999999999999999e-16, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_kilometers', ascii_symbol='mg km^-3', symbol='mgkm⁻³') +microgram_per_cubic_kilometers = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_kilometers', ascii_symbol='ug km^-3', symbol='µgkm⁻³') +nanogram_per_cubic_kilometers = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_kilometers', ascii_symbol='ng km^-3', symbol='ngkm⁻³') +picogram_per_cubic_kilometers = Unit(1.0000000000000001e-24, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_kilometers', ascii_symbol='pg km^-3', symbol='pgkm⁻³') +femtogram_per_cubic_kilometers = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_kilometers', ascii_symbol='fg km^-3', symbol='fgkm⁻³') +attogram_per_cubic_kilometers = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_kilometers', ascii_symbol='ag km^-3', symbol='agkm⁻³') +gram_per_cubic_millimeters = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_millimeters', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') +exagram_per_cubic_millimeters = Unit(1e+24, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_millimeters', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') +petagram_per_cubic_millimeters = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_millimeters', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') +teragram_per_cubic_millimeters = Unit(1e+18, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_millimeters', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') +gigagram_per_cubic_millimeters = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_millimeters', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') +megagram_per_cubic_millimeters = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_millimeters', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') +kilogram_per_cubic_millimeters = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_millimeters', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') +milligram_per_cubic_millimeters = Unit(999.9999999999999, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_millimeters', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') +microgram_per_cubic_millimeters = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_millimeters', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') +nanogram_per_cubic_millimeters = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_millimeters', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') +picogram_per_cubic_millimeters = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_millimeters', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') +femtogram_per_cubic_millimeters = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_millimeters', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') +attogram_per_cubic_millimeters = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_millimeters', ascii_symbol='ag mm^-3', symbol='agmm⁻³') +gram_per_cubic_micrometers = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_micrometers', ascii_symbol='g um^-3', symbol='Noneµm⁻³') +exagram_per_cubic_micrometers = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_micrometers', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') +petagram_per_cubic_micrometers = Unit(1.0000000000000002e+30, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_micrometers', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') +teragram_per_cubic_micrometers = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_micrometers', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') +gigagram_per_cubic_micrometers = Unit(1.0000000000000001e+24, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_micrometers', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') +megagram_per_cubic_micrometers = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_micrometers', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') +kilogram_per_cubic_micrometers = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_micrometers', ascii_symbol='kg um^-3', symbol='kgµm⁻³') +milligram_per_cubic_micrometers = Unit(1000000000000.0001, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_micrometers', ascii_symbol='mg um^-3', symbol='mgµm⁻³') +microgram_per_cubic_micrometers = Unit(1000000000.0000002, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_micrometers', ascii_symbol='ug um^-3', symbol='µgµm⁻³') +nanogram_per_cubic_micrometers = Unit(1000000.0000000003, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_micrometers', ascii_symbol='ng um^-3', symbol='ngµm⁻³') +picogram_per_cubic_micrometers = Unit(1000.0000000000002, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_micrometers', ascii_symbol='pg um^-3', symbol='pgµm⁻³') +femtogram_per_cubic_micrometers = Unit(1.0000000000000002, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_micrometers', ascii_symbol='fg um^-3', symbol='fgµm⁻³') +attogram_per_cubic_micrometers = Unit(0.0010000000000000002, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_micrometers', ascii_symbol='ag um^-3', symbol='agµm⁻³') +gram_per_cubic_nanometers = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_nanometers', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') +exagram_per_cubic_nanometers = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_nanometers', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') +petagram_per_cubic_nanometers = Unit(9.999999999999998e+38, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_nanometers', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') +teragram_per_cubic_nanometers = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_nanometers', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') +gigagram_per_cubic_nanometers = Unit(9.999999999999998e+32, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_nanometers', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') +megagram_per_cubic_nanometers = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_nanometers', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') +kilogram_per_cubic_nanometers = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_nanometers', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') +milligram_per_cubic_nanometers = Unit(9.999999999999997e+20, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_nanometers', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') +microgram_per_cubic_nanometers = Unit(9.999999999999999e+17, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_nanometers', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') +nanogram_per_cubic_nanometers = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_nanometers', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') +picogram_per_cubic_nanometers = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_nanometers', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') +femtogram_per_cubic_nanometers = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_nanometers', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') +attogram_per_cubic_nanometers = Unit(999999.9999999999, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_nanometers', ascii_symbol='ag nm^-3', symbol='agnm⁻³') +gram_per_cubic_picometers = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_picometers', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') +exagram_per_cubic_picometers = Unit(1e+51, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_picometers', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') +petagram_per_cubic_picometers = Unit(1e+48, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_picometers', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') +teragram_per_cubic_picometers = Unit(1.0000000000000001e+45, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_picometers', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') +gigagram_per_cubic_picometers = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_picometers', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') +megagram_per_cubic_picometers = Unit(1.0000000000000001e+39, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_picometers', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') +kilogram_per_cubic_picometers = Unit(1e+36, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_picometers', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') +milligram_per_cubic_picometers = Unit(1e+30, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_picometers', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') +microgram_per_cubic_picometers = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_picometers', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') +nanogram_per_cubic_picometers = Unit(1.0000000000000003e+24, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_picometers', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') +picogram_per_cubic_picometers = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_picometers', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') +femtogram_per_cubic_picometers = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_picometers', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') +attogram_per_cubic_picometers = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_picometers', ascii_symbol='ag pm^-3', symbol='agpm⁻³') +gram_per_cubic_femtometers = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_femtometers', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') +exagram_per_cubic_femtometers = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_femtometers', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') +petagram_per_cubic_femtometers = Unit(9.999999999999997e+56, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_femtometers', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') +teragram_per_cubic_femtometers = Unit(9.999999999999997e+53, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_femtometers', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') +gigagram_per_cubic_femtometers = Unit(9.999999999999997e+50, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_femtometers', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') +megagram_per_cubic_femtometers = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_femtometers', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') +kilogram_per_cubic_femtometers = Unit(9.999999999999998e+44, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_femtometers', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') +milligram_per_cubic_femtometers = Unit(9.999999999999996e+38, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_femtometers', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') +microgram_per_cubic_femtometers = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_femtometers', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') +nanogram_per_cubic_femtometers = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_femtometers', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') +picogram_per_cubic_femtometers = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_femtometers', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') +femtogram_per_cubic_femtometers = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_femtometers', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') +attogram_per_cubic_femtometers = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_femtometers', ascii_symbol='ag fm^-3', symbol='agfm⁻³') +gram_per_cubic_attometers = Unit(9.999999999999998e+50, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_attometers', ascii_symbol='g am^-3', symbol='Noneam⁻³') +exagram_per_cubic_attometers = Unit(9.999999999999999e+68, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_attometers', ascii_symbol='Eg am^-3', symbol='Egam⁻³') +petagram_per_cubic_attometers = Unit(9.999999999999998e+65, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_attometers', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') +teragram_per_cubic_attometers = Unit(9.999999999999999e+62, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_attometers', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') +gigagram_per_cubic_attometers = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_attometers', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') +megagram_per_cubic_attometers = Unit(9.999999999999999e+56, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_attometers', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') +kilogram_per_cubic_attometers = Unit(9.999999999999999e+53, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_attometers', ascii_symbol='kg am^-3', symbol='kgam⁻³') +milligram_per_cubic_attometers = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_attometers', ascii_symbol='mg am^-3', symbol='mgam⁻³') +microgram_per_cubic_attometers = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_attometers', ascii_symbol='ug am^-3', symbol='µgam⁻³') +nanogram_per_cubic_attometers = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_attometers', ascii_symbol='ng am^-3', symbol='ngam⁻³') +picogram_per_cubic_attometers = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_attometers', ascii_symbol='pg am^-3', symbol='pgam⁻³') +femtogram_per_cubic_attometers = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_attometers', ascii_symbol='fg am^-3', symbol='fgam⁻³') +attogram_per_cubic_attometers = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_attometers', ascii_symbol='ag am^-3', symbol='agam⁻³') +gram_per_cubic_angstroms = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_angstroms', ascii_symbol='g A^-3', symbol='NoneÅ⁻³') +exagram_per_cubic_angstroms = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_angstroms', ascii_symbol='Eg A^-3', symbol='EgÅ⁻³') +petagram_per_cubic_angstroms = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_angstroms', ascii_symbol='Pg A^-3', symbol='PgÅ⁻³') +teragram_per_cubic_angstroms = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_angstroms', ascii_symbol='Tg A^-3', symbol='TgÅ⁻³') +gigagram_per_cubic_angstroms = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_angstroms', ascii_symbol='Gg A^-3', symbol='GgÅ⁻³') +megagram_per_cubic_angstroms = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_angstroms', ascii_symbol='Mg A^-3', symbol='MgÅ⁻³') +kilogram_per_cubic_angstroms = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_angstroms', ascii_symbol='kg A^-3', symbol='kgÅ⁻³') +milligram_per_cubic_angstroms = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_angstroms', ascii_symbol='mg A^-3', symbol='mgÅ⁻³') +microgram_per_cubic_angstroms = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_angstroms', ascii_symbol='ug A^-3', symbol='µgÅ⁻³') +nanogram_per_cubic_angstroms = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_angstroms', ascii_symbol='ng A^-3', symbol='ngÅ⁻³') +picogram_per_cubic_angstroms = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_angstroms', ascii_symbol='pg A^-3', symbol='pgÅ⁻³') +femtogram_per_cubic_angstroms = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_angstroms', ascii_symbol='fg A^-3', symbol='fgÅ⁻³') +attogram_per_cubic_angstroms = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_angstroms', ascii_symbol='ag A^-3', symbol='agÅ⁻³') +gram_per_cubic_angstroms = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_angstroms', ascii_symbol='g Ang^-3', symbol='NoneNone⁻³') +exagram_per_cubic_angstroms = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_angstroms', ascii_symbol='Eg Ang^-3', symbol='EgNone⁻³') +petagram_per_cubic_angstroms = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_angstroms', ascii_symbol='Pg Ang^-3', symbol='PgNone⁻³') +teragram_per_cubic_angstroms = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_angstroms', ascii_symbol='Tg Ang^-3', symbol='TgNone⁻³') +gigagram_per_cubic_angstroms = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_angstroms', ascii_symbol='Gg Ang^-3', symbol='GgNone⁻³') +megagram_per_cubic_angstroms = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_angstroms', ascii_symbol='Mg Ang^-3', symbol='MgNone⁻³') +kilogram_per_cubic_angstroms = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_angstroms', ascii_symbol='kg Ang^-3', symbol='kgNone⁻³') +milligram_per_cubic_angstroms = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_angstroms', ascii_symbol='mg Ang^-3', symbol='mgNone⁻³') +microgram_per_cubic_angstroms = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_angstroms', ascii_symbol='ug Ang^-3', symbol='µgNone⁻³') +nanogram_per_cubic_angstroms = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_angstroms', ascii_symbol='ng Ang^-3', symbol='ngNone⁻³') +picogram_per_cubic_angstroms = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_angstroms', ascii_symbol='pg Ang^-3', symbol='pgNone⁻³') +femtogram_per_cubic_angstroms = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_angstroms', ascii_symbol='fg Ang^-3', symbol='fgNone⁻³') +attogram_per_cubic_angstroms = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_angstroms', ascii_symbol='ag Ang^-3', symbol='agNone⁻³') # # Lookup table from symbols to units From 96e1f493bec11bccab78816c57cc3a7a4cfbaf4f Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 6 Aug 2024 15:43:45 +0100 Subject: [PATCH 493/675] Use alias list to remove duplicates --- sasdata/quantities/_units_table.py | 26 +- sasdata/quantities/units.py | 678 +++++++++++------------------ 2 files changed, 263 insertions(+), 441 deletions(-) diff --git a/sasdata/quantities/_units_table.py b/sasdata/quantities/_units_table.py index 27165bde8..0749461f8 100644 --- a/sasdata/quantities/_units_table.py +++ b/sasdata/quantities/_units_table.py @@ -50,20 +50,23 @@ ] non_si_units = [ - ("A", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), - ("Ang", None, "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), + ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, []), - ("hr", None, "hour", "hours", 360, 0, 1, 0, 0, 0, []), + ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, []), ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), - ("day", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), - ("yr", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, []), ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, []), ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []), ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, []) ] +aliases = { + "y": ["yr", "year"], + "d": ["day"], + "h": ["hr", "hour"], + "Ang": ["A", "Å"] +} all_units = base_si_units + derived_si_units + non_si_units @@ -215,8 +218,8 @@ def format_name(name: str): unit_types[hash(accel_dimensions)].append(accel_name) # Density - for length_symbol, length_special_symbol, _, length_name, length_scale, _ in length_units: - for mass_symbol, mass_special_symbol, mass_name, _, mass_scale, _ in mass_units: + for length_symbol, length_special_symbol, length_name, _, length_scale, _ in length_units: + for mass_symbol, mass_special_symbol, _, mass_name, mass_scale, _ in mass_units: name = mass_name + "_per_cubic_" + length_name @@ -231,6 +234,15 @@ def format_name(name: str): unit_types[hash(dimensions)].append(name) + # + # Add aliases to symbol lookup table + # + + for base_name in aliases: + alias_list = aliases[base_name] + for alias in alias_list: + symbol_lookup[alias] = symbol_lookup[base_name] + # # Write out the symbol lookup table # diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 60517eca8..cedfb353a 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -417,14 +417,11 @@ def __init__(self, name: str, units: list[Unit]): femtohenry = Unit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') attohenry = Unit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1),name='degrees_celsius',ascii_symbol='C',symbol='C') -angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0),name='angstroms',ascii_symbol='A',symbol='Å') -angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Ang') +angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') minutes = Unit(60, Dimensions(0, 1, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') -hours = Unit(360, Dimensions(0, 1, 0, 0, 0),name='hours',ascii_symbol='hr',symbol='hr') +hours = Unit(360, Dimensions(0, 1, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') days = Unit(8640, Dimensions(0, 1, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') -days = Unit(8640, Dimensions(0, 1, 0, 0, 0),name='days',ascii_symbol='day',symbol='day') years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') -years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0),name='years',ascii_symbol='yr',symbol='yr') degrees = Unit(57.29577951308232, Dimensions(0, 0, 0, 0, 0),name='degrees',ascii_symbol='deg',symbol='deg') radians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='radians',ascii_symbol='rad',symbol='rad') stradians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='stradians',ascii_symbol='sr',symbol='sr') @@ -494,16 +491,11 @@ def __init__(self, name: str, units: list[Unit]): per_attometer = Unit(9.999999999999999e+17, Dimensions(-1, 0, 0, 0, 0), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') per_square_attometer = Unit(9.999999999999999e+35, Dimensions(-2, 0, 0, 0, 0), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') per_cubic_attometer = Unit(9.999999999999997e+53, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') -square_angstroms = Unit(1.0000000000000001e-20, Dimensions(2, 0, 0, 0, 0), name='square_angstroms', ascii_symbol='A^2', symbol='Ų') -cubic_angstroms = Unit(1e-30, Dimensions(3, 0, 0, 0, 0), name='cubic_angstroms', ascii_symbol='A^3', symbol='ų') -per_angstrom = Unit(10000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_angstrom', ascii_symbol='A^-1', symbol='Å⁻¹') -per_square_angstrom = Unit(1e+20, Dimensions(-2, 0, 0, 0, 0), name='per_square_angstrom', ascii_symbol='A^-2', symbol='Å⁻²') -per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_angstrom', ascii_symbol='A^-3', symbol='Å⁻³') -square_angstroms = Unit(1.0000000000000001e-20, Dimensions(2, 0, 0, 0, 0), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ang²') -cubic_angstroms = Unit(1e-30, Dimensions(3, 0, 0, 0, 0), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='Ang³') -per_angstrom = Unit(10000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Ang⁻¹') -per_square_angstrom = Unit(1e+20, Dimensions(-2, 0, 0, 0, 0), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Ang⁻²') -per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Ang⁻³') +square_angstroms = Unit(1.0000000000000001e-20, Dimensions(2, 0, 0, 0, 0), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ų') +cubic_angstroms = Unit(1e-30, Dimensions(3, 0, 0, 0, 0), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='ų') +per_angstrom = Unit(10000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Å⁻¹') +per_square_angstrom = Unit(1e+20, Dimensions(-2, 0, 0, 0, 0), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Å⁻²') +per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Å⁻³') meters_per_second = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') meters_per_square_second = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') meters_per_millisecond = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') @@ -520,16 +512,12 @@ def __init__(self, name: str, units: list[Unit]): meters_per_square_attosecond = Unit(9.999999999999999e+17, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') meters_per_minute = Unit(0.016666666666666666, Dimensions(1, -1, 0, 0, 0), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') meters_per_square_minute = Unit(0.016666666666666666, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') -meters_per_hour = Unit(0.002777777777777778, Dimensions(1, -1, 0, 0, 0), name='meters_per_hour', ascii_symbol='m/hr', symbol='NoneNone⁻¹') -meters_per_square_hour = Unit(0.002777777777777778, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_hour', ascii_symbol='m/hr^2', symbol='NoneNone⁻²') +meters_per_hour = Unit(0.002777777777777778, Dimensions(1, -1, 0, 0, 0), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') +meters_per_square_hour = Unit(0.002777777777777778, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') meters_per_day = Unit(0.00011574074074074075, Dimensions(1, -1, 0, 0, 0), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') meters_per_square_day = Unit(0.00011574074074074075, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') -meters_per_day = Unit(0.00011574074074074075, Dimensions(1, -1, 0, 0, 0), name='meters_per_day', ascii_symbol='m/day', symbol='NoneNone⁻¹') -meters_per_square_day = Unit(0.00011574074074074075, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_day', ascii_symbol='m/day^2', symbol='NoneNone⁻²') meters_per_year = Unit(3.168873850681143e-07, Dimensions(1, -1, 0, 0, 0), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') -meters_per_year = Unit(3.168873850681143e-07, Dimensions(1, -1, 0, 0, 0), name='meters_per_year', ascii_symbol='m/yr', symbol='NoneNone⁻¹') -meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_year', ascii_symbol='m/yr^2', symbol='NoneNone⁻²') exameters_per_second = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') exameters_per_square_second = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') exameters_per_millisecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') @@ -546,16 +534,12 @@ def __init__(self, name: str, units: list[Unit]): exameters_per_square_attosecond = Unit(9.999999999999999e+35, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') exameters_per_minute = Unit(1.6666666666666666e+16, Dimensions(1, -1, 0, 0, 0), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') exameters_per_square_minute = Unit(1.6666666666666666e+16, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') -exameters_per_hour = Unit(2777777777777778.0, Dimensions(1, -1, 0, 0, 0), name='exameters_per_hour', ascii_symbol='Em/hr', symbol='EmNone⁻¹') -exameters_per_square_hour = Unit(2777777777777778.0, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_hour', ascii_symbol='Em/hr^2', symbol='EmNone⁻²') +exameters_per_hour = Unit(2777777777777778.0, Dimensions(1, -1, 0, 0, 0), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') +exameters_per_square_hour = Unit(2777777777777778.0, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') exameters_per_day = Unit(115740740740740.73, Dimensions(1, -1, 0, 0, 0), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') exameters_per_square_day = Unit(115740740740740.73, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') -exameters_per_day = Unit(115740740740740.73, Dimensions(1, -1, 0, 0, 0), name='exameters_per_day', ascii_symbol='Em/day', symbol='EmNone⁻¹') -exameters_per_square_day = Unit(115740740740740.73, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_day', ascii_symbol='Em/day^2', symbol='EmNone⁻²') exameters_per_year = Unit(316887385068.1143, Dimensions(1, -1, 0, 0, 0), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') exameters_per_square_year = Unit(316887385068.1143, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') -exameters_per_year = Unit(316887385068.1143, Dimensions(1, -1, 0, 0, 0), name='exameters_per_year', ascii_symbol='Em/yr', symbol='EmNone⁻¹') -exameters_per_square_year = Unit(316887385068.1143, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_year', ascii_symbol='Em/yr^2', symbol='EmNone⁻²') petameters_per_second = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') petameters_per_square_second = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') petameters_per_millisecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') @@ -572,16 +556,12 @@ def __init__(self, name: str, units: list[Unit]): petameters_per_square_attosecond = Unit(1e+33, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') petameters_per_minute = Unit(16666666666666.666, Dimensions(1, -1, 0, 0, 0), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') petameters_per_square_minute = Unit(16666666666666.666, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') -petameters_per_hour = Unit(2777777777777.778, Dimensions(1, -1, 0, 0, 0), name='petameters_per_hour', ascii_symbol='Pm/hr', symbol='PmNone⁻¹') -petameters_per_square_hour = Unit(2777777777777.778, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_hour', ascii_symbol='Pm/hr^2', symbol='PmNone⁻²') +petameters_per_hour = Unit(2777777777777.778, Dimensions(1, -1, 0, 0, 0), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') +petameters_per_square_hour = Unit(2777777777777.778, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') petameters_per_day = Unit(115740740740.74074, Dimensions(1, -1, 0, 0, 0), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') petameters_per_square_day = Unit(115740740740.74074, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') -petameters_per_day = Unit(115740740740.74074, Dimensions(1, -1, 0, 0, 0), name='petameters_per_day', ascii_symbol='Pm/day', symbol='PmNone⁻¹') -petameters_per_square_day = Unit(115740740740.74074, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_day', ascii_symbol='Pm/day^2', symbol='PmNone⁻²') petameters_per_year = Unit(316887385.0681143, Dimensions(1, -1, 0, 0, 0), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') petameters_per_square_year = Unit(316887385.0681143, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') -petameters_per_year = Unit(316887385.0681143, Dimensions(1, -1, 0, 0, 0), name='petameters_per_year', ascii_symbol='Pm/yr', symbol='PmNone⁻¹') -petameters_per_square_year = Unit(316887385.0681143, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_year', ascii_symbol='Pm/yr^2', symbol='PmNone⁻²') terameters_per_second = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') terameters_per_square_second = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') terameters_per_millisecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') @@ -598,16 +578,12 @@ def __init__(self, name: str, units: list[Unit]): terameters_per_square_attosecond = Unit(9.999999999999999e+29, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') terameters_per_minute = Unit(16666666666.666666, Dimensions(1, -1, 0, 0, 0), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') terameters_per_square_minute = Unit(16666666666.666666, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') -terameters_per_hour = Unit(2777777777.7777777, Dimensions(1, -1, 0, 0, 0), name='terameters_per_hour', ascii_symbol='Tm/hr', symbol='TmNone⁻¹') -terameters_per_square_hour = Unit(2777777777.7777777, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_hour', ascii_symbol='Tm/hr^2', symbol='TmNone⁻²') +terameters_per_hour = Unit(2777777777.7777777, Dimensions(1, -1, 0, 0, 0), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') +terameters_per_square_hour = Unit(2777777777.7777777, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') terameters_per_day = Unit(115740740.74074075, Dimensions(1, -1, 0, 0, 0), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') terameters_per_square_day = Unit(115740740.74074075, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') -terameters_per_day = Unit(115740740.74074075, Dimensions(1, -1, 0, 0, 0), name='terameters_per_day', ascii_symbol='Tm/day', symbol='TmNone⁻¹') -terameters_per_square_day = Unit(115740740.74074075, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_day', ascii_symbol='Tm/day^2', symbol='TmNone⁻²') terameters_per_year = Unit(316887.38506811426, Dimensions(1, -1, 0, 0, 0), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') terameters_per_square_year = Unit(316887.38506811426, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') -terameters_per_year = Unit(316887.38506811426, Dimensions(1, -1, 0, 0, 0), name='terameters_per_year', ascii_symbol='Tm/yr', symbol='TmNone⁻¹') -terameters_per_square_year = Unit(316887.38506811426, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_year', ascii_symbol='Tm/yr^2', symbol='TmNone⁻²') gigameters_per_second = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') gigameters_per_square_second = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') gigameters_per_millisecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') @@ -624,16 +600,12 @@ def __init__(self, name: str, units: list[Unit]): gigameters_per_square_attosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') gigameters_per_minute = Unit(16666666.666666666, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') gigameters_per_square_minute = Unit(16666666.666666666, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') -gigameters_per_hour = Unit(2777777.777777778, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_hour', ascii_symbol='Gm/hr', symbol='GmNone⁻¹') -gigameters_per_square_hour = Unit(2777777.777777778, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_hour', ascii_symbol='Gm/hr^2', symbol='GmNone⁻²') +gigameters_per_hour = Unit(2777777.777777778, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') +gigameters_per_square_hour = Unit(2777777.777777778, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') gigameters_per_day = Unit(115740.74074074074, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') gigameters_per_square_day = Unit(115740.74074074074, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') -gigameters_per_day = Unit(115740.74074074074, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_day', ascii_symbol='Gm/day', symbol='GmNone⁻¹') -gigameters_per_square_day = Unit(115740.74074074074, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_day', ascii_symbol='Gm/day^2', symbol='GmNone⁻²') gigameters_per_year = Unit(316.88738506811427, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') gigameters_per_square_year = Unit(316.88738506811427, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') -gigameters_per_year = Unit(316.88738506811427, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_year', ascii_symbol='Gm/yr', symbol='GmNone⁻¹') -gigameters_per_square_year = Unit(316.88738506811427, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_year', ascii_symbol='Gm/yr^2', symbol='GmNone⁻²') megameters_per_second = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') megameters_per_square_second = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') megameters_per_millisecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') @@ -650,16 +622,12 @@ def __init__(self, name: str, units: list[Unit]): megameters_per_square_attosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') megameters_per_minute = Unit(16666.666666666668, Dimensions(1, -1, 0, 0, 0), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') megameters_per_square_minute = Unit(16666.666666666668, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') -megameters_per_hour = Unit(2777.777777777778, Dimensions(1, -1, 0, 0, 0), name='megameters_per_hour', ascii_symbol='Mm/hr', symbol='MmNone⁻¹') -megameters_per_square_hour = Unit(2777.777777777778, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_hour', ascii_symbol='Mm/hr^2', symbol='MmNone⁻²') +megameters_per_hour = Unit(2777.777777777778, Dimensions(1, -1, 0, 0, 0), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') +megameters_per_square_hour = Unit(2777.777777777778, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') megameters_per_day = Unit(115.74074074074075, Dimensions(1, -1, 0, 0, 0), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') megameters_per_square_day = Unit(115.74074074074075, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') -megameters_per_day = Unit(115.74074074074075, Dimensions(1, -1, 0, 0, 0), name='megameters_per_day', ascii_symbol='Mm/day', symbol='MmNone⁻¹') -megameters_per_square_day = Unit(115.74074074074075, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_day', ascii_symbol='Mm/day^2', symbol='MmNone⁻²') megameters_per_year = Unit(0.3168873850681143, Dimensions(1, -1, 0, 0, 0), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') megameters_per_square_year = Unit(0.3168873850681143, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') -megameters_per_year = Unit(0.3168873850681143, Dimensions(1, -1, 0, 0, 0), name='megameters_per_year', ascii_symbol='Mm/yr', symbol='MmNone⁻¹') -megameters_per_square_year = Unit(0.3168873850681143, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_year', ascii_symbol='Mm/yr^2', symbol='MmNone⁻²') kilometers_per_second = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') kilometers_per_square_second = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') kilometers_per_millisecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') @@ -676,16 +644,12 @@ def __init__(self, name: str, units: list[Unit]): kilometers_per_square_attosecond = Unit(9.999999999999999e+20, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') kilometers_per_minute = Unit(16.666666666666668, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') kilometers_per_square_minute = Unit(16.666666666666668, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') -kilometers_per_hour = Unit(2.7777777777777777, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_hour', ascii_symbol='km/hr', symbol='kmNone⁻¹') -kilometers_per_square_hour = Unit(2.7777777777777777, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_hour', ascii_symbol='km/hr^2', symbol='kmNone⁻²') +kilometers_per_hour = Unit(2.7777777777777777, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') +kilometers_per_square_hour = Unit(2.7777777777777777, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') kilometers_per_day = Unit(0.11574074074074074, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') -kilometers_per_day = Unit(0.11574074074074074, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_day', ascii_symbol='km/day', symbol='kmNone⁻¹') -kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_day', ascii_symbol='km/day^2', symbol='kmNone⁻²') kilometers_per_year = Unit(0.0003168873850681143, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') -kilometers_per_year = Unit(0.0003168873850681143, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_year', ascii_symbol='km/yr', symbol='kmNone⁻¹') -kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_year', ascii_symbol='km/yr^2', symbol='kmNone⁻²') millimeters_per_second = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') millimeters_per_square_second = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') millimeters_per_millisecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') @@ -702,16 +666,12 @@ def __init__(self, name: str, units: list[Unit]): millimeters_per_square_attosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') millimeters_per_minute = Unit(1.6666666666666667e-05, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') millimeters_per_square_minute = Unit(1.6666666666666667e-05, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') -millimeters_per_hour = Unit(2.777777777777778e-06, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_hour', ascii_symbol='mm/hr', symbol='mmNone⁻¹') -millimeters_per_square_hour = Unit(2.777777777777778e-06, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_hour', ascii_symbol='mm/hr^2', symbol='mmNone⁻²') +millimeters_per_hour = Unit(2.777777777777778e-06, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') +millimeters_per_square_hour = Unit(2.777777777777778e-06, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') -millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_day', ascii_symbol='mm/day', symbol='mmNone⁻¹') -millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_day', ascii_symbol='mm/day^2', symbol='mmNone⁻²') millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') -millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_year', ascii_symbol='mm/yr', symbol='mmNone⁻¹') -millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_year', ascii_symbol='mm/yr^2', symbol='mmNone⁻²') micrometers_per_second = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') micrometers_per_square_second = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') micrometers_per_millisecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') @@ -728,16 +688,12 @@ def __init__(self, name: str, units: list[Unit]): micrometers_per_square_attosecond = Unit(999999999999.9999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') micrometers_per_minute = Unit(1.6666666666666667e-08, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') micrometers_per_square_minute = Unit(1.6666666666666667e-08, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') -micrometers_per_hour = Unit(2.7777777777777776e-09, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_hour', ascii_symbol='um/hr', symbol='µmNone⁻¹') -micrometers_per_square_hour = Unit(2.7777777777777776e-09, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_hour', ascii_symbol='um/hr^2', symbol='µmNone⁻²') +micrometers_per_hour = Unit(2.7777777777777776e-09, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') +micrometers_per_square_hour = Unit(2.7777777777777776e-09, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') -micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_day', ascii_symbol='um/day', symbol='µmNone⁻¹') -micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_day', ascii_symbol='um/day^2', symbol='µmNone⁻²') micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') -micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_year', ascii_symbol='um/yr', symbol='µmNone⁻¹') -micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_year', ascii_symbol='um/yr^2', symbol='µmNone⁻²') nanometers_per_second = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') nanometers_per_square_second = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') nanometers_per_millisecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') @@ -754,16 +710,12 @@ def __init__(self, name: str, units: list[Unit]): nanometers_per_square_attosecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') nanometers_per_minute = Unit(1.6666666666666667e-11, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') nanometers_per_square_minute = Unit(1.6666666666666667e-11, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') -nanometers_per_hour = Unit(2.777777777777778e-12, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_hour', ascii_symbol='nm/hr', symbol='nmNone⁻¹') -nanometers_per_square_hour = Unit(2.777777777777778e-12, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_hour', ascii_symbol='nm/hr^2', symbol='nmNone⁻²') +nanometers_per_hour = Unit(2.777777777777778e-12, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') +nanometers_per_square_hour = Unit(2.777777777777778e-12, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') -nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_day', ascii_symbol='nm/day', symbol='nmNone⁻¹') -nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_day', ascii_symbol='nm/day^2', symbol='nmNone⁻²') nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') -nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_year', ascii_symbol='nm/yr', symbol='nmNone⁻¹') -nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_year', ascii_symbol='nm/yr^2', symbol='nmNone⁻²') picometers_per_second = Unit(1e-12, Dimensions(1, -1, 0, 0, 0), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') picometers_per_square_second = Unit(1e-12, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') picometers_per_millisecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') @@ -780,16 +732,12 @@ def __init__(self, name: str, units: list[Unit]): picometers_per_square_attosecond = Unit(999999.9999999999, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') picometers_per_minute = Unit(1.6666666666666667e-14, Dimensions(1, -1, 0, 0, 0), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') picometers_per_square_minute = Unit(1.6666666666666667e-14, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') -picometers_per_hour = Unit(2.7777777777777776e-15, Dimensions(1, -1, 0, 0, 0), name='picometers_per_hour', ascii_symbol='pm/hr', symbol='pmNone⁻¹') -picometers_per_square_hour = Unit(2.7777777777777776e-15, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_hour', ascii_symbol='pm/hr^2', symbol='pmNone⁻²') +picometers_per_hour = Unit(2.7777777777777776e-15, Dimensions(1, -1, 0, 0, 0), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') +picometers_per_square_hour = Unit(2.7777777777777776e-15, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(1, -1, 0, 0, 0), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') -picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(1, -1, 0, 0, 0), name='picometers_per_day', ascii_symbol='pm/day', symbol='pmNone⁻¹') -picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_day', ascii_symbol='pm/day^2', symbol='pmNone⁻²') picometers_per_year = Unit(3.168873850681143e-19, Dimensions(1, -1, 0, 0, 0), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') -picometers_per_year = Unit(3.168873850681143e-19, Dimensions(1, -1, 0, 0, 0), name='picometers_per_year', ascii_symbol='pm/yr', symbol='pmNone⁻¹') -picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_year', ascii_symbol='pm/yr^2', symbol='pmNone⁻²') femtometers_per_second = Unit(1e-15, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') femtometers_per_square_second = Unit(1e-15, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') femtometers_per_millisecond = Unit(1e-12, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') @@ -806,16 +754,12 @@ def __init__(self, name: str, units: list[Unit]): femtometers_per_square_attosecond = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') femtometers_per_minute = Unit(1.6666666666666667e-17, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') femtometers_per_square_minute = Unit(1.6666666666666667e-17, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') -femtometers_per_hour = Unit(2.777777777777778e-18, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_hour', ascii_symbol='fm/hr', symbol='fmNone⁻¹') -femtometers_per_square_hour = Unit(2.777777777777778e-18, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_hour', ascii_symbol='fm/hr^2', symbol='fmNone⁻²') +femtometers_per_hour = Unit(2.777777777777778e-18, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') +femtometers_per_square_hour = Unit(2.777777777777778e-18, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') -femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_day', ascii_symbol='fm/day', symbol='fmNone⁻¹') -femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_day', ascii_symbol='fm/day^2', symbol='fmNone⁻²') femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') -femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_year', ascii_symbol='fm/yr', symbol='fmNone⁻¹') -femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_year', ascii_symbol='fm/yr^2', symbol='fmNone⁻²') attometers_per_second = Unit(1e-18, Dimensions(1, -1, 0, 0, 0), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') attometers_per_square_second = Unit(1e-18, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') attometers_per_millisecond = Unit(1e-15, Dimensions(1, -1, 0, 0, 0), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') @@ -832,263 +776,216 @@ def __init__(self, name: str, units: list[Unit]): attometers_per_square_attosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') attometers_per_minute = Unit(1.6666666666666668e-20, Dimensions(1, -1, 0, 0, 0), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') attometers_per_square_minute = Unit(1.6666666666666668e-20, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') -attometers_per_hour = Unit(2.7777777777777778e-21, Dimensions(1, -1, 0, 0, 0), name='attometers_per_hour', ascii_symbol='am/hr', symbol='amNone⁻¹') -attometers_per_square_hour = Unit(2.7777777777777778e-21, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_hour', ascii_symbol='am/hr^2', symbol='amNone⁻²') +attometers_per_hour = Unit(2.7777777777777778e-21, Dimensions(1, -1, 0, 0, 0), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') +attometers_per_square_hour = Unit(2.7777777777777778e-21, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(1, -1, 0, 0, 0), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') -attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(1, -1, 0, 0, 0), name='attometers_per_day', ascii_symbol='am/day', symbol='amNone⁻¹') -attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_day', ascii_symbol='am/day^2', symbol='amNone⁻²') attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(1, -1, 0, 0, 0), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') -attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(1, -1, 0, 0, 0), name='attometers_per_year', ascii_symbol='am/yr', symbol='amNone⁻¹') -attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_year', ascii_symbol='am/yr^2', symbol='amNone⁻²') -angstroms_per_second = Unit(1e-10, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_second', ascii_symbol='A/s', symbol='ÅNone⁻¹') -angstroms_per_square_second = Unit(1e-10, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_second', ascii_symbol='A/s^2', symbol='ÅNone⁻²') -angstroms_per_millisecond = Unit(1e-07, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_millisecond', ascii_symbol='A/ms', symbol='Åms⁻¹') -angstroms_per_square_millisecond = Unit(1e-07, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_millisecond', ascii_symbol='A/ms^2', symbol='Åms⁻²') -angstroms_per_microsecond = Unit(0.0001, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_microsecond', ascii_symbol='A/us', symbol='ŵs⁻¹') -angstroms_per_square_microsecond = Unit(0.0001, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_microsecond', ascii_symbol='A/us^2', symbol='ŵs⁻²') -angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_nanosecond', ascii_symbol='A/ns', symbol='Åns⁻¹') -angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_nanosecond', ascii_symbol='A/ns^2', symbol='Åns⁻²') -angstroms_per_picosecond = Unit(100.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_picosecond', ascii_symbol='A/ps', symbol='Åps⁻¹') -angstroms_per_square_picosecond = Unit(100.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_picosecond', ascii_symbol='A/ps^2', symbol='Åps⁻²') -angstroms_per_femtosecond = Unit(100000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_femtosecond', ascii_symbol='A/fs', symbol='Åfs⁻¹') -angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_femtosecond', ascii_symbol='A/fs^2', symbol='Åfs⁻²') -angstroms_per_attosecond = Unit(100000000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_attosecond', ascii_symbol='A/as', symbol='Åas⁻¹') -angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_attosecond', ascii_symbol='A/as^2', symbol='Åas⁻²') -angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_minute', ascii_symbol='A/min', symbol='ÅNone⁻¹') -angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_minute', ascii_symbol='A/min^2', symbol='ÅNone⁻²') -angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_hour', ascii_symbol='A/hr', symbol='ÅNone⁻¹') -angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_hour', ascii_symbol='A/hr^2', symbol='ÅNone⁻²') -angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='A/d', symbol='ÅNone⁻¹') -angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='A/d^2', symbol='ÅNone⁻²') -angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='A/day', symbol='ÅNone⁻¹') -angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='A/day^2', symbol='ÅNone⁻²') -angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='A/y', symbol='ÅNone⁻¹') -angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='A/y^2', symbol='ÅNone⁻²') -angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='A/yr', symbol='ÅNone⁻¹') -angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='A/yr^2', symbol='ÅNone⁻²') -angstroms_per_second = Unit(1e-10, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='NoneNone⁻¹') -angstroms_per_square_second = Unit(1e-10, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='NoneNone⁻²') -angstroms_per_millisecond = Unit(1e-07, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Nonems⁻¹') -angstroms_per_square_millisecond = Unit(1e-07, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Nonems⁻²') -angstroms_per_microsecond = Unit(0.0001, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='Noneµs⁻¹') -angstroms_per_square_microsecond = Unit(0.0001, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='Noneµs⁻²') -angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Nonens⁻¹') -angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Nonens⁻²') -angstroms_per_picosecond = Unit(100.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Noneps⁻¹') -angstroms_per_square_picosecond = Unit(100.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Noneps⁻²') -angstroms_per_femtosecond = Unit(100000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Nonefs⁻¹') -angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Nonefs⁻²') -angstroms_per_attosecond = Unit(100000000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Noneas⁻¹') -angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Noneas⁻²') -angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='NoneNone⁻¹') -angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='NoneNone⁻²') -angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_hour', ascii_symbol='Ang/hr', symbol='NoneNone⁻¹') -angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_hour', ascii_symbol='Ang/hr^2', symbol='NoneNone⁻²') -angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='NoneNone⁻¹') -angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='NoneNone⁻²') -angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='Ang/day', symbol='NoneNone⁻¹') -angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='Ang/day^2', symbol='NoneNone⁻²') -angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='NoneNone⁻¹') -angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='NoneNone⁻²') -angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/yr', symbol='NoneNone⁻¹') -angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/yr^2', symbol='NoneNone⁻²') -gram_per_cubic_meters = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_meters', ascii_symbol='g m^-3', symbol='NoneNone⁻³') -exagram_per_cubic_meters = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_meters', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') -petagram_per_cubic_meters = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_meters', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') -teragram_per_cubic_meters = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_meters', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') -gigagram_per_cubic_meters = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_meters', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') -megagram_per_cubic_meters = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_meters', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') -kilogram_per_cubic_meters = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_meters', ascii_symbol='kg m^-3', symbol='kgNone⁻³') -milligram_per_cubic_meters = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_meters', ascii_symbol='mg m^-3', symbol='mgNone⁻³') -microgram_per_cubic_meters = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_meters', ascii_symbol='ug m^-3', symbol='µgNone⁻³') -nanogram_per_cubic_meters = Unit(1.0000000000000002e-12, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_meters', ascii_symbol='ng m^-3', symbol='ngNone⁻³') -picogram_per_cubic_meters = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_meters', ascii_symbol='pg m^-3', symbol='pgNone⁻³') -femtogram_per_cubic_meters = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_meters', ascii_symbol='fg m^-3', symbol='fgNone⁻³') -attogram_per_cubic_meters = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_meters', ascii_symbol='ag m^-3', symbol='agNone⁻³') -gram_per_cubic_exameters = Unit(1e-57, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_exameters', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') -exagram_per_cubic_exameters = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_exameters', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') -petagram_per_cubic_exameters = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_exameters', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') -teragram_per_cubic_exameters = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_exameters', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') -gigagram_per_cubic_exameters = Unit(1e-48, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_exameters', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') -megagram_per_cubic_exameters = Unit(9.999999999999999e-52, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_exameters', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') -kilogram_per_cubic_exameters = Unit(9.999999999999999e-55, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_exameters', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') -milligram_per_cubic_exameters = Unit(9.999999999999998e-61, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_exameters', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') -microgram_per_cubic_exameters = Unit(9.999999999999999e-64, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_exameters', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') -nanogram_per_cubic_exameters = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_exameters', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') -picogram_per_cubic_exameters = Unit(1e-69, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_exameters', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') -femtogram_per_cubic_exameters = Unit(1e-72, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_exameters', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') -attogram_per_cubic_exameters = Unit(1e-75, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_exameters', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -gram_per_cubic_petameters = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_petameters', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') -exagram_per_cubic_petameters = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_petameters', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') -petagram_per_cubic_petameters = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_petameters', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') -teragram_per_cubic_petameters = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_petameters', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') -gigagram_per_cubic_petameters = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_petameters', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') -megagram_per_cubic_petameters = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_petameters', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') -kilogram_per_cubic_petameters = Unit(1.0000000000000001e-45, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_petameters', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') -milligram_per_cubic_petameters = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_petameters', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') -microgram_per_cubic_petameters = Unit(1.0000000000000002e-54, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_petameters', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') -nanogram_per_cubic_petameters = Unit(1.0000000000000002e-57, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_petameters', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') -picogram_per_cubic_petameters = Unit(1.0000000000000001e-60, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_petameters', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') -femtogram_per_cubic_petameters = Unit(1.0000000000000002e-63, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_petameters', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') -attogram_per_cubic_petameters = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_petameters', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -gram_per_cubic_terameters = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_terameters', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') -exagram_per_cubic_terameters = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_terameters', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') -petagram_per_cubic_terameters = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_terameters', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') -teragram_per_cubic_terameters = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_terameters', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') -gigagram_per_cubic_terameters = Unit(9.999999999999999e-31, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_terameters', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') -megagram_per_cubic_terameters = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_terameters', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') -kilogram_per_cubic_terameters = Unit(1e-36, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_terameters', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') -milligram_per_cubic_terameters = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_terameters', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') -microgram_per_cubic_terameters = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_terameters', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') -nanogram_per_cubic_terameters = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_terameters', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') -picogram_per_cubic_terameters = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_terameters', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') -femtogram_per_cubic_terameters = Unit(1e-54, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_terameters', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') -attogram_per_cubic_terameters = Unit(1.0000000000000001e-57, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_terameters', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -gram_per_cubic_gigameters = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_gigameters', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') -exagram_per_cubic_gigameters = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_gigameters', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') -petagram_per_cubic_gigameters = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_gigameters', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') -teragram_per_cubic_gigameters = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_gigameters', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') -gigagram_per_cubic_gigameters = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_gigameters', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') -megagram_per_cubic_gigameters = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_gigameters', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') -kilogram_per_cubic_gigameters = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_gigameters', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') -milligram_per_cubic_gigameters = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_gigameters', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') -microgram_per_cubic_gigameters = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_gigameters', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') -nanogram_per_cubic_gigameters = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_gigameters', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') -picogram_per_cubic_gigameters = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_gigameters', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') -femtogram_per_cubic_gigameters = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_gigameters', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') -attogram_per_cubic_gigameters = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_gigameters', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -gram_per_cubic_megameters = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_megameters', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') -exagram_per_cubic_megameters = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_megameters', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') -petagram_per_cubic_megameters = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_megameters', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') -teragram_per_cubic_megameters = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_megameters', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') -gigagram_per_cubic_megameters = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_megameters', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') -megagram_per_cubic_megameters = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_megameters', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') -kilogram_per_cubic_megameters = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_megameters', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') -milligram_per_cubic_megameters = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_megameters', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') -microgram_per_cubic_megameters = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_megameters', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') -nanogram_per_cubic_megameters = Unit(1.0000000000000003e-30, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_megameters', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') -picogram_per_cubic_megameters = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_megameters', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') -femtogram_per_cubic_megameters = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_megameters', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') -attogram_per_cubic_megameters = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_megameters', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -gram_per_cubic_kilometers = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_kilometers', ascii_symbol='g km^-3', symbol='Nonekm⁻³') -exagram_per_cubic_kilometers = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_kilometers', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') -petagram_per_cubic_kilometers = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_kilometers', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') -teragram_per_cubic_kilometers = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_kilometers', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') -gigagram_per_cubic_kilometers = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_kilometers', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') -megagram_per_cubic_kilometers = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_kilometers', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') -kilogram_per_cubic_kilometers = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_kilometers', ascii_symbol='kg km^-3', symbol='kgkm⁻³') -milligram_per_cubic_kilometers = Unit(9.999999999999999e-16, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_kilometers', ascii_symbol='mg km^-3', symbol='mgkm⁻³') -microgram_per_cubic_kilometers = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_kilometers', ascii_symbol='ug km^-3', symbol='µgkm⁻³') -nanogram_per_cubic_kilometers = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_kilometers', ascii_symbol='ng km^-3', symbol='ngkm⁻³') -picogram_per_cubic_kilometers = Unit(1.0000000000000001e-24, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_kilometers', ascii_symbol='pg km^-3', symbol='pgkm⁻³') -femtogram_per_cubic_kilometers = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_kilometers', ascii_symbol='fg km^-3', symbol='fgkm⁻³') -attogram_per_cubic_kilometers = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_kilometers', ascii_symbol='ag km^-3', symbol='agkm⁻³') -gram_per_cubic_millimeters = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_millimeters', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') -exagram_per_cubic_millimeters = Unit(1e+24, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_millimeters', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') -petagram_per_cubic_millimeters = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_millimeters', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') -teragram_per_cubic_millimeters = Unit(1e+18, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_millimeters', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') -gigagram_per_cubic_millimeters = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_millimeters', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') -megagram_per_cubic_millimeters = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_millimeters', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') -kilogram_per_cubic_millimeters = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_millimeters', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') -milligram_per_cubic_millimeters = Unit(999.9999999999999, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_millimeters', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') -microgram_per_cubic_millimeters = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_millimeters', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') -nanogram_per_cubic_millimeters = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_millimeters', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') -picogram_per_cubic_millimeters = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_millimeters', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') -femtogram_per_cubic_millimeters = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_millimeters', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') -attogram_per_cubic_millimeters = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_millimeters', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -gram_per_cubic_micrometers = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_micrometers', ascii_symbol='g um^-3', symbol='Noneµm⁻³') -exagram_per_cubic_micrometers = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_micrometers', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') -petagram_per_cubic_micrometers = Unit(1.0000000000000002e+30, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_micrometers', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') -teragram_per_cubic_micrometers = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_micrometers', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') -gigagram_per_cubic_micrometers = Unit(1.0000000000000001e+24, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_micrometers', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') -megagram_per_cubic_micrometers = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_micrometers', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') -kilogram_per_cubic_micrometers = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_micrometers', ascii_symbol='kg um^-3', symbol='kgµm⁻³') -milligram_per_cubic_micrometers = Unit(1000000000000.0001, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_micrometers', ascii_symbol='mg um^-3', symbol='mgµm⁻³') -microgram_per_cubic_micrometers = Unit(1000000000.0000002, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_micrometers', ascii_symbol='ug um^-3', symbol='µgµm⁻³') -nanogram_per_cubic_micrometers = Unit(1000000.0000000003, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_micrometers', ascii_symbol='ng um^-3', symbol='ngµm⁻³') -picogram_per_cubic_micrometers = Unit(1000.0000000000002, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_micrometers', ascii_symbol='pg um^-3', symbol='pgµm⁻³') -femtogram_per_cubic_micrometers = Unit(1.0000000000000002, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_micrometers', ascii_symbol='fg um^-3', symbol='fgµm⁻³') -attogram_per_cubic_micrometers = Unit(0.0010000000000000002, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_micrometers', ascii_symbol='ag um^-3', symbol='agµm⁻³') -gram_per_cubic_nanometers = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_nanometers', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') -exagram_per_cubic_nanometers = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_nanometers', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') -petagram_per_cubic_nanometers = Unit(9.999999999999998e+38, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_nanometers', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') -teragram_per_cubic_nanometers = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_nanometers', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') -gigagram_per_cubic_nanometers = Unit(9.999999999999998e+32, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_nanometers', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') -megagram_per_cubic_nanometers = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_nanometers', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') -kilogram_per_cubic_nanometers = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_nanometers', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') -milligram_per_cubic_nanometers = Unit(9.999999999999997e+20, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_nanometers', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') -microgram_per_cubic_nanometers = Unit(9.999999999999999e+17, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_nanometers', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') -nanogram_per_cubic_nanometers = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_nanometers', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') -picogram_per_cubic_nanometers = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_nanometers', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') -femtogram_per_cubic_nanometers = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_nanometers', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') -attogram_per_cubic_nanometers = Unit(999999.9999999999, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_nanometers', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -gram_per_cubic_picometers = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_picometers', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') -exagram_per_cubic_picometers = Unit(1e+51, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_picometers', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') -petagram_per_cubic_picometers = Unit(1e+48, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_picometers', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') -teragram_per_cubic_picometers = Unit(1.0000000000000001e+45, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_picometers', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') -gigagram_per_cubic_picometers = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_picometers', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') -megagram_per_cubic_picometers = Unit(1.0000000000000001e+39, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_picometers', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') -kilogram_per_cubic_picometers = Unit(1e+36, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_picometers', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') -milligram_per_cubic_picometers = Unit(1e+30, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_picometers', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') -microgram_per_cubic_picometers = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_picometers', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') -nanogram_per_cubic_picometers = Unit(1.0000000000000003e+24, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_picometers', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') -picogram_per_cubic_picometers = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_picometers', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') -femtogram_per_cubic_picometers = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_picometers', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') -attogram_per_cubic_picometers = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_picometers', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -gram_per_cubic_femtometers = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_femtometers', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') -exagram_per_cubic_femtometers = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_femtometers', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') -petagram_per_cubic_femtometers = Unit(9.999999999999997e+56, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_femtometers', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') -teragram_per_cubic_femtometers = Unit(9.999999999999997e+53, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_femtometers', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') -gigagram_per_cubic_femtometers = Unit(9.999999999999997e+50, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_femtometers', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') -megagram_per_cubic_femtometers = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_femtometers', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') -kilogram_per_cubic_femtometers = Unit(9.999999999999998e+44, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_femtometers', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') -milligram_per_cubic_femtometers = Unit(9.999999999999996e+38, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_femtometers', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') -microgram_per_cubic_femtometers = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_femtometers', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') -nanogram_per_cubic_femtometers = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_femtometers', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') -picogram_per_cubic_femtometers = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_femtometers', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') -femtogram_per_cubic_femtometers = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_femtometers', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') -attogram_per_cubic_femtometers = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_femtometers', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -gram_per_cubic_attometers = Unit(9.999999999999998e+50, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_attometers', ascii_symbol='g am^-3', symbol='Noneam⁻³') -exagram_per_cubic_attometers = Unit(9.999999999999999e+68, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_attometers', ascii_symbol='Eg am^-3', symbol='Egam⁻³') -petagram_per_cubic_attometers = Unit(9.999999999999998e+65, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_attometers', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') -teragram_per_cubic_attometers = Unit(9.999999999999999e+62, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_attometers', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') -gigagram_per_cubic_attometers = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_attometers', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') -megagram_per_cubic_attometers = Unit(9.999999999999999e+56, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_attometers', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') -kilogram_per_cubic_attometers = Unit(9.999999999999999e+53, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_attometers', ascii_symbol='kg am^-3', symbol='kgam⁻³') -milligram_per_cubic_attometers = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_attometers', ascii_symbol='mg am^-3', symbol='mgam⁻³') -microgram_per_cubic_attometers = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_attometers', ascii_symbol='ug am^-3', symbol='µgam⁻³') -nanogram_per_cubic_attometers = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_attometers', ascii_symbol='ng am^-3', symbol='ngam⁻³') -picogram_per_cubic_attometers = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_attometers', ascii_symbol='pg am^-3', symbol='pgam⁻³') -femtogram_per_cubic_attometers = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_attometers', ascii_symbol='fg am^-3', symbol='fgam⁻³') -attogram_per_cubic_attometers = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_attometers', ascii_symbol='ag am^-3', symbol='agam⁻³') -gram_per_cubic_angstroms = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_angstroms', ascii_symbol='g A^-3', symbol='NoneÅ⁻³') -exagram_per_cubic_angstroms = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_angstroms', ascii_symbol='Eg A^-3', symbol='EgÅ⁻³') -petagram_per_cubic_angstroms = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_angstroms', ascii_symbol='Pg A^-3', symbol='PgÅ⁻³') -teragram_per_cubic_angstroms = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_angstroms', ascii_symbol='Tg A^-3', symbol='TgÅ⁻³') -gigagram_per_cubic_angstroms = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_angstroms', ascii_symbol='Gg A^-3', symbol='GgÅ⁻³') -megagram_per_cubic_angstroms = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_angstroms', ascii_symbol='Mg A^-3', symbol='MgÅ⁻³') -kilogram_per_cubic_angstroms = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_angstroms', ascii_symbol='kg A^-3', symbol='kgÅ⁻³') -milligram_per_cubic_angstroms = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_angstroms', ascii_symbol='mg A^-3', symbol='mgÅ⁻³') -microgram_per_cubic_angstroms = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_angstroms', ascii_symbol='ug A^-3', symbol='µgÅ⁻³') -nanogram_per_cubic_angstroms = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_angstroms', ascii_symbol='ng A^-3', symbol='ngÅ⁻³') -picogram_per_cubic_angstroms = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_angstroms', ascii_symbol='pg A^-3', symbol='pgÅ⁻³') -femtogram_per_cubic_angstroms = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_angstroms', ascii_symbol='fg A^-3', symbol='fgÅ⁻³') -attogram_per_cubic_angstroms = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_angstroms', ascii_symbol='ag A^-3', symbol='agÅ⁻³') -gram_per_cubic_angstroms = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='gram_per_cubic_angstroms', ascii_symbol='g Ang^-3', symbol='NoneNone⁻³') -exagram_per_cubic_angstroms = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='exagram_per_cubic_angstroms', ascii_symbol='Eg Ang^-3', symbol='EgNone⁻³') -petagram_per_cubic_angstroms = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='petagram_per_cubic_angstroms', ascii_symbol='Pg Ang^-3', symbol='PgNone⁻³') -teragram_per_cubic_angstroms = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='teragram_per_cubic_angstroms', ascii_symbol='Tg Ang^-3', symbol='TgNone⁻³') -gigagram_per_cubic_angstroms = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='gigagram_per_cubic_angstroms', ascii_symbol='Gg Ang^-3', symbol='GgNone⁻³') -megagram_per_cubic_angstroms = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='megagram_per_cubic_angstroms', ascii_symbol='Mg Ang^-3', symbol='MgNone⁻³') -kilogram_per_cubic_angstroms = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='kilogram_per_cubic_angstroms', ascii_symbol='kg Ang^-3', symbol='kgNone⁻³') -milligram_per_cubic_angstroms = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='milligram_per_cubic_angstroms', ascii_symbol='mg Ang^-3', symbol='mgNone⁻³') -microgram_per_cubic_angstroms = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='microgram_per_cubic_angstroms', ascii_symbol='ug Ang^-3', symbol='µgNone⁻³') -nanogram_per_cubic_angstroms = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='nanogram_per_cubic_angstroms', ascii_symbol='ng Ang^-3', symbol='ngNone⁻³') -picogram_per_cubic_angstroms = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='picogram_per_cubic_angstroms', ascii_symbol='pg Ang^-3', symbol='pgNone⁻³') -femtogram_per_cubic_angstroms = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='femtogram_per_cubic_angstroms', ascii_symbol='fg Ang^-3', symbol='fgNone⁻³') -attogram_per_cubic_angstroms = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='attogram_per_cubic_angstroms', ascii_symbol='ag Ang^-3', symbol='agNone⁻³') +angstroms_per_second = Unit(1e-10, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') +angstroms_per_square_second = Unit(1e-10, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') +angstroms_per_millisecond = Unit(1e-07, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') +angstroms_per_square_millisecond = Unit(1e-07, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') +angstroms_per_microsecond = Unit(0.0001, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') +angstroms_per_square_microsecond = Unit(0.0001, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') +angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') +angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') +angstroms_per_picosecond = Unit(100.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') +angstroms_per_square_picosecond = Unit(100.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') +angstroms_per_femtosecond = Unit(100000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') +angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') +angstroms_per_attosecond = Unit(100000000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') +angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') +angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') +angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') +angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') +angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') +angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') +angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') +angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') +angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') +grams_per_cubic_meter = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_meter = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') +petagrams_per_cubic_meter = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') +teragrams_per_cubic_meter = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_meter = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') +megagrams_per_cubic_meter = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') +kilograms_per_cubic_meter = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgNone⁻³') +milligrams_per_cubic_meter = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgNone⁻³') +micrograms_per_cubic_meter = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgNone⁻³') +nanograms_per_cubic_meter = Unit(1.0000000000000002e-12, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngNone⁻³') +picograms_per_cubic_meter = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') +femtograms_per_cubic_meter = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') +attograms_per_cubic_meter = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') +grams_per_cubic_exameter = Unit(1e-57, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') +exagrams_per_cubic_exameter = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') +petagrams_per_cubic_exameter = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') +teragrams_per_cubic_exameter = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') +gigagrams_per_cubic_exameter = Unit(1e-48, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_exameter', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') +megagrams_per_cubic_exameter = Unit(9.999999999999999e-52, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_exameter', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') +kilograms_per_cubic_exameter = Unit(9.999999999999999e-55, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_exameter', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') +milligrams_per_cubic_exameter = Unit(9.999999999999998e-61, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_exameter', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') +micrograms_per_cubic_exameter = Unit(9.999999999999999e-64, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_exameter', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') +nanograms_per_cubic_exameter = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_exameter', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') +picograms_per_cubic_exameter = Unit(1e-69, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') +femtograms_per_cubic_exameter = Unit(1e-72, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') +attograms_per_cubic_exameter = Unit(1e-75, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') +grams_per_cubic_petameter = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') +exagrams_per_cubic_petameter = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') +petagrams_per_cubic_petameter = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') +teragrams_per_cubic_petameter = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') +gigagrams_per_cubic_petameter = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_petameter', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') +megagrams_per_cubic_petameter = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_petameter', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') +kilograms_per_cubic_petameter = Unit(1.0000000000000001e-45, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_petameter', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') +milligrams_per_cubic_petameter = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_petameter', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') +micrograms_per_cubic_petameter = Unit(1.0000000000000002e-54, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_petameter', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') +nanograms_per_cubic_petameter = Unit(1.0000000000000002e-57, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_petameter', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') +picograms_per_cubic_petameter = Unit(1.0000000000000001e-60, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') +femtograms_per_cubic_petameter = Unit(1.0000000000000002e-63, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') +attograms_per_cubic_petameter = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') +grams_per_cubic_terameter = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') +exagrams_per_cubic_terameter = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') +petagrams_per_cubic_terameter = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') +teragrams_per_cubic_terameter = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') +gigagrams_per_cubic_terameter = Unit(9.999999999999999e-31, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_terameter', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') +megagrams_per_cubic_terameter = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_terameter', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') +kilograms_per_cubic_terameter = Unit(1e-36, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_terameter', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') +milligrams_per_cubic_terameter = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_terameter', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') +micrograms_per_cubic_terameter = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_terameter', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') +nanograms_per_cubic_terameter = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_terameter', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') +picograms_per_cubic_terameter = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') +femtograms_per_cubic_terameter = Unit(1e-54, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') +attograms_per_cubic_terameter = Unit(1.0000000000000001e-57, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') +grams_per_cubic_gigameter = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') +exagrams_per_cubic_gigameter = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') +petagrams_per_cubic_gigameter = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') +teragrams_per_cubic_gigameter = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') +gigagrams_per_cubic_gigameter = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_gigameter', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') +megagrams_per_cubic_gigameter = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_gigameter', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') +kilograms_per_cubic_gigameter = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_gigameter', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') +milligrams_per_cubic_gigameter = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_gigameter', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') +micrograms_per_cubic_gigameter = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_gigameter', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') +nanograms_per_cubic_gigameter = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_gigameter', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') +picograms_per_cubic_gigameter = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') +femtograms_per_cubic_gigameter = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') +attograms_per_cubic_gigameter = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') +grams_per_cubic_megameter = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') +exagrams_per_cubic_megameter = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') +petagrams_per_cubic_megameter = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') +teragrams_per_cubic_megameter = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') +gigagrams_per_cubic_megameter = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_megameter', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') +megagrams_per_cubic_megameter = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_megameter', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') +kilograms_per_cubic_megameter = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_megameter', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') +milligrams_per_cubic_megameter = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_megameter', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') +micrograms_per_cubic_megameter = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_megameter', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') +nanograms_per_cubic_megameter = Unit(1.0000000000000003e-30, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_megameter', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') +picograms_per_cubic_megameter = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') +femtograms_per_cubic_megameter = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') +attograms_per_cubic_megameter = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') +grams_per_cubic_kilometer = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') +exagrams_per_cubic_kilometer = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') +petagrams_per_cubic_kilometer = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') +teragrams_per_cubic_kilometer = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') +gigagrams_per_cubic_kilometer = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_kilometer', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') +megagrams_per_cubic_kilometer = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_kilometer', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') +kilograms_per_cubic_kilometer = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_kilometer', ascii_symbol='kg km^-3', symbol='kgkm⁻³') +milligrams_per_cubic_kilometer = Unit(9.999999999999999e-16, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_kilometer', ascii_symbol='mg km^-3', symbol='mgkm⁻³') +micrograms_per_cubic_kilometer = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_kilometer', ascii_symbol='ug km^-3', symbol='µgkm⁻³') +nanograms_per_cubic_kilometer = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_kilometer', ascii_symbol='ng km^-3', symbol='ngkm⁻³') +picograms_per_cubic_kilometer = Unit(1.0000000000000001e-24, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') +femtograms_per_cubic_kilometer = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') +attograms_per_cubic_kilometer = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') +grams_per_cubic_millimeter = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') +exagrams_per_cubic_millimeter = Unit(1e+24, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') +petagrams_per_cubic_millimeter = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') +teragrams_per_cubic_millimeter = Unit(1e+18, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') +gigagrams_per_cubic_millimeter = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_millimeter', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') +megagrams_per_cubic_millimeter = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_millimeter', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') +kilograms_per_cubic_millimeter = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_millimeter', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') +milligrams_per_cubic_millimeter = Unit(999.9999999999999, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_millimeter', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') +micrograms_per_cubic_millimeter = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_millimeter', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') +nanograms_per_cubic_millimeter = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_millimeter', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') +picograms_per_cubic_millimeter = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') +femtograms_per_cubic_millimeter = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') +attograms_per_cubic_millimeter = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') +grams_per_cubic_micrometer = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') +exagrams_per_cubic_micrometer = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') +petagrams_per_cubic_micrometer = Unit(1.0000000000000002e+30, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') +teragrams_per_cubic_micrometer = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') +gigagrams_per_cubic_micrometer = Unit(1.0000000000000001e+24, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_micrometer', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') +megagrams_per_cubic_micrometer = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_micrometer', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') +kilograms_per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_micrometer', ascii_symbol='kg um^-3', symbol='kgµm⁻³') +milligrams_per_cubic_micrometer = Unit(1000000000000.0001, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_micrometer', ascii_symbol='mg um^-3', symbol='mgµm⁻³') +micrograms_per_cubic_micrometer = Unit(1000000000.0000002, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_micrometer', ascii_symbol='ug um^-3', symbol='µgµm⁻³') +nanograms_per_cubic_micrometer = Unit(1000000.0000000003, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_micrometer', ascii_symbol='ng um^-3', symbol='ngµm⁻³') +picograms_per_cubic_micrometer = Unit(1000.0000000000002, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') +femtograms_per_cubic_micrometer = Unit(1.0000000000000002, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') +attograms_per_cubic_micrometer = Unit(0.0010000000000000002, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') +grams_per_cubic_nanometer = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') +exagrams_per_cubic_nanometer = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') +petagrams_per_cubic_nanometer = Unit(9.999999999999998e+38, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') +teragrams_per_cubic_nanometer = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') +gigagrams_per_cubic_nanometer = Unit(9.999999999999998e+32, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_nanometer', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') +megagrams_per_cubic_nanometer = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_nanometer', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') +kilograms_per_cubic_nanometer = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_nanometer', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') +milligrams_per_cubic_nanometer = Unit(9.999999999999997e+20, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_nanometer', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') +micrograms_per_cubic_nanometer = Unit(9.999999999999999e+17, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_nanometer', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') +nanograms_per_cubic_nanometer = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_nanometer', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') +picograms_per_cubic_nanometer = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') +femtograms_per_cubic_nanometer = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') +attograms_per_cubic_nanometer = Unit(999999.9999999999, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') +grams_per_cubic_picometer = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') +exagrams_per_cubic_picometer = Unit(1e+51, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') +petagrams_per_cubic_picometer = Unit(1e+48, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') +teragrams_per_cubic_picometer = Unit(1.0000000000000001e+45, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') +gigagrams_per_cubic_picometer = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_picometer', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') +megagrams_per_cubic_picometer = Unit(1.0000000000000001e+39, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_picometer', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') +kilograms_per_cubic_picometer = Unit(1e+36, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_picometer', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') +milligrams_per_cubic_picometer = Unit(1e+30, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_picometer', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') +micrograms_per_cubic_picometer = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_picometer', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') +nanograms_per_cubic_picometer = Unit(1.0000000000000003e+24, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_picometer', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') +picograms_per_cubic_picometer = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') +femtograms_per_cubic_picometer = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') +attograms_per_cubic_picometer = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') +grams_per_cubic_femtometer = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') +exagrams_per_cubic_femtometer = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') +petagrams_per_cubic_femtometer = Unit(9.999999999999997e+56, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') +teragrams_per_cubic_femtometer = Unit(9.999999999999997e+53, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') +gigagrams_per_cubic_femtometer = Unit(9.999999999999997e+50, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_femtometer', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') +megagrams_per_cubic_femtometer = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_femtometer', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') +kilograms_per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_femtometer', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') +milligrams_per_cubic_femtometer = Unit(9.999999999999996e+38, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_femtometer', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') +micrograms_per_cubic_femtometer = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_femtometer', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') +nanograms_per_cubic_femtometer = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_femtometer', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') +picograms_per_cubic_femtometer = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') +femtograms_per_cubic_femtometer = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') +attograms_per_cubic_femtometer = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') +grams_per_cubic_attometer = Unit(9.999999999999998e+50, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') +exagrams_per_cubic_attometer = Unit(9.999999999999999e+68, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') +petagrams_per_cubic_attometer = Unit(9.999999999999998e+65, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') +teragrams_per_cubic_attometer = Unit(9.999999999999999e+62, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') +gigagrams_per_cubic_attometer = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_attometer', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') +megagrams_per_cubic_attometer = Unit(9.999999999999999e+56, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_attometer', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') +kilograms_per_cubic_attometer = Unit(9.999999999999999e+53, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_attometer', ascii_symbol='kg am^-3', symbol='kgam⁻³') +milligrams_per_cubic_attometer = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_attometer', ascii_symbol='mg am^-3', symbol='mgam⁻³') +micrograms_per_cubic_attometer = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_attometer', ascii_symbol='ug am^-3', symbol='µgam⁻³') +nanograms_per_cubic_attometer = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_attometer', ascii_symbol='ng am^-3', symbol='ngam⁻³') +picograms_per_cubic_attometer = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') +femtograms_per_cubic_attometer = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') +attograms_per_cubic_attometer = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') +grams_per_cubic_angstrom = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') +exagrams_per_cubic_angstrom = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') +petagrams_per_cubic_angstrom = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') +teragrams_per_cubic_angstrom = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') +gigagrams_per_cubic_angstrom = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_angstrom', ascii_symbol='Gg Ang^-3', symbol='GgÅ⁻³') +megagrams_per_cubic_angstrom = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_angstrom', ascii_symbol='Mg Ang^-3', symbol='MgÅ⁻³') +kilograms_per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_angstrom', ascii_symbol='kg Ang^-3', symbol='kgÅ⁻³') +milligrams_per_cubic_angstrom = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_angstrom', ascii_symbol='mg Ang^-3', symbol='mgÅ⁻³') +micrograms_per_cubic_angstrom = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_angstrom', ascii_symbol='ug Ang^-3', symbol='µgÅ⁻³') +nanograms_per_cubic_angstrom = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_angstrom', ascii_symbol='ng Ang^-3', symbol='ngÅ⁻³') +picograms_per_cubic_angstrom = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') +femtograms_per_cubic_angstrom = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') +attograms_per_cubic_angstrom = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') # # Lookup table from symbols to units @@ -1353,18 +1250,21 @@ def __init__(self, name: str, units: list[Unit]): "pH": picohenry, "fH": femtohenry, "aH": attohenry, - "Å": angstroms, "Ang": angstroms, + "Å": angstroms, "min": minutes, - "hr": hours, + "h": hours, "d": days, - "day": days, "y": years, - "yr": years, "deg": degrees, "rad": radians, "sr": stradians, "none": none, + "yr": years, + "year": years, + "day": days, + "hr": hours, + "hour": hours, } @@ -1390,7 +1290,6 @@ def __init__(self, name: str, units: list[Unit]): femtometers, attometers, angstroms, - angstroms, ]) area = UnitGroup( @@ -1410,7 +1309,6 @@ def __init__(self, name: str, units: list[Unit]): square_femtometers, square_attometers, square_angstroms, - square_angstroms, ]) volume = UnitGroup( @@ -1430,7 +1328,6 @@ def __init__(self, name: str, units: list[Unit]): cubic_femtometers, cubic_attometers, cubic_angstroms, - cubic_angstroms, ]) inverse_length = UnitGroup( @@ -1450,7 +1347,6 @@ def __init__(self, name: str, units: list[Unit]): per_femtometer, per_attometer, per_angstrom, - per_angstrom, ]) inverse_area = UnitGroup( @@ -1470,7 +1366,6 @@ def __init__(self, name: str, units: list[Unit]): per_square_femtometer, per_square_attometer, per_square_angstrom, - per_square_angstrom, ]) inverse_volume = UnitGroup( @@ -1490,7 +1385,6 @@ def __init__(self, name: str, units: list[Unit]): per_cubic_femtometer, per_cubic_attometer, per_cubic_angstrom, - per_cubic_angstrom, ]) time = UnitGroup( @@ -1506,8 +1400,6 @@ def __init__(self, name: str, units: list[Unit]): minutes, hours, days, - days, - years, years, ]) @@ -1543,8 +1435,6 @@ def __init__(self, name: str, units: list[Unit]): meters_per_minute, meters_per_hour, meters_per_day, - meters_per_day, - meters_per_year, meters_per_year, exameters_per_second, exameters_per_millisecond, @@ -1556,8 +1446,6 @@ def __init__(self, name: str, units: list[Unit]): exameters_per_minute, exameters_per_hour, exameters_per_day, - exameters_per_day, - exameters_per_year, exameters_per_year, petameters_per_second, petameters_per_millisecond, @@ -1569,8 +1457,6 @@ def __init__(self, name: str, units: list[Unit]): petameters_per_minute, petameters_per_hour, petameters_per_day, - petameters_per_day, - petameters_per_year, petameters_per_year, terameters_per_second, terameters_per_millisecond, @@ -1582,8 +1468,6 @@ def __init__(self, name: str, units: list[Unit]): terameters_per_minute, terameters_per_hour, terameters_per_day, - terameters_per_day, - terameters_per_year, terameters_per_year, gigameters_per_second, gigameters_per_millisecond, @@ -1595,8 +1479,6 @@ def __init__(self, name: str, units: list[Unit]): gigameters_per_minute, gigameters_per_hour, gigameters_per_day, - gigameters_per_day, - gigameters_per_year, gigameters_per_year, megameters_per_second, megameters_per_millisecond, @@ -1608,8 +1490,6 @@ def __init__(self, name: str, units: list[Unit]): megameters_per_minute, megameters_per_hour, megameters_per_day, - megameters_per_day, - megameters_per_year, megameters_per_year, kilometers_per_second, kilometers_per_millisecond, @@ -1621,8 +1501,6 @@ def __init__(self, name: str, units: list[Unit]): kilometers_per_minute, kilometers_per_hour, kilometers_per_day, - kilometers_per_day, - kilometers_per_year, kilometers_per_year, millimeters_per_second, millimeters_per_millisecond, @@ -1634,8 +1512,6 @@ def __init__(self, name: str, units: list[Unit]): millimeters_per_minute, millimeters_per_hour, millimeters_per_day, - millimeters_per_day, - millimeters_per_year, millimeters_per_year, micrometers_per_second, micrometers_per_millisecond, @@ -1647,8 +1523,6 @@ def __init__(self, name: str, units: list[Unit]): micrometers_per_minute, micrometers_per_hour, micrometers_per_day, - micrometers_per_day, - micrometers_per_year, micrometers_per_year, nanometers_per_second, nanometers_per_millisecond, @@ -1660,8 +1534,6 @@ def __init__(self, name: str, units: list[Unit]): nanometers_per_minute, nanometers_per_hour, nanometers_per_day, - nanometers_per_day, - nanometers_per_year, nanometers_per_year, picometers_per_second, picometers_per_millisecond, @@ -1673,8 +1545,6 @@ def __init__(self, name: str, units: list[Unit]): picometers_per_minute, picometers_per_hour, picometers_per_day, - picometers_per_day, - picometers_per_year, picometers_per_year, femtometers_per_second, femtometers_per_millisecond, @@ -1686,8 +1556,6 @@ def __init__(self, name: str, units: list[Unit]): femtometers_per_minute, femtometers_per_hour, femtometers_per_day, - femtometers_per_day, - femtometers_per_year, femtometers_per_year, attometers_per_second, attometers_per_millisecond, @@ -1699,8 +1567,6 @@ def __init__(self, name: str, units: list[Unit]): attometers_per_minute, attometers_per_hour, attometers_per_day, - attometers_per_day, - attometers_per_year, attometers_per_year, angstroms_per_second, angstroms_per_millisecond, @@ -1712,21 +1578,6 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_minute, angstroms_per_hour, angstroms_per_day, - angstroms_per_day, - angstroms_per_year, - angstroms_per_year, - angstroms_per_second, - angstroms_per_millisecond, - angstroms_per_microsecond, - angstroms_per_nanosecond, - angstroms_per_picosecond, - angstroms_per_femtosecond, - angstroms_per_attosecond, - angstroms_per_minute, - angstroms_per_hour, - angstroms_per_day, - angstroms_per_day, - angstroms_per_year, angstroms_per_year, ]) @@ -1743,8 +1594,6 @@ def __init__(self, name: str, units: list[Unit]): meters_per_square_minute, meters_per_square_hour, meters_per_square_day, - meters_per_square_day, - meters_per_square_year, meters_per_square_year, exameters_per_square_second, exameters_per_square_millisecond, @@ -1756,8 +1605,6 @@ def __init__(self, name: str, units: list[Unit]): exameters_per_square_minute, exameters_per_square_hour, exameters_per_square_day, - exameters_per_square_day, - exameters_per_square_year, exameters_per_square_year, petameters_per_square_second, petameters_per_square_millisecond, @@ -1769,8 +1616,6 @@ def __init__(self, name: str, units: list[Unit]): petameters_per_square_minute, petameters_per_square_hour, petameters_per_square_day, - petameters_per_square_day, - petameters_per_square_year, petameters_per_square_year, terameters_per_square_second, terameters_per_square_millisecond, @@ -1782,8 +1627,6 @@ def __init__(self, name: str, units: list[Unit]): terameters_per_square_minute, terameters_per_square_hour, terameters_per_square_day, - terameters_per_square_day, - terameters_per_square_year, terameters_per_square_year, gigameters_per_square_second, gigameters_per_square_millisecond, @@ -1795,8 +1638,6 @@ def __init__(self, name: str, units: list[Unit]): gigameters_per_square_minute, gigameters_per_square_hour, gigameters_per_square_day, - gigameters_per_square_day, - gigameters_per_square_year, gigameters_per_square_year, megameters_per_square_second, megameters_per_square_millisecond, @@ -1808,8 +1649,6 @@ def __init__(self, name: str, units: list[Unit]): megameters_per_square_minute, megameters_per_square_hour, megameters_per_square_day, - megameters_per_square_day, - megameters_per_square_year, megameters_per_square_year, kilometers_per_square_second, kilometers_per_square_millisecond, @@ -1821,8 +1660,6 @@ def __init__(self, name: str, units: list[Unit]): kilometers_per_square_minute, kilometers_per_square_hour, kilometers_per_square_day, - kilometers_per_square_day, - kilometers_per_square_year, kilometers_per_square_year, millimeters_per_square_second, millimeters_per_square_millisecond, @@ -1834,8 +1671,6 @@ def __init__(self, name: str, units: list[Unit]): millimeters_per_square_minute, millimeters_per_square_hour, millimeters_per_square_day, - millimeters_per_square_day, - millimeters_per_square_year, millimeters_per_square_year, micrometers_per_square_second, micrometers_per_square_millisecond, @@ -1847,8 +1682,6 @@ def __init__(self, name: str, units: list[Unit]): micrometers_per_square_minute, micrometers_per_square_hour, micrometers_per_square_day, - micrometers_per_square_day, - micrometers_per_square_year, micrometers_per_square_year, nanometers_per_square_second, nanometers_per_square_millisecond, @@ -1860,8 +1693,6 @@ def __init__(self, name: str, units: list[Unit]): nanometers_per_square_minute, nanometers_per_square_hour, nanometers_per_square_day, - nanometers_per_square_day, - nanometers_per_square_year, nanometers_per_square_year, picometers_per_square_second, picometers_per_square_millisecond, @@ -1873,8 +1704,6 @@ def __init__(self, name: str, units: list[Unit]): picometers_per_square_minute, picometers_per_square_hour, picometers_per_square_day, - picometers_per_square_day, - picometers_per_square_year, picometers_per_square_year, femtometers_per_square_second, femtometers_per_square_millisecond, @@ -1886,8 +1715,6 @@ def __init__(self, name: str, units: list[Unit]): femtometers_per_square_minute, femtometers_per_square_hour, femtometers_per_square_day, - femtometers_per_square_day, - femtometers_per_square_year, femtometers_per_square_year, attometers_per_square_second, attometers_per_square_millisecond, @@ -1899,8 +1726,6 @@ def __init__(self, name: str, units: list[Unit]): attometers_per_square_minute, attometers_per_square_hour, attometers_per_square_day, - attometers_per_square_day, - attometers_per_square_year, attometers_per_square_year, angstroms_per_square_second, angstroms_per_square_millisecond, @@ -1912,21 +1737,6 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_square_minute, angstroms_per_square_hour, angstroms_per_square_day, - angstroms_per_square_day, - angstroms_per_square_year, - angstroms_per_square_year, - angstroms_per_square_second, - angstroms_per_square_millisecond, - angstroms_per_square_microsecond, - angstroms_per_square_nanosecond, - angstroms_per_square_picosecond, - angstroms_per_square_femtosecond, - angstroms_per_square_attosecond, - angstroms_per_square_minute, - angstroms_per_square_hour, - angstroms_per_square_day, - angstroms_per_square_day, - angstroms_per_square_year, angstroms_per_square_year, ]) From f6863a532720ac3f44b1ccb5be9655c5907ce0d2 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 6 Aug 2024 18:45:07 +0100 Subject: [PATCH 494/675] More units, towards formatting --- sasdata/quantities/_units_base.py | 107 +- sasdata/quantities/_units_table.py | 112 +- sasdata/quantities/quantities.py | 43 +- sasdata/quantities/unit_formatting.py | 2 + sasdata/quantities/units.py | 1974 +++++++++++++++++-------- 5 files changed, 1547 insertions(+), 691 deletions(-) diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index d87bb2b9e..754ca9e96 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -5,13 +5,14 @@ from sasdata.quantities.unicode_superscript import int_as_unicode_superscript - class Dimensions: """ - Note that some SI Base units are + Note that some SI Base units are not useful from the perspecive of the sasview project, and make things + behave badly. In particular: moles and angular measures are dimensionless, and candelas are really a weighted + measure of power. - For example, moles and angular measures are dimensionless from this perspective, and candelas are + We do however track angle and amount, because its really useful for formatting units """ def __init__(self, @@ -19,13 +20,17 @@ def __init__(self, time: int = 0, mass: int = 0, current: int = 0, - temperature: int = 0): + temperature: int = 0, + moles_hint: int = 0, + angle_hint: int = 0): self.length = length self.time = time self.mass = mass self.current = current self.temperature = temperature + self.moles_hint = moles_hint + self.angle_hint = angle_hint def __mul__(self: Self, other: Self): @@ -37,7 +42,9 @@ def __mul__(self: Self, other: Self): self.time + other.time, self.mass + other.mass, self.current + other.current, - self.temperature + other.temperature) + self.temperature + other.temperature, + self.moles_hint + other.moles_hint, + self.angle_hint + other.angle_hint) def __truediv__(self: Self, other: Self): @@ -49,7 +56,9 @@ def __truediv__(self: Self, other: Self): self.time - other.time, self.mass - other.mass, self.current - other.current, - self.temperature - other.temperature) + self.temperature - other.temperature, + self.moles_hint - other.moles_hint, + self.angle_hint - other.angle_hint) def __pow__(self, power: int): @@ -61,7 +70,9 @@ def __pow__(self, power: int): self.time * power, self.mass * power, self.current * power, - self.temperature * power) + self.temperature * power, + self.moles_hint * power, + self.angle_hint * power) def __eq__(self: Self, other: Self): if isinstance(other, Dimensions): @@ -69,7 +80,9 @@ def __eq__(self: Self, other: Self): self.time == other.time and self.mass == other.mass and self.current == other.current and - self.temperature == other.temperature) + self.temperature == other.temperature and + self.moles_hint == other.moles_hint and + self.angle_hint == other.angle_hint) return NotImplemented @@ -92,17 +105,26 @@ def __hash__(self): if self.temperature < 0: two_powers += 16 + if self.moles_hint < 0: + two_powers += 32 + + if self.angle_hint < 0: + two_powers += 64 + return 2**two_powers * 3**abs(self.length) * 5**abs(self.time) * \ - 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) + 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) * \ + 17**abs(self.moles_hint) * 19**abs(self.angle_hint) def __repr__(self): s = "" for name, size in [ - ("L", self.length), - ("T", self.time), - ("M", self.mass), - ("C", self.current), - ("K", self.temperature)]: + ("length", self.length), + ("time", self.time), + ("mass", self.mass), + ("current", self.current), + ("temperature", self.temperature), + ("amount", self.moles_hint), + ("angle", self.angle_hint)]: if size == 0: pass @@ -162,7 +184,64 @@ def equivalent(self: Self, other: Self): def __eq__(self: Self, other: Self): return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 + def si_equivalent(self): + """ Get the SI unit corresponding to this unit""" + return Unit(1, self.dimensions) + + def _format_unit(self, format_process: list["UnitFormatProcessor"]): + for processor in format_process: + pass + + +class NamedUnit: + # TODO: Add named unit class + +# +# Parsing plan: +# Require unknown amounts of units to be explicitly positive or negative? +# +# + + + +@dataclass +class ProcessedUnitToken: + """ Mid processing representation of formatted units """ + base_string: str + exponent_string: str + latex_exponent_string: str + exponent: int + +class UnitFormatProcessor: + """ Represents a step in the unit processing pipeline""" + def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: + """ This will be called to deal with each processing stage""" + +class RequiredUnitFormatProcessor(UnitFormatProcessor): + """ This unit is required to exist in the formatting """ + def __init__(self, unit: Unit, power: int = 1): + self.unit = unit + self.power = power + def apply(self, scale, dimensions) -> tuple[float, Dimensions, ProcessedUnitToken]: + new_scale = scale / (self.unit.scale * self.power) + new_dimensions = self.unit.dimensions / (dimensions**self.power) + token = ProcessedUnitToken(self.unit, self.power) + + return new_scale, new_dimensions, token +class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): + """ This processor minimises the dimensionality of the unit by multiplying by as many + units of the specified type as needed """ + def __init__(self, unit: Unit): + self.unit = unit + + def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: + pass + +class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): + pass + class UnitGroup: + """ A group of units that all have the same dimensionality """ def __init__(self, name: str, units: list[Unit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) diff --git a/sasdata/quantities/_units_table.py b/sasdata/quantities/_units_table.py index 0749461f8..c69b9577a 100644 --- a/sasdata/quantities/_units_table.py +++ b/sasdata/quantities/_units_table.py @@ -22,50 +22,60 @@ ("f", None, "femto", 1e-15), ("a", None, "atto", 1e-18)] +unusual_magnitudes = [ + ("d", None, "deci", 1e-1), + ("c", None, "centi", 1e-2) +] + all_magnitudes = bigger_magnitudes + smaller_magnitudes # Length, time, mass, current, temperature base_si_units = [ - ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, all_magnitudes), - ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, smaller_magnitudes), - ("g", None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, all_magnitudes), - ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, all_magnitudes), - ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, all_magnitudes) ] + ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, 0, 0, all_magnitudes + unusual_magnitudes), + ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, 0, 0, smaller_magnitudes), + ("g", None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, 0, 0, all_magnitudes), + ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), + ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, 0, 0, all_magnitudes) ] derived_si_units = [ - ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, all_magnitudes), - ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, all_magnitudes), - ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, all_magnitudes), - ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, all_magnitudes), - ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, all_magnitudes), - ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, all_magnitudes), - ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, all_magnitudes), - ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, all_magnitudes), - ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, all_magnitudes), - ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, all_magnitudes), - ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, all_magnitudes), - ("T", None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, all_magnitudes), - ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, all_magnitudes), - ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, []) + ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, 0, 0, all_magnitudes), + ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, 0, 0, all_magnitudes), + ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, 0, 0, all_magnitudes), + ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), + ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, 0, 0, all_magnitudes), + ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, 0, 0, all_magnitudes), + ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, 0, 0, all_magnitudes), + ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, 0, 0, all_magnitudes), + ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, 0, 0, all_magnitudes), + ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, 0, 0, all_magnitudes), + ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, 0, 0, all_magnitudes), + ("T", None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, 0, 0, all_magnitudes), + ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), + ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []) ] non_si_units = [ - ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, []), - ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, []), - ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, []), - ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, []), - ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, []), - ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, []), - ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, []), - ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, []), - ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, []) + ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, 0, 0, []), + ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, 0, 0, []), + ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, 0, 0, []), + ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, 0, 0, []), + ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, 0, 0, []), + ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, 0, 1, []), + ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, 0, 1, []), + ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, 0, 2, []), + ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), + ("l", None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), + ("eV", None, "electronvolt", "electronvolts", 1.602e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), + ("au", None, "atomic mass unit", "atomic mass units", 1.661e-27, 0, 0, 1, 0, 0, 0, 0, []), + ("mol", None, "mole", "moles", 6.022e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes) ] aliases = { "y": ["yr", "year"], "d": ["day"], "h": ["hr", "hour"], - "Ang": ["A", "Å"] + "Ang": ["A", "Å"], + "au": ["a.u.", "amu"] } @@ -113,13 +123,13 @@ def format_name(name: str): unit_types_temp = defaultdict(list) # Keep track of unit types unit_types = defaultdict(list) - for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, magnitudes in all_units: + for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, moles_hint, angle_hint, magnitudes in all_units: formatted_plural = format_name(plural) formatted_singular = format_name(singular) - dimensions = Dimensions(length, time, mass, current, temperature) - fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature})," + dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) + fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," f"name='{formatted_plural}'," f"ascii_symbol='{symbol}'," f"symbol='{symbol if special_symbol is None else special_symbol}')\n") @@ -148,7 +158,7 @@ def format_name(name: str): combined_scale = scale * mag_scale # Units - dimensions = Dimensions(length, time, mass, current, temperature) + dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) fid.write(f"{combined_name_plural} = Unit({combined_scale}, " f"Dimensions({length}, {time}, {mass}, {current}, {temperature})," f"name='{combined_name_plural}'," @@ -171,6 +181,7 @@ def format_name(name: str): length_units = unit_types_temp[hash(Dimensions(length=1))] time_units = unit_types_temp[hash(Dimensions(time=1))] mass_units = unit_types_temp[hash(Dimensions(mass=1))] + amount_units = unit_types_temp[hash(Dimensions(moles_hint=1))] # Length based for symbol, special_symbol, singular, plural, scale, _ in length_units: @@ -185,7 +196,7 @@ def format_name(name: str): unit_name = prefix + name unit_special_symbol = (symbol if special_symbol is None else special_symbol) + unicode_suffix unit_symbol = symbol + f"^{power}" - fid.write(f"{unit_name} = Unit({scale**power}, Dimensions({power}, 0, 0, 0, 0), " + fid.write(f"{unit_name} = Unit({scale**power}, Dimensions(length={power}), " f"name='{unit_name}', " f"ascii_symbol='{unit_symbol}', " f"symbol='{unit_special_symbol}')\n") @@ -203,13 +214,13 @@ def format_name(name: str): fid.write(f"{speed_name} " f"= Unit({length_scale / time_scale}, " - f"Dimensions(1, -1, 0, 0, 0), " + f"Dimensions(length=1, time=-1), " f"name='{speed_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}', " f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") fid.write(f"{accel_name} = Unit({length_scale / time_scale}, " - f"Dimensions(1, -2, 0, 0, 0), " + f"Dimensions(length=1, time=-2), " f"name='{accel_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}^2', " f"symbol='{length_special_symbol}{time_special_symbol}⁻²')\n") @@ -223,17 +234,35 @@ def format_name(name: str): name = mass_name + "_per_cubic_" + length_name - dimensions = Dimensions(length=-3, time=1) + dimensions = Dimensions(length=-3, mass=1) fid.write(f"{name} " f"= Unit({mass_scale / length_scale**3}, " - f"Dimensions(-3, 1, 0, 0, 0), " + f"Dimensions(length=-3, mass=1), " f"name='{name}', " f"ascii_symbol='{mass_symbol} {length_symbol}^-3', " f"symbol='{mass_special_symbol}{length_special_symbol}⁻³')\n") unit_types[hash(dimensions)].append(name) + # Concentration + for length_symbol, length_special_symbol, length_name, _, length_scale, _ in length_units: + for amount_symbol, amount_special_symbol, _, amount_name, amount_scale, _ in amount_units: + + name = amount_name + "_per_cubic_" + length_name + + dimensions = Dimensions(length=-3, moles_hint=1) + + fid.write(f"{name} " + f"= Unit({amount_scale / length_scale**3}, " + f"Dimensions(length=-3, moles_hint=1), " + f"name='{name}', " + f"ascii_symbol='{amount_symbol} {length_symbol}^-3', " + f"symbol='{amount_special_symbol}{length_special_symbol}⁻³')\n") + + unit_types[hash(dimensions)].append(name) + + # # Add aliases to symbol lookup table # @@ -281,14 +310,17 @@ def format_name(name: str): ("magnetic_flux_density", Dimensions(0, -2, 1, -1, 0)), ("inductance", Dimensions(2, -2, 1, -2, 0)), ("temperature", Dimensions(temperature=1)), - ("dimensionless", Dimensions()) + ("dimensionless", Dimensions()), + ("angle", Dimensions(angle_hint=1)), + ("solid_angle", Dimensions(angle_hint=2)), + ("amount", Dimensions(moles_hint=1)), + ("concentration", Dimensions(length=-3, moles_hint=1)) ] fid.write("\n#\n# Units by type \n#\n\n") for dimension_name, dimensions in dimension_names: - print(dimensions, hash(dimensions)) fid.write(f"\n" f"{dimension_name} = UnitGroup(\n" diff --git a/sasdata/quantities/quantities.py b/sasdata/quantities/quantities.py index 7b41f8f6d..707ce1590 100644 --- a/sasdata/quantities/quantities.py +++ b/sasdata/quantities/quantities.py @@ -5,6 +5,11 @@ from sasdata.quantities.units import Unit + +class UnitError(Exception): + """ Errors caused by unit specification not being correct """ + + QuantityType = TypeVar("QuantityType") class Quantity[QuantityType]: @@ -13,7 +18,10 @@ def __init__(self, value: QuantityType, units: Unit): self.units = units def in_units_of(self, units: Unit) -> QuantityType: - pass + if self.units.equivalent(units): + return (units.scale / self.units.scale) * self.value + else: + raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") def __mul__(self: Self, other: ArrayLike | Self ) -> Self: if isinstance(other, Quantity): @@ -43,36 +51,3 @@ def __sub__(self: Self, other: Self) -> Self: if isinstance(other, Quantity): pass - -class ExpressionMethod: - pass - - -class SetExpressionMethod(ExpressionMethod): - pass - - -class AnyExpressionMethod(ExpressionMethod): - pass - - -class ForceExpressionMethod(ExpressionMethod): - pass - - -class UnitToken: - def __init__(self, unit: Collection[NamedUnit], method: ExpressionMethod): - pass - -unit_dictionary = { - "Amps": Unit(1, Dimensions(current=1), UnitName("A")), - "Coulombs": Unit(1, Dimensions(current=1, time=1), UnitName("C")) -} - -@dataclass -class Disambiguator: - A: Unit = unit_dictionary["Amps"] - C: Unit = unit_dictionary["Coulombs"] - -def parse_units(unit_string: str, disambiguator: Disambiguator = Disambiguator()) -> Unit: - pass diff --git a/sasdata/quantities/unit_formatting.py b/sasdata/quantities/unit_formatting.py index adcc7e6ba..904ce801f 100644 --- a/sasdata/quantities/unit_formatting.py +++ b/sasdata/quantities/unit_formatting.py @@ -1,5 +1,7 @@ + + import numpy as np diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index cedfb353a..c68f6f676 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -21,13 +21,14 @@ from sasdata.quantities.unicode_superscript import int_as_unicode_superscript - class Dimensions: """ - Note that some SI Base units are + Note that some SI Base units are not useful from the perspecive of the sasview project, and make things + behave badly. In particular: moles and angular measures are dimensionless, and candelas are really a weighted + measure of power. - For example, moles and angular measures are dimensionless from this perspective, and candelas are + We do however track angle and amount, because its really useful for formatting units """ def __init__(self, @@ -35,13 +36,17 @@ def __init__(self, time: int = 0, mass: int = 0, current: int = 0, - temperature: int = 0): + temperature: int = 0, + moles_hint: int = 0, + angle_hint: int = 0): self.length = length self.time = time self.mass = mass self.current = current self.temperature = temperature + self.moles_hint = moles_hint + self.angle_hint = angle_hint def __mul__(self: Self, other: Self): @@ -53,7 +58,9 @@ def __mul__(self: Self, other: Self): self.time + other.time, self.mass + other.mass, self.current + other.current, - self.temperature + other.temperature) + self.temperature + other.temperature, + self.moles_hint + other.moles_hint, + self.angle_hint + other.angle_hint) def __truediv__(self: Self, other: Self): @@ -65,7 +72,9 @@ def __truediv__(self: Self, other: Self): self.time - other.time, self.mass - other.mass, self.current - other.current, - self.temperature - other.temperature) + self.temperature - other.temperature, + self.moles_hint - other.moles_hint, + self.angle_hint - other.angle_hint) def __pow__(self, power: int): @@ -77,7 +86,9 @@ def __pow__(self, power: int): self.time * power, self.mass * power, self.current * power, - self.temperature * power) + self.temperature * power, + self.moles_hint * power, + self.angle_hint * power) def __eq__(self: Self, other: Self): if isinstance(other, Dimensions): @@ -85,7 +96,9 @@ def __eq__(self: Self, other: Self): self.time == other.time and self.mass == other.mass and self.current == other.current and - self.temperature == other.temperature) + self.temperature == other.temperature and + self.moles_hint == other.moles_hint and + self.angle_hint == other.angle_hint) return NotImplemented @@ -108,17 +121,26 @@ def __hash__(self): if self.temperature < 0: two_powers += 16 + if self.moles_hint < 0: + two_powers += 32 + + if self.angle_hint < 0: + two_powers += 64 + return 2**two_powers * 3**abs(self.length) * 5**abs(self.time) * \ - 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) + 7**abs(self.mass) * 11**abs(self.current) * 13**abs(self.temperature) * \ + 17**abs(self.moles_hint) * 19**abs(self.angle_hint) def __repr__(self): s = "" for name, size in [ - ("L", self.length), - ("T", self.time), - ("M", self.mass), - ("C", self.current), - ("K", self.temperature)]: + ("length", self.length), + ("time", self.time), + ("mass", self.mass), + ("current", self.current), + ("temperature", self.temperature), + ("amount", self.moles_hint), + ("angle", self.angle_hint)]: if size == 0: pass @@ -178,7 +200,60 @@ def equivalent(self: Self, other: Self): def __eq__(self: Self, other: Self): return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 + def si_equivalent(self): + """ Get the SI unit corresponding to this unit""" + return Unit(1, self.dimensions) + + def _format_unit(self, format_process: list["UnitFormatProcessor"]): + for processor in format_process: + pass + +# +# Parsing plan: +# Require unknown amounts of units to be explicitly positive or negative? +# +# + + + +@dataclass +class ProcessedUnitToken: + """ Mid processing representation of formatted units """ + base_string: str + exponent_string: str + latex_exponent_string: str + exponent: int + +class UnitFormatProcessor: + """ Represents a step in the unit processing pipeline""" + def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: + """ This will be called to deal with each processing stage""" + +class RequiredUnitFormatProcessor(UnitFormatProcessor): + """ This unit is required to exist in the formatting """ + def __init__(self, unit: Unit, power: int = 1): + self.unit = unit + self.power = power + def apply(self, scale, dimensions) -> tuple[float, Dimensions, ProcessedUnitToken]: + new_scale = scale / (self.unit.scale * self.power) + new_dimensions = self.unit.dimensions / (dimensions**self.power) + token = ProcessedUnitToken(self.unit, self.power) + + return new_scale, new_dimensions, token +class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): + """ This processor minimises the dimensionality of the unit by multiplying by as many + units of the specified type as needed """ + def __init__(self, unit: Unit): + self.unit = unit + + def apply(self, scale, dimensions) -> tuple[ProcessedUnitToken, float, Dimensions]: + pass + +class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): + pass + class UnitGroup: + """ A group of units that all have the same dimensionality """ def __init__(self, name: str, units: list[Unit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) @@ -188,7 +263,7 @@ def __init__(self, name: str, units: list[Unit]): # Specific units # -meters = Unit(1, Dimensions(1, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') +meters = Unit(1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') exameters = Unit(1e+18, Dimensions(1, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') petameters = Unit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') terameters = Unit(1000000000000.0, Dimensions(1, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') @@ -201,14 +276,16 @@ def __init__(self, name: str, units: list[Unit]): picometers = Unit(1e-12, Dimensions(1, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') femtometers = Unit(1e-15, Dimensions(1, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') attometers = Unit(1e-18, Dimensions(1, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') -seconds = Unit(1, Dimensions(0, 1, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') +decimeters = Unit(0.1, Dimensions(1, 0, 0, 0, 0),name='decimeters',ascii_symbol='dm',symbol='dm') +centimeters = Unit(0.01, Dimensions(1, 0, 0, 0, 0),name='centimeters',ascii_symbol='cm',symbol='cm') +seconds = Unit(1, Dimensions(0, 1, 0, 0, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') milliseconds = Unit(0.001, Dimensions(0, 1, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') microseconds = Unit(1e-06, Dimensions(0, 1, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') nanoseconds = Unit(1e-09, Dimensions(0, 1, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') picoseconds = Unit(1e-12, Dimensions(0, 1, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') femtoseconds = Unit(1e-15, Dimensions(0, 1, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') attoseconds = Unit(1e-18, Dimensions(0, 1, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') -grams = Unit(0.001, Dimensions(0, 0, 1, 0, 0),name='grams',ascii_symbol='g',symbol='g') +grams = Unit(0.001, Dimensions(0, 0, 1, 0, 0, 0, 0),name='grams',ascii_symbol='g',symbol='g') exagrams = Unit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') petagrams = Unit(1000000000000.0, Dimensions(0, 0, 1, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') teragrams = Unit(1000000000.0, Dimensions(0, 0, 1, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') @@ -221,7 +298,7 @@ def __init__(self, name: str, units: list[Unit]): picograms = Unit(1e-15, Dimensions(0, 0, 1, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') femtograms = Unit(1e-18, Dimensions(0, 0, 1, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') attograms = Unit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') -amps = Unit(1, Dimensions(0, 0, 0, 1, 0),name='amps',ascii_symbol='A',symbol='A') +amps = Unit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amps',ascii_symbol='A',symbol='A') exaamps = Unit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamps',ascii_symbol='EA',symbol='EA') petaamps = Unit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamps',ascii_symbol='PA',symbol='PA') teraamps = Unit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamps',ascii_symbol='TA',symbol='TA') @@ -234,7 +311,7 @@ def __init__(self, name: str, units: list[Unit]): picoamps = Unit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamps',ascii_symbol='pA',symbol='pA') femtoamps = Unit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamps',ascii_symbol='fA',symbol='fA') attoamps = Unit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamps',ascii_symbol='aA',symbol='aA') -kelvin = Unit(1, Dimensions(0, 0, 0, 0, 1),name='kelvin',ascii_symbol='K',symbol='K') +kelvin = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kelvin',ascii_symbol='K',symbol='K') exakelvin = Unit(1e+18, Dimensions(0, 0, 0, 0, 1),name='exakelvin',ascii_symbol='EK',symbol='EK') petakelvin = Unit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1),name='petakelvin',ascii_symbol='PK',symbol='PK') terakelvin = Unit(1000000000000.0, Dimensions(0, 0, 0, 0, 1),name='terakelvin',ascii_symbol='TK',symbol='TK') @@ -247,7 +324,7 @@ def __init__(self, name: str, units: list[Unit]): picokelvin = Unit(1e-12, Dimensions(0, 0, 0, 0, 1),name='picokelvin',ascii_symbol='pK',symbol='pK') femtokelvin = Unit(1e-15, Dimensions(0, 0, 0, 0, 1),name='femtokelvin',ascii_symbol='fK',symbol='fK') attokelvin = Unit(1e-18, Dimensions(0, 0, 0, 0, 1),name='attokelvin',ascii_symbol='aK',symbol='aK') -hertz = Unit(1, Dimensions(0, -1, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') +hertz = Unit(1, Dimensions(0, -1, 0, 0, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') exahertz = Unit(1e+18, Dimensions(0, -1, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') petahertz = Unit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') terahertz = Unit(1000000000000.0, Dimensions(0, -1, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') @@ -260,7 +337,7 @@ def __init__(self, name: str, units: list[Unit]): picohertz = Unit(1e-12, Dimensions(0, -1, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') femtohertz = Unit(1e-15, Dimensions(0, -1, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') attohertz = Unit(1e-18, Dimensions(0, -1, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') -newtons = Unit(1, Dimensions(1, -2, 1, 0, 0),name='newtons',ascii_symbol='N',symbol='N') +newtons = Unit(1, Dimensions(1, -2, 1, 0, 0, 0, 0),name='newtons',ascii_symbol='N',symbol='N') exanewtons = Unit(1e+18, Dimensions(1, -2, 1, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') petanewtons = Unit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') teranewtons = Unit(1000000000000.0, Dimensions(1, -2, 1, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') @@ -273,7 +350,7 @@ def __init__(self, name: str, units: list[Unit]): piconewtons = Unit(1e-12, Dimensions(1, -2, 1, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') femtonewtons = Unit(1e-15, Dimensions(1, -2, 1, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') attonewtons = Unit(1e-18, Dimensions(1, -2, 1, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') -pascals = Unit(1, Dimensions(-1, -2, 1, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') +pascals = Unit(1, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') exapascals = Unit(1e+18, Dimensions(-1, -2, 1, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') petapascals = Unit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') terapascals = Unit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') @@ -286,7 +363,7 @@ def __init__(self, name: str, units: list[Unit]): picopascals = Unit(1e-12, Dimensions(-1, -2, 1, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') femtopascals = Unit(1e-15, Dimensions(-1, -2, 1, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') attopascals = Unit(1e-18, Dimensions(-1, -2, 1, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') -joules = Unit(1, Dimensions(2, -2, 1, 0, 0),name='joules',ascii_symbol='J',symbol='J') +joules = Unit(1, Dimensions(2, -2, 1, 0, 0, 0, 0),name='joules',ascii_symbol='J',symbol='J') exajoules = Unit(1e+18, Dimensions(2, -2, 1, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') petajoules = Unit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') terajoules = Unit(1000000000000.0, Dimensions(2, -2, 1, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') @@ -299,7 +376,7 @@ def __init__(self, name: str, units: list[Unit]): picojoules = Unit(1e-12, Dimensions(2, -2, 1, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') femtojoules = Unit(1e-15, Dimensions(2, -2, 1, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') attojoules = Unit(1e-18, Dimensions(2, -2, 1, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') -watts = Unit(1, Dimensions(2, -3, 1, 0, 0),name='watts',ascii_symbol='W',symbol='W') +watts = Unit(1, Dimensions(2, -3, 1, 0, 0, 0, 0),name='watts',ascii_symbol='W',symbol='W') exawatts = Unit(1e+18, Dimensions(2, -3, 1, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') petawatts = Unit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') terawatts = Unit(1000000000000.0, Dimensions(2, -3, 1, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') @@ -312,7 +389,7 @@ def __init__(self, name: str, units: list[Unit]): picowatts = Unit(1e-12, Dimensions(2, -3, 1, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') femtowatts = Unit(1e-15, Dimensions(2, -3, 1, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') attowatts = Unit(1e-18, Dimensions(2, -3, 1, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') -coulombs = Unit(1, Dimensions(0, 1, 0, 1, 0),name='coulombs',ascii_symbol='C',symbol='C') +coulombs = Unit(1, Dimensions(0, 1, 0, 1, 0, 0, 0),name='coulombs',ascii_symbol='C',symbol='C') exacoulombs = Unit(1e+18, Dimensions(0, 1, 0, 1, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') petacoulombs = Unit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') teracoulombs = Unit(1000000000000.0, Dimensions(0, 1, 0, 1, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') @@ -325,7 +402,7 @@ def __init__(self, name: str, units: list[Unit]): picocoulombs = Unit(1e-12, Dimensions(0, 1, 0, 1, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') femtocoulombs = Unit(1e-15, Dimensions(0, 1, 0, 1, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') attocoulombs = Unit(1e-18, Dimensions(0, 1, 0, 1, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') -volts = Unit(1, Dimensions(2, -3, 1, -1, 0),name='volts',ascii_symbol='V',symbol='V') +volts = Unit(1, Dimensions(2, -3, 1, -1, 0, 0, 0),name='volts',ascii_symbol='V',symbol='V') exavolts = Unit(1e+18, Dimensions(2, -3, 1, -1, 0),name='exavolts',ascii_symbol='EV',symbol='EV') petavolts = Unit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0),name='petavolts',ascii_symbol='PV',symbol='PV') teravolts = Unit(1000000000000.0, Dimensions(2, -3, 1, -1, 0),name='teravolts',ascii_symbol='TV',symbol='TV') @@ -338,7 +415,7 @@ def __init__(self, name: str, units: list[Unit]): picovolts = Unit(1e-12, Dimensions(2, -3, 1, -1, 0),name='picovolts',ascii_symbol='pV',symbol='pV') femtovolts = Unit(1e-15, Dimensions(2, -3, 1, -1, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') attovolts = Unit(1e-18, Dimensions(2, -3, 1, -1, 0),name='attovolts',ascii_symbol='aV',symbol='aV') -ohms = Unit(1, Dimensions(2, -3, 1, -2, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') +ohms = Unit(1, Dimensions(2, -3, 1, -2, 0, 0, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') exaohms = Unit(1e+18, Dimensions(2, -3, 1, -2, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') petaohms = Unit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') teraohms = Unit(1000000000000.0, Dimensions(2, -3, 1, -2, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') @@ -351,7 +428,7 @@ def __init__(self, name: str, units: list[Unit]): picoohms = Unit(1e-12, Dimensions(2, -3, 1, -2, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') femtoohms = Unit(1e-15, Dimensions(2, -3, 1, -2, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') attoohms = Unit(1e-18, Dimensions(2, -3, 1, -2, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') -farads = Unit(1, Dimensions(-2, 4, -1, 2, 0),name='farads',ascii_symbol='F',symbol='F') +farads = Unit(1, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='farads',ascii_symbol='F',symbol='F') exafarads = Unit(1e+18, Dimensions(-2, 4, -1, 2, 0),name='exafarads',ascii_symbol='EF',symbol='EF') petafarads = Unit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='petafarads',ascii_symbol='PF',symbol='PF') terafarads = Unit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='terafarads',ascii_symbol='TF',symbol='TF') @@ -364,7 +441,7 @@ def __init__(self, name: str, units: list[Unit]): picofarads = Unit(1e-12, Dimensions(-2, 4, -1, 2, 0),name='picofarads',ascii_symbol='pF',symbol='pF') femtofarads = Unit(1e-15, Dimensions(-2, 4, -1, 2, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') attofarads = Unit(1e-18, Dimensions(-2, 4, -1, 2, 0),name='attofarads',ascii_symbol='aF',symbol='aF') -siemens = Unit(1, Dimensions(-2, 3, -1, 2, 0),name='siemens',ascii_symbol='S',symbol='S') +siemens = Unit(1, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='siemens',ascii_symbol='S',symbol='S') exasiemens = Unit(1e+18, Dimensions(-2, 3, -1, 2, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') petasiemens = Unit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') terasiemens = Unit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') @@ -377,7 +454,7 @@ def __init__(self, name: str, units: list[Unit]): picosiemens = Unit(1e-12, Dimensions(-2, 3, -1, 2, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') femtosiemens = Unit(1e-15, Dimensions(-2, 3, -1, 2, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') attosiemens = Unit(1e-18, Dimensions(-2, 3, -1, 2, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') -webers = Unit(1, Dimensions(2, -2, 1, -1, 0),name='webers',ascii_symbol='Wb',symbol='Wb') +webers = Unit(1, Dimensions(2, -2, 1, -1, 0, 0, 0),name='webers',ascii_symbol='Wb',symbol='Wb') exawebers = Unit(1e+18, Dimensions(2, -2, 1, -1, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') petawebers = Unit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') terawebers = Unit(1000000000000.0, Dimensions(2, -2, 1, -1, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') @@ -390,7 +467,7 @@ def __init__(self, name: str, units: list[Unit]): picowebers = Unit(1e-12, Dimensions(2, -2, 1, -1, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') femtowebers = Unit(1e-15, Dimensions(2, -2, 1, -1, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') attowebers = Unit(1e-18, Dimensions(2, -2, 1, -1, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') -tesla = Unit(1, Dimensions(0, -2, 1, -1, 0),name='tesla',ascii_symbol='T',symbol='T') +tesla = Unit(1, Dimensions(0, -2, 1, -1, 0, 0, 0),name='tesla',ascii_symbol='T',symbol='T') exatesla = Unit(1e+18, Dimensions(0, -2, 1, -1, 0),name='exatesla',ascii_symbol='ET',symbol='ET') petatesla = Unit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0),name='petatesla',ascii_symbol='PT',symbol='PT') teratesla = Unit(1000000000000.0, Dimensions(0, -2, 1, -1, 0),name='teratesla',ascii_symbol='TT',symbol='TT') @@ -403,7 +480,7 @@ def __init__(self, name: str, units: list[Unit]): picotesla = Unit(1e-12, Dimensions(0, -2, 1, -1, 0),name='picotesla',ascii_symbol='pT',symbol='pT') femtotesla = Unit(1e-15, Dimensions(0, -2, 1, -1, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') attotesla = Unit(1e-18, Dimensions(0, -2, 1, -1, 0),name='attotesla',ascii_symbol='aT',symbol='aT') -henry = Unit(1, Dimensions(2, -2, 1, -2, 0),name='henry',ascii_symbol='H',symbol='H') +henry = Unit(1, Dimensions(2, -2, 1, -2, 0, 0, 0),name='henry',ascii_symbol='H',symbol='H') exahenry = Unit(1e+18, Dimensions(2, -2, 1, -2, 0),name='exahenry',ascii_symbol='EH',symbol='EH') petahenry = Unit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0),name='petahenry',ascii_symbol='PH',symbol='PH') terahenry = Unit(1000000000000.0, Dimensions(2, -2, 1, -2, 0),name='terahenry',ascii_symbol='TH',symbol='TH') @@ -416,576 +493,806 @@ def __init__(self, name: str, units: list[Unit]): picohenry = Unit(1e-12, Dimensions(2, -2, 1, -2, 0),name='picohenry',ascii_symbol='pH',symbol='pH') femtohenry = Unit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') attohenry = Unit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') -degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1),name='degrees_celsius',ascii_symbol='C',symbol='C') -angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') -minutes = Unit(60, Dimensions(0, 1, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') -hours = Unit(360, Dimensions(0, 1, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') -days = Unit(8640, Dimensions(0, 1, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') -years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') -degrees = Unit(57.29577951308232, Dimensions(0, 0, 0, 0, 0),name='degrees',ascii_symbol='deg',symbol='deg') -radians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='radians',ascii_symbol='rad',symbol='rad') -stradians = Unit(1, Dimensions(0, 0, 0, 0, 0),name='stradians',ascii_symbol='sr',symbol='sr') -none = Unit(1, Dimensions(0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') -square_meters = Unit(1, Dimensions(2, 0, 0, 0, 0), name='square_meters', ascii_symbol='m^2', symbol='m²') -cubic_meters = Unit(1, Dimensions(3, 0, 0, 0, 0), name='cubic_meters', ascii_symbol='m^3', symbol='m³') -per_meter = Unit(1.0, Dimensions(-1, 0, 0, 0, 0), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') -per_square_meter = Unit(1.0, Dimensions(-2, 0, 0, 0, 0), name='per_square_meter', ascii_symbol='m^-2', symbol='m⁻²') -per_cubic_meter = Unit(1.0, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_meter', ascii_symbol='m^-3', symbol='m⁻³') -square_exameters = Unit(1e+36, Dimensions(2, 0, 0, 0, 0), name='square_exameters', ascii_symbol='Em^2', symbol='Em²') -cubic_exameters = Unit(1e+54, Dimensions(3, 0, 0, 0, 0), name='cubic_exameters', ascii_symbol='Em^3', symbol='Em³') -per_exameter = Unit(1e-18, Dimensions(-1, 0, 0, 0, 0), name='per_exameter', ascii_symbol='Em^-1', symbol='Em⁻¹') -per_square_exameter = Unit(1e-36, Dimensions(-2, 0, 0, 0, 0), name='per_square_exameter', ascii_symbol='Em^-2', symbol='Em⁻²') -per_cubic_exameter = Unit(1e-54, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_exameter', ascii_symbol='Em^-3', symbol='Em⁻³') -square_petameters = Unit(1e+30, Dimensions(2, 0, 0, 0, 0), name='square_petameters', ascii_symbol='Pm^2', symbol='Pm²') -cubic_petameters = Unit(1e+45, Dimensions(3, 0, 0, 0, 0), name='cubic_petameters', ascii_symbol='Pm^3', symbol='Pm³') -per_petameter = Unit(1e-15, Dimensions(-1, 0, 0, 0, 0), name='per_petameter', ascii_symbol='Pm^-1', symbol='Pm⁻¹') -per_square_petameter = Unit(1e-30, Dimensions(-2, 0, 0, 0, 0), name='per_square_petameter', ascii_symbol='Pm^-2', symbol='Pm⁻²') -per_cubic_petameter = Unit(1e-45, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_petameter', ascii_symbol='Pm^-3', symbol='Pm⁻³') -square_terameters = Unit(1e+24, Dimensions(2, 0, 0, 0, 0), name='square_terameters', ascii_symbol='Tm^2', symbol='Tm²') -cubic_terameters = Unit(1e+36, Dimensions(3, 0, 0, 0, 0), name='cubic_terameters', ascii_symbol='Tm^3', symbol='Tm³') -per_terameter = Unit(1e-12, Dimensions(-1, 0, 0, 0, 0), name='per_terameter', ascii_symbol='Tm^-1', symbol='Tm⁻¹') -per_square_terameter = Unit(1e-24, Dimensions(-2, 0, 0, 0, 0), name='per_square_terameter', ascii_symbol='Tm^-2', symbol='Tm⁻²') -per_cubic_terameter = Unit(1e-36, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_terameter', ascii_symbol='Tm^-3', symbol='Tm⁻³') -square_gigameters = Unit(1e+18, Dimensions(2, 0, 0, 0, 0), name='square_gigameters', ascii_symbol='Gm^2', symbol='Gm²') -cubic_gigameters = Unit(1e+27, Dimensions(3, 0, 0, 0, 0), name='cubic_gigameters', ascii_symbol='Gm^3', symbol='Gm³') -per_gigameter = Unit(1e-09, Dimensions(-1, 0, 0, 0, 0), name='per_gigameter', ascii_symbol='Gm^-1', symbol='Gm⁻¹') -per_square_gigameter = Unit(1e-18, Dimensions(-2, 0, 0, 0, 0), name='per_square_gigameter', ascii_symbol='Gm^-2', symbol='Gm⁻²') -per_cubic_gigameter = Unit(1e-27, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_gigameter', ascii_symbol='Gm^-3', symbol='Gm⁻³') -square_megameters = Unit(1000000000000.0, Dimensions(2, 0, 0, 0, 0), name='square_megameters', ascii_symbol='Mm^2', symbol='Mm²') -cubic_megameters = Unit(1e+18, Dimensions(3, 0, 0, 0, 0), name='cubic_megameters', ascii_symbol='Mm^3', symbol='Mm³') -per_megameter = Unit(1e-06, Dimensions(-1, 0, 0, 0, 0), name='per_megameter', ascii_symbol='Mm^-1', symbol='Mm⁻¹') -per_square_megameter = Unit(1e-12, Dimensions(-2, 0, 0, 0, 0), name='per_square_megameter', ascii_symbol='Mm^-2', symbol='Mm⁻²') -per_cubic_megameter = Unit(1e-18, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_megameter', ascii_symbol='Mm^-3', symbol='Mm⁻³') -square_kilometers = Unit(1000000.0, Dimensions(2, 0, 0, 0, 0), name='square_kilometers', ascii_symbol='km^2', symbol='km²') -cubic_kilometers = Unit(1000000000.0, Dimensions(3, 0, 0, 0, 0), name='cubic_kilometers', ascii_symbol='km^3', symbol='km³') -per_kilometer = Unit(0.001, Dimensions(-1, 0, 0, 0, 0), name='per_kilometer', ascii_symbol='km^-1', symbol='km⁻¹') -per_square_kilometer = Unit(1e-06, Dimensions(-2, 0, 0, 0, 0), name='per_square_kilometer', ascii_symbol='km^-2', symbol='km⁻²') -per_cubic_kilometer = Unit(1e-09, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_kilometer', ascii_symbol='km^-3', symbol='km⁻³') -square_millimeters = Unit(1e-06, Dimensions(2, 0, 0, 0, 0), name='square_millimeters', ascii_symbol='mm^2', symbol='mm²') -cubic_millimeters = Unit(1e-09, Dimensions(3, 0, 0, 0, 0), name='cubic_millimeters', ascii_symbol='mm^3', symbol='mm³') -per_millimeter = Unit(1000.0, Dimensions(-1, 0, 0, 0, 0), name='per_millimeter', ascii_symbol='mm^-1', symbol='mm⁻¹') -per_square_millimeter = Unit(1000000.0, Dimensions(-2, 0, 0, 0, 0), name='per_square_millimeter', ascii_symbol='mm^-2', symbol='mm⁻²') -per_cubic_millimeter = Unit(999999999.9999999, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_millimeter', ascii_symbol='mm^-3', symbol='mm⁻³') -square_micrometers = Unit(1e-12, Dimensions(2, 0, 0, 0, 0), name='square_micrometers', ascii_symbol='um^2', symbol='µm²') -cubic_micrometers = Unit(9.999999999999999e-19, Dimensions(3, 0, 0, 0, 0), name='cubic_micrometers', ascii_symbol='um^3', symbol='µm³') -per_micrometer = Unit(1000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_micrometer', ascii_symbol='um^-1', symbol='µm⁻¹') -per_square_micrometer = Unit(1000000000000.0001, Dimensions(-2, 0, 0, 0, 0), name='per_square_micrometer', ascii_symbol='um^-2', symbol='µm⁻²') -per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_micrometer', ascii_symbol='um^-3', symbol='µm⁻³') -square_nanometers = Unit(1e-18, Dimensions(2, 0, 0, 0, 0), name='square_nanometers', ascii_symbol='nm^2', symbol='nm²') -cubic_nanometers = Unit(1.0000000000000002e-27, Dimensions(3, 0, 0, 0, 0), name='cubic_nanometers', ascii_symbol='nm^3', symbol='nm³') -per_nanometer = Unit(999999999.9999999, Dimensions(-1, 0, 0, 0, 0), name='per_nanometer', ascii_symbol='nm^-1', symbol='nm⁻¹') -per_square_nanometer = Unit(9.999999999999999e+17, Dimensions(-2, 0, 0, 0, 0), name='per_square_nanometer', ascii_symbol='nm^-2', symbol='nm⁻²') -per_cubic_nanometer = Unit(9.999999999999999e+26, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_nanometer', ascii_symbol='nm^-3', symbol='nm⁻³') -square_picometers = Unit(1e-24, Dimensions(2, 0, 0, 0, 0), name='square_picometers', ascii_symbol='pm^2', symbol='pm²') -cubic_picometers = Unit(1e-36, Dimensions(3, 0, 0, 0, 0), name='cubic_picometers', ascii_symbol='pm^3', symbol='pm³') -per_picometer = Unit(1000000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_picometer', ascii_symbol='pm^-1', symbol='pm⁻¹') -per_square_picometer = Unit(1e+24, Dimensions(-2, 0, 0, 0, 0), name='per_square_picometer', ascii_symbol='pm^-2', symbol='pm⁻²') -per_cubic_picometer = Unit(1e+36, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_picometer', ascii_symbol='pm^-3', symbol='pm⁻³') -square_femtometers = Unit(1e-30, Dimensions(2, 0, 0, 0, 0), name='square_femtometers', ascii_symbol='fm^2', symbol='fm²') -cubic_femtometers = Unit(1.0000000000000003e-45, Dimensions(3, 0, 0, 0, 0), name='cubic_femtometers', ascii_symbol='fm^3', symbol='fm³') -per_femtometer = Unit(999999999999999.9, Dimensions(-1, 0, 0, 0, 0), name='per_femtometer', ascii_symbol='fm^-1', symbol='fm⁻¹') -per_square_femtometer = Unit(9.999999999999999e+29, Dimensions(-2, 0, 0, 0, 0), name='per_square_femtometer', ascii_symbol='fm^-2', symbol='fm⁻²') -per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_femtometer', ascii_symbol='fm^-3', symbol='fm⁻³') -square_attometers = Unit(1.0000000000000001e-36, Dimensions(2, 0, 0, 0, 0), name='square_attometers', ascii_symbol='am^2', symbol='am²') -cubic_attometers = Unit(1.0000000000000002e-54, Dimensions(3, 0, 0, 0, 0), name='cubic_attometers', ascii_symbol='am^3', symbol='am³') -per_attometer = Unit(9.999999999999999e+17, Dimensions(-1, 0, 0, 0, 0), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') -per_square_attometer = Unit(9.999999999999999e+35, Dimensions(-2, 0, 0, 0, 0), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') -per_cubic_attometer = Unit(9.999999999999997e+53, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') -square_angstroms = Unit(1.0000000000000001e-20, Dimensions(2, 0, 0, 0, 0), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ų') -cubic_angstroms = Unit(1e-30, Dimensions(3, 0, 0, 0, 0), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='ų') -per_angstrom = Unit(10000000000.0, Dimensions(-1, 0, 0, 0, 0), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Å⁻¹') -per_square_angstrom = Unit(1e+20, Dimensions(-2, 0, 0, 0, 0), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Å⁻²') -per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 0, 0, 0, 0), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Å⁻³') -meters_per_second = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') -meters_per_square_second = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') -meters_per_millisecond = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') -meters_per_square_millisecond = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') -meters_per_microsecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') -meters_per_square_microsecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') -meters_per_nanosecond = Unit(999999999.9999999, Dimensions(1, -1, 0, 0, 0), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') -meters_per_square_nanosecond = Unit(999999999.9999999, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') -meters_per_picosecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') -meters_per_square_picosecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') -meters_per_femtosecond = Unit(999999999999999.9, Dimensions(1, -1, 0, 0, 0), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') -meters_per_square_femtosecond = Unit(999999999999999.9, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') -meters_per_attosecond = Unit(9.999999999999999e+17, Dimensions(1, -1, 0, 0, 0), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') -meters_per_square_attosecond = Unit(9.999999999999999e+17, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') -meters_per_minute = Unit(0.016666666666666666, Dimensions(1, -1, 0, 0, 0), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') -meters_per_square_minute = Unit(0.016666666666666666, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') -meters_per_hour = Unit(0.002777777777777778, Dimensions(1, -1, 0, 0, 0), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') -meters_per_square_hour = Unit(0.002777777777777778, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') -meters_per_day = Unit(0.00011574074074074075, Dimensions(1, -1, 0, 0, 0), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') -meters_per_square_day = Unit(0.00011574074074074075, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') -meters_per_year = Unit(3.168873850681143e-07, Dimensions(1, -1, 0, 0, 0), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') -meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(1, -2, 0, 0, 0), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') -exameters_per_second = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') -exameters_per_square_second = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') -exameters_per_millisecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') -exameters_per_square_millisecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') -exameters_per_microsecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') -exameters_per_square_microsecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') -exameters_per_nanosecond = Unit(9.999999999999999e+26, Dimensions(1, -1, 0, 0, 0), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') -exameters_per_square_nanosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') -exameters_per_picosecond = Unit(1e+30, Dimensions(1, -1, 0, 0, 0), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') -exameters_per_square_picosecond = Unit(1e+30, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') -exameters_per_femtosecond = Unit(1e+33, Dimensions(1, -1, 0, 0, 0), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') -exameters_per_square_femtosecond = Unit(1e+33, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') -exameters_per_attosecond = Unit(9.999999999999999e+35, Dimensions(1, -1, 0, 0, 0), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') -exameters_per_square_attosecond = Unit(9.999999999999999e+35, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') -exameters_per_minute = Unit(1.6666666666666666e+16, Dimensions(1, -1, 0, 0, 0), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') -exameters_per_square_minute = Unit(1.6666666666666666e+16, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') -exameters_per_hour = Unit(2777777777777778.0, Dimensions(1, -1, 0, 0, 0), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') -exameters_per_square_hour = Unit(2777777777777778.0, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') -exameters_per_day = Unit(115740740740740.73, Dimensions(1, -1, 0, 0, 0), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') -exameters_per_square_day = Unit(115740740740740.73, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') -exameters_per_year = Unit(316887385068.1143, Dimensions(1, -1, 0, 0, 0), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') -exameters_per_square_year = Unit(316887385068.1143, Dimensions(1, -2, 0, 0, 0), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') -petameters_per_second = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') -petameters_per_square_second = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') -petameters_per_millisecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') -petameters_per_square_millisecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') -petameters_per_microsecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') -petameters_per_square_microsecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') -petameters_per_nanosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') -petameters_per_square_nanosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') -petameters_per_picosecond = Unit(1e+27, Dimensions(1, -1, 0, 0, 0), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') -petameters_per_square_picosecond = Unit(1e+27, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') -petameters_per_femtosecond = Unit(9.999999999999999e+29, Dimensions(1, -1, 0, 0, 0), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') -petameters_per_square_femtosecond = Unit(9.999999999999999e+29, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') -petameters_per_attosecond = Unit(1e+33, Dimensions(1, -1, 0, 0, 0), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') -petameters_per_square_attosecond = Unit(1e+33, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') -petameters_per_minute = Unit(16666666666666.666, Dimensions(1, -1, 0, 0, 0), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') -petameters_per_square_minute = Unit(16666666666666.666, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') -petameters_per_hour = Unit(2777777777777.778, Dimensions(1, -1, 0, 0, 0), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') -petameters_per_square_hour = Unit(2777777777777.778, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') -petameters_per_day = Unit(115740740740.74074, Dimensions(1, -1, 0, 0, 0), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') -petameters_per_square_day = Unit(115740740740.74074, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') -petameters_per_year = Unit(316887385.0681143, Dimensions(1, -1, 0, 0, 0), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') -petameters_per_square_year = Unit(316887385.0681143, Dimensions(1, -2, 0, 0, 0), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') -terameters_per_second = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') -terameters_per_square_second = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') -terameters_per_millisecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') -terameters_per_square_millisecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') -terameters_per_microsecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') -terameters_per_square_microsecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') -terameters_per_nanosecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') -terameters_per_square_nanosecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') -terameters_per_picosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') -terameters_per_square_picosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') -terameters_per_femtosecond = Unit(9.999999999999999e+26, Dimensions(1, -1, 0, 0, 0), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') -terameters_per_square_femtosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') -terameters_per_attosecond = Unit(9.999999999999999e+29, Dimensions(1, -1, 0, 0, 0), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') -terameters_per_square_attosecond = Unit(9.999999999999999e+29, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') -terameters_per_minute = Unit(16666666666.666666, Dimensions(1, -1, 0, 0, 0), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') -terameters_per_square_minute = Unit(16666666666.666666, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') -terameters_per_hour = Unit(2777777777.7777777, Dimensions(1, -1, 0, 0, 0), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') -terameters_per_square_hour = Unit(2777777777.7777777, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') -terameters_per_day = Unit(115740740.74074075, Dimensions(1, -1, 0, 0, 0), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') -terameters_per_square_day = Unit(115740740.74074075, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') -terameters_per_year = Unit(316887.38506811426, Dimensions(1, -1, 0, 0, 0), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') -terameters_per_square_year = Unit(316887.38506811426, Dimensions(1, -2, 0, 0, 0), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') -gigameters_per_second = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') -gigameters_per_square_second = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') -gigameters_per_millisecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') -gigameters_per_square_millisecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') -gigameters_per_microsecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') -gigameters_per_square_microsecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') -gigameters_per_nanosecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') -gigameters_per_square_nanosecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') -gigameters_per_picosecond = Unit(1e+21, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') -gigameters_per_square_picosecond = Unit(1e+21, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') -gigameters_per_femtosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') -gigameters_per_square_femtosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') -gigameters_per_attosecond = Unit(9.999999999999999e+26, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') -gigameters_per_square_attosecond = Unit(9.999999999999999e+26, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') -gigameters_per_minute = Unit(16666666.666666666, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') -gigameters_per_square_minute = Unit(16666666.666666666, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') -gigameters_per_hour = Unit(2777777.777777778, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') -gigameters_per_square_hour = Unit(2777777.777777778, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') -gigameters_per_day = Unit(115740.74074074074, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') -gigameters_per_square_day = Unit(115740.74074074074, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') -gigameters_per_year = Unit(316.88738506811427, Dimensions(1, -1, 0, 0, 0), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') -gigameters_per_square_year = Unit(316.88738506811427, Dimensions(1, -2, 0, 0, 0), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') -megameters_per_second = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') -megameters_per_square_second = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') -megameters_per_millisecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') -megameters_per_square_millisecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') -megameters_per_microsecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') -megameters_per_square_microsecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') -megameters_per_nanosecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') -megameters_per_square_nanosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') -megameters_per_picosecond = Unit(1e+18, Dimensions(1, -1, 0, 0, 0), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') -megameters_per_square_picosecond = Unit(1e+18, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') -megameters_per_femtosecond = Unit(9.999999999999999e+20, Dimensions(1, -1, 0, 0, 0), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') -megameters_per_square_femtosecond = Unit(9.999999999999999e+20, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') -megameters_per_attosecond = Unit(1e+24, Dimensions(1, -1, 0, 0, 0), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') -megameters_per_square_attosecond = Unit(1e+24, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') -megameters_per_minute = Unit(16666.666666666668, Dimensions(1, -1, 0, 0, 0), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') -megameters_per_square_minute = Unit(16666.666666666668, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') -megameters_per_hour = Unit(2777.777777777778, Dimensions(1, -1, 0, 0, 0), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') -megameters_per_square_hour = Unit(2777.777777777778, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') -megameters_per_day = Unit(115.74074074074075, Dimensions(1, -1, 0, 0, 0), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') -megameters_per_square_day = Unit(115.74074074074075, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') -megameters_per_year = Unit(0.3168873850681143, Dimensions(1, -1, 0, 0, 0), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') -megameters_per_square_year = Unit(0.3168873850681143, Dimensions(1, -2, 0, 0, 0), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') -kilometers_per_second = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') -kilometers_per_square_second = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') -kilometers_per_millisecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') -kilometers_per_square_millisecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') -kilometers_per_microsecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') -kilometers_per_square_microsecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') -kilometers_per_nanosecond = Unit(999999999999.9999, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') -kilometers_per_square_nanosecond = Unit(999999999999.9999, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') -kilometers_per_picosecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') -kilometers_per_square_picosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') -kilometers_per_femtosecond = Unit(9.999999999999999e+17, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') -kilometers_per_square_femtosecond = Unit(9.999999999999999e+17, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') -kilometers_per_attosecond = Unit(9.999999999999999e+20, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') -kilometers_per_square_attosecond = Unit(9.999999999999999e+20, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') -kilometers_per_minute = Unit(16.666666666666668, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') -kilometers_per_square_minute = Unit(16.666666666666668, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') -kilometers_per_hour = Unit(2.7777777777777777, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') -kilometers_per_square_hour = Unit(2.7777777777777777, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') -kilometers_per_day = Unit(0.11574074074074074, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') -kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') -kilometers_per_year = Unit(0.0003168873850681143, Dimensions(1, -1, 0, 0, 0), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') -kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(1, -2, 0, 0, 0), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') -millimeters_per_second = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') -millimeters_per_square_second = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') -millimeters_per_millisecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') -millimeters_per_square_millisecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') -millimeters_per_microsecond = Unit(1000.0000000000001, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') -millimeters_per_square_microsecond = Unit(1000.0000000000001, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') -millimeters_per_nanosecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') -millimeters_per_square_nanosecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') -millimeters_per_picosecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') -millimeters_per_square_picosecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') -millimeters_per_femtosecond = Unit(1000000000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') -millimeters_per_square_femtosecond = Unit(1000000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') -millimeters_per_attosecond = Unit(1000000000000000.0, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') -millimeters_per_square_attosecond = Unit(1000000000000000.0, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') -millimeters_per_minute = Unit(1.6666666666666667e-05, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') -millimeters_per_square_minute = Unit(1.6666666666666667e-05, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') -millimeters_per_hour = Unit(2.777777777777778e-06, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') -millimeters_per_square_hour = Unit(2.777777777777778e-06, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') -millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') -millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') -millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(1, -1, 0, 0, 0), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') -millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(1, -2, 0, 0, 0), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') -micrometers_per_second = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') -micrometers_per_square_second = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') -micrometers_per_millisecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') -micrometers_per_square_millisecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') -micrometers_per_microsecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') -micrometers_per_square_microsecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') -micrometers_per_nanosecond = Unit(999.9999999999999, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') -micrometers_per_square_nanosecond = Unit(999.9999999999999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') -micrometers_per_picosecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') -micrometers_per_square_picosecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') -micrometers_per_femtosecond = Unit(999999999.9999999, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') -micrometers_per_square_femtosecond = Unit(999999999.9999999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') -micrometers_per_attosecond = Unit(999999999999.9999, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') -micrometers_per_square_attosecond = Unit(999999999999.9999, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') -micrometers_per_minute = Unit(1.6666666666666667e-08, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') -micrometers_per_square_minute = Unit(1.6666666666666667e-08, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') -micrometers_per_hour = Unit(2.7777777777777776e-09, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') -micrometers_per_square_hour = Unit(2.7777777777777776e-09, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') -micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') -micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') -micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(1, -1, 0, 0, 0), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') -micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(1, -2, 0, 0, 0), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') -nanometers_per_second = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') -nanometers_per_square_second = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') -nanometers_per_millisecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') -nanometers_per_square_millisecond = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') -nanometers_per_microsecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') -nanometers_per_square_microsecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') -nanometers_per_nanosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') -nanometers_per_square_nanosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') -nanometers_per_picosecond = Unit(1000.0000000000001, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') -nanometers_per_square_picosecond = Unit(1000.0000000000001, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') -nanometers_per_femtosecond = Unit(1000000.0, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') -nanometers_per_square_femtosecond = Unit(1000000.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') -nanometers_per_attosecond = Unit(1000000000.0, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') -nanometers_per_square_attosecond = Unit(1000000000.0, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') -nanometers_per_minute = Unit(1.6666666666666667e-11, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') -nanometers_per_square_minute = Unit(1.6666666666666667e-11, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') -nanometers_per_hour = Unit(2.777777777777778e-12, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') -nanometers_per_square_hour = Unit(2.777777777777778e-12, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') -nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') -nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') -nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(1, -1, 0, 0, 0), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') -nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(1, -2, 0, 0, 0), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') -picometers_per_second = Unit(1e-12, Dimensions(1, -1, 0, 0, 0), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') -picometers_per_square_second = Unit(1e-12, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') -picometers_per_millisecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') -picometers_per_square_millisecond = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') -picometers_per_microsecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') -picometers_per_square_microsecond = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') -picometers_per_nanosecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') -picometers_per_square_nanosecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') -picometers_per_picosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') -picometers_per_square_picosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') -picometers_per_femtosecond = Unit(999.9999999999999, Dimensions(1, -1, 0, 0, 0), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') -picometers_per_square_femtosecond = Unit(999.9999999999999, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') -picometers_per_attosecond = Unit(999999.9999999999, Dimensions(1, -1, 0, 0, 0), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') -picometers_per_square_attosecond = Unit(999999.9999999999, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') -picometers_per_minute = Unit(1.6666666666666667e-14, Dimensions(1, -1, 0, 0, 0), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') -picometers_per_square_minute = Unit(1.6666666666666667e-14, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') -picometers_per_hour = Unit(2.7777777777777776e-15, Dimensions(1, -1, 0, 0, 0), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') -picometers_per_square_hour = Unit(2.7777777777777776e-15, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') -picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(1, -1, 0, 0, 0), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') -picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') -picometers_per_year = Unit(3.168873850681143e-19, Dimensions(1, -1, 0, 0, 0), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') -picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(1, -2, 0, 0, 0), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') -femtometers_per_second = Unit(1e-15, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') -femtometers_per_square_second = Unit(1e-15, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') -femtometers_per_millisecond = Unit(1e-12, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') -femtometers_per_square_millisecond = Unit(1e-12, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') -femtometers_per_microsecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') -femtometers_per_square_microsecond = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') -femtometers_per_nanosecond = Unit(1e-06, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') -femtometers_per_square_nanosecond = Unit(1e-06, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') -femtometers_per_picosecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') -femtometers_per_square_picosecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') -femtometers_per_femtosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') -femtometers_per_square_femtosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') -femtometers_per_attosecond = Unit(1000.0, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') -femtometers_per_square_attosecond = Unit(1000.0, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') -femtometers_per_minute = Unit(1.6666666666666667e-17, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') -femtometers_per_square_minute = Unit(1.6666666666666667e-17, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') -femtometers_per_hour = Unit(2.777777777777778e-18, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') -femtometers_per_square_hour = Unit(2.777777777777778e-18, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') -femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') -femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') -femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(1, -1, 0, 0, 0), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') -femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(1, -2, 0, 0, 0), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') -attometers_per_second = Unit(1e-18, Dimensions(1, -1, 0, 0, 0), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') -attometers_per_square_second = Unit(1e-18, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') -attometers_per_millisecond = Unit(1e-15, Dimensions(1, -1, 0, 0, 0), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') -attometers_per_square_millisecond = Unit(1e-15, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') -attometers_per_microsecond = Unit(1.0000000000000002e-12, Dimensions(1, -1, 0, 0, 0), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') -attometers_per_square_microsecond = Unit(1.0000000000000002e-12, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') -attometers_per_nanosecond = Unit(1e-09, Dimensions(1, -1, 0, 0, 0), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') -attometers_per_square_nanosecond = Unit(1e-09, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') -attometers_per_picosecond = Unit(1.0000000000000002e-06, Dimensions(1, -1, 0, 0, 0), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') -attometers_per_square_picosecond = Unit(1.0000000000000002e-06, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') -attometers_per_femtosecond = Unit(0.001, Dimensions(1, -1, 0, 0, 0), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') -attometers_per_square_femtosecond = Unit(0.001, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') -attometers_per_attosecond = Unit(1.0, Dimensions(1, -1, 0, 0, 0), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') -attometers_per_square_attosecond = Unit(1.0, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') -attometers_per_minute = Unit(1.6666666666666668e-20, Dimensions(1, -1, 0, 0, 0), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') -attometers_per_square_minute = Unit(1.6666666666666668e-20, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') -attometers_per_hour = Unit(2.7777777777777778e-21, Dimensions(1, -1, 0, 0, 0), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') -attometers_per_square_hour = Unit(2.7777777777777778e-21, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') -attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(1, -1, 0, 0, 0), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') -attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') -attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(1, -1, 0, 0, 0), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') -attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(1, -2, 0, 0, 0), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') -angstroms_per_second = Unit(1e-10, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') -angstroms_per_square_second = Unit(1e-10, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') -angstroms_per_millisecond = Unit(1e-07, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') -angstroms_per_square_millisecond = Unit(1e-07, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') -angstroms_per_microsecond = Unit(0.0001, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') -angstroms_per_square_microsecond = Unit(0.0001, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') -angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') -angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') -angstroms_per_picosecond = Unit(100.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') -angstroms_per_square_picosecond = Unit(100.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') -angstroms_per_femtosecond = Unit(100000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') -angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') -angstroms_per_attosecond = Unit(100000000.0, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') -angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') -angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') -angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') -angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') -angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') -angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') -angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') -angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(1, -1, 0, 0, 0), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') -angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(1, -2, 0, 0, 0), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') -grams_per_cubic_meter = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_meter = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') -petagrams_per_cubic_meter = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') -teragrams_per_cubic_meter = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_meter = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') -megagrams_per_cubic_meter = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') -kilograms_per_cubic_meter = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgNone⁻³') -milligrams_per_cubic_meter = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgNone⁻³') -micrograms_per_cubic_meter = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgNone⁻³') -nanograms_per_cubic_meter = Unit(1.0000000000000002e-12, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngNone⁻³') -picograms_per_cubic_meter = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') -femtograms_per_cubic_meter = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') -attograms_per_cubic_meter = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') -grams_per_cubic_exameter = Unit(1e-57, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') -exagrams_per_cubic_exameter = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') -petagrams_per_cubic_exameter = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') -teragrams_per_cubic_exameter = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') -gigagrams_per_cubic_exameter = Unit(1e-48, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_exameter', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') -megagrams_per_cubic_exameter = Unit(9.999999999999999e-52, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_exameter', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') -kilograms_per_cubic_exameter = Unit(9.999999999999999e-55, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_exameter', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') -milligrams_per_cubic_exameter = Unit(9.999999999999998e-61, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_exameter', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') -micrograms_per_cubic_exameter = Unit(9.999999999999999e-64, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_exameter', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') -nanograms_per_cubic_exameter = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_exameter', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') -picograms_per_cubic_exameter = Unit(1e-69, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') -femtograms_per_cubic_exameter = Unit(1e-72, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') -attograms_per_cubic_exameter = Unit(1e-75, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -grams_per_cubic_petameter = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') -exagrams_per_cubic_petameter = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') -petagrams_per_cubic_petameter = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') -teragrams_per_cubic_petameter = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') -gigagrams_per_cubic_petameter = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_petameter', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') -megagrams_per_cubic_petameter = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_petameter', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') -kilograms_per_cubic_petameter = Unit(1.0000000000000001e-45, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_petameter', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') -milligrams_per_cubic_petameter = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_petameter', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') -micrograms_per_cubic_petameter = Unit(1.0000000000000002e-54, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_petameter', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') -nanograms_per_cubic_petameter = Unit(1.0000000000000002e-57, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_petameter', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') -picograms_per_cubic_petameter = Unit(1.0000000000000001e-60, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') -femtograms_per_cubic_petameter = Unit(1.0000000000000002e-63, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') -attograms_per_cubic_petameter = Unit(1.0000000000000001e-66, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -grams_per_cubic_terameter = Unit(1e-39, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') -exagrams_per_cubic_terameter = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') -petagrams_per_cubic_terameter = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') -teragrams_per_cubic_terameter = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') -gigagrams_per_cubic_terameter = Unit(9.999999999999999e-31, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_terameter', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') -megagrams_per_cubic_terameter = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_terameter', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') -kilograms_per_cubic_terameter = Unit(1e-36, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_terameter', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') -milligrams_per_cubic_terameter = Unit(9.999999999999999e-43, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_terameter', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') -micrograms_per_cubic_terameter = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_terameter', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') -nanograms_per_cubic_terameter = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_terameter', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') -picograms_per_cubic_terameter = Unit(1e-51, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') -femtograms_per_cubic_terameter = Unit(1e-54, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') -attograms_per_cubic_terameter = Unit(1.0000000000000001e-57, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -grams_per_cubic_gigameter = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') -exagrams_per_cubic_gigameter = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') -petagrams_per_cubic_gigameter = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') -teragrams_per_cubic_gigameter = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') -gigagrams_per_cubic_gigameter = Unit(1e-21, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_gigameter', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') -megagrams_per_cubic_gigameter = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_gigameter', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') -kilograms_per_cubic_gigameter = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_gigameter', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') -milligrams_per_cubic_gigameter = Unit(9.999999999999999e-34, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_gigameter', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') -micrograms_per_cubic_gigameter = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_gigameter', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') -nanograms_per_cubic_gigameter = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_gigameter', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') -picograms_per_cubic_gigameter = Unit(1e-42, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') -femtograms_per_cubic_gigameter = Unit(1e-45, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') -attograms_per_cubic_gigameter = Unit(1.0000000000000001e-48, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -grams_per_cubic_megameter = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') -exagrams_per_cubic_megameter = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') -petagrams_per_cubic_megameter = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') -teragrams_per_cubic_megameter = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') -gigagrams_per_cubic_megameter = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_megameter', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') -megagrams_per_cubic_megameter = Unit(1e-15, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_megameter', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') -kilograms_per_cubic_megameter = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_megameter', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') -milligrams_per_cubic_megameter = Unit(1e-24, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_megameter', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') -micrograms_per_cubic_megameter = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_megameter', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') -nanograms_per_cubic_megameter = Unit(1.0000000000000003e-30, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_megameter', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') -picograms_per_cubic_megameter = Unit(1e-33, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') -femtograms_per_cubic_megameter = Unit(1.0000000000000001e-36, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') -attograms_per_cubic_megameter = Unit(1.0000000000000001e-39, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -grams_per_cubic_kilometer = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') -exagrams_per_cubic_kilometer = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') -petagrams_per_cubic_kilometer = Unit(1000.0, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') -teragrams_per_cubic_kilometer = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') -gigagrams_per_cubic_kilometer = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_kilometer', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') -megagrams_per_cubic_kilometer = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_kilometer', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') -kilograms_per_cubic_kilometer = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_kilometer', ascii_symbol='kg km^-3', symbol='kgkm⁻³') -milligrams_per_cubic_kilometer = Unit(9.999999999999999e-16, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_kilometer', ascii_symbol='mg km^-3', symbol='mgkm⁻³') -micrograms_per_cubic_kilometer = Unit(1e-18, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_kilometer', ascii_symbol='ug km^-3', symbol='µgkm⁻³') -nanograms_per_cubic_kilometer = Unit(1.0000000000000001e-21, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_kilometer', ascii_symbol='ng km^-3', symbol='ngkm⁻³') -picograms_per_cubic_kilometer = Unit(1.0000000000000001e-24, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') -femtograms_per_cubic_kilometer = Unit(1e-27, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') -attograms_per_cubic_kilometer = Unit(1e-30, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') -grams_per_cubic_millimeter = Unit(1000000.0, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') -exagrams_per_cubic_millimeter = Unit(1e+24, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') -petagrams_per_cubic_millimeter = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') -teragrams_per_cubic_millimeter = Unit(1e+18, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') -gigagrams_per_cubic_millimeter = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_millimeter', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') -megagrams_per_cubic_millimeter = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_millimeter', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') -kilograms_per_cubic_millimeter = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_millimeter', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') -milligrams_per_cubic_millimeter = Unit(999.9999999999999, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_millimeter', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') -micrograms_per_cubic_millimeter = Unit(1.0, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_millimeter', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') -nanograms_per_cubic_millimeter = Unit(0.001, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_millimeter', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') -picograms_per_cubic_millimeter = Unit(1e-06, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') -femtograms_per_cubic_millimeter = Unit(1e-09, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') -attograms_per_cubic_millimeter = Unit(1e-12, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -grams_per_cubic_micrometer = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') -exagrams_per_cubic_micrometer = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') -petagrams_per_cubic_micrometer = Unit(1.0000000000000002e+30, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') -teragrams_per_cubic_micrometer = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') -gigagrams_per_cubic_micrometer = Unit(1.0000000000000001e+24, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_micrometer', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') -megagrams_per_cubic_micrometer = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_micrometer', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') -kilograms_per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_micrometer', ascii_symbol='kg um^-3', symbol='kgµm⁻³') -milligrams_per_cubic_micrometer = Unit(1000000000000.0001, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_micrometer', ascii_symbol='mg um^-3', symbol='mgµm⁻³') -micrograms_per_cubic_micrometer = Unit(1000000000.0000002, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_micrometer', ascii_symbol='ug um^-3', symbol='µgµm⁻³') -nanograms_per_cubic_micrometer = Unit(1000000.0000000003, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_micrometer', ascii_symbol='ng um^-3', symbol='ngµm⁻³') -picograms_per_cubic_micrometer = Unit(1000.0000000000002, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') -femtograms_per_cubic_micrometer = Unit(1.0000000000000002, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') -attograms_per_cubic_micrometer = Unit(0.0010000000000000002, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') -grams_per_cubic_nanometer = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') -exagrams_per_cubic_nanometer = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') -petagrams_per_cubic_nanometer = Unit(9.999999999999998e+38, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') -teragrams_per_cubic_nanometer = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') -gigagrams_per_cubic_nanometer = Unit(9.999999999999998e+32, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_nanometer', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') -megagrams_per_cubic_nanometer = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_nanometer', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') -kilograms_per_cubic_nanometer = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_nanometer', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') -milligrams_per_cubic_nanometer = Unit(9.999999999999997e+20, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_nanometer', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') -micrograms_per_cubic_nanometer = Unit(9.999999999999999e+17, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_nanometer', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') -nanograms_per_cubic_nanometer = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_nanometer', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') -picograms_per_cubic_nanometer = Unit(999999999999.9999, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') -femtograms_per_cubic_nanometer = Unit(999999999.9999999, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') -attograms_per_cubic_nanometer = Unit(999999.9999999999, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -grams_per_cubic_picometer = Unit(1.0000000000000001e+33, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') -exagrams_per_cubic_picometer = Unit(1e+51, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') -petagrams_per_cubic_picometer = Unit(1e+48, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') -teragrams_per_cubic_picometer = Unit(1.0000000000000001e+45, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') -gigagrams_per_cubic_picometer = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_picometer', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') -megagrams_per_cubic_picometer = Unit(1.0000000000000001e+39, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_picometer', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') -kilograms_per_cubic_picometer = Unit(1e+36, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_picometer', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') -milligrams_per_cubic_picometer = Unit(1e+30, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_picometer', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') -micrograms_per_cubic_picometer = Unit(1.0000000000000002e+27, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_picometer', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') -nanograms_per_cubic_picometer = Unit(1.0000000000000003e+24, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_picometer', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') -picograms_per_cubic_picometer = Unit(1.0000000000000001e+21, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') -femtograms_per_cubic_picometer = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') -attograms_per_cubic_picometer = Unit(1000000000000000.1, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -grams_per_cubic_femtometer = Unit(9.999999999999997e+41, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') -exagrams_per_cubic_femtometer = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') -petagrams_per_cubic_femtometer = Unit(9.999999999999997e+56, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') -teragrams_per_cubic_femtometer = Unit(9.999999999999997e+53, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') -gigagrams_per_cubic_femtometer = Unit(9.999999999999997e+50, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_femtometer', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') -megagrams_per_cubic_femtometer = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_femtometer', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') -kilograms_per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_femtometer', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') -milligrams_per_cubic_femtometer = Unit(9.999999999999996e+38, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_femtometer', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') -micrograms_per_cubic_femtometer = Unit(9.999999999999997e+35, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_femtometer', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') -nanograms_per_cubic_femtometer = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_femtometer', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') -picograms_per_cubic_femtometer = Unit(9.999999999999997e+29, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') -femtograms_per_cubic_femtometer = Unit(9.999999999999997e+26, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') -attograms_per_cubic_femtometer = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -grams_per_cubic_attometer = Unit(9.999999999999998e+50, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') -exagrams_per_cubic_attometer = Unit(9.999999999999999e+68, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') -petagrams_per_cubic_attometer = Unit(9.999999999999998e+65, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') -teragrams_per_cubic_attometer = Unit(9.999999999999999e+62, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') -gigagrams_per_cubic_attometer = Unit(9.999999999999998e+59, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_attometer', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') -megagrams_per_cubic_attometer = Unit(9.999999999999999e+56, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_attometer', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') -kilograms_per_cubic_attometer = Unit(9.999999999999999e+53, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_attometer', ascii_symbol='kg am^-3', symbol='kgam⁻³') -milligrams_per_cubic_attometer = Unit(9.999999999999997e+47, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_attometer', ascii_symbol='mg am^-3', symbol='mgam⁻³') -micrograms_per_cubic_attometer = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_attometer', ascii_symbol='ug am^-3', symbol='µgam⁻³') -nanograms_per_cubic_attometer = Unit(1e+42, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_attometer', ascii_symbol='ng am^-3', symbol='ngam⁻³') -picograms_per_cubic_attometer = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') -femtograms_per_cubic_attometer = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') -attograms_per_cubic_attometer = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') -grams_per_cubic_angstrom = Unit(9.999999999999999e+26, Dimensions(-3, 1, 0, 0, 0), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') -exagrams_per_cubic_angstrom = Unit(1e+45, Dimensions(-3, 1, 0, 0, 0), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') -petagrams_per_cubic_angstrom = Unit(9.999999999999999e+41, Dimensions(-3, 1, 0, 0, 0), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') -teragrams_per_cubic_angstrom = Unit(1e+39, Dimensions(-3, 1, 0, 0, 0), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') -gigagrams_per_cubic_angstrom = Unit(9.999999999999999e+35, Dimensions(-3, 1, 0, 0, 0), name='gigagrams_per_cubic_angstrom', ascii_symbol='Gg Ang^-3', symbol='GgÅ⁻³') -megagrams_per_cubic_angstrom = Unit(1e+33, Dimensions(-3, 1, 0, 0, 0), name='megagrams_per_cubic_angstrom', ascii_symbol='Mg Ang^-3', symbol='MgÅ⁻³') -kilograms_per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(-3, 1, 0, 0, 0), name='kilograms_per_cubic_angstrom', ascii_symbol='kg Ang^-3', symbol='kgÅ⁻³') -milligrams_per_cubic_angstrom = Unit(9.999999999999998e+23, Dimensions(-3, 1, 0, 0, 0), name='milligrams_per_cubic_angstrom', ascii_symbol='mg Ang^-3', symbol='mgÅ⁻³') -micrograms_per_cubic_angstrom = Unit(1e+21, Dimensions(-3, 1, 0, 0, 0), name='micrograms_per_cubic_angstrom', ascii_symbol='ug Ang^-3', symbol='µgÅ⁻³') -nanograms_per_cubic_angstrom = Unit(1.0000000000000001e+18, Dimensions(-3, 1, 0, 0, 0), name='nanograms_per_cubic_angstrom', ascii_symbol='ng Ang^-3', symbol='ngÅ⁻³') -picograms_per_cubic_angstrom = Unit(1000000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') -femtograms_per_cubic_angstrom = Unit(1000000000000.0, Dimensions(-3, 1, 0, 0, 0), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') -attograms_per_cubic_angstrom = Unit(1000000000.0, Dimensions(-3, 1, 0, 0, 0), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') +degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') +angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') +minutes = Unit(60, Dimensions(0, 1, 0, 0, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') +hours = Unit(360, Dimensions(0, 1, 0, 0, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') +days = Unit(8640, Dimensions(0, 1, 0, 0, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') +years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') +degrees = Unit(57.29577951308232, Dimensions(0, 0, 0, 0, 0, 0, 1),name='degrees',ascii_symbol='deg',symbol='deg') +radians = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 1),name='radians',ascii_symbol='rad',symbol='rad') +stradians = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') +none = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') +litres = Unit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') +electronvolts = Unit(1.602e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') +exaelectronvolts = Unit(0.1602, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') +petaelectronvolts = Unit(0.0001602, Dimensions(2, -2, 1, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') +teraelectronvolts = Unit(1.602e-07, Dimensions(2, -2, 1, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') +gigaelectronvolts = Unit(1.602e-10, Dimensions(2, -2, 1, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') +megaelectronvolts = Unit(1.602e-13, Dimensions(2, -2, 1, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') +kiloelectronvolts = Unit(1.602e-16, Dimensions(2, -2, 1, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') +millielectronvolts = Unit(1.602e-22, Dimensions(2, -2, 1, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') +microelectronvolts = Unit(1.6019999999999998e-25, Dimensions(2, -2, 1, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') +nanoelectronvolts = Unit(1.602e-28, Dimensions(2, -2, 1, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') +picoelectronvolts = Unit(1.6019999999999998e-31, Dimensions(2, -2, 1, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') +femtoelectronvolts = Unit(1.602e-34, Dimensions(2, -2, 1, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') +attoelectronvolts = Unit(1.6020000000000002e-37, Dimensions(2, -2, 1, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') +atomic_mass_units = Unit(1.661e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') +moles = Unit(6.022e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') +millimoles = Unit(6.022e+20, Dimensions(0, 0, 0, 0, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') +micromoles = Unit(6.022e+17, Dimensions(0, 0, 0, 0, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') +nanomoles = Unit(602200000000000.1, Dimensions(0, 0, 0, 0, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') +picomoles = Unit(602200000000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') +femtomoles = Unit(602200000.0000001, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') +attomoles = Unit(602200.0000000001, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') +square_meters = Unit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') +cubic_meters = Unit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') +per_meter = Unit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') +per_square_meter = Unit(1.0, Dimensions(length=-2), name='per_square_meter', ascii_symbol='m^-2', symbol='m⁻²') +per_cubic_meter = Unit(1.0, Dimensions(length=-3), name='per_cubic_meter', ascii_symbol='m^-3', symbol='m⁻³') +square_exameters = Unit(1e+36, Dimensions(length=2), name='square_exameters', ascii_symbol='Em^2', symbol='Em²') +cubic_exameters = Unit(1e+54, Dimensions(length=3), name='cubic_exameters', ascii_symbol='Em^3', symbol='Em³') +per_exameter = Unit(1e-18, Dimensions(length=-1), name='per_exameter', ascii_symbol='Em^-1', symbol='Em⁻¹') +per_square_exameter = Unit(1e-36, Dimensions(length=-2), name='per_square_exameter', ascii_symbol='Em^-2', symbol='Em⁻²') +per_cubic_exameter = Unit(1e-54, Dimensions(length=-3), name='per_cubic_exameter', ascii_symbol='Em^-3', symbol='Em⁻³') +square_petameters = Unit(1e+30, Dimensions(length=2), name='square_petameters', ascii_symbol='Pm^2', symbol='Pm²') +cubic_petameters = Unit(1e+45, Dimensions(length=3), name='cubic_petameters', ascii_symbol='Pm^3', symbol='Pm³') +per_petameter = Unit(1e-15, Dimensions(length=-1), name='per_petameter', ascii_symbol='Pm^-1', symbol='Pm⁻¹') +per_square_petameter = Unit(1e-30, Dimensions(length=-2), name='per_square_petameter', ascii_symbol='Pm^-2', symbol='Pm⁻²') +per_cubic_petameter = Unit(1e-45, Dimensions(length=-3), name='per_cubic_petameter', ascii_symbol='Pm^-3', symbol='Pm⁻³') +square_terameters = Unit(1e+24, Dimensions(length=2), name='square_terameters', ascii_symbol='Tm^2', symbol='Tm²') +cubic_terameters = Unit(1e+36, Dimensions(length=3), name='cubic_terameters', ascii_symbol='Tm^3', symbol='Tm³') +per_terameter = Unit(1e-12, Dimensions(length=-1), name='per_terameter', ascii_symbol='Tm^-1', symbol='Tm⁻¹') +per_square_terameter = Unit(1e-24, Dimensions(length=-2), name='per_square_terameter', ascii_symbol='Tm^-2', symbol='Tm⁻²') +per_cubic_terameter = Unit(1e-36, Dimensions(length=-3), name='per_cubic_terameter', ascii_symbol='Tm^-3', symbol='Tm⁻³') +square_gigameters = Unit(1e+18, Dimensions(length=2), name='square_gigameters', ascii_symbol='Gm^2', symbol='Gm²') +cubic_gigameters = Unit(1e+27, Dimensions(length=3), name='cubic_gigameters', ascii_symbol='Gm^3', symbol='Gm³') +per_gigameter = Unit(1e-09, Dimensions(length=-1), name='per_gigameter', ascii_symbol='Gm^-1', symbol='Gm⁻¹') +per_square_gigameter = Unit(1e-18, Dimensions(length=-2), name='per_square_gigameter', ascii_symbol='Gm^-2', symbol='Gm⁻²') +per_cubic_gigameter = Unit(1e-27, Dimensions(length=-3), name='per_cubic_gigameter', ascii_symbol='Gm^-3', symbol='Gm⁻³') +square_megameters = Unit(1000000000000.0, Dimensions(length=2), name='square_megameters', ascii_symbol='Mm^2', symbol='Mm²') +cubic_megameters = Unit(1e+18, Dimensions(length=3), name='cubic_megameters', ascii_symbol='Mm^3', symbol='Mm³') +per_megameter = Unit(1e-06, Dimensions(length=-1), name='per_megameter', ascii_symbol='Mm^-1', symbol='Mm⁻¹') +per_square_megameter = Unit(1e-12, Dimensions(length=-2), name='per_square_megameter', ascii_symbol='Mm^-2', symbol='Mm⁻²') +per_cubic_megameter = Unit(1e-18, Dimensions(length=-3), name='per_cubic_megameter', ascii_symbol='Mm^-3', symbol='Mm⁻³') +square_kilometers = Unit(1000000.0, Dimensions(length=2), name='square_kilometers', ascii_symbol='km^2', symbol='km²') +cubic_kilometers = Unit(1000000000.0, Dimensions(length=3), name='cubic_kilometers', ascii_symbol='km^3', symbol='km³') +per_kilometer = Unit(0.001, Dimensions(length=-1), name='per_kilometer', ascii_symbol='km^-1', symbol='km⁻¹') +per_square_kilometer = Unit(1e-06, Dimensions(length=-2), name='per_square_kilometer', ascii_symbol='km^-2', symbol='km⁻²') +per_cubic_kilometer = Unit(1e-09, Dimensions(length=-3), name='per_cubic_kilometer', ascii_symbol='km^-3', symbol='km⁻³') +square_millimeters = Unit(1e-06, Dimensions(length=2), name='square_millimeters', ascii_symbol='mm^2', symbol='mm²') +cubic_millimeters = Unit(1e-09, Dimensions(length=3), name='cubic_millimeters', ascii_symbol='mm^3', symbol='mm³') +per_millimeter = Unit(1000.0, Dimensions(length=-1), name='per_millimeter', ascii_symbol='mm^-1', symbol='mm⁻¹') +per_square_millimeter = Unit(1000000.0, Dimensions(length=-2), name='per_square_millimeter', ascii_symbol='mm^-2', symbol='mm⁻²') +per_cubic_millimeter = Unit(999999999.9999999, Dimensions(length=-3), name='per_cubic_millimeter', ascii_symbol='mm^-3', symbol='mm⁻³') +square_micrometers = Unit(1e-12, Dimensions(length=2), name='square_micrometers', ascii_symbol='um^2', symbol='µm²') +cubic_micrometers = Unit(9.999999999999999e-19, Dimensions(length=3), name='cubic_micrometers', ascii_symbol='um^3', symbol='µm³') +per_micrometer = Unit(1000000.0, Dimensions(length=-1), name='per_micrometer', ascii_symbol='um^-1', symbol='µm⁻¹') +per_square_micrometer = Unit(1000000000000.0001, Dimensions(length=-2), name='per_square_micrometer', ascii_symbol='um^-2', symbol='µm⁻²') +per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(length=-3), name='per_cubic_micrometer', ascii_symbol='um^-3', symbol='µm⁻³') +square_nanometers = Unit(1e-18, Dimensions(length=2), name='square_nanometers', ascii_symbol='nm^2', symbol='nm²') +cubic_nanometers = Unit(1.0000000000000002e-27, Dimensions(length=3), name='cubic_nanometers', ascii_symbol='nm^3', symbol='nm³') +per_nanometer = Unit(999999999.9999999, Dimensions(length=-1), name='per_nanometer', ascii_symbol='nm^-1', symbol='nm⁻¹') +per_square_nanometer = Unit(9.999999999999999e+17, Dimensions(length=-2), name='per_square_nanometer', ascii_symbol='nm^-2', symbol='nm⁻²') +per_cubic_nanometer = Unit(9.999999999999999e+26, Dimensions(length=-3), name='per_cubic_nanometer', ascii_symbol='nm^-3', symbol='nm⁻³') +square_picometers = Unit(1e-24, Dimensions(length=2), name='square_picometers', ascii_symbol='pm^2', symbol='pm²') +cubic_picometers = Unit(1e-36, Dimensions(length=3), name='cubic_picometers', ascii_symbol='pm^3', symbol='pm³') +per_picometer = Unit(1000000000000.0, Dimensions(length=-1), name='per_picometer', ascii_symbol='pm^-1', symbol='pm⁻¹') +per_square_picometer = Unit(1e+24, Dimensions(length=-2), name='per_square_picometer', ascii_symbol='pm^-2', symbol='pm⁻²') +per_cubic_picometer = Unit(1e+36, Dimensions(length=-3), name='per_cubic_picometer', ascii_symbol='pm^-3', symbol='pm⁻³') +square_femtometers = Unit(1e-30, Dimensions(length=2), name='square_femtometers', ascii_symbol='fm^2', symbol='fm²') +cubic_femtometers = Unit(1.0000000000000003e-45, Dimensions(length=3), name='cubic_femtometers', ascii_symbol='fm^3', symbol='fm³') +per_femtometer = Unit(999999999999999.9, Dimensions(length=-1), name='per_femtometer', ascii_symbol='fm^-1', symbol='fm⁻¹') +per_square_femtometer = Unit(9.999999999999999e+29, Dimensions(length=-2), name='per_square_femtometer', ascii_symbol='fm^-2', symbol='fm⁻²') +per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(length=-3), name='per_cubic_femtometer', ascii_symbol='fm^-3', symbol='fm⁻³') +square_attometers = Unit(1.0000000000000001e-36, Dimensions(length=2), name='square_attometers', ascii_symbol='am^2', symbol='am²') +cubic_attometers = Unit(1.0000000000000002e-54, Dimensions(length=3), name='cubic_attometers', ascii_symbol='am^3', symbol='am³') +per_attometer = Unit(9.999999999999999e+17, Dimensions(length=-1), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') +per_square_attometer = Unit(9.999999999999999e+35, Dimensions(length=-2), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') +per_cubic_attometer = Unit(9.999999999999997e+53, Dimensions(length=-3), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') +square_decimeters = Unit(0.010000000000000002, Dimensions(length=2), name='square_decimeters', ascii_symbol='dm^2', symbol='dm²') +cubic_decimeters = Unit(0.0010000000000000002, Dimensions(length=3), name='cubic_decimeters', ascii_symbol='dm^3', symbol='dm³') +per_decimeter = Unit(10.0, Dimensions(length=-1), name='per_decimeter', ascii_symbol='dm^-1', symbol='dm⁻¹') +per_square_decimeter = Unit(99.99999999999999, Dimensions(length=-2), name='per_square_decimeter', ascii_symbol='dm^-2', symbol='dm⁻²') +per_cubic_decimeter = Unit(999.9999999999999, Dimensions(length=-3), name='per_cubic_decimeter', ascii_symbol='dm^-3', symbol='dm⁻³') +square_centimeters = Unit(0.0001, Dimensions(length=2), name='square_centimeters', ascii_symbol='cm^2', symbol='cm²') +cubic_centimeters = Unit(1.0000000000000002e-06, Dimensions(length=3), name='cubic_centimeters', ascii_symbol='cm^3', symbol='cm³') +per_centimeter = Unit(100.0, Dimensions(length=-1), name='per_centimeter', ascii_symbol='cm^-1', symbol='cm⁻¹') +per_square_centimeter = Unit(10000.0, Dimensions(length=-2), name='per_square_centimeter', ascii_symbol='cm^-2', symbol='cm⁻²') +per_cubic_centimeter = Unit(999999.9999999999, Dimensions(length=-3), name='per_cubic_centimeter', ascii_symbol='cm^-3', symbol='cm⁻³') +square_angstroms = Unit(1.0000000000000001e-20, Dimensions(length=2), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ų') +cubic_angstroms = Unit(1e-30, Dimensions(length=3), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='ų') +per_angstrom = Unit(10000000000.0, Dimensions(length=-1), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Å⁻¹') +per_square_angstrom = Unit(1e+20, Dimensions(length=-2), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Å⁻²') +per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(length=-3), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Å⁻³') +meters_per_second = Unit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') +meters_per_square_second = Unit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') +meters_per_millisecond = Unit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') +meters_per_square_millisecond = Unit(1000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') +meters_per_microsecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') +meters_per_square_microsecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') +meters_per_nanosecond = Unit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') +meters_per_square_nanosecond = Unit(999999999.9999999, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') +meters_per_picosecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') +meters_per_square_picosecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') +meters_per_femtosecond = Unit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') +meters_per_square_femtosecond = Unit(999999999999999.9, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') +meters_per_attosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') +meters_per_square_attosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') +meters_per_minute = Unit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') +meters_per_square_minute = Unit(0.016666666666666666, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') +meters_per_hour = Unit(0.002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') +meters_per_square_hour = Unit(0.002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') +meters_per_day = Unit(0.00011574074074074075, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') +meters_per_square_day = Unit(0.00011574074074074075, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') +meters_per_year = Unit(3.168873850681143e-07, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') +meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') +exameters_per_second = Unit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') +exameters_per_square_second = Unit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') +exameters_per_millisecond = Unit(1e+21, Dimensions(length=1, time=-1), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') +exameters_per_square_millisecond = Unit(1e+21, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') +exameters_per_microsecond = Unit(1e+24, Dimensions(length=1, time=-1), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') +exameters_per_square_microsecond = Unit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') +exameters_per_nanosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') +exameters_per_square_nanosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') +exameters_per_picosecond = Unit(1e+30, Dimensions(length=1, time=-1), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') +exameters_per_square_picosecond = Unit(1e+30, Dimensions(length=1, time=-2), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') +exameters_per_femtosecond = Unit(1e+33, Dimensions(length=1, time=-1), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') +exameters_per_square_femtosecond = Unit(1e+33, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') +exameters_per_attosecond = Unit(9.999999999999999e+35, Dimensions(length=1, time=-1), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') +exameters_per_square_attosecond = Unit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') +exameters_per_minute = Unit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') +exameters_per_square_minute = Unit(1.6666666666666666e+16, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') +exameters_per_hour = Unit(2777777777777778.0, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') +exameters_per_square_hour = Unit(2777777777777778.0, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') +exameters_per_day = Unit(115740740740740.73, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') +exameters_per_square_day = Unit(115740740740740.73, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') +exameters_per_year = Unit(316887385068.1143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') +exameters_per_square_year = Unit(316887385068.1143, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') +petameters_per_second = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') +petameters_per_square_second = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') +petameters_per_millisecond = Unit(1e+18, Dimensions(length=1, time=-1), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') +petameters_per_square_millisecond = Unit(1e+18, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') +petameters_per_microsecond = Unit(1e+21, Dimensions(length=1, time=-1), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') +petameters_per_square_microsecond = Unit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') +petameters_per_nanosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') +petameters_per_square_nanosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') +petameters_per_picosecond = Unit(1e+27, Dimensions(length=1, time=-1), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') +petameters_per_square_picosecond = Unit(1e+27, Dimensions(length=1, time=-2), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') +petameters_per_femtosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') +petameters_per_square_femtosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') +petameters_per_attosecond = Unit(1e+33, Dimensions(length=1, time=-1), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') +petameters_per_square_attosecond = Unit(1e+33, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') +petameters_per_minute = Unit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') +petameters_per_square_minute = Unit(16666666666666.666, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') +petameters_per_hour = Unit(2777777777777.778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') +petameters_per_square_hour = Unit(2777777777777.778, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') +petameters_per_day = Unit(115740740740.74074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') +petameters_per_square_day = Unit(115740740740.74074, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') +petameters_per_year = Unit(316887385.0681143, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') +petameters_per_square_year = Unit(316887385.0681143, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') +terameters_per_second = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') +terameters_per_square_second = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') +terameters_per_millisecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') +terameters_per_square_millisecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') +terameters_per_microsecond = Unit(1e+18, Dimensions(length=1, time=-1), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') +terameters_per_square_microsecond = Unit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') +terameters_per_nanosecond = Unit(1e+21, Dimensions(length=1, time=-1), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') +terameters_per_square_nanosecond = Unit(1e+21, Dimensions(length=1, time=-2), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') +terameters_per_picosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') +terameters_per_square_picosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') +terameters_per_femtosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') +terameters_per_square_femtosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') +terameters_per_attosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') +terameters_per_square_attosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') +terameters_per_minute = Unit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') +terameters_per_square_minute = Unit(16666666666.666666, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') +terameters_per_hour = Unit(2777777777.7777777, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') +terameters_per_square_hour = Unit(2777777777.7777777, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') +terameters_per_day = Unit(115740740.74074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') +terameters_per_square_day = Unit(115740740.74074075, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') +terameters_per_year = Unit(316887.38506811426, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') +terameters_per_square_year = Unit(316887.38506811426, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') +gigameters_per_second = Unit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') +gigameters_per_square_second = Unit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') +gigameters_per_millisecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') +gigameters_per_square_millisecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') +gigameters_per_microsecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') +gigameters_per_square_microsecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') +gigameters_per_nanosecond = Unit(1e+18, Dimensions(length=1, time=-1), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') +gigameters_per_square_nanosecond = Unit(1e+18, Dimensions(length=1, time=-2), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') +gigameters_per_picosecond = Unit(1e+21, Dimensions(length=1, time=-1), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') +gigameters_per_square_picosecond = Unit(1e+21, Dimensions(length=1, time=-2), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') +gigameters_per_femtosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') +gigameters_per_square_femtosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') +gigameters_per_attosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') +gigameters_per_square_attosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') +gigameters_per_minute = Unit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') +gigameters_per_square_minute = Unit(16666666.666666666, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') +gigameters_per_hour = Unit(2777777.777777778, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') +gigameters_per_square_hour = Unit(2777777.777777778, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') +gigameters_per_day = Unit(115740.74074074074, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') +gigameters_per_square_day = Unit(115740.74074074074, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') +gigameters_per_year = Unit(316.88738506811427, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') +gigameters_per_square_year = Unit(316.88738506811427, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') +megameters_per_second = Unit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') +megameters_per_square_second = Unit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') +megameters_per_millisecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') +megameters_per_square_millisecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') +megameters_per_microsecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') +megameters_per_square_microsecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') +megameters_per_nanosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') +megameters_per_square_nanosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') +megameters_per_picosecond = Unit(1e+18, Dimensions(length=1, time=-1), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') +megameters_per_square_picosecond = Unit(1e+18, Dimensions(length=1, time=-2), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') +megameters_per_femtosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') +megameters_per_square_femtosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') +megameters_per_attosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') +megameters_per_square_attosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') +megameters_per_minute = Unit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') +megameters_per_square_minute = Unit(16666.666666666668, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') +megameters_per_hour = Unit(2777.777777777778, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') +megameters_per_square_hour = Unit(2777.777777777778, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') +megameters_per_day = Unit(115.74074074074075, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') +megameters_per_square_day = Unit(115.74074074074075, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') +megameters_per_year = Unit(0.3168873850681143, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') +megameters_per_square_year = Unit(0.3168873850681143, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') +kilometers_per_second = Unit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') +kilometers_per_square_second = Unit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') +kilometers_per_millisecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') +kilometers_per_square_millisecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') +kilometers_per_microsecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') +kilometers_per_square_microsecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') +kilometers_per_nanosecond = Unit(999999999999.9999, Dimensions(length=1, time=-1), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') +kilometers_per_square_nanosecond = Unit(999999999999.9999, Dimensions(length=1, time=-2), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') +kilometers_per_picosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') +kilometers_per_square_picosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') +kilometers_per_femtosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') +kilometers_per_square_femtosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') +kilometers_per_attosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') +kilometers_per_square_attosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') +kilometers_per_minute = Unit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') +kilometers_per_square_minute = Unit(16.666666666666668, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') +kilometers_per_hour = Unit(2.7777777777777777, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') +kilometers_per_square_hour = Unit(2.7777777777777777, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') +kilometers_per_day = Unit(0.11574074074074074, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') +kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') +kilometers_per_year = Unit(0.0003168873850681143, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') +kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') +millimeters_per_second = Unit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') +millimeters_per_square_second = Unit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') +millimeters_per_millisecond = Unit(1.0, Dimensions(length=1, time=-1), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') +millimeters_per_square_millisecond = Unit(1.0, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') +millimeters_per_microsecond = Unit(1000.0000000000001, Dimensions(length=1, time=-1), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') +millimeters_per_square_microsecond = Unit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') +millimeters_per_nanosecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') +millimeters_per_square_nanosecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') +millimeters_per_picosecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') +millimeters_per_square_picosecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') +millimeters_per_femtosecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') +millimeters_per_square_femtosecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') +millimeters_per_attosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') +millimeters_per_square_attosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') +millimeters_per_minute = Unit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') +millimeters_per_square_minute = Unit(1.6666666666666667e-05, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') +millimeters_per_hour = Unit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') +millimeters_per_square_hour = Unit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') +millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') +millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') +millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') +millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') +micrometers_per_second = Unit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') +micrometers_per_square_second = Unit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') +micrometers_per_millisecond = Unit(0.001, Dimensions(length=1, time=-1), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') +micrometers_per_square_millisecond = Unit(0.001, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') +micrometers_per_microsecond = Unit(1.0, Dimensions(length=1, time=-1), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') +micrometers_per_square_microsecond = Unit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') +micrometers_per_nanosecond = Unit(999.9999999999999, Dimensions(length=1, time=-1), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') +micrometers_per_square_nanosecond = Unit(999.9999999999999, Dimensions(length=1, time=-2), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') +micrometers_per_picosecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') +micrometers_per_square_picosecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') +micrometers_per_femtosecond = Unit(999999999.9999999, Dimensions(length=1, time=-1), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') +micrometers_per_square_femtosecond = Unit(999999999.9999999, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') +micrometers_per_attosecond = Unit(999999999999.9999, Dimensions(length=1, time=-1), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') +micrometers_per_square_attosecond = Unit(999999999999.9999, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') +micrometers_per_minute = Unit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') +micrometers_per_square_minute = Unit(1.6666666666666667e-08, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') +micrometers_per_hour = Unit(2.7777777777777776e-09, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') +micrometers_per_square_hour = Unit(2.7777777777777776e-09, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') +micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') +micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') +micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') +micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') +nanometers_per_second = Unit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') +nanometers_per_square_second = Unit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') +nanometers_per_millisecond = Unit(1e-06, Dimensions(length=1, time=-1), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') +nanometers_per_square_millisecond = Unit(1e-06, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') +nanometers_per_microsecond = Unit(0.001, Dimensions(length=1, time=-1), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') +nanometers_per_square_microsecond = Unit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') +nanometers_per_nanosecond = Unit(1.0, Dimensions(length=1, time=-1), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') +nanometers_per_square_nanosecond = Unit(1.0, Dimensions(length=1, time=-2), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') +nanometers_per_picosecond = Unit(1000.0000000000001, Dimensions(length=1, time=-1), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') +nanometers_per_square_picosecond = Unit(1000.0000000000001, Dimensions(length=1, time=-2), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') +nanometers_per_femtosecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') +nanometers_per_square_femtosecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') +nanometers_per_attosecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') +nanometers_per_square_attosecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') +nanometers_per_minute = Unit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') +nanometers_per_square_minute = Unit(1.6666666666666667e-11, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') +nanometers_per_hour = Unit(2.777777777777778e-12, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') +nanometers_per_square_hour = Unit(2.777777777777778e-12, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') +nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') +nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') +nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') +nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') +picometers_per_second = Unit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') +picometers_per_square_second = Unit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') +picometers_per_millisecond = Unit(1e-09, Dimensions(length=1, time=-1), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') +picometers_per_square_millisecond = Unit(1e-09, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') +picometers_per_microsecond = Unit(1e-06, Dimensions(length=1, time=-1), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') +picometers_per_square_microsecond = Unit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') +picometers_per_nanosecond = Unit(0.001, Dimensions(length=1, time=-1), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') +picometers_per_square_nanosecond = Unit(0.001, Dimensions(length=1, time=-2), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') +picometers_per_picosecond = Unit(1.0, Dimensions(length=1, time=-1), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') +picometers_per_square_picosecond = Unit(1.0, Dimensions(length=1, time=-2), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') +picometers_per_femtosecond = Unit(999.9999999999999, Dimensions(length=1, time=-1), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') +picometers_per_square_femtosecond = Unit(999.9999999999999, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') +picometers_per_attosecond = Unit(999999.9999999999, Dimensions(length=1, time=-1), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') +picometers_per_square_attosecond = Unit(999999.9999999999, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') +picometers_per_minute = Unit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') +picometers_per_square_minute = Unit(1.6666666666666667e-14, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') +picometers_per_hour = Unit(2.7777777777777776e-15, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') +picometers_per_square_hour = Unit(2.7777777777777776e-15, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') +picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') +picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') +picometers_per_year = Unit(3.168873850681143e-19, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') +picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') +femtometers_per_second = Unit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') +femtometers_per_square_second = Unit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') +femtometers_per_millisecond = Unit(1e-12, Dimensions(length=1, time=-1), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') +femtometers_per_square_millisecond = Unit(1e-12, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') +femtometers_per_microsecond = Unit(1e-09, Dimensions(length=1, time=-1), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') +femtometers_per_square_microsecond = Unit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') +femtometers_per_nanosecond = Unit(1e-06, Dimensions(length=1, time=-1), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') +femtometers_per_square_nanosecond = Unit(1e-06, Dimensions(length=1, time=-2), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') +femtometers_per_picosecond = Unit(0.001, Dimensions(length=1, time=-1), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') +femtometers_per_square_picosecond = Unit(0.001, Dimensions(length=1, time=-2), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') +femtometers_per_femtosecond = Unit(1.0, Dimensions(length=1, time=-1), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') +femtometers_per_square_femtosecond = Unit(1.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') +femtometers_per_attosecond = Unit(1000.0, Dimensions(length=1, time=-1), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') +femtometers_per_square_attosecond = Unit(1000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') +femtometers_per_minute = Unit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') +femtometers_per_square_minute = Unit(1.6666666666666667e-17, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') +femtometers_per_hour = Unit(2.777777777777778e-18, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') +femtometers_per_square_hour = Unit(2.777777777777778e-18, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') +femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') +femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') +femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') +femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') +attometers_per_second = Unit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') +attometers_per_square_second = Unit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') +attometers_per_millisecond = Unit(1e-15, Dimensions(length=1, time=-1), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') +attometers_per_square_millisecond = Unit(1e-15, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') +attometers_per_microsecond = Unit(1.0000000000000002e-12, Dimensions(length=1, time=-1), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') +attometers_per_square_microsecond = Unit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') +attometers_per_nanosecond = Unit(1e-09, Dimensions(length=1, time=-1), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') +attometers_per_square_nanosecond = Unit(1e-09, Dimensions(length=1, time=-2), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') +attometers_per_picosecond = Unit(1.0000000000000002e-06, Dimensions(length=1, time=-1), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') +attometers_per_square_picosecond = Unit(1.0000000000000002e-06, Dimensions(length=1, time=-2), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') +attometers_per_femtosecond = Unit(0.001, Dimensions(length=1, time=-1), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') +attometers_per_square_femtosecond = Unit(0.001, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') +attometers_per_attosecond = Unit(1.0, Dimensions(length=1, time=-1), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') +attometers_per_square_attosecond = Unit(1.0, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') +attometers_per_minute = Unit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') +attometers_per_square_minute = Unit(1.6666666666666668e-20, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') +attometers_per_hour = Unit(2.7777777777777778e-21, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') +attometers_per_square_hour = Unit(2.7777777777777778e-21, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') +attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') +attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') +attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') +attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') +decimeters_per_second = Unit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dmNone⁻¹') +decimeters_per_square_second = Unit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dmNone⁻²') +decimeters_per_millisecond = Unit(100.0, Dimensions(length=1, time=-1), name='decimeters_per_millisecond', ascii_symbol='dm/ms', symbol='dmms⁻¹') +decimeters_per_square_millisecond = Unit(100.0, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') +decimeters_per_microsecond = Unit(100000.00000000001, Dimensions(length=1, time=-1), name='decimeters_per_microsecond', ascii_symbol='dm/us', symbol='dmµs⁻¹') +decimeters_per_square_microsecond = Unit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_microsecond', ascii_symbol='dm/us^2', symbol='dmµs⁻²') +decimeters_per_nanosecond = Unit(100000000.0, Dimensions(length=1, time=-1), name='decimeters_per_nanosecond', ascii_symbol='dm/ns', symbol='dmns⁻¹') +decimeters_per_square_nanosecond = Unit(100000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_nanosecond', ascii_symbol='dm/ns^2', symbol='dmns⁻²') +decimeters_per_picosecond = Unit(100000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_picosecond', ascii_symbol='dm/ps', symbol='dmps⁻¹') +decimeters_per_square_picosecond = Unit(100000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_picosecond', ascii_symbol='dm/ps^2', symbol='dmps⁻²') +decimeters_per_femtosecond = Unit(100000000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_femtosecond', ascii_symbol='dm/fs', symbol='dmfs⁻¹') +decimeters_per_square_femtosecond = Unit(100000000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') +decimeters_per_attosecond = Unit(1e+17, Dimensions(length=1, time=-1), name='decimeters_per_attosecond', ascii_symbol='dm/as', symbol='dmas⁻¹') +decimeters_per_square_attosecond = Unit(1e+17, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') +decimeters_per_minute = Unit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmNone⁻¹') +decimeters_per_square_minute = Unit(0.0016666666666666668, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmNone⁻²') +decimeters_per_hour = Unit(0.0002777777777777778, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmNone⁻¹') +decimeters_per_square_hour = Unit(0.0002777777777777778, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmNone⁻²') +decimeters_per_day = Unit(1.1574074074074075e-05, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmNone⁻¹') +decimeters_per_square_day = Unit(1.1574074074074075e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmNone⁻²') +decimeters_per_year = Unit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmNone⁻¹') +decimeters_per_square_year = Unit(3.168873850681143e-08, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmNone⁻²') +centimeters_per_second = Unit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cmNone⁻¹') +centimeters_per_square_second = Unit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cmNone⁻²') +centimeters_per_millisecond = Unit(10.0, Dimensions(length=1, time=-1), name='centimeters_per_millisecond', ascii_symbol='cm/ms', symbol='cmms⁻¹') +centimeters_per_square_millisecond = Unit(10.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') +centimeters_per_microsecond = Unit(10000.0, Dimensions(length=1, time=-1), name='centimeters_per_microsecond', ascii_symbol='cm/us', symbol='cmµs⁻¹') +centimeters_per_square_microsecond = Unit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_microsecond', ascii_symbol='cm/us^2', symbol='cmµs⁻²') +centimeters_per_nanosecond = Unit(10000000.0, Dimensions(length=1, time=-1), name='centimeters_per_nanosecond', ascii_symbol='cm/ns', symbol='cmns⁻¹') +centimeters_per_square_nanosecond = Unit(10000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_nanosecond', ascii_symbol='cm/ns^2', symbol='cmns⁻²') +centimeters_per_picosecond = Unit(10000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_picosecond', ascii_symbol='cm/ps', symbol='cmps⁻¹') +centimeters_per_square_picosecond = Unit(10000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_picosecond', ascii_symbol='cm/ps^2', symbol='cmps⁻²') +centimeters_per_femtosecond = Unit(10000000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_femtosecond', ascii_symbol='cm/fs', symbol='cmfs⁻¹') +centimeters_per_square_femtosecond = Unit(10000000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') +centimeters_per_attosecond = Unit(1e+16, Dimensions(length=1, time=-1), name='centimeters_per_attosecond', ascii_symbol='cm/as', symbol='cmas⁻¹') +centimeters_per_square_attosecond = Unit(1e+16, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') +centimeters_per_minute = Unit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmNone⁻¹') +centimeters_per_square_minute = Unit(0.00016666666666666666, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmNone⁻²') +centimeters_per_hour = Unit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmNone⁻¹') +centimeters_per_square_hour = Unit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmNone⁻²') +centimeters_per_day = Unit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmNone⁻¹') +centimeters_per_square_day = Unit(1.1574074074074074e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmNone⁻²') +centimeters_per_year = Unit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmNone⁻¹') +centimeters_per_square_year = Unit(3.168873850681143e-09, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmNone⁻²') +angstroms_per_second = Unit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') +angstroms_per_square_second = Unit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') +angstroms_per_millisecond = Unit(1e-07, Dimensions(length=1, time=-1), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') +angstroms_per_square_millisecond = Unit(1e-07, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') +angstroms_per_microsecond = Unit(0.0001, Dimensions(length=1, time=-1), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') +angstroms_per_square_microsecond = Unit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') +angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(length=1, time=-1), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') +angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(length=1, time=-2), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') +angstroms_per_picosecond = Unit(100.0, Dimensions(length=1, time=-1), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') +angstroms_per_square_picosecond = Unit(100.0, Dimensions(length=1, time=-2), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') +angstroms_per_femtosecond = Unit(100000.0, Dimensions(length=1, time=-1), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') +angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') +angstroms_per_attosecond = Unit(100000000.0, Dimensions(length=1, time=-1), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') +angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') +angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') +angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') +angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') +angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') +angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') +angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') +angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') +angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') +grams_per_cubic_meter = Unit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_meter = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') +petagrams_per_cubic_meter = Unit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') +teragrams_per_cubic_meter = Unit(1000000000.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_meter = Unit(1000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') +megagrams_per_cubic_meter = Unit(1000.0, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') +kilograms_per_cubic_meter = Unit(1.0, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgNone⁻³') +milligrams_per_cubic_meter = Unit(1e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgNone⁻³') +micrograms_per_cubic_meter = Unit(1e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgNone⁻³') +nanograms_per_cubic_meter = Unit(1.0000000000000002e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngNone⁻³') +picograms_per_cubic_meter = Unit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') +femtograms_per_cubic_meter = Unit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') +attograms_per_cubic_meter = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') +atomic_mass_units_per_cubic_meter = Unit(1.661e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='NoneNone⁻³') +grams_per_cubic_exameter = Unit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') +exagrams_per_cubic_exameter = Unit(1e-39, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') +petagrams_per_cubic_exameter = Unit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') +teragrams_per_cubic_exameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') +gigagrams_per_cubic_exameter = Unit(1e-48, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_exameter', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') +megagrams_per_cubic_exameter = Unit(9.999999999999999e-52, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_exameter', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') +kilograms_per_cubic_exameter = Unit(9.999999999999999e-55, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_exameter', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') +milligrams_per_cubic_exameter = Unit(9.999999999999998e-61, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_exameter', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') +micrograms_per_cubic_exameter = Unit(9.999999999999999e-64, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_exameter', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') +nanograms_per_cubic_exameter = Unit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_exameter', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') +picograms_per_cubic_exameter = Unit(1e-69, Dimensions(length=-3, mass=1), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') +femtograms_per_cubic_exameter = Unit(1e-72, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') +attograms_per_cubic_exameter = Unit(1e-75, Dimensions(length=-3, mass=1), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') +atomic_mass_units_per_cubic_exameter = Unit(1.661e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='NoneEm⁻³') +grams_per_cubic_petameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') +exagrams_per_cubic_petameter = Unit(1e-30, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') +petagrams_per_cubic_petameter = Unit(1e-33, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') +teragrams_per_cubic_petameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') +gigagrams_per_cubic_petameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_petameter', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') +megagrams_per_cubic_petameter = Unit(1e-42, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_petameter', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') +kilograms_per_cubic_petameter = Unit(1.0000000000000001e-45, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_petameter', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') +milligrams_per_cubic_petameter = Unit(1e-51, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_petameter', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') +micrograms_per_cubic_petameter = Unit(1.0000000000000002e-54, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_petameter', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') +nanograms_per_cubic_petameter = Unit(1.0000000000000002e-57, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_petameter', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') +picograms_per_cubic_petameter = Unit(1.0000000000000001e-60, Dimensions(length=-3, mass=1), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') +femtograms_per_cubic_petameter = Unit(1.0000000000000002e-63, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') +attograms_per_cubic_petameter = Unit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') +atomic_mass_units_per_cubic_petameter = Unit(1.6610000000000003e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='NonePm⁻³') +grams_per_cubic_terameter = Unit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') +exagrams_per_cubic_terameter = Unit(1e-21, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') +petagrams_per_cubic_terameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') +teragrams_per_cubic_terameter = Unit(1e-27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') +gigagrams_per_cubic_terameter = Unit(9.999999999999999e-31, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_terameter', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') +megagrams_per_cubic_terameter = Unit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_terameter', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') +kilograms_per_cubic_terameter = Unit(1e-36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_terameter', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') +milligrams_per_cubic_terameter = Unit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_terameter', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') +micrograms_per_cubic_terameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_terameter', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') +nanograms_per_cubic_terameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_terameter', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') +picograms_per_cubic_terameter = Unit(1e-51, Dimensions(length=-3, mass=1), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') +femtograms_per_cubic_terameter = Unit(1e-54, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') +attograms_per_cubic_terameter = Unit(1.0000000000000001e-57, Dimensions(length=-3, mass=1), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') +atomic_mass_units_per_cubic_terameter = Unit(1.6610000000000002e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='NoneTm⁻³') +grams_per_cubic_gigameter = Unit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') +exagrams_per_cubic_gigameter = Unit(1e-12, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') +petagrams_per_cubic_gigameter = Unit(1e-15, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') +teragrams_per_cubic_gigameter = Unit(1e-18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') +gigagrams_per_cubic_gigameter = Unit(1e-21, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_gigameter', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') +megagrams_per_cubic_gigameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_gigameter', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') +kilograms_per_cubic_gigameter = Unit(1e-27, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_gigameter', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') +milligrams_per_cubic_gigameter = Unit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_gigameter', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') +micrograms_per_cubic_gigameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_gigameter', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') +nanograms_per_cubic_gigameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_gigameter', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') +picograms_per_cubic_gigameter = Unit(1e-42, Dimensions(length=-3, mass=1), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') +femtograms_per_cubic_gigameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') +attograms_per_cubic_gigameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') +atomic_mass_units_per_cubic_gigameter = Unit(1.6610000000000002e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='NoneGm⁻³') +grams_per_cubic_megameter = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') +exagrams_per_cubic_megameter = Unit(0.001, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') +petagrams_per_cubic_megameter = Unit(1e-06, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') +teragrams_per_cubic_megameter = Unit(1e-09, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') +gigagrams_per_cubic_megameter = Unit(1e-12, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_megameter', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') +megagrams_per_cubic_megameter = Unit(1e-15, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_megameter', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') +kilograms_per_cubic_megameter = Unit(1e-18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_megameter', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') +milligrams_per_cubic_megameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_megameter', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') +micrograms_per_cubic_megameter = Unit(1e-27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_megameter', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') +nanograms_per_cubic_megameter = Unit(1.0000000000000003e-30, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_megameter', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') +picograms_per_cubic_megameter = Unit(1e-33, Dimensions(length=-3, mass=1), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') +femtograms_per_cubic_megameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') +attograms_per_cubic_megameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') +atomic_mass_units_per_cubic_megameter = Unit(1.661e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='NoneMm⁻³') +grams_per_cubic_kilometer = Unit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') +exagrams_per_cubic_kilometer = Unit(1000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') +petagrams_per_cubic_kilometer = Unit(1000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') +teragrams_per_cubic_kilometer = Unit(1.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') +gigagrams_per_cubic_kilometer = Unit(0.001, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_kilometer', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') +megagrams_per_cubic_kilometer = Unit(1e-06, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_kilometer', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') +kilograms_per_cubic_kilometer = Unit(1e-09, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_kilometer', ascii_symbol='kg km^-3', symbol='kgkm⁻³') +milligrams_per_cubic_kilometer = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_kilometer', ascii_symbol='mg km^-3', symbol='mgkm⁻³') +micrograms_per_cubic_kilometer = Unit(1e-18, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_kilometer', ascii_symbol='ug km^-3', symbol='µgkm⁻³') +nanograms_per_cubic_kilometer = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_kilometer', ascii_symbol='ng km^-3', symbol='ngkm⁻³') +picograms_per_cubic_kilometer = Unit(1.0000000000000001e-24, Dimensions(length=-3, mass=1), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') +femtograms_per_cubic_kilometer = Unit(1e-27, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') +attograms_per_cubic_kilometer = Unit(1e-30, Dimensions(length=-3, mass=1), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') +atomic_mass_units_per_cubic_kilometer = Unit(1.661e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='Nonekm⁻³') +grams_per_cubic_millimeter = Unit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') +exagrams_per_cubic_millimeter = Unit(1e+24, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') +petagrams_per_cubic_millimeter = Unit(1e+21, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') +teragrams_per_cubic_millimeter = Unit(1e+18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') +gigagrams_per_cubic_millimeter = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_millimeter', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') +megagrams_per_cubic_millimeter = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_millimeter', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') +kilograms_per_cubic_millimeter = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_millimeter', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') +milligrams_per_cubic_millimeter = Unit(999.9999999999999, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_millimeter', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') +micrograms_per_cubic_millimeter = Unit(1.0, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_millimeter', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') +nanograms_per_cubic_millimeter = Unit(0.001, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_millimeter', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') +picograms_per_cubic_millimeter = Unit(1e-06, Dimensions(length=-3, mass=1), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') +femtograms_per_cubic_millimeter = Unit(1e-09, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') +attograms_per_cubic_millimeter = Unit(1e-12, Dimensions(length=-3, mass=1), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') +atomic_mass_units_per_cubic_millimeter = Unit(1.661e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='Nonemm⁻³') +grams_per_cubic_micrometer = Unit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') +exagrams_per_cubic_micrometer = Unit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') +petagrams_per_cubic_micrometer = Unit(1.0000000000000002e+30, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') +teragrams_per_cubic_micrometer = Unit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') +gigagrams_per_cubic_micrometer = Unit(1.0000000000000001e+24, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_micrometer', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') +megagrams_per_cubic_micrometer = Unit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_micrometer', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') +kilograms_per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_micrometer', ascii_symbol='kg um^-3', symbol='kgµm⁻³') +milligrams_per_cubic_micrometer = Unit(1000000000000.0001, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_micrometer', ascii_symbol='mg um^-3', symbol='mgµm⁻³') +micrograms_per_cubic_micrometer = Unit(1000000000.0000002, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_micrometer', ascii_symbol='ug um^-3', symbol='µgµm⁻³') +nanograms_per_cubic_micrometer = Unit(1000000.0000000003, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_micrometer', ascii_symbol='ng um^-3', symbol='ngµm⁻³') +picograms_per_cubic_micrometer = Unit(1000.0000000000002, Dimensions(length=-3, mass=1), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') +femtograms_per_cubic_micrometer = Unit(1.0000000000000002, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') +attograms_per_cubic_micrometer = Unit(0.0010000000000000002, Dimensions(length=-3, mass=1), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') +atomic_mass_units_per_cubic_micrometer = Unit(1.6610000000000004e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='Noneµm⁻³') +grams_per_cubic_nanometer = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') +exagrams_per_cubic_nanometer = Unit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') +petagrams_per_cubic_nanometer = Unit(9.999999999999998e+38, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') +teragrams_per_cubic_nanometer = Unit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') +gigagrams_per_cubic_nanometer = Unit(9.999999999999998e+32, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_nanometer', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') +megagrams_per_cubic_nanometer = Unit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_nanometer', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') +kilograms_per_cubic_nanometer = Unit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_nanometer', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') +milligrams_per_cubic_nanometer = Unit(9.999999999999997e+20, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_nanometer', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') +micrograms_per_cubic_nanometer = Unit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_nanometer', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') +nanograms_per_cubic_nanometer = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_nanometer', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') +picograms_per_cubic_nanometer = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') +femtograms_per_cubic_nanometer = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') +attograms_per_cubic_nanometer = Unit(999999.9999999999, Dimensions(length=-3, mass=1), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') +atomic_mass_units_per_cubic_nanometer = Unit(1.6609999999999998, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='Nonenm⁻³') +grams_per_cubic_picometer = Unit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') +exagrams_per_cubic_picometer = Unit(1e+51, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') +petagrams_per_cubic_picometer = Unit(1e+48, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') +teragrams_per_cubic_picometer = Unit(1.0000000000000001e+45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') +gigagrams_per_cubic_picometer = Unit(1e+42, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_picometer', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') +megagrams_per_cubic_picometer = Unit(1.0000000000000001e+39, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_picometer', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') +kilograms_per_cubic_picometer = Unit(1e+36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_picometer', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') +milligrams_per_cubic_picometer = Unit(1e+30, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_picometer', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') +micrograms_per_cubic_picometer = Unit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_picometer', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') +nanograms_per_cubic_picometer = Unit(1.0000000000000003e+24, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_picometer', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') +picograms_per_cubic_picometer = Unit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') +femtograms_per_cubic_picometer = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') +attograms_per_cubic_picometer = Unit(1000000000000000.1, Dimensions(length=-3, mass=1), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') +atomic_mass_units_per_cubic_picometer = Unit(1661000000.0000002, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='Nonepm⁻³') +grams_per_cubic_femtometer = Unit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') +exagrams_per_cubic_femtometer = Unit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') +petagrams_per_cubic_femtometer = Unit(9.999999999999997e+56, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') +teragrams_per_cubic_femtometer = Unit(9.999999999999997e+53, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') +gigagrams_per_cubic_femtometer = Unit(9.999999999999997e+50, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_femtometer', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') +megagrams_per_cubic_femtometer = Unit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_femtometer', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') +kilograms_per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_femtometer', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') +milligrams_per_cubic_femtometer = Unit(9.999999999999996e+38, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_femtometer', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') +micrograms_per_cubic_femtometer = Unit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_femtometer', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') +nanograms_per_cubic_femtometer = Unit(1e+33, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_femtometer', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') +picograms_per_cubic_femtometer = Unit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') +femtograms_per_cubic_femtometer = Unit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') +attograms_per_cubic_femtometer = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') +atomic_mass_units_per_cubic_femtometer = Unit(1.6609999999999997e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='Nonefm⁻³') +grams_per_cubic_attometer = Unit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') +exagrams_per_cubic_attometer = Unit(9.999999999999999e+68, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') +petagrams_per_cubic_attometer = Unit(9.999999999999998e+65, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') +teragrams_per_cubic_attometer = Unit(9.999999999999999e+62, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') +gigagrams_per_cubic_attometer = Unit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_attometer', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') +megagrams_per_cubic_attometer = Unit(9.999999999999999e+56, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_attometer', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') +kilograms_per_cubic_attometer = Unit(9.999999999999999e+53, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_attometer', ascii_symbol='kg am^-3', symbol='kgam⁻³') +milligrams_per_cubic_attometer = Unit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_attometer', ascii_symbol='mg am^-3', symbol='mgam⁻³') +micrograms_per_cubic_attometer = Unit(1e+45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_attometer', ascii_symbol='ug am^-3', symbol='µgam⁻³') +nanograms_per_cubic_attometer = Unit(1e+42, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_attometer', ascii_symbol='ng am^-3', symbol='ngam⁻³') +picograms_per_cubic_attometer = Unit(1e+39, Dimensions(length=-3, mass=1), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') +femtograms_per_cubic_attometer = Unit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') +attograms_per_cubic_attometer = Unit(1e+33, Dimensions(length=-3, mass=1), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') +atomic_mass_units_per_cubic_attometer = Unit(1.661e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='Noneam⁻³') +grams_per_cubic_decimeter = Unit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='Nonedm⁻³') +exagrams_per_cubic_decimeter = Unit(9.999999999999997e+17, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_decimeter', ascii_symbol='Eg dm^-3', symbol='Egdm⁻³') +petagrams_per_cubic_decimeter = Unit(999999999999999.8, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_decimeter', ascii_symbol='Pg dm^-3', symbol='Pgdm⁻³') +teragrams_per_cubic_decimeter = Unit(999999999999.9998, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_decimeter', ascii_symbol='Tg dm^-3', symbol='Tgdm⁻³') +gigagrams_per_cubic_decimeter = Unit(999999999.9999998, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_decimeter', ascii_symbol='Gg dm^-3', symbol='Ggdm⁻³') +megagrams_per_cubic_decimeter = Unit(999999.9999999998, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_decimeter', ascii_symbol='Mg dm^-3', symbol='Mgdm⁻³') +kilograms_per_cubic_decimeter = Unit(999.9999999999998, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_decimeter', ascii_symbol='kg dm^-3', symbol='kgdm⁻³') +milligrams_per_cubic_decimeter = Unit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_decimeter', ascii_symbol='mg dm^-3', symbol='mgdm⁻³') +micrograms_per_cubic_decimeter = Unit(9.999999999999997e-07, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_decimeter', ascii_symbol='ug dm^-3', symbol='µgdm⁻³') +nanograms_per_cubic_decimeter = Unit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_decimeter', ascii_symbol='ng dm^-3', symbol='ngdm⁻³') +picograms_per_cubic_decimeter = Unit(9.999999999999998e-13, Dimensions(length=-3, mass=1), name='picograms_per_cubic_decimeter', ascii_symbol='pg dm^-3', symbol='pgdm⁻³') +femtograms_per_cubic_decimeter = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_decimeter', ascii_symbol='fg dm^-3', symbol='fgdm⁻³') +attograms_per_cubic_decimeter = Unit(9.999999999999999e-19, Dimensions(length=-3, mass=1), name='attograms_per_cubic_decimeter', ascii_symbol='ag dm^-3', symbol='agdm⁻³') +atomic_mass_units_per_cubic_decimeter = Unit(1.661e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='Nonedm⁻³') +grams_per_cubic_centimeter = Unit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='Nonecm⁻³') +exagrams_per_cubic_centimeter = Unit(9.999999999999999e+20, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_centimeter', ascii_symbol='Eg cm^-3', symbol='Egcm⁻³') +petagrams_per_cubic_centimeter = Unit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_centimeter', ascii_symbol='Pg cm^-3', symbol='Pgcm⁻³') +teragrams_per_cubic_centimeter = Unit(999999999999999.9, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_centimeter', ascii_symbol='Tg cm^-3', symbol='Tgcm⁻³') +gigagrams_per_cubic_centimeter = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_centimeter', ascii_symbol='Gg cm^-3', symbol='Ggcm⁻³') +megagrams_per_cubic_centimeter = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_centimeter', ascii_symbol='Mg cm^-3', symbol='Mgcm⁻³') +kilograms_per_cubic_centimeter = Unit(999999.9999999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_centimeter', ascii_symbol='kg cm^-3', symbol='kgcm⁻³') +milligrams_per_cubic_centimeter = Unit(0.9999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_centimeter', ascii_symbol='mg cm^-3', symbol='mgcm⁻³') +micrograms_per_cubic_centimeter = Unit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_centimeter', ascii_symbol='ug cm^-3', symbol='µgcm⁻³') +nanograms_per_cubic_centimeter = Unit(1e-06, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_centimeter', ascii_symbol='ng cm^-3', symbol='ngcm⁻³') +picograms_per_cubic_centimeter = Unit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='picograms_per_cubic_centimeter', ascii_symbol='pg cm^-3', symbol='pgcm⁻³') +femtograms_per_cubic_centimeter = Unit(1e-12, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_centimeter', ascii_symbol='fg cm^-3', symbol='fgcm⁻³') +attograms_per_cubic_centimeter = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='attograms_per_cubic_centimeter', ascii_symbol='ag cm^-3', symbol='agcm⁻³') +atomic_mass_units_per_cubic_centimeter = Unit(1.6609999999999998e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='Nonecm⁻³') +grams_per_cubic_angstrom = Unit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') +exagrams_per_cubic_angstrom = Unit(1e+45, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') +petagrams_per_cubic_angstrom = Unit(9.999999999999999e+41, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') +teragrams_per_cubic_angstrom = Unit(1e+39, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') +gigagrams_per_cubic_angstrom = Unit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_angstrom', ascii_symbol='Gg Ang^-3', symbol='GgÅ⁻³') +megagrams_per_cubic_angstrom = Unit(1e+33, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_angstrom', ascii_symbol='Mg Ang^-3', symbol='MgÅ⁻³') +kilograms_per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_angstrom', ascii_symbol='kg Ang^-3', symbol='kgÅ⁻³') +milligrams_per_cubic_angstrom = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_angstrom', ascii_symbol='mg Ang^-3', symbol='mgÅ⁻³') +micrograms_per_cubic_angstrom = Unit(1e+21, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_angstrom', ascii_symbol='ug Ang^-3', symbol='µgÅ⁻³') +nanograms_per_cubic_angstrom = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_angstrom', ascii_symbol='ng Ang^-3', symbol='ngÅ⁻³') +picograms_per_cubic_angstrom = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') +femtograms_per_cubic_angstrom = Unit(1000000000000.0, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') +attograms_per_cubic_angstrom = Unit(1000000000.0, Dimensions(length=-3, mass=1), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') +atomic_mass_units_per_cubic_angstrom = Unit(1661.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='NoneÅ⁻³') +moles_per_cubic_meter = Unit(6.022e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_meter = Unit(6.022e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_meter = Unit(6.022e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_meter = Unit(602200000000000.1, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_meter = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_meter = Unit(602200000.0000001, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_meter = Unit(602200.0000000001, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolNone⁻³') +moles_per_cubic_exameter = Unit(6.022e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='NoneEm⁻³') +millimoles_per_cubic_exameter = Unit(6.021999999999999e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') +micromoles_per_cubic_exameter = Unit(6.022e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') +nanomoles_per_cubic_exameter = Unit(6.022000000000001e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') +picomoles_per_cubic_exameter = Unit(6.022e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') +femtomoles_per_cubic_exameter = Unit(6.022e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') +attomoles_per_cubic_exameter = Unit(6.0220000000000006e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') +moles_per_cubic_petameter = Unit(6.022e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='NonePm⁻³') +millimoles_per_cubic_petameter = Unit(6.022e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') +micromoles_per_cubic_petameter = Unit(6.022e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') +nanomoles_per_cubic_petameter = Unit(6.0220000000000015e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') +picomoles_per_cubic_petameter = Unit(6.022e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') +femtomoles_per_cubic_petameter = Unit(6.022000000000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') +attomoles_per_cubic_petameter = Unit(6.0220000000000015e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') +moles_per_cubic_terameter = Unit(6.022e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='NoneTm⁻³') +millimoles_per_cubic_terameter = Unit(6.022e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') +micromoles_per_cubic_terameter = Unit(6.022e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') +nanomoles_per_cubic_terameter = Unit(6.022000000000001e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') +picomoles_per_cubic_terameter = Unit(6.0219999999999995e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') +femtomoles_per_cubic_terameter = Unit(6.022000000000001e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') +attomoles_per_cubic_terameter = Unit(6.022000000000001e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') +moles_per_cubic_gigameter = Unit(0.0006022, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='NoneGm⁻³') +millimoles_per_cubic_gigameter = Unit(6.022e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') +micromoles_per_cubic_gigameter = Unit(6.021999999999999e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') +nanomoles_per_cubic_gigameter = Unit(6.022000000000001e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') +picomoles_per_cubic_gigameter = Unit(6.022e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') +femtomoles_per_cubic_gigameter = Unit(6.022000000000002e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') +attomoles_per_cubic_gigameter = Unit(6.022000000000001e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') +moles_per_cubic_megameter = Unit(602200.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='NoneMm⁻³') +millimoles_per_cubic_megameter = Unit(602.2, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') +micromoles_per_cubic_megameter = Unit(0.6022, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') +nanomoles_per_cubic_megameter = Unit(0.0006022000000000001, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') +picomoles_per_cubic_megameter = Unit(6.022e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') +femtomoles_per_cubic_megameter = Unit(6.022000000000002e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') +attomoles_per_cubic_megameter = Unit(6.022000000000001e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') +moles_per_cubic_kilometer = Unit(602200000000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='Nonekm⁻³') +millimoles_per_cubic_kilometer = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') +micromoles_per_cubic_kilometer = Unit(602200000.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') +nanomoles_per_cubic_kilometer = Unit(602200.0000000001, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') +picomoles_per_cubic_kilometer = Unit(602.2, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') +femtomoles_per_cubic_kilometer = Unit(0.6022000000000001, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') +attomoles_per_cubic_kilometer = Unit(0.0006022000000000001, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') +moles_per_cubic_millimeter = Unit(6.022e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='Nonemm⁻³') +millimoles_per_cubic_millimeter = Unit(6.0219999999999996e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') +micromoles_per_cubic_millimeter = Unit(6.022e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') +nanomoles_per_cubic_millimeter = Unit(6.022000000000001e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') +picomoles_per_cubic_millimeter = Unit(6.022e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') +femtomoles_per_cubic_millimeter = Unit(6.022000000000001e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') +attomoles_per_cubic_millimeter = Unit(602200000000000.1, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') +moles_per_cubic_micrometer = Unit(6.022000000000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='Noneµm⁻³') +millimoles_per_cubic_micrometer = Unit(6.0220000000000006e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') +micromoles_per_cubic_micrometer = Unit(6.022000000000001e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') +nanomoles_per_cubic_micrometer = Unit(6.022000000000002e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') +picomoles_per_cubic_micrometer = Unit(6.022000000000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') +femtomoles_per_cubic_micrometer = Unit(6.022000000000002e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') +attomoles_per_cubic_micrometer = Unit(6.0220000000000016e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') +moles_per_cubic_nanometer = Unit(6.021999999999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='Nonenm⁻³') +millimoles_per_cubic_nanometer = Unit(6.0219999999999984e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') +micromoles_per_cubic_nanometer = Unit(6.0219999999999985e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') +nanomoles_per_cubic_nanometer = Unit(6.022e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') +picomoles_per_cubic_nanometer = Unit(6.021999999999999e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') +femtomoles_per_cubic_nanometer = Unit(6.0219999999999995e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') +attomoles_per_cubic_nanometer = Unit(6.022e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') +moles_per_cubic_picometer = Unit(6.022e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='Nonepm⁻³') +millimoles_per_cubic_picometer = Unit(6.0220000000000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') +micromoles_per_cubic_picometer = Unit(6.0220000000000005e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') +nanomoles_per_cubic_picometer = Unit(6.022000000000002e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') +picomoles_per_cubic_picometer = Unit(6.022e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') +femtomoles_per_cubic_picometer = Unit(6.022000000000002e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') +attomoles_per_cubic_picometer = Unit(6.022000000000002e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') +moles_per_cubic_femtometer = Unit(6.021999999999999e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='Nonefm⁻³') +millimoles_per_cubic_femtometer = Unit(6.021999999999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') +micromoles_per_cubic_femtometer = Unit(6.021999999999998e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') +nanomoles_per_cubic_femtometer = Unit(6.021999999999999e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') +picomoles_per_cubic_femtometer = Unit(6.021999999999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') +femtomoles_per_cubic_femtometer = Unit(6.022e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') +attomoles_per_cubic_femtometer = Unit(6.021999999999999e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') +moles_per_cubic_attometer = Unit(6.021999999999999e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='Noneam⁻³') +millimoles_per_cubic_attometer = Unit(6.021999999999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') +micromoles_per_cubic_attometer = Unit(6.021999999999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') +nanomoles_per_cubic_attometer = Unit(6.022000000000001e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') +picomoles_per_cubic_attometer = Unit(6.021999999999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') +femtomoles_per_cubic_attometer = Unit(6.022e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') +attomoles_per_cubic_attometer = Unit(6.022e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') +moles_per_cubic_decimeter = Unit(6.0219999999999985e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='Nonedm⁻³') +millimoles_per_cubic_decimeter = Unit(6.021999999999998e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') +micromoles_per_cubic_decimeter = Unit(6.021999999999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') +nanomoles_per_cubic_decimeter = Unit(6.022e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') +picomoles_per_cubic_decimeter = Unit(602199999999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') +femtomoles_per_cubic_decimeter = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') +attomoles_per_cubic_decimeter = Unit(602200000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') +moles_per_cubic_centimeter = Unit(6.0219999999999996e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='Nonecm⁻³') +millimoles_per_cubic_centimeter = Unit(6.021999999999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') +micromoles_per_cubic_centimeter = Unit(6.021999999999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') +nanomoles_per_cubic_centimeter = Unit(6.022e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') +picomoles_per_cubic_centimeter = Unit(6.021999999999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') +femtomoles_per_cubic_centimeter = Unit(602200000000000.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') +attomoles_per_cubic_centimeter = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') +moles_per_cubic_angstrom = Unit(6.022e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='NoneÅ⁻³') +millimoles_per_cubic_angstrom = Unit(6.021999999999999e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') +micromoles_per_cubic_angstrom = Unit(6.021999999999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') +nanomoles_per_cubic_angstrom = Unit(6.022000000000001e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') +picomoles_per_cubic_angstrom = Unit(6.0219999999999994e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') +femtomoles_per_cubic_angstrom = Unit(6.0220000000000006e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') +attomoles_per_cubic_angstrom = Unit(6.022000000000001e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') # # Lookup table from symbols to units @@ -1006,6 +1313,8 @@ def __init__(self, name: str, units: list[Unit]): "pm": picometers, "fm": femtometers, "am": attometers, + "dm": decimeters, + "cm": centimeters, "s": seconds, "ms": milliseconds, "us": microseconds, @@ -1260,11 +1569,37 @@ def __init__(self, name: str, units: list[Unit]): "rad": radians, "sr": stradians, "none": none, + "l": litres, + "eV": electronvolts, + "EeV": exaelectronvolts, + "PeV": petaelectronvolts, + "TeV": teraelectronvolts, + "GeV": gigaelectronvolts, + "MeV": megaelectronvolts, + "keV": kiloelectronvolts, + "meV": millielectronvolts, + "ueV": microelectronvolts, + "µeV": microelectronvolts, + "neV": nanoelectronvolts, + "peV": picoelectronvolts, + "feV": femtoelectronvolts, + "aeV": attoelectronvolts, + "au": atomic_mass_units, + "mol": moles, + "mmol": millimoles, + "umol": micromoles, + "µmol": micromoles, + "nmol": nanomoles, + "pmol": picomoles, + "fmol": femtomoles, + "amol": attomoles, "yr": years, "year": years, "day": days, "hr": hours, "hour": hours, + "a.u.": atomic_mass_units, + "amu": atomic_mass_units, } @@ -1289,6 +1624,8 @@ def __init__(self, name: str, units: list[Unit]): picometers, femtometers, attometers, + decimeters, + centimeters, angstroms, ]) @@ -1308,12 +1645,15 @@ def __init__(self, name: str, units: list[Unit]): square_picometers, square_femtometers, square_attometers, + square_decimeters, + square_centimeters, square_angstroms, ]) volume = UnitGroup( name = 'volume', units = [ + litres, cubic_meters, cubic_exameters, cubic_petameters, @@ -1327,6 +1667,8 @@ def __init__(self, name: str, units: list[Unit]): cubic_picometers, cubic_femtometers, cubic_attometers, + cubic_decimeters, + cubic_centimeters, cubic_angstroms, ]) @@ -1346,6 +1688,8 @@ def __init__(self, name: str, units: list[Unit]): per_picometer, per_femtometer, per_attometer, + per_decimeter, + per_centimeter, per_angstrom, ]) @@ -1365,6 +1709,8 @@ def __init__(self, name: str, units: list[Unit]): per_square_picometer, per_square_femtometer, per_square_attometer, + per_square_decimeter, + per_square_centimeter, per_square_angstrom, ]) @@ -1384,6 +1730,8 @@ def __init__(self, name: str, units: list[Unit]): per_cubic_picometer, per_cubic_femtometer, per_cubic_attometer, + per_cubic_decimeter, + per_cubic_centimeter, per_cubic_angstrom, ]) @@ -1568,6 +1916,28 @@ def __init__(self, name: str, units: list[Unit]): attometers_per_hour, attometers_per_day, attometers_per_year, + decimeters_per_second, + decimeters_per_millisecond, + decimeters_per_microsecond, + decimeters_per_nanosecond, + decimeters_per_picosecond, + decimeters_per_femtosecond, + decimeters_per_attosecond, + decimeters_per_minute, + decimeters_per_hour, + decimeters_per_day, + decimeters_per_year, + centimeters_per_second, + centimeters_per_millisecond, + centimeters_per_microsecond, + centimeters_per_nanosecond, + centimeters_per_picosecond, + centimeters_per_femtosecond, + centimeters_per_attosecond, + centimeters_per_minute, + centimeters_per_hour, + centimeters_per_day, + centimeters_per_year, angstroms_per_second, angstroms_per_millisecond, angstroms_per_microsecond, @@ -1727,6 +2097,28 @@ def __init__(self, name: str, units: list[Unit]): attometers_per_square_hour, attometers_per_square_day, attometers_per_square_year, + decimeters_per_square_second, + decimeters_per_square_millisecond, + decimeters_per_square_microsecond, + decimeters_per_square_nanosecond, + decimeters_per_square_picosecond, + decimeters_per_square_femtosecond, + decimeters_per_square_attosecond, + decimeters_per_square_minute, + decimeters_per_square_hour, + decimeters_per_square_day, + decimeters_per_square_year, + centimeters_per_square_second, + centimeters_per_square_millisecond, + centimeters_per_square_microsecond, + centimeters_per_square_nanosecond, + centimeters_per_square_picosecond, + centimeters_per_square_femtosecond, + centimeters_per_square_attosecond, + centimeters_per_square_minute, + centimeters_per_square_hour, + centimeters_per_square_day, + centimeters_per_square_year, angstroms_per_square_second, angstroms_per_square_millisecond, angstroms_per_square_microsecond, @@ -1743,6 +2135,230 @@ def __init__(self, name: str, units: list[Unit]): density = UnitGroup( name = 'density', units = [ + grams_per_cubic_meter, + exagrams_per_cubic_meter, + petagrams_per_cubic_meter, + teragrams_per_cubic_meter, + gigagrams_per_cubic_meter, + megagrams_per_cubic_meter, + kilograms_per_cubic_meter, + milligrams_per_cubic_meter, + micrograms_per_cubic_meter, + nanograms_per_cubic_meter, + picograms_per_cubic_meter, + femtograms_per_cubic_meter, + attograms_per_cubic_meter, + atomic_mass_units_per_cubic_meter, + grams_per_cubic_exameter, + exagrams_per_cubic_exameter, + petagrams_per_cubic_exameter, + teragrams_per_cubic_exameter, + gigagrams_per_cubic_exameter, + megagrams_per_cubic_exameter, + kilograms_per_cubic_exameter, + milligrams_per_cubic_exameter, + micrograms_per_cubic_exameter, + nanograms_per_cubic_exameter, + picograms_per_cubic_exameter, + femtograms_per_cubic_exameter, + attograms_per_cubic_exameter, + atomic_mass_units_per_cubic_exameter, + grams_per_cubic_petameter, + exagrams_per_cubic_petameter, + petagrams_per_cubic_petameter, + teragrams_per_cubic_petameter, + gigagrams_per_cubic_petameter, + megagrams_per_cubic_petameter, + kilograms_per_cubic_petameter, + milligrams_per_cubic_petameter, + micrograms_per_cubic_petameter, + nanograms_per_cubic_petameter, + picograms_per_cubic_petameter, + femtograms_per_cubic_petameter, + attograms_per_cubic_petameter, + atomic_mass_units_per_cubic_petameter, + grams_per_cubic_terameter, + exagrams_per_cubic_terameter, + petagrams_per_cubic_terameter, + teragrams_per_cubic_terameter, + gigagrams_per_cubic_terameter, + megagrams_per_cubic_terameter, + kilograms_per_cubic_terameter, + milligrams_per_cubic_terameter, + micrograms_per_cubic_terameter, + nanograms_per_cubic_terameter, + picograms_per_cubic_terameter, + femtograms_per_cubic_terameter, + attograms_per_cubic_terameter, + atomic_mass_units_per_cubic_terameter, + grams_per_cubic_gigameter, + exagrams_per_cubic_gigameter, + petagrams_per_cubic_gigameter, + teragrams_per_cubic_gigameter, + gigagrams_per_cubic_gigameter, + megagrams_per_cubic_gigameter, + kilograms_per_cubic_gigameter, + milligrams_per_cubic_gigameter, + micrograms_per_cubic_gigameter, + nanograms_per_cubic_gigameter, + picograms_per_cubic_gigameter, + femtograms_per_cubic_gigameter, + attograms_per_cubic_gigameter, + atomic_mass_units_per_cubic_gigameter, + grams_per_cubic_megameter, + exagrams_per_cubic_megameter, + petagrams_per_cubic_megameter, + teragrams_per_cubic_megameter, + gigagrams_per_cubic_megameter, + megagrams_per_cubic_megameter, + kilograms_per_cubic_megameter, + milligrams_per_cubic_megameter, + micrograms_per_cubic_megameter, + nanograms_per_cubic_megameter, + picograms_per_cubic_megameter, + femtograms_per_cubic_megameter, + attograms_per_cubic_megameter, + atomic_mass_units_per_cubic_megameter, + grams_per_cubic_kilometer, + exagrams_per_cubic_kilometer, + petagrams_per_cubic_kilometer, + teragrams_per_cubic_kilometer, + gigagrams_per_cubic_kilometer, + megagrams_per_cubic_kilometer, + kilograms_per_cubic_kilometer, + milligrams_per_cubic_kilometer, + micrograms_per_cubic_kilometer, + nanograms_per_cubic_kilometer, + picograms_per_cubic_kilometer, + femtograms_per_cubic_kilometer, + attograms_per_cubic_kilometer, + atomic_mass_units_per_cubic_kilometer, + grams_per_cubic_millimeter, + exagrams_per_cubic_millimeter, + petagrams_per_cubic_millimeter, + teragrams_per_cubic_millimeter, + gigagrams_per_cubic_millimeter, + megagrams_per_cubic_millimeter, + kilograms_per_cubic_millimeter, + milligrams_per_cubic_millimeter, + micrograms_per_cubic_millimeter, + nanograms_per_cubic_millimeter, + picograms_per_cubic_millimeter, + femtograms_per_cubic_millimeter, + attograms_per_cubic_millimeter, + atomic_mass_units_per_cubic_millimeter, + grams_per_cubic_micrometer, + exagrams_per_cubic_micrometer, + petagrams_per_cubic_micrometer, + teragrams_per_cubic_micrometer, + gigagrams_per_cubic_micrometer, + megagrams_per_cubic_micrometer, + kilograms_per_cubic_micrometer, + milligrams_per_cubic_micrometer, + micrograms_per_cubic_micrometer, + nanograms_per_cubic_micrometer, + picograms_per_cubic_micrometer, + femtograms_per_cubic_micrometer, + attograms_per_cubic_micrometer, + atomic_mass_units_per_cubic_micrometer, + grams_per_cubic_nanometer, + exagrams_per_cubic_nanometer, + petagrams_per_cubic_nanometer, + teragrams_per_cubic_nanometer, + gigagrams_per_cubic_nanometer, + megagrams_per_cubic_nanometer, + kilograms_per_cubic_nanometer, + milligrams_per_cubic_nanometer, + micrograms_per_cubic_nanometer, + nanograms_per_cubic_nanometer, + picograms_per_cubic_nanometer, + femtograms_per_cubic_nanometer, + attograms_per_cubic_nanometer, + atomic_mass_units_per_cubic_nanometer, + grams_per_cubic_picometer, + exagrams_per_cubic_picometer, + petagrams_per_cubic_picometer, + teragrams_per_cubic_picometer, + gigagrams_per_cubic_picometer, + megagrams_per_cubic_picometer, + kilograms_per_cubic_picometer, + milligrams_per_cubic_picometer, + micrograms_per_cubic_picometer, + nanograms_per_cubic_picometer, + picograms_per_cubic_picometer, + femtograms_per_cubic_picometer, + attograms_per_cubic_picometer, + atomic_mass_units_per_cubic_picometer, + grams_per_cubic_femtometer, + exagrams_per_cubic_femtometer, + petagrams_per_cubic_femtometer, + teragrams_per_cubic_femtometer, + gigagrams_per_cubic_femtometer, + megagrams_per_cubic_femtometer, + kilograms_per_cubic_femtometer, + milligrams_per_cubic_femtometer, + micrograms_per_cubic_femtometer, + nanograms_per_cubic_femtometer, + picograms_per_cubic_femtometer, + femtograms_per_cubic_femtometer, + attograms_per_cubic_femtometer, + atomic_mass_units_per_cubic_femtometer, + grams_per_cubic_attometer, + exagrams_per_cubic_attometer, + petagrams_per_cubic_attometer, + teragrams_per_cubic_attometer, + gigagrams_per_cubic_attometer, + megagrams_per_cubic_attometer, + kilograms_per_cubic_attometer, + milligrams_per_cubic_attometer, + micrograms_per_cubic_attometer, + nanograms_per_cubic_attometer, + picograms_per_cubic_attometer, + femtograms_per_cubic_attometer, + attograms_per_cubic_attometer, + atomic_mass_units_per_cubic_attometer, + grams_per_cubic_decimeter, + exagrams_per_cubic_decimeter, + petagrams_per_cubic_decimeter, + teragrams_per_cubic_decimeter, + gigagrams_per_cubic_decimeter, + megagrams_per_cubic_decimeter, + kilograms_per_cubic_decimeter, + milligrams_per_cubic_decimeter, + micrograms_per_cubic_decimeter, + nanograms_per_cubic_decimeter, + picograms_per_cubic_decimeter, + femtograms_per_cubic_decimeter, + attograms_per_cubic_decimeter, + atomic_mass_units_per_cubic_decimeter, + grams_per_cubic_centimeter, + exagrams_per_cubic_centimeter, + petagrams_per_cubic_centimeter, + teragrams_per_cubic_centimeter, + gigagrams_per_cubic_centimeter, + megagrams_per_cubic_centimeter, + kilograms_per_cubic_centimeter, + milligrams_per_cubic_centimeter, + micrograms_per_cubic_centimeter, + nanograms_per_cubic_centimeter, + picograms_per_cubic_centimeter, + femtograms_per_cubic_centimeter, + attograms_per_cubic_centimeter, + atomic_mass_units_per_cubic_centimeter, + grams_per_cubic_angstrom, + exagrams_per_cubic_angstrom, + petagrams_per_cubic_angstrom, + teragrams_per_cubic_angstrom, + gigagrams_per_cubic_angstrom, + megagrams_per_cubic_angstrom, + kilograms_per_cubic_angstrom, + milligrams_per_cubic_angstrom, + micrograms_per_cubic_angstrom, + nanograms_per_cubic_angstrom, + picograms_per_cubic_angstrom, + femtograms_per_cubic_angstrom, + attograms_per_cubic_angstrom, + atomic_mass_units_per_cubic_angstrom, ]) force = UnitGroup( @@ -1797,6 +2413,19 @@ def __init__(self, name: str, units: list[Unit]): picojoules, femtojoules, attojoules, + electronvolts, + exaelectronvolts, + petaelectronvolts, + teraelectronvolts, + gigaelectronvolts, + megaelectronvolts, + kiloelectronvolts, + millielectronvolts, + microelectronvolts, + nanoelectronvolts, + picoelectronvolts, + femtoelectronvolts, + attoelectronvolts, ]) power = UnitGroup( @@ -1982,9 +2611,148 @@ def __init__(self, name: str, units: list[Unit]): dimensionless = UnitGroup( name = 'dimensionless', + units = [ + none, +]) + +angle = UnitGroup( + name = 'angle', units = [ degrees, radians, +]) + +solid_angle = UnitGroup( + name = 'solid_angle', + units = [ stradians, - none, +]) + +amount = UnitGroup( + name = 'amount', + units = [ + moles, + millimoles, + micromoles, + nanomoles, + picomoles, + femtomoles, + attomoles, +]) + +concentration = UnitGroup( + name = 'concentration', + units = [ + moles_per_cubic_meter, + millimoles_per_cubic_meter, + micromoles_per_cubic_meter, + nanomoles_per_cubic_meter, + picomoles_per_cubic_meter, + femtomoles_per_cubic_meter, + attomoles_per_cubic_meter, + moles_per_cubic_exameter, + millimoles_per_cubic_exameter, + micromoles_per_cubic_exameter, + nanomoles_per_cubic_exameter, + picomoles_per_cubic_exameter, + femtomoles_per_cubic_exameter, + attomoles_per_cubic_exameter, + moles_per_cubic_petameter, + millimoles_per_cubic_petameter, + micromoles_per_cubic_petameter, + nanomoles_per_cubic_petameter, + picomoles_per_cubic_petameter, + femtomoles_per_cubic_petameter, + attomoles_per_cubic_petameter, + moles_per_cubic_terameter, + millimoles_per_cubic_terameter, + micromoles_per_cubic_terameter, + nanomoles_per_cubic_terameter, + picomoles_per_cubic_terameter, + femtomoles_per_cubic_terameter, + attomoles_per_cubic_terameter, + moles_per_cubic_gigameter, + millimoles_per_cubic_gigameter, + micromoles_per_cubic_gigameter, + nanomoles_per_cubic_gigameter, + picomoles_per_cubic_gigameter, + femtomoles_per_cubic_gigameter, + attomoles_per_cubic_gigameter, + moles_per_cubic_megameter, + millimoles_per_cubic_megameter, + micromoles_per_cubic_megameter, + nanomoles_per_cubic_megameter, + picomoles_per_cubic_megameter, + femtomoles_per_cubic_megameter, + attomoles_per_cubic_megameter, + moles_per_cubic_kilometer, + millimoles_per_cubic_kilometer, + micromoles_per_cubic_kilometer, + nanomoles_per_cubic_kilometer, + picomoles_per_cubic_kilometer, + femtomoles_per_cubic_kilometer, + attomoles_per_cubic_kilometer, + moles_per_cubic_millimeter, + millimoles_per_cubic_millimeter, + micromoles_per_cubic_millimeter, + nanomoles_per_cubic_millimeter, + picomoles_per_cubic_millimeter, + femtomoles_per_cubic_millimeter, + attomoles_per_cubic_millimeter, + moles_per_cubic_micrometer, + millimoles_per_cubic_micrometer, + micromoles_per_cubic_micrometer, + nanomoles_per_cubic_micrometer, + picomoles_per_cubic_micrometer, + femtomoles_per_cubic_micrometer, + attomoles_per_cubic_micrometer, + moles_per_cubic_nanometer, + millimoles_per_cubic_nanometer, + micromoles_per_cubic_nanometer, + nanomoles_per_cubic_nanometer, + picomoles_per_cubic_nanometer, + femtomoles_per_cubic_nanometer, + attomoles_per_cubic_nanometer, + moles_per_cubic_picometer, + millimoles_per_cubic_picometer, + micromoles_per_cubic_picometer, + nanomoles_per_cubic_picometer, + picomoles_per_cubic_picometer, + femtomoles_per_cubic_picometer, + attomoles_per_cubic_picometer, + moles_per_cubic_femtometer, + millimoles_per_cubic_femtometer, + micromoles_per_cubic_femtometer, + nanomoles_per_cubic_femtometer, + picomoles_per_cubic_femtometer, + femtomoles_per_cubic_femtometer, + attomoles_per_cubic_femtometer, + moles_per_cubic_attometer, + millimoles_per_cubic_attometer, + micromoles_per_cubic_attometer, + nanomoles_per_cubic_attometer, + picomoles_per_cubic_attometer, + femtomoles_per_cubic_attometer, + attomoles_per_cubic_attometer, + moles_per_cubic_decimeter, + millimoles_per_cubic_decimeter, + micromoles_per_cubic_decimeter, + nanomoles_per_cubic_decimeter, + picomoles_per_cubic_decimeter, + femtomoles_per_cubic_decimeter, + attomoles_per_cubic_decimeter, + moles_per_cubic_centimeter, + millimoles_per_cubic_centimeter, + micromoles_per_cubic_centimeter, + nanomoles_per_cubic_centimeter, + picomoles_per_cubic_centimeter, + femtomoles_per_cubic_centimeter, + attomoles_per_cubic_centimeter, + moles_per_cubic_angstrom, + millimoles_per_cubic_angstrom, + micromoles_per_cubic_angstrom, + nanomoles_per_cubic_angstrom, + picomoles_per_cubic_angstrom, + femtomoles_per_cubic_angstrom, + attomoles_per_cubic_angstrom, ]) From ebf6aa1b2bb67d7e9d8003a3edbc482698b4fb5f Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 13:54:18 +0100 Subject: [PATCH 495/675] Units and accessors draft ready to begin tests on --- sasdata/data.py | 2 +- sasdata/metadata.py | 2 +- sasdata/quantities/_accessor_base.py | 150 +- sasdata/quantities/_build_tables.py | 136 +- sasdata/quantities/_units_base.py | 6 + sasdata/quantities/_units_table.py | 333 -- sasdata/quantities/accessors.py | 8147 ++++---------------------- sasdata/quantities/quantities.py | 53 - sasdata/quantities/quantity.py | 1510 +---- sasdata/quantities/units.py | 388 +- sasdata/transforms/operation.py | 2 +- 11 files changed, 1336 insertions(+), 9393 deletions(-) delete mode 100644 sasdata/quantities/_units_table.py delete mode 100644 sasdata/quantities/quantities.py diff --git a/sasdata/data.py b/sasdata/data.py index fe47552cc..2c3dfad37 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from quantities.quantities import Quantity, NamedQuantity +from quantities.quantity import Quantity, NamedQuantity from sasdata.metadata import MetaData import numpy as np diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 8c0a28258..28b94d42e 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -2,7 +2,7 @@ from numpy._typing import ArrayLike -from sasdata.quantities.quantities import Unit, Quantity +from sasdata.quantities.quantity import Unit, Quantity def __init__(self, target_object: AccessorTarget): diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index b56ecce68..c887b7242 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -1,153 +1,17 @@ -from typing import TypeVar, Sequence +from typing import TypeVar from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units -from sasdata.quantities.units import Dimensions, Unit -from sasdata.quantities.unit_parser import parse_unit, parse_unit_from_group -from sasdata.data_backing import Group, Dataset -import logging -# logger = logging.getLogger("Accessors") -class LoggerDummy: - def info(self, data): - print(data) -logger = LoggerDummy() +T = TypeVar("T") -DataType = TypeVar("DataType") -OutputType = TypeVar("OutputType") - - -class AccessorTarget: - def __init__(self, data: Group, verbose=False, prefix_tokens: tuple=()): - self._data = data - self.verbose = verbose - - self.prefix_tokens = list(prefix_tokens) - - def with_path_prefix(self, path_prexix: str): - """ Get an accessor that looks at a subtree of the metadata with the supplied prefix - - For example, accessors aiming at a.b, when the target it c.d will look at c.d.a.b - """ - return AccessorTarget(self._data, - verbose=self.verbose, - prefix_tokens=tuple(self.prefix_tokens + [path_prexix])) - - def get_value(self, path: str): - - tokens = self.prefix_tokens + path.split(".") - - if self.verbose: - logger.info(f"Finding: {path}") - logger.info(f"Full path: {tokens}") - - # Navigate the tree from the entry we need - - current_tree_position: Group | Dataset = self._data - - for token in tokens: - - options = token.split("|") - - if isinstance(current_tree_position, Group): - - found = False - for option in options: - if option in current_tree_position.children: - current_tree_position = current_tree_position.children[option] - found = True - - if self.verbose: - logger.info(f"Found option: {option}") - - if not found: - if self.verbose: - logger.info(f"Failed to find any of {options} on group {current_tree_position.name}. Options: " + - ",".join([key for key in current_tree_position.children])) - return None - - elif isinstance(current_tree_position, Dataset): - - found = False - for option in options: - if option in current_tree_position.attributes: - current_tree_position = current_tree_position.attributes[option] - found = True - - if self.verbose: - logger.info(f"Found option: {option}") - - if not found: - if self.verbose: - logger.info(f"Failed to find any of {options} on attribute {current_tree_position.name}. Options: " + - ",".join([key for key in current_tree_position.attributes])) - return None - - if self.verbose: - logger.info(f"Found value: {current_tree_position}") - - return current_tree_position.data - - - -class Accessor[DataType, OutputType]: +class Accessor[T]: """ Base class """ - def __init__(self, target_object: AccessorTarget, value_target: str): - self.target_object = target_object - self.value_target = value_target - - @property - def value(self) -> OutputType | None: - return self.target_object.get_value(self.value_target) - -class StringAccessor(Accessor[str, str]): - """ String based fields """ - @property - def value(self) -> str | None: - return self.target_object.get_value(self.value_target) - -class FloatAccessor(Accessor[float, float]): - """ Float based fields """ - @property - def value(self) -> float | None: - return self.target_object.get_value(self.value_target) - - - - -class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): - """ Base class for accessors that work with quantities that have units """ - def __init__(self, target_object: AccessorTarget, value_target: str, unit_target: str, default_unit=units.none): - super().__init__(target_object, value_target) + def __init__(self, value_target: str, unit_target: str): + self._value_target = value_target self._unit_target = unit_target - self.default_unit = default_unit - - def _numerical_part(self) -> DataType | None: - """ Numerical part of the data """ - return self.target_object.get_value(self.value_target) - - def _unit_part(self) -> str | None: - """ String form of units for the data """ - return self.target_object.get_value(self._unit_target) @property - def unit(self) -> Unit: - u = self._unit_part() - if u is None: - return self.default_unit - else: - return parse_unit(u) - - @property - def value(self) -> Quantity[DataType] | None: - if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self._numerical_part(), self.unit) - return None - - @property - def quantity(self): - if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self._numerical_part(), self.unit) - return None - + def quantity(self) -> Quantity[T]: + raise NotImplementedError("Not implemented yet") diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index cf7f3cc57..bd7495d01 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -35,7 +35,7 @@ ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, 0, 0, all_magnitudes + unusual_magnitudes), ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, 0, 0, smaller_magnitudes), ("g", None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, 0, 0, all_magnitudes), - ("A", None, "ampere", "amperes", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), + ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, 0, 0, all_magnitudes) ] derived_si_units = [ @@ -52,9 +52,10 @@ ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, 0, 0, all_magnitudes), ("T", None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, 0, 0, all_magnitudes), ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), + ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []) ] -non_si_dimensioned_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ +non_si_units = [ ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, 0, 0, []), ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, 0, 0, []), ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, 0, 0, []), @@ -63,54 +64,22 @@ ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, 0, 1, []), ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, 0, 1, []), ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, 0, 2, []), + ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), ("l", None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), ("eV", None, "electronvolt", "electronvolts", 1.602176634e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), ("au", None, "atomic mass unit", "atomic mass units", 1.660538921e-27, 0, 0, 1, 0, 0, 0, 0, []), - ("mol", None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes), - ("kgForce", None, "kg force", "kg force", 9.80665, 1, -2, 1, 0, 0, 0, 0, []), - ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []), - ("miles", None, "mile", "miles", 1760*3*0.3048, 1, 0, 0, 0, 0, 0, 0, []), - ("yrd", None, "yard", "yards", 3*0.3048, 1, 0, 0, 0, 0, 0, 0, []), - ("ft", None, "foot", "feet", 0.3048, 1, 0, 0, 0, 0, 0, 0, []), - ("in", None, "inch", "inches", 0.0254, 1, 0, 0, 0, 0, 0, 0, []), - ("lb", None, "pound", "pounds", 0.45359237, 0, 0, 1, 0, 0, 0, 0, []), - ("lbf", None, "pound force", "pounds force", 4.448222, 1, -2, 1, 0, 0, 0, 0, []), - ("oz", None, "ounce", "ounces", 0.45359237/16, 0, 0, 1, 0, 0, 0, 0, []), - ("psi", None, "pound force per square inch", "pounds force per square inch", 4.448222/(0.0254**2), -1, -2, 1, 0, 0, 0, 0, []), -] - -non_si_dimensionless_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ - ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), - ("percent", "%", "percent", "percent", 0.01, 0, 0, 0, 0, 0, 0, 0, []) + ("mol", None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes) ] -non_si_units = non_si_dimensioned_units + non_si_dimensionless_units - -# TODO: -# Add Hartree? Rydberg? Bohrs? -# Add CGS - -# Two stages of aliases, to make sure units don't get lost - -aliases_1 = { - "A": ["Amps", "amps"], - "C": ["Coulombs", "coulombs"] -} - -aliases_2 = { +aliases = { "y": ["yr", "year"], "d": ["day"], "h": ["hr", "hour"], "Ang": ["A", "Å"], - "au": ["amu"], - "percent": ["%"], - "deg": ["degr", "Deg", "degrees", "Degrees"], - "none": ["Counts", "counts", "cnts", "Cnts", "a.u.", "fraction", "Fraction"], - "K": ["C"] # Ugh, cansas + "au": ["a.u.", "amu"] } - all_units = base_si_units + derived_si_units + non_si_units encoding = "utf-8" @@ -143,20 +112,13 @@ def format_name(name: str): unit_types_temp = defaultdict(list) # Keep track of unit types unit_types = defaultdict(list) - for unit_def in all_units: - - try: - symbol, special_symbol, singular, plural, scale, length, time, \ - mass, current, temperature, moles_hint, angle_hint, magnitudes = unit_def - except Exception as e: - print(unit_def) - raise e + for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, moles_hint, angle_hint, magnitudes in all_units: formatted_plural = format_name(plural) formatted_singular = format_name(singular) dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) - fid.write(f"{formatted_plural} = NamedUnit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," + fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," f"name='{formatted_plural}'," f"ascii_symbol='{symbol}'," f"symbol='{symbol if special_symbol is None else special_symbol}')\n") @@ -186,8 +148,8 @@ def format_name(name: str): # Units dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) - fid.write(f"{combined_name_plural} = NamedUnit({combined_scale}, " - f"Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," + fid.write(f"{combined_name_plural} = Unit({combined_scale}, " + f"Dimensions({length}, {time}, {mass}, {current}, {temperature})," f"name='{combined_name_plural}'," f"ascii_symbol='{combined_symbol}'," f"symbol='{combined_special_symbol}')\n") @@ -223,7 +185,7 @@ def format_name(name: str): unit_name = prefix + name unit_special_symbol = (symbol if special_symbol is None else special_symbol) + unicode_suffix unit_symbol = symbol + f"^{power}" - fid.write(f"{unit_name} = NamedUnit({scale**power}, Dimensions(length={power}), " + fid.write(f"{unit_name} = Unit({scale**power}, Dimensions(length={power}), " f"name='{unit_name}', " f"ascii_symbol='{unit_symbol}', " f"symbol='{unit_special_symbol}')\n") @@ -239,21 +201,18 @@ def format_name(name: str): speed_dimensions = Dimensions(length=1, time=-1) accel_dimensions = Dimensions(length=1, time=-2) - length_special = length_special_symbol if length_special_symbol is not None else length_symbol - time_special = time_special_symbol if time_special_symbol is not None else time_symbol - fid.write(f"{speed_name} " - f"= NamedUnit({length_scale / time_scale}, " + f"= Unit({length_scale / time_scale}, " f"Dimensions(length=1, time=-1), " f"name='{speed_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}', " - f"symbol='{length_special}{time_special}⁻¹')\n") + f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") - fid.write(f"{accel_name} = NamedUnit({length_scale / time_scale**2}, " + fid.write(f"{accel_name} = Unit({length_scale / time_scale}, " f"Dimensions(length=1, time=-2), " f"name='{accel_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}^2', " - f"symbol='{length_special}{time_special}⁻²')\n") + f"symbol='{length_special_symbol}{time_special_symbol}⁻²')\n") unit_types[hash(speed_dimensions)].append(speed_name) unit_types[hash(accel_dimensions)].append(accel_name) @@ -266,15 +225,12 @@ def format_name(name: str): dimensions = Dimensions(length=-3, mass=1) - mass_special = mass_symbol if mass_special_symbol is None else mass_special_symbol - length_special = length_symbol if length_special_symbol is None else length_special_symbol - fid.write(f"{name} " - f"= NamedUnit({mass_scale / length_scale**3}, " + f"= Unit({mass_scale / length_scale**3}, " f"Dimensions(length=-3, mass=1), " f"name='{name}', " f"ascii_symbol='{mass_symbol} {length_symbol}^-3', " - f"symbol='{mass_special}{length_special}⁻³')\n") + f"symbol='{mass_special_symbol}{length_special_symbol}⁻³')\n") unit_types[hash(dimensions)].append(name) @@ -286,30 +242,24 @@ def format_name(name: str): dimensions = Dimensions(length=-3, moles_hint=1) - length_special = length_symbol if length_special_symbol is None else length_special_symbol - amount_special = amount_symbol if amount_special_symbol is None else amount_special_symbol - fid.write(f"{name} " - f"= NamedUnit({amount_scale / length_scale**3}, " + f"= Unit({amount_scale / length_scale**3}, " f"Dimensions(length=-3, moles_hint=1), " f"name='{name}', " f"ascii_symbol='{amount_symbol} {length_symbol}^-3', " - f"symbol='{amount_special}{length_special}⁻³')\n") + f"symbol='{amount_special_symbol}{length_special_symbol}⁻³')\n") unit_types[hash(dimensions)].append(name) - # TODO: Torque, Momentum, Entropy # # Add aliases to symbol lookup table # - # Apply the alias transforms sequentially - for aliases in [aliases_1, aliases_2]: - for base_name in aliases: - alias_list = aliases[base_name] - for alias in alias_list: - symbol_lookup[alias] = symbol_lookup[base_name] + for base_name in aliases: + alias_list = aliases[base_name] + for alias in alias_list: + symbol_lookup[alias] = symbol_lookup[base_name] # # Write out the symbol lookup table @@ -317,8 +267,7 @@ def format_name(name: str): fid.write("\n#\n# Lookup table from symbols to units\n#\n\n") fid.write("symbol_lookup = {\n") for k in symbol_lookup: - if k != "none": - fid.write(f' "{k}": {symbol_lookup[k]},\n') + fid.write(f' "{k}": {symbol_lookup[k]},\n') fid.write("}\n\n") # @@ -354,7 +303,7 @@ def format_name(name: str): ("angle", Dimensions(angle_hint=1)), ("solid_angle", Dimensions(angle_hint=2)), ("amount", Dimensions(moles_hint=1)), - ("concentration", Dimensions(length=-3, moles_hint=1)), + ("concentration", Dimensions(length=-3, moles_hint=1)) ] fid.write("\n#\n# Units by type\n#\n\n") @@ -372,20 +321,6 @@ def format_name(name: str): fid.write("])\n") - - # List of dimensions - fid.write("\n\n") - fid.write("unit_group_names = [\n") - for dimension_name, _ in dimension_names: - fid.write(f" '{dimension_name}',\n") - fid.write("]\n\n") - - fid.write("unit_groups = {\n") - for dimension_name, _ in dimension_names: - fid.write(f" '{dimension_name}': {dimension_name},\n") - fid.write("}\n\n") - - with open("accessors.py", 'w', encoding=encoding) as fid: @@ -400,28 +335,15 @@ def format_name(name: str): accessor_name = dimension_name.capitalize().replace("_", "") + "Accessor" fid.write(f"\n" - f"class {accessor_name}[T](QuantityAccessor[T]):\n" + f"class {accessor_name}[T](Accessor[T]):\n" f" dimension_name = '{dimension_name}'\n" f"\n") for unit_name in unit_types[hash(dimensions)]: fid.write(f" @property\n" f" def {unit_name}(self) -> T:\n" - f" quantity = self.quantity\n" - f" if quantity is None:\n" - f" return None\n" - f" else:\n" - f" return quantity.in_units_of(units.{unit_name})\n" + f" return self.quantity.in_units_of(units.{unit_name})\n" f"\n") - fid.write("\n")with open("si.py", 'w') as fid: - - fid.write('"""'+(warning_text%"_build_tables.py")+'"""\n\n') - si_unit_names = [values[3] for values in base_si_units + derived_si_units if values[3] != "grams"] + ["kilograms"] - - for name in si_unit_names: + fid.write("\n") - fid.write(f"from sasdata.quantities.units import {name}\n") fid.write("\nall_si = [\n") - for name in si_unit_names: - fid.write(f" {name},\n") - fid.write("]\n") \ No newline at end of file diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 754ca9e96..a8c713d30 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -32,6 +32,11 @@ def __init__(self, self.moles_hint = moles_hint self.angle_hint = angle_hint + @property + def is_dimensionless(self): + """ Is this dimension dimensionless (ignores moles_hint and angle_hint) """ + return self.length == 0 and self.time == 0 and self.mass == 0 and self.current == 0 and self.temperature == 0 + def __mul__(self: Self, other: Self): if not isinstance(other, Dimensions): @@ -195,6 +200,7 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): class NamedUnit: # TODO: Add named unit class + pass # # Parsing plan: diff --git a/sasdata/quantities/_units_table.py b/sasdata/quantities/_units_table.py deleted file mode 100644 index c69b9577a..000000000 --- a/sasdata/quantities/_units_table.py +++ /dev/null @@ -1,333 +0,0 @@ -""" -Builds a data file containing details of units -""" - -import numpy as np -from collections import defaultdict -from _units_base import Dimensions, Unit - -bigger_magnitudes = [ - ("E", None, "exa", 1e18), - ("P", None, "peta", 1e15), - ("T", None, "tera", 1e12), - ("G", None, "giga", 1e9), - ("M", None, "mega", 1e6), - ("k", None, "kilo", 1e3) ] - -smaller_magnitudes = [ - ("m", None, "milli", 1e-3), - ("u", "µ", "micro", 1e-6), - ("n", None, "nano", 1e-9), - ("p", None, "pico", 1e-12), - ("f", None, "femto", 1e-15), - ("a", None, "atto", 1e-18)] - -unusual_magnitudes = [ - ("d", None, "deci", 1e-1), - ("c", None, "centi", 1e-2) -] - -all_magnitudes = bigger_magnitudes + smaller_magnitudes - -# Length, time, mass, current, temperature -base_si_units = [ - ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, 0, 0, all_magnitudes + unusual_magnitudes), - ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, 0, 0, smaller_magnitudes), - ("g", None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, 0, 0, all_magnitudes), - ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), - ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, 0, 0, all_magnitudes) ] - -derived_si_units = [ - ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, 0, 0, all_magnitudes), - ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, 0, 0, all_magnitudes), - ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, 0, 0, all_magnitudes), - ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), - ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, 0, 0, all_magnitudes), - ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, 0, 0, all_magnitudes), - ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, 0, 0, all_magnitudes), - ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, 0, 0, all_magnitudes), - ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, 0, 0, all_magnitudes), - ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, 0, 0, all_magnitudes), - ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, 0, 0, all_magnitudes), - ("T", None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, 0, 0, all_magnitudes), - ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), - ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []) -] - -non_si_units = [ - ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, 0, 0, []), - ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, 0, 0, []), - ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, 0, 0, []), - ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, 0, 0, []), - ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, 0, 0, []), - ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, 0, 1, []), - ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, 0, 1, []), - ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, 0, 2, []), - ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), - ("l", None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), - ("eV", None, "electronvolt", "electronvolts", 1.602e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), - ("au", None, "atomic mass unit", "atomic mass units", 1.661e-27, 0, 0, 1, 0, 0, 0, 0, []), - ("mol", None, "mole", "moles", 6.022e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes) -] - -aliases = { - "y": ["yr", "year"], - "d": ["day"], - "h": ["hr", "hour"], - "Ang": ["A", "Å"], - "au": ["a.u.", "amu"] -} - - -all_units = base_si_units + derived_si_units + non_si_units - -encoding = "utf-8" - -def format_name(name: str): - return name.lower().replace(" ", "_") - -header = """ - -Autogenerated file by _units_table.py - - - - ******** DO NOT EDIT BY HAND ******** - - - -""" - -with open("units.py", 'w', encoding=encoding) as fid: - - # Write warning header - fid.write('"""'+header+'"""') - - # Write in class definitions - fid.write("\n\n" - "#\n" - "# Included from _units_base.py\n" - "#\n\n") - - with open("_units_base.py", 'r') as base: - for line in base: - fid.write(line) - - # Write in unit definitions - fid.write("\n\n" - "#\n" - "# Specific units \n" - "#\n\n") - - symbol_lookup = {} - unit_types_temp = defaultdict(list) # Keep track of unit types - unit_types = defaultdict(list) - - for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, moles_hint, angle_hint, magnitudes in all_units: - - formatted_plural = format_name(plural) - formatted_singular = format_name(singular) - - dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) - fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," - f"name='{formatted_plural}'," - f"ascii_symbol='{symbol}'," - f"symbol='{symbol if special_symbol is None else special_symbol}')\n") - - symbol_lookup[symbol] = formatted_plural - if special_symbol is not None: - symbol_lookup[special_symbol] = formatted_plural - - unit_types_temp[hash(dimensions)].append( - (symbol, special_symbol, formatted_singular, formatted_plural, scale, dimensions)) - - unit_types[hash(dimensions)].append(formatted_plural) - - for mag_symbol, mag_special_symbol, name, mag_scale in magnitudes: - - # Work out the combined symbol, accounts for unicode or not - combined_special_symbol = (mag_symbol if mag_special_symbol is None else mag_special_symbol) + \ - (symbol if special_symbol is None else special_symbol) - - combined_symbol = mag_symbol + symbol - - # Combined unit name - combined_name_singular = f"{name}{formatted_singular}" - combined_name_plural = f"{name}{formatted_plural}" - - combined_scale = scale * mag_scale - - # Units - dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) - fid.write(f"{combined_name_plural} = Unit({combined_scale}, " - f"Dimensions({length}, {time}, {mass}, {current}, {temperature})," - f"name='{combined_name_plural}'," - f"ascii_symbol='{combined_symbol}'," - f"symbol='{combined_special_symbol}')\n") - - symbol_lookup[combined_symbol] = combined_name_plural - symbol_lookup[combined_special_symbol] = combined_name_plural - - unit_types_temp[hash(dimensions)].append( - (combined_symbol, combined_special_symbol, combined_name_singular, - combined_name_plural, combined_scale, dimensions)) - - unit_types[hash(dimensions)].append(combined_name_plural) - - # - # Higher dimensioned types - # - - length_units = unit_types_temp[hash(Dimensions(length=1))] - time_units = unit_types_temp[hash(Dimensions(time=1))] - mass_units = unit_types_temp[hash(Dimensions(mass=1))] - amount_units = unit_types_temp[hash(Dimensions(moles_hint=1))] - - # Length based - for symbol, special_symbol, singular, plural, scale, _ in length_units: - for prefix, power, name, unicode_suffix in [ - ("square_", 2, plural, '²'), - ("cubic_", 3, plural, '³'), - ("per_", -1, singular, '⁻¹'), - ("per_square_", -2, singular,'⁻²'), - ("per_cubic_", -3, singular,'⁻³')]: - - dimensions = Dimensions(length=power) - unit_name = prefix + name - unit_special_symbol = (symbol if special_symbol is None else special_symbol) + unicode_suffix - unit_symbol = symbol + f"^{power}" - fid.write(f"{unit_name} = Unit({scale**power}, Dimensions(length={power}), " - f"name='{unit_name}', " - f"ascii_symbol='{unit_symbol}', " - f"symbol='{unit_special_symbol}')\n") - - unit_types[hash(dimensions)].append(unit_name) - - # Speed and acceleration - for length_symbol, length_special_symbol, _, length_name, length_scale, _ in length_units: - for time_symbol, time_special_symbol, time_name, _, time_scale, _ in time_units: - speed_name = length_name + "_per_" + time_name - accel_name = length_name + "_per_square_" + time_name - - speed_dimensions = Dimensions(length=1, time=-1) - accel_dimensions = Dimensions(length=1, time=-2) - - fid.write(f"{speed_name} " - f"= Unit({length_scale / time_scale}, " - f"Dimensions(length=1, time=-1), " - f"name='{speed_name}', " - f"ascii_symbol='{length_symbol}/{time_symbol}', " - f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") - - fid.write(f"{accel_name} = Unit({length_scale / time_scale}, " - f"Dimensions(length=1, time=-2), " - f"name='{accel_name}', " - f"ascii_symbol='{length_symbol}/{time_symbol}^2', " - f"symbol='{length_special_symbol}{time_special_symbol}⁻²')\n") - - unit_types[hash(speed_dimensions)].append(speed_name) - unit_types[hash(accel_dimensions)].append(accel_name) - - # Density - for length_symbol, length_special_symbol, length_name, _, length_scale, _ in length_units: - for mass_symbol, mass_special_symbol, _, mass_name, mass_scale, _ in mass_units: - - name = mass_name + "_per_cubic_" + length_name - - dimensions = Dimensions(length=-3, mass=1) - - fid.write(f"{name} " - f"= Unit({mass_scale / length_scale**3}, " - f"Dimensions(length=-3, mass=1), " - f"name='{name}', " - f"ascii_symbol='{mass_symbol} {length_symbol}^-3', " - f"symbol='{mass_special_symbol}{length_special_symbol}⁻³')\n") - - unit_types[hash(dimensions)].append(name) - - # Concentration - for length_symbol, length_special_symbol, length_name, _, length_scale, _ in length_units: - for amount_symbol, amount_special_symbol, _, amount_name, amount_scale, _ in amount_units: - - name = amount_name + "_per_cubic_" + length_name - - dimensions = Dimensions(length=-3, moles_hint=1) - - fid.write(f"{name} " - f"= Unit({amount_scale / length_scale**3}, " - f"Dimensions(length=-3, moles_hint=1), " - f"name='{name}', " - f"ascii_symbol='{amount_symbol} {length_symbol}^-3', " - f"symbol='{amount_special_symbol}{length_special_symbol}⁻³')\n") - - unit_types[hash(dimensions)].append(name) - - - # - # Add aliases to symbol lookup table - # - - for base_name in aliases: - alias_list = aliases[base_name] - for alias in alias_list: - symbol_lookup[alias] = symbol_lookup[base_name] - - # - # Write out the symbol lookup table - # - fid.write("\n#\n# Lookup table from symbols to units\n#\n\n") - fid.write("symbol_lookup = {\n") - for k in symbol_lookup: - fid.write(f' "{k}": {symbol_lookup[k]},\n') - fid.write("}\n\n") - - # - # Collections of units by type - # - - dimension_names = [ - ("length", Dimensions(length=1)), - ("area", Dimensions(length=2)), - ("volume", Dimensions(length=3)), - ("inverse_length", Dimensions(length=-1)), - ("inverse_area", Dimensions(length=-2)), - ("inverse_volume", Dimensions(length=-3)), - ("time", Dimensions(time=1)), - ("rate", Dimensions(time=-1)), - ("speed", Dimensions(length=1, time=-1)), - ("acceleration", Dimensions(length=1, time=-2)), - ("density", Dimensions(length=-3, mass=1)), - ("force", Dimensions(1, -2, 1, 0, 0)), - ("pressure", Dimensions(-1, -2, 1, 0, 0)), - ("energy", Dimensions(2, -2, 1, 0, 0)), - ("power", Dimensions(2, -3, 1, 0, 0)), - ("charge", Dimensions(0, 1, 0, 1, 0)), - ("potential", Dimensions(2, -3, 1, -1, 0)), - ("resistance", Dimensions(2, -3, 1, -2, 0)), - ("capacitance", Dimensions(-2, 4, -1, 2, 0)), - ("conductance", Dimensions(-2, 3, -1, 2, 0)), - ("magnetic_flux", Dimensions(2, -2, 1, -1, 0)), - ("magnetic_flux_density", Dimensions(0, -2, 1, -1, 0)), - ("inductance", Dimensions(2, -2, 1, -2, 0)), - ("temperature", Dimensions(temperature=1)), - ("dimensionless", Dimensions()), - ("angle", Dimensions(angle_hint=1)), - ("solid_angle", Dimensions(angle_hint=2)), - ("amount", Dimensions(moles_hint=1)), - ("concentration", Dimensions(length=-3, moles_hint=1)) - ] - - fid.write("\n#\n# Units by type \n#\n\n") - - for dimension_name, dimensions in dimension_names: - - - fid.write(f"\n" - f"{dimension_name} = UnitGroup(\n" - f" name = '{dimension_name}', \n" - f" units = [\n") - - for unit_name in unit_types[hash(dimensions)]: - fid.write(" " + unit_name + ",\n") - - fid.write("])\n") \ No newline at end of file diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index be7b7bddd..575298eb9 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -78,10246 +78,4177 @@ """ -from typing import TypeVar, Sequence +from typing import TypeVar from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units -from sasdata.quantities.units import Dimensions, Unit -from sasdata.quantities.unit_parser import parse_unit, parse_unit_from_group -from sasdata.raw_form import Group, Dataset -from sasdata.data_backing import Group, Dataset -import logging -# logger = logging.getLogger("Accessors") -class LoggerDummy: - def info(self, data): - print(data) -logger = LoggerDummy() +T = TypeVar("T") -DataType = TypeVar("DataType") -OutputType = TypeVar("OutputType") - - -class AccessorTarget: - def __init__(self, data: Group, verbose=False, prefix_tokens: tuple=()): - self._data = data - self.verbose = verbose - - self.prefix_tokens = list(prefix_tokens) - - def with_path_prefix(self, path_prexix: str): - """ Get an accessor that looks at a subtree of the metadata with the supplied prefix - - For example, accessors aiming at a.b, when the target it c.d will look at c.d.a.b - """ - return AccessorTarget(self._data, - verbose=self.verbose, - prefix_tokens=tuple(self.prefix_tokens + [path_prexix])) - - def get_value(self, path: str): - - tokens = self.prefix_tokens + path.split(".") - - if self.verbose: - logger.info(f"Finding: {path}") - logger.info(f"Full path: {tokens}") - - # Navigate the tree from the entry we need - - current_tree_position: Group | Dataset = self._data - - for token in tokens: - - options = token.split("|") - - if isinstance(current_tree_position, Group): - - found = False - for option in options: - if option in current_tree_position.children: - current_tree_position = current_tree_position.children[option] - found = True - - if self.verbose: - logger.info(f"Found option: {option}") - - if not found: - if self.verbose: - logger.info(f"Failed to find any of {options} on group {current_tree_position.name}. Options: " + - ",".join([key for key in current_tree_position.children])) - return None - - elif isinstance(current_tree_position, Dataset): - - found = False - for option in options: - if option in current_tree_position.attributes: - current_tree_position = current_tree_position.attributes[option] - found = True - - if self.verbose: - logger.info(f"Found option: {option}") - - if not found: - if self.verbose: - logger.info(f"Failed to find any of {options} on attribute {current_tree_position.name}. Options: " + - ",".join([key for key in current_tree_position.attributes])) - return None - - if self.verbose: - logger.info(f"Found value: {current_tree_position}") - - return current_tree_position.data - - - -class Accessor[DataType, OutputType]: +class Accessor[T]: """ Base class """ - def __init__(self, target_object: AccessorTarget, value_target: str): - self.target_object = target_object - self.value_target = value_target - - @property - def value(self) -> OutputType | None: - return self.target_object.get_value(self.value_target) - -class StringAccessor(Accessor[str, str]): - """ String based fields """ - @property - def value(self) -> str | None: - return self.target_object.get_value(self.value_target) - -class FloatAccessor(Accessor[float, float]): - """ Float based fields """ - @property - def value(self) -> float | None: - return self.target_object.get_value(self.value_target) - - - - -class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): - """ Base class for accessors that work with quantities that have units """ - def __init__(self, target_object: AccessorTarget, value_target: str, unit_target: str, default_unit=units.none): - super().__init__(target_object, value_target) + def __init__(self, value_target: str, unit_target: str): + self._value_target = value_target self._unit_target = unit_target - self.default_unit = default_unit - - def _numerical_part(self) -> DataType | None: - """ Numerical part of the data """ - return self.target_object.get_value(self.value_target) - - def _unit_part(self) -> str | None: - """ String form of units for the data """ - return self.target_object.get_value(self._unit_target) - - @property - def unit(self) -> Unit: - u = self._unit_part() - if u is None: - return self.default_unit - else: - return parse_unit(u) - - @property - def value(self) -> Quantity[DataType] | None: - if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self._numerical_part(), self.unit) - return None @property - def quantity(self): - if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self._numerical_part(), self.unit) - return None - + def quantity(self) -> Quantity[T]: + raise NotImplementedError("Not implemented yet") -class LengthAccessor[T](QuantityAccessor[T]): +class LengthAccessor[T](Accessor[T]): dimension_name = 'length' @property def meters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters) + return self.quantity.in_units_of(units.meters) @property def exameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters) + return self.quantity.in_units_of(units.exameters) @property def petameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters) + return self.quantity.in_units_of(units.petameters) @property def terameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters) + return self.quantity.in_units_of(units.terameters) @property def gigameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters) + return self.quantity.in_units_of(units.gigameters) @property def megameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters) + return self.quantity.in_units_of(units.megameters) @property def kilometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers) + return self.quantity.in_units_of(units.kilometers) @property def millimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters) + return self.quantity.in_units_of(units.millimeters) @property def micrometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers) + return self.quantity.in_units_of(units.micrometers) @property def nanometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers) + return self.quantity.in_units_of(units.nanometers) @property def picometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers) + return self.quantity.in_units_of(units.picometers) @property def femtometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers) + return self.quantity.in_units_of(units.femtometers) @property def attometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers) + return self.quantity.in_units_of(units.attometers) @property def decimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters) + return self.quantity.in_units_of(units.decimeters) @property def centimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters) + return self.quantity.in_units_of(units.centimeters) @property def angstroms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms) - - @property - def miles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles) - - @property - def yards(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards) - - @property - def feet(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet) - - @property - def inches(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches) + return self.quantity.in_units_of(units.angstroms) -class AreaAccessor[T](QuantityAccessor[T]): +class AreaAccessor[T](Accessor[T]): dimension_name = 'area' @property def square_meters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_meters) + return self.quantity.in_units_of(units.square_meters) @property def square_exameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_exameters) + return self.quantity.in_units_of(units.square_exameters) @property def square_petameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_petameters) + return self.quantity.in_units_of(units.square_petameters) @property def square_terameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_terameters) + return self.quantity.in_units_of(units.square_terameters) @property def square_gigameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_gigameters) + return self.quantity.in_units_of(units.square_gigameters) @property def square_megameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_megameters) + return self.quantity.in_units_of(units.square_megameters) @property def square_kilometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_kilometers) + return self.quantity.in_units_of(units.square_kilometers) @property def square_millimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_millimeters) + return self.quantity.in_units_of(units.square_millimeters) @property def square_micrometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_micrometers) + return self.quantity.in_units_of(units.square_micrometers) @property def square_nanometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_nanometers) + return self.quantity.in_units_of(units.square_nanometers) @property def square_picometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_picometers) + return self.quantity.in_units_of(units.square_picometers) @property def square_femtometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_femtometers) + return self.quantity.in_units_of(units.square_femtometers) @property def square_attometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_attometers) + return self.quantity.in_units_of(units.square_attometers) @property def square_decimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_decimeters) + return self.quantity.in_units_of(units.square_decimeters) @property def square_centimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_centimeters) + return self.quantity.in_units_of(units.square_centimeters) @property def square_angstroms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_angstroms) - - @property - def square_miles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_miles) - - @property - def square_yards(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_yards) - - @property - def square_feet(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_feet) - - @property - def square_inches(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.square_inches) + return self.quantity.in_units_of(units.square_angstroms) -class VolumeAccessor[T](QuantityAccessor[T]): +class VolumeAccessor[T](Accessor[T]): dimension_name = 'volume' @property def litres(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.litres) + return self.quantity.in_units_of(units.litres) @property def cubic_meters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_meters) + return self.quantity.in_units_of(units.cubic_meters) @property def cubic_exameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_exameters) + return self.quantity.in_units_of(units.cubic_exameters) @property def cubic_petameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_petameters) + return self.quantity.in_units_of(units.cubic_petameters) @property def cubic_terameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_terameters) + return self.quantity.in_units_of(units.cubic_terameters) @property def cubic_gigameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_gigameters) + return self.quantity.in_units_of(units.cubic_gigameters) @property def cubic_megameters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_megameters) + return self.quantity.in_units_of(units.cubic_megameters) @property def cubic_kilometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_kilometers) + return self.quantity.in_units_of(units.cubic_kilometers) @property def cubic_millimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_millimeters) + return self.quantity.in_units_of(units.cubic_millimeters) @property def cubic_micrometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_micrometers) + return self.quantity.in_units_of(units.cubic_micrometers) @property def cubic_nanometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_nanometers) + return self.quantity.in_units_of(units.cubic_nanometers) @property def cubic_picometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_picometers) + return self.quantity.in_units_of(units.cubic_picometers) @property def cubic_femtometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_femtometers) + return self.quantity.in_units_of(units.cubic_femtometers) @property def cubic_attometers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_attometers) + return self.quantity.in_units_of(units.cubic_attometers) @property def cubic_decimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_decimeters) + return self.quantity.in_units_of(units.cubic_decimeters) @property def cubic_centimeters(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_centimeters) + return self.quantity.in_units_of(units.cubic_centimeters) @property def cubic_angstroms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_angstroms) - - @property - def cubic_miles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_miles) - - @property - def cubic_yards(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_yards) - - @property - def cubic_feet(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_feet) - - @property - def cubic_inches(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.cubic_inches) + return self.quantity.in_units_of(units.cubic_angstroms) -class InverselengthAccessor[T](QuantityAccessor[T]): +class InverselengthAccessor[T](Accessor[T]): dimension_name = 'inverse_length' @property def per_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_meter) + return self.quantity.in_units_of(units.per_meter) @property def per_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_exameter) + return self.quantity.in_units_of(units.per_exameter) @property def per_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_petameter) + return self.quantity.in_units_of(units.per_petameter) @property def per_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_terameter) + return self.quantity.in_units_of(units.per_terameter) @property def per_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_gigameter) + return self.quantity.in_units_of(units.per_gigameter) @property def per_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_megameter) + return self.quantity.in_units_of(units.per_megameter) @property def per_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_kilometer) + return self.quantity.in_units_of(units.per_kilometer) @property def per_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_millimeter) + return self.quantity.in_units_of(units.per_millimeter) @property def per_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_micrometer) + return self.quantity.in_units_of(units.per_micrometer) @property def per_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_nanometer) + return self.quantity.in_units_of(units.per_nanometer) @property def per_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_picometer) + return self.quantity.in_units_of(units.per_picometer) @property def per_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_femtometer) + return self.quantity.in_units_of(units.per_femtometer) @property def per_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_attometer) + return self.quantity.in_units_of(units.per_attometer) @property def per_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_decimeter) + return self.quantity.in_units_of(units.per_decimeter) @property def per_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_centimeter) + return self.quantity.in_units_of(units.per_centimeter) @property def per_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_angstrom) - - @property - def per_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_mile) - - @property - def per_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_yard) - - @property - def per_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_foot) - - @property - def per_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_inch) + return self.quantity.in_units_of(units.per_angstrom) -class InverseareaAccessor[T](QuantityAccessor[T]): +class InverseareaAccessor[T](Accessor[T]): dimension_name = 'inverse_area' @property def per_square_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_meter) + return self.quantity.in_units_of(units.per_square_meter) @property def per_square_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_exameter) + return self.quantity.in_units_of(units.per_square_exameter) @property def per_square_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_petameter) + return self.quantity.in_units_of(units.per_square_petameter) @property def per_square_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_terameter) + return self.quantity.in_units_of(units.per_square_terameter) @property def per_square_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_gigameter) + return self.quantity.in_units_of(units.per_square_gigameter) @property def per_square_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_megameter) + return self.quantity.in_units_of(units.per_square_megameter) @property def per_square_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_kilometer) + return self.quantity.in_units_of(units.per_square_kilometer) @property def per_square_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_millimeter) + return self.quantity.in_units_of(units.per_square_millimeter) @property def per_square_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_micrometer) + return self.quantity.in_units_of(units.per_square_micrometer) @property def per_square_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_nanometer) + return self.quantity.in_units_of(units.per_square_nanometer) @property def per_square_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_picometer) + return self.quantity.in_units_of(units.per_square_picometer) @property def per_square_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_femtometer) + return self.quantity.in_units_of(units.per_square_femtometer) @property def per_square_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_attometer) + return self.quantity.in_units_of(units.per_square_attometer) @property def per_square_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_decimeter) + return self.quantity.in_units_of(units.per_square_decimeter) @property def per_square_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_centimeter) + return self.quantity.in_units_of(units.per_square_centimeter) @property def per_square_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_angstrom) - - @property - def per_square_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_mile) - - @property - def per_square_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_yard) - - @property - def per_square_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_foot) - - @property - def per_square_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_square_inch) + return self.quantity.in_units_of(units.per_square_angstrom) -class InversevolumeAccessor[T](QuantityAccessor[T]): +class InversevolumeAccessor[T](Accessor[T]): dimension_name = 'inverse_volume' @property def per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_meter) + return self.quantity.in_units_of(units.per_cubic_meter) @property def per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_exameter) + return self.quantity.in_units_of(units.per_cubic_exameter) @property def per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_petameter) + return self.quantity.in_units_of(units.per_cubic_petameter) @property def per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_terameter) + return self.quantity.in_units_of(units.per_cubic_terameter) @property def per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_gigameter) + return self.quantity.in_units_of(units.per_cubic_gigameter) @property def per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_megameter) + return self.quantity.in_units_of(units.per_cubic_megameter) @property def per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_kilometer) + return self.quantity.in_units_of(units.per_cubic_kilometer) @property def per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_millimeter) + return self.quantity.in_units_of(units.per_cubic_millimeter) @property def per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_micrometer) + return self.quantity.in_units_of(units.per_cubic_micrometer) @property def per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_nanometer) + return self.quantity.in_units_of(units.per_cubic_nanometer) @property def per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_picometer) + return self.quantity.in_units_of(units.per_cubic_picometer) @property def per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_femtometer) + return self.quantity.in_units_of(units.per_cubic_femtometer) @property def per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_attometer) + return self.quantity.in_units_of(units.per_cubic_attometer) @property def per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_decimeter) + return self.quantity.in_units_of(units.per_cubic_decimeter) @property def per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_centimeter) + return self.quantity.in_units_of(units.per_cubic_centimeter) @property def per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_angstrom) - - @property - def per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_mile) - - @property - def per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_yard) - - @property - def per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_foot) - - @property - def per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.per_cubic_inch) + return self.quantity.in_units_of(units.per_cubic_angstrom) -class TimeAccessor[T](QuantityAccessor[T]): +class TimeAccessor[T](Accessor[T]): dimension_name = 'time' @property def seconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.seconds) + return self.quantity.in_units_of(units.seconds) @property def milliseconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milliseconds) + return self.quantity.in_units_of(units.milliseconds) @property def microseconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microseconds) + return self.quantity.in_units_of(units.microseconds) @property def nanoseconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanoseconds) + return self.quantity.in_units_of(units.nanoseconds) @property def picoseconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picoseconds) + return self.quantity.in_units_of(units.picoseconds) @property def femtoseconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtoseconds) + return self.quantity.in_units_of(units.femtoseconds) @property def attoseconds(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attoseconds) + return self.quantity.in_units_of(units.attoseconds) @property def minutes(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.minutes) + return self.quantity.in_units_of(units.minutes) @property def hours(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.hours) + return self.quantity.in_units_of(units.hours) @property def days(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.days) + return self.quantity.in_units_of(units.days) @property def years(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.years) + return self.quantity.in_units_of(units.years) -class RateAccessor[T](QuantityAccessor[T]): +class RateAccessor[T](Accessor[T]): dimension_name = 'rate' @property def hertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.hertz) + return self.quantity.in_units_of(units.hertz) @property def exahertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exahertz) + return self.quantity.in_units_of(units.exahertz) @property def petahertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petahertz) + return self.quantity.in_units_of(units.petahertz) @property def terahertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terahertz) + return self.quantity.in_units_of(units.terahertz) @property def gigahertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigahertz) + return self.quantity.in_units_of(units.gigahertz) @property def megahertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megahertz) + return self.quantity.in_units_of(units.megahertz) @property def kilohertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilohertz) + return self.quantity.in_units_of(units.kilohertz) @property def millihertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millihertz) + return self.quantity.in_units_of(units.millihertz) @property def microhertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microhertz) + return self.quantity.in_units_of(units.microhertz) @property def nanohertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanohertz) + return self.quantity.in_units_of(units.nanohertz) @property def picohertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picohertz) + return self.quantity.in_units_of(units.picohertz) @property def femtohertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtohertz) + return self.quantity.in_units_of(units.femtohertz) @property def attohertz(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attohertz) + return self.quantity.in_units_of(units.attohertz) -class SpeedAccessor[T](QuantityAccessor[T]): +class SpeedAccessor[T](Accessor[T]): dimension_name = 'speed' @property def meters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_second) + return self.quantity.in_units_of(units.meters_per_second) @property def meters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_millisecond) + return self.quantity.in_units_of(units.meters_per_millisecond) @property def meters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_microsecond) + return self.quantity.in_units_of(units.meters_per_microsecond) @property def meters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_nanosecond) + return self.quantity.in_units_of(units.meters_per_nanosecond) @property def meters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_picosecond) + return self.quantity.in_units_of(units.meters_per_picosecond) @property def meters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_femtosecond) + return self.quantity.in_units_of(units.meters_per_femtosecond) @property def meters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_attosecond) + return self.quantity.in_units_of(units.meters_per_attosecond) @property def meters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_minute) + return self.quantity.in_units_of(units.meters_per_minute) @property def meters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_hour) + return self.quantity.in_units_of(units.meters_per_hour) @property def meters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_day) + return self.quantity.in_units_of(units.meters_per_day) @property def meters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_year) + return self.quantity.in_units_of(units.meters_per_year) @property def exameters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_second) + return self.quantity.in_units_of(units.exameters_per_second) @property def exameters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_millisecond) + return self.quantity.in_units_of(units.exameters_per_millisecond) @property def exameters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_microsecond) + return self.quantity.in_units_of(units.exameters_per_microsecond) @property def exameters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_nanosecond) + return self.quantity.in_units_of(units.exameters_per_nanosecond) @property def exameters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_picosecond) + return self.quantity.in_units_of(units.exameters_per_picosecond) @property def exameters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_femtosecond) + return self.quantity.in_units_of(units.exameters_per_femtosecond) @property def exameters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_attosecond) + return self.quantity.in_units_of(units.exameters_per_attosecond) @property def exameters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_minute) + return self.quantity.in_units_of(units.exameters_per_minute) @property def exameters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_hour) + return self.quantity.in_units_of(units.exameters_per_hour) @property def exameters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_day) + return self.quantity.in_units_of(units.exameters_per_day) @property def exameters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_year) + return self.quantity.in_units_of(units.exameters_per_year) @property def petameters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_second) + return self.quantity.in_units_of(units.petameters_per_second) @property def petameters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_millisecond) + return self.quantity.in_units_of(units.petameters_per_millisecond) @property def petameters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_microsecond) + return self.quantity.in_units_of(units.petameters_per_microsecond) @property def petameters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_nanosecond) + return self.quantity.in_units_of(units.petameters_per_nanosecond) @property def petameters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_picosecond) + return self.quantity.in_units_of(units.petameters_per_picosecond) @property def petameters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_femtosecond) + return self.quantity.in_units_of(units.petameters_per_femtosecond) @property def petameters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_attosecond) + return self.quantity.in_units_of(units.petameters_per_attosecond) @property def petameters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_minute) + return self.quantity.in_units_of(units.petameters_per_minute) @property def petameters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_hour) + return self.quantity.in_units_of(units.petameters_per_hour) @property def petameters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_day) + return self.quantity.in_units_of(units.petameters_per_day) @property def petameters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_year) + return self.quantity.in_units_of(units.petameters_per_year) @property def terameters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_second) + return self.quantity.in_units_of(units.terameters_per_second) @property def terameters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_millisecond) + return self.quantity.in_units_of(units.terameters_per_millisecond) @property def terameters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_microsecond) + return self.quantity.in_units_of(units.terameters_per_microsecond) @property def terameters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_nanosecond) + return self.quantity.in_units_of(units.terameters_per_nanosecond) @property def terameters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_picosecond) + return self.quantity.in_units_of(units.terameters_per_picosecond) @property def terameters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_femtosecond) + return self.quantity.in_units_of(units.terameters_per_femtosecond) @property def terameters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_attosecond) + return self.quantity.in_units_of(units.terameters_per_attosecond) @property def terameters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_minute) + return self.quantity.in_units_of(units.terameters_per_minute) @property def terameters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_hour) + return self.quantity.in_units_of(units.terameters_per_hour) @property def terameters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_day) + return self.quantity.in_units_of(units.terameters_per_day) @property def terameters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_year) + return self.quantity.in_units_of(units.terameters_per_year) @property def gigameters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_second) + return self.quantity.in_units_of(units.gigameters_per_second) @property def gigameters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_millisecond) + return self.quantity.in_units_of(units.gigameters_per_millisecond) @property def gigameters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_microsecond) + return self.quantity.in_units_of(units.gigameters_per_microsecond) @property def gigameters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_nanosecond) + return self.quantity.in_units_of(units.gigameters_per_nanosecond) @property def gigameters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_picosecond) + return self.quantity.in_units_of(units.gigameters_per_picosecond) @property def gigameters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_femtosecond) + return self.quantity.in_units_of(units.gigameters_per_femtosecond) @property def gigameters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_attosecond) + return self.quantity.in_units_of(units.gigameters_per_attosecond) @property def gigameters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_minute) + return self.quantity.in_units_of(units.gigameters_per_minute) @property def gigameters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_hour) + return self.quantity.in_units_of(units.gigameters_per_hour) @property def gigameters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_day) + return self.quantity.in_units_of(units.gigameters_per_day) @property def gigameters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_year) + return self.quantity.in_units_of(units.gigameters_per_year) @property def megameters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_second) + return self.quantity.in_units_of(units.megameters_per_second) @property def megameters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_millisecond) + return self.quantity.in_units_of(units.megameters_per_millisecond) @property def megameters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_microsecond) + return self.quantity.in_units_of(units.megameters_per_microsecond) @property def megameters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_nanosecond) + return self.quantity.in_units_of(units.megameters_per_nanosecond) @property def megameters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_picosecond) + return self.quantity.in_units_of(units.megameters_per_picosecond) @property def megameters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_femtosecond) + return self.quantity.in_units_of(units.megameters_per_femtosecond) @property def megameters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_attosecond) + return self.quantity.in_units_of(units.megameters_per_attosecond) @property def megameters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_minute) + return self.quantity.in_units_of(units.megameters_per_minute) @property def megameters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_hour) + return self.quantity.in_units_of(units.megameters_per_hour) @property def megameters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_day) + return self.quantity.in_units_of(units.megameters_per_day) @property def megameters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_year) + return self.quantity.in_units_of(units.megameters_per_year) @property def kilometers_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_second) + return self.quantity.in_units_of(units.kilometers_per_second) @property def kilometers_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_millisecond) + return self.quantity.in_units_of(units.kilometers_per_millisecond) @property def kilometers_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_microsecond) + return self.quantity.in_units_of(units.kilometers_per_microsecond) @property def kilometers_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_nanosecond) + return self.quantity.in_units_of(units.kilometers_per_nanosecond) @property def kilometers_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_picosecond) + return self.quantity.in_units_of(units.kilometers_per_picosecond) @property def kilometers_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_femtosecond) + return self.quantity.in_units_of(units.kilometers_per_femtosecond) @property def kilometers_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_attosecond) + return self.quantity.in_units_of(units.kilometers_per_attosecond) @property def kilometers_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_minute) + return self.quantity.in_units_of(units.kilometers_per_minute) @property def kilometers_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_hour) + return self.quantity.in_units_of(units.kilometers_per_hour) @property def kilometers_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_day) + return self.quantity.in_units_of(units.kilometers_per_day) @property def kilometers_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_year) + return self.quantity.in_units_of(units.kilometers_per_year) @property def millimeters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_second) + return self.quantity.in_units_of(units.millimeters_per_second) @property def millimeters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_millisecond) + return self.quantity.in_units_of(units.millimeters_per_millisecond) @property def millimeters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_microsecond) + return self.quantity.in_units_of(units.millimeters_per_microsecond) @property def millimeters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_nanosecond) + return self.quantity.in_units_of(units.millimeters_per_nanosecond) @property def millimeters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_picosecond) + return self.quantity.in_units_of(units.millimeters_per_picosecond) @property def millimeters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_femtosecond) + return self.quantity.in_units_of(units.millimeters_per_femtosecond) @property def millimeters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_attosecond) + return self.quantity.in_units_of(units.millimeters_per_attosecond) @property def millimeters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_minute) + return self.quantity.in_units_of(units.millimeters_per_minute) @property def millimeters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_hour) + return self.quantity.in_units_of(units.millimeters_per_hour) @property def millimeters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_day) + return self.quantity.in_units_of(units.millimeters_per_day) @property def millimeters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_year) + return self.quantity.in_units_of(units.millimeters_per_year) @property def micrometers_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_second) + return self.quantity.in_units_of(units.micrometers_per_second) @property def micrometers_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_millisecond) + return self.quantity.in_units_of(units.micrometers_per_millisecond) @property def micrometers_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_microsecond) + return self.quantity.in_units_of(units.micrometers_per_microsecond) @property def micrometers_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_nanosecond) + return self.quantity.in_units_of(units.micrometers_per_nanosecond) @property def micrometers_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_picosecond) + return self.quantity.in_units_of(units.micrometers_per_picosecond) @property def micrometers_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_femtosecond) + return self.quantity.in_units_of(units.micrometers_per_femtosecond) @property def micrometers_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_attosecond) + return self.quantity.in_units_of(units.micrometers_per_attosecond) @property def micrometers_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_minute) + return self.quantity.in_units_of(units.micrometers_per_minute) @property def micrometers_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_hour) + return self.quantity.in_units_of(units.micrometers_per_hour) @property def micrometers_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_day) + return self.quantity.in_units_of(units.micrometers_per_day) @property def micrometers_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_year) + return self.quantity.in_units_of(units.micrometers_per_year) @property def nanometers_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_second) + return self.quantity.in_units_of(units.nanometers_per_second) @property def nanometers_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_millisecond) + return self.quantity.in_units_of(units.nanometers_per_millisecond) @property def nanometers_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_microsecond) + return self.quantity.in_units_of(units.nanometers_per_microsecond) @property def nanometers_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_nanosecond) + return self.quantity.in_units_of(units.nanometers_per_nanosecond) @property def nanometers_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_picosecond) + return self.quantity.in_units_of(units.nanometers_per_picosecond) @property def nanometers_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_femtosecond) + return self.quantity.in_units_of(units.nanometers_per_femtosecond) @property def nanometers_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_attosecond) + return self.quantity.in_units_of(units.nanometers_per_attosecond) @property def nanometers_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_minute) + return self.quantity.in_units_of(units.nanometers_per_minute) @property def nanometers_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_hour) + return self.quantity.in_units_of(units.nanometers_per_hour) @property def nanometers_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_day) + return self.quantity.in_units_of(units.nanometers_per_day) @property def nanometers_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_year) + return self.quantity.in_units_of(units.nanometers_per_year) @property def picometers_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_second) + return self.quantity.in_units_of(units.picometers_per_second) @property def picometers_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_millisecond) + return self.quantity.in_units_of(units.picometers_per_millisecond) @property def picometers_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_microsecond) + return self.quantity.in_units_of(units.picometers_per_microsecond) @property def picometers_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_nanosecond) + return self.quantity.in_units_of(units.picometers_per_nanosecond) @property def picometers_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_picosecond) + return self.quantity.in_units_of(units.picometers_per_picosecond) @property def picometers_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_femtosecond) + return self.quantity.in_units_of(units.picometers_per_femtosecond) @property def picometers_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_attosecond) + return self.quantity.in_units_of(units.picometers_per_attosecond) @property def picometers_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_minute) + return self.quantity.in_units_of(units.picometers_per_minute) @property def picometers_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_hour) + return self.quantity.in_units_of(units.picometers_per_hour) @property def picometers_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_day) + return self.quantity.in_units_of(units.picometers_per_day) @property def picometers_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_year) + return self.quantity.in_units_of(units.picometers_per_year) @property def femtometers_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_second) + return self.quantity.in_units_of(units.femtometers_per_second) @property def femtometers_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_millisecond) + return self.quantity.in_units_of(units.femtometers_per_millisecond) @property def femtometers_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_microsecond) + return self.quantity.in_units_of(units.femtometers_per_microsecond) @property def femtometers_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_nanosecond) + return self.quantity.in_units_of(units.femtometers_per_nanosecond) @property def femtometers_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_picosecond) + return self.quantity.in_units_of(units.femtometers_per_picosecond) @property def femtometers_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_femtosecond) + return self.quantity.in_units_of(units.femtometers_per_femtosecond) @property def femtometers_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_attosecond) + return self.quantity.in_units_of(units.femtometers_per_attosecond) @property def femtometers_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_minute) + return self.quantity.in_units_of(units.femtometers_per_minute) @property def femtometers_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_hour) + return self.quantity.in_units_of(units.femtometers_per_hour) @property def femtometers_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_day) + return self.quantity.in_units_of(units.femtometers_per_day) @property def femtometers_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_year) + return self.quantity.in_units_of(units.femtometers_per_year) @property def attometers_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_second) + return self.quantity.in_units_of(units.attometers_per_second) @property def attometers_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_millisecond) + return self.quantity.in_units_of(units.attometers_per_millisecond) @property def attometers_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_microsecond) + return self.quantity.in_units_of(units.attometers_per_microsecond) @property def attometers_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_nanosecond) + return self.quantity.in_units_of(units.attometers_per_nanosecond) @property def attometers_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_picosecond) + return self.quantity.in_units_of(units.attometers_per_picosecond) @property def attometers_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_femtosecond) + return self.quantity.in_units_of(units.attometers_per_femtosecond) @property def attometers_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_attosecond) + return self.quantity.in_units_of(units.attometers_per_attosecond) @property def attometers_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_minute) + return self.quantity.in_units_of(units.attometers_per_minute) @property def attometers_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_hour) + return self.quantity.in_units_of(units.attometers_per_hour) @property def attometers_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_day) + return self.quantity.in_units_of(units.attometers_per_day) @property def attometers_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_year) + return self.quantity.in_units_of(units.attometers_per_year) @property def decimeters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_second) + return self.quantity.in_units_of(units.decimeters_per_second) @property def decimeters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_millisecond) + return self.quantity.in_units_of(units.decimeters_per_millisecond) @property def decimeters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_microsecond) + return self.quantity.in_units_of(units.decimeters_per_microsecond) @property def decimeters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_nanosecond) + return self.quantity.in_units_of(units.decimeters_per_nanosecond) @property def decimeters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_picosecond) + return self.quantity.in_units_of(units.decimeters_per_picosecond) @property def decimeters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_femtosecond) + return self.quantity.in_units_of(units.decimeters_per_femtosecond) @property def decimeters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_attosecond) + return self.quantity.in_units_of(units.decimeters_per_attosecond) @property def decimeters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_minute) + return self.quantity.in_units_of(units.decimeters_per_minute) @property def decimeters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_hour) + return self.quantity.in_units_of(units.decimeters_per_hour) @property def decimeters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_day) + return self.quantity.in_units_of(units.decimeters_per_day) @property def decimeters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_year) + return self.quantity.in_units_of(units.decimeters_per_year) @property def centimeters_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_second) + return self.quantity.in_units_of(units.centimeters_per_second) @property def centimeters_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_millisecond) + return self.quantity.in_units_of(units.centimeters_per_millisecond) @property def centimeters_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_microsecond) + return self.quantity.in_units_of(units.centimeters_per_microsecond) @property def centimeters_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_nanosecond) + return self.quantity.in_units_of(units.centimeters_per_nanosecond) @property def centimeters_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_picosecond) + return self.quantity.in_units_of(units.centimeters_per_picosecond) @property def centimeters_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_femtosecond) + return self.quantity.in_units_of(units.centimeters_per_femtosecond) @property def centimeters_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_attosecond) + return self.quantity.in_units_of(units.centimeters_per_attosecond) @property def centimeters_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_minute) + return self.quantity.in_units_of(units.centimeters_per_minute) @property def centimeters_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_hour) + return self.quantity.in_units_of(units.centimeters_per_hour) @property def centimeters_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_day) + return self.quantity.in_units_of(units.centimeters_per_day) @property def centimeters_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_year) + return self.quantity.in_units_of(units.centimeters_per_year) @property def angstroms_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_second) + return self.quantity.in_units_of(units.angstroms_per_second) @property def angstroms_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_millisecond) + return self.quantity.in_units_of(units.angstroms_per_millisecond) @property def angstroms_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_microsecond) + return self.quantity.in_units_of(units.angstroms_per_microsecond) @property def angstroms_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_nanosecond) + return self.quantity.in_units_of(units.angstroms_per_nanosecond) @property def angstroms_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_picosecond) + return self.quantity.in_units_of(units.angstroms_per_picosecond) @property def angstroms_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_femtosecond) + return self.quantity.in_units_of(units.angstroms_per_femtosecond) @property def angstroms_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_attosecond) + return self.quantity.in_units_of(units.angstroms_per_attosecond) @property def angstroms_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_minute) + return self.quantity.in_units_of(units.angstroms_per_minute) @property def angstroms_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_hour) + return self.quantity.in_units_of(units.angstroms_per_hour) @property def angstroms_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_day) + return self.quantity.in_units_of(units.angstroms_per_day) @property def angstroms_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_year) - - @property - def miles_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_second) - - @property - def miles_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_millisecond) - - @property - def miles_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_microsecond) + return self.quantity.in_units_of(units.angstroms_per_year) - @property - def miles_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_nanosecond) - - @property - def miles_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_picosecond) - - @property - def miles_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_femtosecond) - - @property - def miles_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_attosecond) - - @property - def miles_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_minute) - - @property - def miles_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_hour) - - @property - def miles_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_day) - - @property - def miles_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_year) - - @property - def yards_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_second) - - @property - def yards_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_millisecond) - - @property - def yards_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_microsecond) - - @property - def yards_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_nanosecond) - - @property - def yards_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_picosecond) - - @property - def yards_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_femtosecond) - - @property - def yards_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_attosecond) - - @property - def yards_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_minute) - @property - def yards_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_hour) - @property - def yards_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_day) - - @property - def yards_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_year) - - @property - def feet_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_second) - - @property - def feet_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_millisecond) - - @property - def feet_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_microsecond) - - @property - def feet_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_nanosecond) - - @property - def feet_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_picosecond) - - @property - def feet_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_femtosecond) - - @property - def feet_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_attosecond) - - @property - def feet_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_minute) - - @property - def feet_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_hour) - - @property - def feet_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_day) - - @property - def feet_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_year) - - @property - def inches_per_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_second) - - @property - def inches_per_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_millisecond) - - @property - def inches_per_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_microsecond) - - @property - def inches_per_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_nanosecond) - - @property - def inches_per_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_picosecond) - - @property - def inches_per_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_femtosecond) - - @property - def inches_per_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_attosecond) - - @property - def inches_per_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_minute) - - @property - def inches_per_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_hour) - - @property - def inches_per_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_day) - - @property - def inches_per_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_year) - - - -class AccelerationAccessor[T](QuantityAccessor[T]): +class AccelerationAccessor[T](Accessor[T]): dimension_name = 'acceleration' @property def meters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_second) + return self.quantity.in_units_of(units.meters_per_square_second) @property def meters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_millisecond) + return self.quantity.in_units_of(units.meters_per_square_millisecond) @property def meters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_microsecond) + return self.quantity.in_units_of(units.meters_per_square_microsecond) @property def meters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_nanosecond) + return self.quantity.in_units_of(units.meters_per_square_nanosecond) @property def meters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_picosecond) + return self.quantity.in_units_of(units.meters_per_square_picosecond) @property def meters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_femtosecond) + return self.quantity.in_units_of(units.meters_per_square_femtosecond) @property def meters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_attosecond) + return self.quantity.in_units_of(units.meters_per_square_attosecond) @property def meters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_minute) + return self.quantity.in_units_of(units.meters_per_square_minute) @property def meters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_hour) + return self.quantity.in_units_of(units.meters_per_square_hour) @property def meters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_day) + return self.quantity.in_units_of(units.meters_per_square_day) @property def meters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meters_per_square_year) + return self.quantity.in_units_of(units.meters_per_square_year) @property def exameters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_second) + return self.quantity.in_units_of(units.exameters_per_square_second) @property def exameters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_millisecond) + return self.quantity.in_units_of(units.exameters_per_square_millisecond) @property def exameters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_microsecond) + return self.quantity.in_units_of(units.exameters_per_square_microsecond) @property def exameters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_nanosecond) + return self.quantity.in_units_of(units.exameters_per_square_nanosecond) @property def exameters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_picosecond) + return self.quantity.in_units_of(units.exameters_per_square_picosecond) @property def exameters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_femtosecond) + return self.quantity.in_units_of(units.exameters_per_square_femtosecond) @property def exameters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_attosecond) + return self.quantity.in_units_of(units.exameters_per_square_attosecond) @property def exameters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_minute) + return self.quantity.in_units_of(units.exameters_per_square_minute) @property def exameters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_hour) + return self.quantity.in_units_of(units.exameters_per_square_hour) @property def exameters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_day) + return self.quantity.in_units_of(units.exameters_per_square_day) @property def exameters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exameters_per_square_year) + return self.quantity.in_units_of(units.exameters_per_square_year) @property def petameters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_second) + return self.quantity.in_units_of(units.petameters_per_square_second) @property def petameters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_millisecond) + return self.quantity.in_units_of(units.petameters_per_square_millisecond) @property def petameters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_microsecond) + return self.quantity.in_units_of(units.petameters_per_square_microsecond) @property def petameters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_nanosecond) + return self.quantity.in_units_of(units.petameters_per_square_nanosecond) @property def petameters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_picosecond) + return self.quantity.in_units_of(units.petameters_per_square_picosecond) @property def petameters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_femtosecond) + return self.quantity.in_units_of(units.petameters_per_square_femtosecond) @property def petameters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_attosecond) + return self.quantity.in_units_of(units.petameters_per_square_attosecond) @property def petameters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_minute) + return self.quantity.in_units_of(units.petameters_per_square_minute) @property def petameters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_hour) + return self.quantity.in_units_of(units.petameters_per_square_hour) @property def petameters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_day) + return self.quantity.in_units_of(units.petameters_per_square_day) @property def petameters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petameters_per_square_year) + return self.quantity.in_units_of(units.petameters_per_square_year) @property def terameters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_second) + return self.quantity.in_units_of(units.terameters_per_square_second) @property def terameters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_millisecond) + return self.quantity.in_units_of(units.terameters_per_square_millisecond) @property def terameters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_microsecond) + return self.quantity.in_units_of(units.terameters_per_square_microsecond) @property def terameters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_nanosecond) + return self.quantity.in_units_of(units.terameters_per_square_nanosecond) @property def terameters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_picosecond) + return self.quantity.in_units_of(units.terameters_per_square_picosecond) @property def terameters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_femtosecond) + return self.quantity.in_units_of(units.terameters_per_square_femtosecond) @property def terameters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_attosecond) + return self.quantity.in_units_of(units.terameters_per_square_attosecond) @property def terameters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_minute) + return self.quantity.in_units_of(units.terameters_per_square_minute) @property def terameters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_hour) + return self.quantity.in_units_of(units.terameters_per_square_hour) @property def terameters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_day) + return self.quantity.in_units_of(units.terameters_per_square_day) @property def terameters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terameters_per_square_year) + return self.quantity.in_units_of(units.terameters_per_square_year) @property def gigameters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_second) + return self.quantity.in_units_of(units.gigameters_per_square_second) @property def gigameters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_millisecond) + return self.quantity.in_units_of(units.gigameters_per_square_millisecond) @property def gigameters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_microsecond) + return self.quantity.in_units_of(units.gigameters_per_square_microsecond) @property def gigameters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_nanosecond) + return self.quantity.in_units_of(units.gigameters_per_square_nanosecond) @property def gigameters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_picosecond) + return self.quantity.in_units_of(units.gigameters_per_square_picosecond) @property def gigameters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_femtosecond) + return self.quantity.in_units_of(units.gigameters_per_square_femtosecond) @property def gigameters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_attosecond) + return self.quantity.in_units_of(units.gigameters_per_square_attosecond) @property def gigameters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_minute) + return self.quantity.in_units_of(units.gigameters_per_square_minute) @property def gigameters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_hour) + return self.quantity.in_units_of(units.gigameters_per_square_hour) @property def gigameters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_day) + return self.quantity.in_units_of(units.gigameters_per_square_day) @property def gigameters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigameters_per_square_year) + return self.quantity.in_units_of(units.gigameters_per_square_year) @property def megameters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_second) + return self.quantity.in_units_of(units.megameters_per_square_second) @property def megameters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_millisecond) + return self.quantity.in_units_of(units.megameters_per_square_millisecond) @property def megameters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_microsecond) + return self.quantity.in_units_of(units.megameters_per_square_microsecond) @property def megameters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_nanosecond) + return self.quantity.in_units_of(units.megameters_per_square_nanosecond) @property def megameters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_picosecond) + return self.quantity.in_units_of(units.megameters_per_square_picosecond) @property def megameters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_femtosecond) + return self.quantity.in_units_of(units.megameters_per_square_femtosecond) @property def megameters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_attosecond) + return self.quantity.in_units_of(units.megameters_per_square_attosecond) @property def megameters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_minute) + return self.quantity.in_units_of(units.megameters_per_square_minute) @property def megameters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_hour) + return self.quantity.in_units_of(units.megameters_per_square_hour) @property def megameters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_day) + return self.quantity.in_units_of(units.megameters_per_square_day) @property def megameters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megameters_per_square_year) + return self.quantity.in_units_of(units.megameters_per_square_year) @property def kilometers_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_second) + return self.quantity.in_units_of(units.kilometers_per_square_second) @property def kilometers_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_millisecond) + return self.quantity.in_units_of(units.kilometers_per_square_millisecond) @property def kilometers_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_microsecond) + return self.quantity.in_units_of(units.kilometers_per_square_microsecond) @property def kilometers_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_nanosecond) + return self.quantity.in_units_of(units.kilometers_per_square_nanosecond) @property def kilometers_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_picosecond) + return self.quantity.in_units_of(units.kilometers_per_square_picosecond) @property def kilometers_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_femtosecond) + return self.quantity.in_units_of(units.kilometers_per_square_femtosecond) @property def kilometers_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_attosecond) + return self.quantity.in_units_of(units.kilometers_per_square_attosecond) @property def kilometers_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_minute) + return self.quantity.in_units_of(units.kilometers_per_square_minute) @property def kilometers_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_hour) + return self.quantity.in_units_of(units.kilometers_per_square_hour) @property def kilometers_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_day) + return self.quantity.in_units_of(units.kilometers_per_square_day) @property def kilometers_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilometers_per_square_year) + return self.quantity.in_units_of(units.kilometers_per_square_year) @property def millimeters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_second) + return self.quantity.in_units_of(units.millimeters_per_square_second) @property def millimeters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_millisecond) + return self.quantity.in_units_of(units.millimeters_per_square_millisecond) @property def millimeters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_microsecond) + return self.quantity.in_units_of(units.millimeters_per_square_microsecond) @property def millimeters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_nanosecond) + return self.quantity.in_units_of(units.millimeters_per_square_nanosecond) @property def millimeters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_picosecond) + return self.quantity.in_units_of(units.millimeters_per_square_picosecond) @property def millimeters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_femtosecond) + return self.quantity.in_units_of(units.millimeters_per_square_femtosecond) @property def millimeters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_attosecond) + return self.quantity.in_units_of(units.millimeters_per_square_attosecond) @property def millimeters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_minute) + return self.quantity.in_units_of(units.millimeters_per_square_minute) @property def millimeters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_hour) + return self.quantity.in_units_of(units.millimeters_per_square_hour) @property def millimeters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_day) + return self.quantity.in_units_of(units.millimeters_per_square_day) @property def millimeters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimeters_per_square_year) + return self.quantity.in_units_of(units.millimeters_per_square_year) @property def micrometers_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_second) + return self.quantity.in_units_of(units.micrometers_per_square_second) @property def micrometers_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_millisecond) + return self.quantity.in_units_of(units.micrometers_per_square_millisecond) @property def micrometers_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_microsecond) + return self.quantity.in_units_of(units.micrometers_per_square_microsecond) @property def micrometers_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_nanosecond) + return self.quantity.in_units_of(units.micrometers_per_square_nanosecond) @property def micrometers_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_picosecond) + return self.quantity.in_units_of(units.micrometers_per_square_picosecond) @property def micrometers_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_femtosecond) + return self.quantity.in_units_of(units.micrometers_per_square_femtosecond) @property def micrometers_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_attosecond) + return self.quantity.in_units_of(units.micrometers_per_square_attosecond) @property def micrometers_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_minute) + return self.quantity.in_units_of(units.micrometers_per_square_minute) @property def micrometers_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_hour) + return self.quantity.in_units_of(units.micrometers_per_square_hour) @property def micrometers_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_day) + return self.quantity.in_units_of(units.micrometers_per_square_day) @property def micrometers_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrometers_per_square_year) + return self.quantity.in_units_of(units.micrometers_per_square_year) @property def nanometers_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_second) + return self.quantity.in_units_of(units.nanometers_per_square_second) @property def nanometers_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_millisecond) + return self.quantity.in_units_of(units.nanometers_per_square_millisecond) @property def nanometers_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_microsecond) + return self.quantity.in_units_of(units.nanometers_per_square_microsecond) @property def nanometers_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_nanosecond) + return self.quantity.in_units_of(units.nanometers_per_square_nanosecond) @property def nanometers_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_picosecond) + return self.quantity.in_units_of(units.nanometers_per_square_picosecond) @property def nanometers_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_femtosecond) + return self.quantity.in_units_of(units.nanometers_per_square_femtosecond) @property def nanometers_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_attosecond) + return self.quantity.in_units_of(units.nanometers_per_square_attosecond) @property def nanometers_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_minute) + return self.quantity.in_units_of(units.nanometers_per_square_minute) @property def nanometers_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_hour) + return self.quantity.in_units_of(units.nanometers_per_square_hour) @property def nanometers_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_day) + return self.quantity.in_units_of(units.nanometers_per_square_day) @property def nanometers_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanometers_per_square_year) + return self.quantity.in_units_of(units.nanometers_per_square_year) @property def picometers_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_second) + return self.quantity.in_units_of(units.picometers_per_square_second) @property def picometers_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_millisecond) + return self.quantity.in_units_of(units.picometers_per_square_millisecond) @property def picometers_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_microsecond) + return self.quantity.in_units_of(units.picometers_per_square_microsecond) @property def picometers_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_nanosecond) + return self.quantity.in_units_of(units.picometers_per_square_nanosecond) @property def picometers_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_picosecond) + return self.quantity.in_units_of(units.picometers_per_square_picosecond) @property def picometers_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_femtosecond) + return self.quantity.in_units_of(units.picometers_per_square_femtosecond) @property def picometers_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_attosecond) + return self.quantity.in_units_of(units.picometers_per_square_attosecond) @property def picometers_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_minute) + return self.quantity.in_units_of(units.picometers_per_square_minute) @property def picometers_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_hour) + return self.quantity.in_units_of(units.picometers_per_square_hour) @property def picometers_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_day) + return self.quantity.in_units_of(units.picometers_per_square_day) @property def picometers_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picometers_per_square_year) + return self.quantity.in_units_of(units.picometers_per_square_year) @property def femtometers_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_second) + return self.quantity.in_units_of(units.femtometers_per_square_second) @property def femtometers_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_millisecond) + return self.quantity.in_units_of(units.femtometers_per_square_millisecond) @property def femtometers_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_microsecond) + return self.quantity.in_units_of(units.femtometers_per_square_microsecond) @property def femtometers_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_nanosecond) + return self.quantity.in_units_of(units.femtometers_per_square_nanosecond) @property def femtometers_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_picosecond) + return self.quantity.in_units_of(units.femtometers_per_square_picosecond) @property def femtometers_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_femtosecond) + return self.quantity.in_units_of(units.femtometers_per_square_femtosecond) @property def femtometers_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_attosecond) + return self.quantity.in_units_of(units.femtometers_per_square_attosecond) @property def femtometers_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_minute) + return self.quantity.in_units_of(units.femtometers_per_square_minute) @property def femtometers_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_hour) + return self.quantity.in_units_of(units.femtometers_per_square_hour) @property def femtometers_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_day) + return self.quantity.in_units_of(units.femtometers_per_square_day) @property def femtometers_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtometers_per_square_year) + return self.quantity.in_units_of(units.femtometers_per_square_year) @property def attometers_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_second) + return self.quantity.in_units_of(units.attometers_per_square_second) @property def attometers_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_millisecond) + return self.quantity.in_units_of(units.attometers_per_square_millisecond) @property def attometers_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_microsecond) + return self.quantity.in_units_of(units.attometers_per_square_microsecond) @property def attometers_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_nanosecond) + return self.quantity.in_units_of(units.attometers_per_square_nanosecond) @property def attometers_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_picosecond) + return self.quantity.in_units_of(units.attometers_per_square_picosecond) @property def attometers_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_femtosecond) + return self.quantity.in_units_of(units.attometers_per_square_femtosecond) @property def attometers_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_attosecond) + return self.quantity.in_units_of(units.attometers_per_square_attosecond) @property def attometers_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_minute) + return self.quantity.in_units_of(units.attometers_per_square_minute) @property def attometers_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_hour) + return self.quantity.in_units_of(units.attometers_per_square_hour) @property def attometers_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_day) + return self.quantity.in_units_of(units.attometers_per_square_day) @property def attometers_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attometers_per_square_year) + return self.quantity.in_units_of(units.attometers_per_square_year) @property def decimeters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_second) + return self.quantity.in_units_of(units.decimeters_per_square_second) @property def decimeters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_millisecond) + return self.quantity.in_units_of(units.decimeters_per_square_millisecond) @property def decimeters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_microsecond) + return self.quantity.in_units_of(units.decimeters_per_square_microsecond) @property def decimeters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_nanosecond) + return self.quantity.in_units_of(units.decimeters_per_square_nanosecond) @property def decimeters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_picosecond) + return self.quantity.in_units_of(units.decimeters_per_square_picosecond) @property def decimeters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_femtosecond) + return self.quantity.in_units_of(units.decimeters_per_square_femtosecond) @property def decimeters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_attosecond) + return self.quantity.in_units_of(units.decimeters_per_square_attosecond) @property def decimeters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_minute) + return self.quantity.in_units_of(units.decimeters_per_square_minute) @property def decimeters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_hour) + return self.quantity.in_units_of(units.decimeters_per_square_hour) @property def decimeters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_day) + return self.quantity.in_units_of(units.decimeters_per_square_day) @property def decimeters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.decimeters_per_square_year) + return self.quantity.in_units_of(units.decimeters_per_square_year) @property def centimeters_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_second) + return self.quantity.in_units_of(units.centimeters_per_square_second) @property def centimeters_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_millisecond) + return self.quantity.in_units_of(units.centimeters_per_square_millisecond) @property def centimeters_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_microsecond) + return self.quantity.in_units_of(units.centimeters_per_square_microsecond) @property def centimeters_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_nanosecond) + return self.quantity.in_units_of(units.centimeters_per_square_nanosecond) @property def centimeters_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_picosecond) + return self.quantity.in_units_of(units.centimeters_per_square_picosecond) @property def centimeters_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_femtosecond) + return self.quantity.in_units_of(units.centimeters_per_square_femtosecond) @property def centimeters_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_attosecond) + return self.quantity.in_units_of(units.centimeters_per_square_attosecond) @property def centimeters_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_minute) + return self.quantity.in_units_of(units.centimeters_per_square_minute) @property def centimeters_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_hour) + return self.quantity.in_units_of(units.centimeters_per_square_hour) @property def centimeters_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_day) + return self.quantity.in_units_of(units.centimeters_per_square_day) @property def centimeters_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.centimeters_per_square_year) + return self.quantity.in_units_of(units.centimeters_per_square_year) @property def angstroms_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_second) + return self.quantity.in_units_of(units.angstroms_per_square_second) @property def angstroms_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_millisecond) + return self.quantity.in_units_of(units.angstroms_per_square_millisecond) @property def angstroms_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_microsecond) + return self.quantity.in_units_of(units.angstroms_per_square_microsecond) @property def angstroms_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_nanosecond) + return self.quantity.in_units_of(units.angstroms_per_square_nanosecond) @property def angstroms_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_picosecond) + return self.quantity.in_units_of(units.angstroms_per_square_picosecond) @property def angstroms_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_femtosecond) + return self.quantity.in_units_of(units.angstroms_per_square_femtosecond) @property def angstroms_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_attosecond) + return self.quantity.in_units_of(units.angstroms_per_square_attosecond) @property def angstroms_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_minute) + return self.quantity.in_units_of(units.angstroms_per_square_minute) @property def angstroms_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_hour) + return self.quantity.in_units_of(units.angstroms_per_square_hour) @property def angstroms_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_day) + return self.quantity.in_units_of(units.angstroms_per_square_day) @property def angstroms_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.angstroms_per_square_year) - - @property - def miles_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_second) - - @property - def miles_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_millisecond) - - @property - def miles_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_microsecond) - - @property - def miles_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_nanosecond) - - @property - def miles_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_picosecond) - - @property - def miles_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_femtosecond) - - @property - def miles_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_attosecond) - - @property - def miles_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_minute) - - @property - def miles_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_hour) - - @property - def miles_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_day) - - @property - def miles_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.miles_per_square_year) - - @property - def yards_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_second) - - @property - def yards_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_millisecond) - - @property - def yards_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_microsecond) - - @property - def yards_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_nanosecond) - - @property - def yards_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_picosecond) - - @property - def yards_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_femtosecond) - - @property - def yards_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_attosecond) - - @property - def yards_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_minute) - - @property - def yards_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_hour) - - @property - def yards_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_day) - - @property - def yards_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.yards_per_square_year) - - @property - def feet_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_second) - - @property - def feet_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_millisecond) - - @property - def feet_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_microsecond) - - @property - def feet_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_nanosecond) - - @property - def feet_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_picosecond) - - @property - def feet_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_femtosecond) - - @property - def feet_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_attosecond) - - @property - def feet_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_minute) - - @property - def feet_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_hour) - - @property - def feet_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_day) - - @property - def feet_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.feet_per_square_year) - - @property - def inches_per_square_second(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_second) - - @property - def inches_per_square_millisecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_millisecond) - - @property - def inches_per_square_microsecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_microsecond) - - @property - def inches_per_square_nanosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_nanosecond) - - @property - def inches_per_square_picosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_picosecond) - - @property - def inches_per_square_femtosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_femtosecond) - - @property - def inches_per_square_attosecond(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_attosecond) - - @property - def inches_per_square_minute(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_minute) - - @property - def inches_per_square_hour(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_hour) - - @property - def inches_per_square_day(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_day) - - @property - def inches_per_square_year(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.inches_per_square_year) + return self.quantity.in_units_of(units.angstroms_per_square_year) -class DensityAccessor[T](QuantityAccessor[T]): +class DensityAccessor[T](Accessor[T]): dimension_name = 'density' @property def grams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_meter) + return self.quantity.in_units_of(units.grams_per_cubic_meter) @property def exagrams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_meter) + return self.quantity.in_units_of(units.exagrams_per_cubic_meter) @property def petagrams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_meter) + return self.quantity.in_units_of(units.petagrams_per_cubic_meter) @property def teragrams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_meter) + return self.quantity.in_units_of(units.teragrams_per_cubic_meter) @property def gigagrams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_meter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_meter) @property def megagrams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_meter) + return self.quantity.in_units_of(units.megagrams_per_cubic_meter) @property def kilograms_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_meter) + return self.quantity.in_units_of(units.kilograms_per_cubic_meter) @property def milligrams_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_meter) + return self.quantity.in_units_of(units.milligrams_per_cubic_meter) @property def micrograms_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_meter) + return self.quantity.in_units_of(units.micrograms_per_cubic_meter) @property def nanograms_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_meter) + return self.quantity.in_units_of(units.nanograms_per_cubic_meter) @property def picograms_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_meter) + return self.quantity.in_units_of(units.picograms_per_cubic_meter) @property def femtograms_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_meter) + return self.quantity.in_units_of(units.femtograms_per_cubic_meter) @property def attograms_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_meter) + return self.quantity.in_units_of(units.attograms_per_cubic_meter) @property def atomic_mass_units_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_meter) - - @property - def pounds_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_meter) - - @property - def ounces_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_meter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_meter) @property def grams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_exameter) + return self.quantity.in_units_of(units.grams_per_cubic_exameter) @property def exagrams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_exameter) + return self.quantity.in_units_of(units.exagrams_per_cubic_exameter) @property def petagrams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_exameter) + return self.quantity.in_units_of(units.petagrams_per_cubic_exameter) @property def teragrams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_exameter) + return self.quantity.in_units_of(units.teragrams_per_cubic_exameter) @property def gigagrams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_exameter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_exameter) @property def megagrams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_exameter) + return self.quantity.in_units_of(units.megagrams_per_cubic_exameter) @property def kilograms_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_exameter) + return self.quantity.in_units_of(units.kilograms_per_cubic_exameter) @property def milligrams_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_exameter) + return self.quantity.in_units_of(units.milligrams_per_cubic_exameter) @property def micrograms_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_exameter) + return self.quantity.in_units_of(units.micrograms_per_cubic_exameter) @property def nanograms_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_exameter) + return self.quantity.in_units_of(units.nanograms_per_cubic_exameter) @property def picograms_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_exameter) + return self.quantity.in_units_of(units.picograms_per_cubic_exameter) @property def femtograms_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_exameter) + return self.quantity.in_units_of(units.femtograms_per_cubic_exameter) @property def attograms_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_exameter) + return self.quantity.in_units_of(units.attograms_per_cubic_exameter) @property def atomic_mass_units_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_exameter) - - @property - def pounds_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_exameter) - - @property - def ounces_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_exameter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_exameter) @property def grams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_petameter) + return self.quantity.in_units_of(units.grams_per_cubic_petameter) @property def exagrams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_petameter) + return self.quantity.in_units_of(units.exagrams_per_cubic_petameter) @property def petagrams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_petameter) + return self.quantity.in_units_of(units.petagrams_per_cubic_petameter) @property def teragrams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_petameter) + return self.quantity.in_units_of(units.teragrams_per_cubic_petameter) @property def gigagrams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_petameter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_petameter) @property def megagrams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_petameter) + return self.quantity.in_units_of(units.megagrams_per_cubic_petameter) @property def kilograms_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_petameter) + return self.quantity.in_units_of(units.kilograms_per_cubic_petameter) @property def milligrams_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_petameter) + return self.quantity.in_units_of(units.milligrams_per_cubic_petameter) @property def micrograms_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_petameter) + return self.quantity.in_units_of(units.micrograms_per_cubic_petameter) @property def nanograms_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_petameter) + return self.quantity.in_units_of(units.nanograms_per_cubic_petameter) @property def picograms_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_petameter) + return self.quantity.in_units_of(units.picograms_per_cubic_petameter) @property def femtograms_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_petameter) + return self.quantity.in_units_of(units.femtograms_per_cubic_petameter) @property def attograms_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_petameter) + return self.quantity.in_units_of(units.attograms_per_cubic_petameter) @property def atomic_mass_units_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_petameter) - - @property - def pounds_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_petameter) - - @property - def ounces_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_petameter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_petameter) @property def grams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_terameter) + return self.quantity.in_units_of(units.grams_per_cubic_terameter) @property def exagrams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_terameter) + return self.quantity.in_units_of(units.exagrams_per_cubic_terameter) @property def petagrams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_terameter) + return self.quantity.in_units_of(units.petagrams_per_cubic_terameter) @property def teragrams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_terameter) + return self.quantity.in_units_of(units.teragrams_per_cubic_terameter) @property def gigagrams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_terameter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_terameter) @property def megagrams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_terameter) + return self.quantity.in_units_of(units.megagrams_per_cubic_terameter) @property def kilograms_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_terameter) + return self.quantity.in_units_of(units.kilograms_per_cubic_terameter) @property def milligrams_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_terameter) + return self.quantity.in_units_of(units.milligrams_per_cubic_terameter) @property def micrograms_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_terameter) + return self.quantity.in_units_of(units.micrograms_per_cubic_terameter) @property def nanograms_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_terameter) + return self.quantity.in_units_of(units.nanograms_per_cubic_terameter) @property def picograms_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_terameter) + return self.quantity.in_units_of(units.picograms_per_cubic_terameter) @property def femtograms_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_terameter) + return self.quantity.in_units_of(units.femtograms_per_cubic_terameter) @property def attograms_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_terameter) + return self.quantity.in_units_of(units.attograms_per_cubic_terameter) @property def atomic_mass_units_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_terameter) - - @property - def pounds_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_terameter) - - @property - def ounces_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_terameter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_terameter) @property def grams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_gigameter) + return self.quantity.in_units_of(units.grams_per_cubic_gigameter) @property def exagrams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_gigameter) + return self.quantity.in_units_of(units.exagrams_per_cubic_gigameter) @property def petagrams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_gigameter) + return self.quantity.in_units_of(units.petagrams_per_cubic_gigameter) @property def teragrams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_gigameter) + return self.quantity.in_units_of(units.teragrams_per_cubic_gigameter) @property def gigagrams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_gigameter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_gigameter) @property def megagrams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_gigameter) + return self.quantity.in_units_of(units.megagrams_per_cubic_gigameter) @property def kilograms_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_gigameter) + return self.quantity.in_units_of(units.kilograms_per_cubic_gigameter) @property def milligrams_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_gigameter) + return self.quantity.in_units_of(units.milligrams_per_cubic_gigameter) @property def micrograms_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_gigameter) + return self.quantity.in_units_of(units.micrograms_per_cubic_gigameter) @property def nanograms_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_gigameter) + return self.quantity.in_units_of(units.nanograms_per_cubic_gigameter) @property def picograms_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_gigameter) + return self.quantity.in_units_of(units.picograms_per_cubic_gigameter) @property def femtograms_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_gigameter) + return self.quantity.in_units_of(units.femtograms_per_cubic_gigameter) @property def attograms_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_gigameter) + return self.quantity.in_units_of(units.attograms_per_cubic_gigameter) @property def atomic_mass_units_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_gigameter) - - @property - def pounds_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_gigameter) - - @property - def ounces_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_gigameter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_gigameter) @property def grams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_megameter) + return self.quantity.in_units_of(units.grams_per_cubic_megameter) @property def exagrams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_megameter) + return self.quantity.in_units_of(units.exagrams_per_cubic_megameter) @property def petagrams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_megameter) + return self.quantity.in_units_of(units.petagrams_per_cubic_megameter) @property def teragrams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_megameter) + return self.quantity.in_units_of(units.teragrams_per_cubic_megameter) @property def gigagrams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_megameter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_megameter) @property def megagrams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_megameter) + return self.quantity.in_units_of(units.megagrams_per_cubic_megameter) @property def kilograms_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_megameter) + return self.quantity.in_units_of(units.kilograms_per_cubic_megameter) @property def milligrams_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_megameter) + return self.quantity.in_units_of(units.milligrams_per_cubic_megameter) @property def micrograms_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_megameter) + return self.quantity.in_units_of(units.micrograms_per_cubic_megameter) @property def nanograms_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_megameter) + return self.quantity.in_units_of(units.nanograms_per_cubic_megameter) @property def picograms_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_megameter) + return self.quantity.in_units_of(units.picograms_per_cubic_megameter) @property def femtograms_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_megameter) + return self.quantity.in_units_of(units.femtograms_per_cubic_megameter) @property def attograms_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_megameter) + return self.quantity.in_units_of(units.attograms_per_cubic_megameter) @property def atomic_mass_units_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_megameter) - - @property - def pounds_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_megameter) - - @property - def ounces_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_megameter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_megameter) @property def grams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_kilometer) + return self.quantity.in_units_of(units.grams_per_cubic_kilometer) @property def exagrams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_kilometer) + return self.quantity.in_units_of(units.exagrams_per_cubic_kilometer) @property def petagrams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_kilometer) + return self.quantity.in_units_of(units.petagrams_per_cubic_kilometer) @property def teragrams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_kilometer) + return self.quantity.in_units_of(units.teragrams_per_cubic_kilometer) @property def gigagrams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_kilometer) + return self.quantity.in_units_of(units.gigagrams_per_cubic_kilometer) @property def megagrams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_kilometer) + return self.quantity.in_units_of(units.megagrams_per_cubic_kilometer) @property def kilograms_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_kilometer) + return self.quantity.in_units_of(units.kilograms_per_cubic_kilometer) @property def milligrams_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_kilometer) + return self.quantity.in_units_of(units.milligrams_per_cubic_kilometer) @property def micrograms_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_kilometer) + return self.quantity.in_units_of(units.micrograms_per_cubic_kilometer) @property def nanograms_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_kilometer) + return self.quantity.in_units_of(units.nanograms_per_cubic_kilometer) @property def picograms_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_kilometer) + return self.quantity.in_units_of(units.picograms_per_cubic_kilometer) @property def femtograms_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_kilometer) + return self.quantity.in_units_of(units.femtograms_per_cubic_kilometer) @property def attograms_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_kilometer) + return self.quantity.in_units_of(units.attograms_per_cubic_kilometer) @property def atomic_mass_units_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_kilometer) - - @property - def pounds_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_kilometer) - - @property - def ounces_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_kilometer) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_kilometer) @property def grams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_millimeter) + return self.quantity.in_units_of(units.grams_per_cubic_millimeter) @property def exagrams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_millimeter) + return self.quantity.in_units_of(units.exagrams_per_cubic_millimeter) @property def petagrams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_millimeter) + return self.quantity.in_units_of(units.petagrams_per_cubic_millimeter) @property def teragrams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_millimeter) + return self.quantity.in_units_of(units.teragrams_per_cubic_millimeter) @property def gigagrams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_millimeter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_millimeter) @property def megagrams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_millimeter) + return self.quantity.in_units_of(units.megagrams_per_cubic_millimeter) @property def kilograms_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_millimeter) + return self.quantity.in_units_of(units.kilograms_per_cubic_millimeter) @property def milligrams_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_millimeter) + return self.quantity.in_units_of(units.milligrams_per_cubic_millimeter) @property def micrograms_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_millimeter) + return self.quantity.in_units_of(units.micrograms_per_cubic_millimeter) @property def nanograms_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_millimeter) + return self.quantity.in_units_of(units.nanograms_per_cubic_millimeter) @property def picograms_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_millimeter) + return self.quantity.in_units_of(units.picograms_per_cubic_millimeter) @property def femtograms_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_millimeter) + return self.quantity.in_units_of(units.femtograms_per_cubic_millimeter) @property def attograms_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_millimeter) + return self.quantity.in_units_of(units.attograms_per_cubic_millimeter) @property def atomic_mass_units_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_millimeter) - - @property - def pounds_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_millimeter) - - @property - def ounces_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_millimeter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_millimeter) @property def grams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_micrometer) + return self.quantity.in_units_of(units.grams_per_cubic_micrometer) @property def exagrams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_micrometer) + return self.quantity.in_units_of(units.exagrams_per_cubic_micrometer) @property def petagrams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_micrometer) + return self.quantity.in_units_of(units.petagrams_per_cubic_micrometer) @property def teragrams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_micrometer) + return self.quantity.in_units_of(units.teragrams_per_cubic_micrometer) @property def gigagrams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_micrometer) + return self.quantity.in_units_of(units.gigagrams_per_cubic_micrometer) @property def megagrams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_micrometer) + return self.quantity.in_units_of(units.megagrams_per_cubic_micrometer) @property def kilograms_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_micrometer) + return self.quantity.in_units_of(units.kilograms_per_cubic_micrometer) @property def milligrams_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_micrometer) + return self.quantity.in_units_of(units.milligrams_per_cubic_micrometer) @property def micrograms_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_micrometer) + return self.quantity.in_units_of(units.micrograms_per_cubic_micrometer) @property def nanograms_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_micrometer) + return self.quantity.in_units_of(units.nanograms_per_cubic_micrometer) @property def picograms_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_micrometer) + return self.quantity.in_units_of(units.picograms_per_cubic_micrometer) @property def femtograms_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_micrometer) + return self.quantity.in_units_of(units.femtograms_per_cubic_micrometer) @property def attograms_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_micrometer) + return self.quantity.in_units_of(units.attograms_per_cubic_micrometer) @property def atomic_mass_units_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_micrometer) - - @property - def pounds_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_micrometer) - - @property - def ounces_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_micrometer) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_micrometer) @property def grams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_nanometer) + return self.quantity.in_units_of(units.grams_per_cubic_nanometer) @property def exagrams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_nanometer) + return self.quantity.in_units_of(units.exagrams_per_cubic_nanometer) @property def petagrams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_nanometer) + return self.quantity.in_units_of(units.petagrams_per_cubic_nanometer) @property def teragrams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_nanometer) + return self.quantity.in_units_of(units.teragrams_per_cubic_nanometer) @property def gigagrams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_nanometer) + return self.quantity.in_units_of(units.gigagrams_per_cubic_nanometer) @property def megagrams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_nanometer) + return self.quantity.in_units_of(units.megagrams_per_cubic_nanometer) @property def kilograms_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_nanometer) + return self.quantity.in_units_of(units.kilograms_per_cubic_nanometer) @property def milligrams_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_nanometer) + return self.quantity.in_units_of(units.milligrams_per_cubic_nanometer) @property def micrograms_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_nanometer) + return self.quantity.in_units_of(units.micrograms_per_cubic_nanometer) @property def nanograms_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_nanometer) + return self.quantity.in_units_of(units.nanograms_per_cubic_nanometer) @property def picograms_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_nanometer) + return self.quantity.in_units_of(units.picograms_per_cubic_nanometer) @property def femtograms_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_nanometer) + return self.quantity.in_units_of(units.femtograms_per_cubic_nanometer) @property def attograms_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_nanometer) + return self.quantity.in_units_of(units.attograms_per_cubic_nanometer) @property def atomic_mass_units_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_nanometer) - - @property - def pounds_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_nanometer) - - @property - def ounces_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_nanometer) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_nanometer) @property def grams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_picometer) + return self.quantity.in_units_of(units.grams_per_cubic_picometer) @property def exagrams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_picometer) + return self.quantity.in_units_of(units.exagrams_per_cubic_picometer) @property def petagrams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_picometer) + return self.quantity.in_units_of(units.petagrams_per_cubic_picometer) @property def teragrams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_picometer) + return self.quantity.in_units_of(units.teragrams_per_cubic_picometer) @property def gigagrams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_picometer) + return self.quantity.in_units_of(units.gigagrams_per_cubic_picometer) @property def megagrams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_picometer) + return self.quantity.in_units_of(units.megagrams_per_cubic_picometer) @property def kilograms_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_picometer) + return self.quantity.in_units_of(units.kilograms_per_cubic_picometer) @property def milligrams_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_picometer) + return self.quantity.in_units_of(units.milligrams_per_cubic_picometer) @property def micrograms_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_picometer) + return self.quantity.in_units_of(units.micrograms_per_cubic_picometer) @property def nanograms_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_picometer) + return self.quantity.in_units_of(units.nanograms_per_cubic_picometer) @property def picograms_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_picometer) + return self.quantity.in_units_of(units.picograms_per_cubic_picometer) @property def femtograms_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_picometer) + return self.quantity.in_units_of(units.femtograms_per_cubic_picometer) @property def attograms_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_picometer) + return self.quantity.in_units_of(units.attograms_per_cubic_picometer) @property def atomic_mass_units_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_picometer) - - @property - def pounds_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_picometer) - - @property - def ounces_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_picometer) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_picometer) @property def grams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_femtometer) + return self.quantity.in_units_of(units.grams_per_cubic_femtometer) @property def exagrams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_femtometer) + return self.quantity.in_units_of(units.exagrams_per_cubic_femtometer) @property def petagrams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_femtometer) + return self.quantity.in_units_of(units.petagrams_per_cubic_femtometer) @property def teragrams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_femtometer) + return self.quantity.in_units_of(units.teragrams_per_cubic_femtometer) @property def gigagrams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_femtometer) + return self.quantity.in_units_of(units.gigagrams_per_cubic_femtometer) @property def megagrams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_femtometer) + return self.quantity.in_units_of(units.megagrams_per_cubic_femtometer) @property def kilograms_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_femtometer) + return self.quantity.in_units_of(units.kilograms_per_cubic_femtometer) @property def milligrams_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_femtometer) + return self.quantity.in_units_of(units.milligrams_per_cubic_femtometer) @property def micrograms_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_femtometer) + return self.quantity.in_units_of(units.micrograms_per_cubic_femtometer) @property def nanograms_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_femtometer) + return self.quantity.in_units_of(units.nanograms_per_cubic_femtometer) @property def picograms_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_femtometer) + return self.quantity.in_units_of(units.picograms_per_cubic_femtometer) @property def femtograms_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_femtometer) + return self.quantity.in_units_of(units.femtograms_per_cubic_femtometer) @property def attograms_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_femtometer) + return self.quantity.in_units_of(units.attograms_per_cubic_femtometer) @property def atomic_mass_units_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_femtometer) - - @property - def pounds_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_femtometer) - - @property - def ounces_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_femtometer) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_femtometer) @property def grams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_attometer) + return self.quantity.in_units_of(units.grams_per_cubic_attometer) @property def exagrams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_attometer) + return self.quantity.in_units_of(units.exagrams_per_cubic_attometer) @property def petagrams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_attometer) + return self.quantity.in_units_of(units.petagrams_per_cubic_attometer) @property def teragrams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_attometer) + return self.quantity.in_units_of(units.teragrams_per_cubic_attometer) @property def gigagrams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_attometer) + return self.quantity.in_units_of(units.gigagrams_per_cubic_attometer) @property def megagrams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_attometer) + return self.quantity.in_units_of(units.megagrams_per_cubic_attometer) @property def kilograms_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_attometer) + return self.quantity.in_units_of(units.kilograms_per_cubic_attometer) @property def milligrams_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_attometer) + return self.quantity.in_units_of(units.milligrams_per_cubic_attometer) @property def micrograms_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_attometer) + return self.quantity.in_units_of(units.micrograms_per_cubic_attometer) @property def nanograms_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_attometer) + return self.quantity.in_units_of(units.nanograms_per_cubic_attometer) @property def picograms_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_attometer) + return self.quantity.in_units_of(units.picograms_per_cubic_attometer) @property def femtograms_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_attometer) + return self.quantity.in_units_of(units.femtograms_per_cubic_attometer) @property def attograms_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_attometer) + return self.quantity.in_units_of(units.attograms_per_cubic_attometer) @property def atomic_mass_units_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_attometer) - - @property - def pounds_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_attometer) - - @property - def ounces_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_attometer) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_attometer) @property def grams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_decimeter) + return self.quantity.in_units_of(units.grams_per_cubic_decimeter) @property def exagrams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_decimeter) + return self.quantity.in_units_of(units.exagrams_per_cubic_decimeter) @property def petagrams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_decimeter) + return self.quantity.in_units_of(units.petagrams_per_cubic_decimeter) @property def teragrams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_decimeter) + return self.quantity.in_units_of(units.teragrams_per_cubic_decimeter) @property def gigagrams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_decimeter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_decimeter) @property def megagrams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_decimeter) + return self.quantity.in_units_of(units.megagrams_per_cubic_decimeter) @property def kilograms_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_decimeter) + return self.quantity.in_units_of(units.kilograms_per_cubic_decimeter) @property def milligrams_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_decimeter) + return self.quantity.in_units_of(units.milligrams_per_cubic_decimeter) @property def micrograms_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_decimeter) + return self.quantity.in_units_of(units.micrograms_per_cubic_decimeter) @property def nanograms_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_decimeter) + return self.quantity.in_units_of(units.nanograms_per_cubic_decimeter) @property def picograms_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_decimeter) + return self.quantity.in_units_of(units.picograms_per_cubic_decimeter) @property def femtograms_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_decimeter) + return self.quantity.in_units_of(units.femtograms_per_cubic_decimeter) @property def attograms_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_decimeter) + return self.quantity.in_units_of(units.attograms_per_cubic_decimeter) @property def atomic_mass_units_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_decimeter) - - @property - def pounds_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_decimeter) - - @property - def ounces_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_decimeter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_decimeter) @property def grams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_centimeter) + return self.quantity.in_units_of(units.grams_per_cubic_centimeter) @property def exagrams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_centimeter) + return self.quantity.in_units_of(units.exagrams_per_cubic_centimeter) @property def petagrams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_centimeter) + return self.quantity.in_units_of(units.petagrams_per_cubic_centimeter) @property def teragrams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_centimeter) + return self.quantity.in_units_of(units.teragrams_per_cubic_centimeter) @property def gigagrams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_centimeter) + return self.quantity.in_units_of(units.gigagrams_per_cubic_centimeter) @property def megagrams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_centimeter) + return self.quantity.in_units_of(units.megagrams_per_cubic_centimeter) @property def kilograms_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_centimeter) + return self.quantity.in_units_of(units.kilograms_per_cubic_centimeter) @property def milligrams_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_centimeter) + return self.quantity.in_units_of(units.milligrams_per_cubic_centimeter) @property def micrograms_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_centimeter) + return self.quantity.in_units_of(units.micrograms_per_cubic_centimeter) @property def nanograms_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_centimeter) + return self.quantity.in_units_of(units.nanograms_per_cubic_centimeter) @property def picograms_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_centimeter) + return self.quantity.in_units_of(units.picograms_per_cubic_centimeter) @property def femtograms_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_centimeter) + return self.quantity.in_units_of(units.femtograms_per_cubic_centimeter) @property def attograms_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_centimeter) + return self.quantity.in_units_of(units.attograms_per_cubic_centimeter) @property def atomic_mass_units_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_centimeter) - - @property - def pounds_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_centimeter) - - @property - def ounces_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_centimeter) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_centimeter) @property def grams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_angstrom) + return self.quantity.in_units_of(units.grams_per_cubic_angstrom) @property def exagrams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_angstrom) + return self.quantity.in_units_of(units.exagrams_per_cubic_angstrom) @property def petagrams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_angstrom) + return self.quantity.in_units_of(units.petagrams_per_cubic_angstrom) @property def teragrams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_angstrom) + return self.quantity.in_units_of(units.teragrams_per_cubic_angstrom) @property def gigagrams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_angstrom) + return self.quantity.in_units_of(units.gigagrams_per_cubic_angstrom) @property def megagrams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_angstrom) + return self.quantity.in_units_of(units.megagrams_per_cubic_angstrom) @property def kilograms_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_angstrom) + return self.quantity.in_units_of(units.kilograms_per_cubic_angstrom) @property def milligrams_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_angstrom) + return self.quantity.in_units_of(units.milligrams_per_cubic_angstrom) @property def micrograms_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_angstrom) + return self.quantity.in_units_of(units.micrograms_per_cubic_angstrom) @property def nanograms_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_angstrom) + return self.quantity.in_units_of(units.nanograms_per_cubic_angstrom) @property def picograms_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_angstrom) + return self.quantity.in_units_of(units.picograms_per_cubic_angstrom) @property def femtograms_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_angstrom) + return self.quantity.in_units_of(units.femtograms_per_cubic_angstrom) @property def attograms_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_angstrom) + return self.quantity.in_units_of(units.attograms_per_cubic_angstrom) @property def atomic_mass_units_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_angstrom) - - @property - def pounds_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_angstrom) - - @property - def ounces_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_angstrom) - - @property - def grams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_mile) - - @property - def exagrams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_mile) - - @property - def petagrams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_mile) - - @property - def teragrams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_mile) - - @property - def gigagrams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_mile) - - @property - def megagrams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_mile) - - @property - def kilograms_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_mile) - - @property - def milligrams_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_mile) - - @property - def micrograms_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_mile) - - @property - def nanograms_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_mile) - - @property - def picograms_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_mile) - - @property - def femtograms_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_mile) - - @property - def attograms_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_mile) - - @property - def atomic_mass_units_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_mile) - - @property - def pounds_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_mile) - - @property - def ounces_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_mile) - - @property - def grams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_yard) - - @property - def exagrams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_yard) - - @property - def petagrams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_yard) - - @property - def teragrams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_yard) - - @property - def gigagrams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_yard) - - @property - def megagrams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_yard) - - @property - def kilograms_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_yard) - - @property - def milligrams_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_yard) - - @property - def micrograms_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_yard) + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_angstrom) - @property - def nanograms_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_yard) - @property - def picograms_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_yard) - @property - def femtograms_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_yard) - - @property - def attograms_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_yard) - - @property - def atomic_mass_units_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_yard) - - @property - def pounds_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_yard) - - @property - def ounces_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_yard) - - @property - def grams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_foot) - - @property - def exagrams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_foot) - - @property - def petagrams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_foot) - - @property - def teragrams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_foot) - - @property - def gigagrams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_foot) - - @property - def megagrams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_foot) - - @property - def kilograms_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_foot) - - @property - def milligrams_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_foot) - - @property - def micrograms_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_foot) - - @property - def nanograms_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_foot) - - @property - def picograms_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_foot) - - @property - def femtograms_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_foot) - - @property - def attograms_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_foot) - - @property - def atomic_mass_units_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_foot) - - @property - def pounds_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_foot) - - @property - def ounces_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_foot) - - @property - def grams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.grams_per_cubic_inch) - - @property - def exagrams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exagrams_per_cubic_inch) - - @property - def petagrams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petagrams_per_cubic_inch) - - @property - def teragrams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teragrams_per_cubic_inch) - - @property - def gigagrams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigagrams_per_cubic_inch) - - @property - def megagrams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megagrams_per_cubic_inch) - - @property - def kilograms_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilograms_per_cubic_inch) - - @property - def milligrams_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milligrams_per_cubic_inch) - - @property - def micrograms_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micrograms_per_cubic_inch) - - @property - def nanograms_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanograms_per_cubic_inch) - - @property - def picograms_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picograms_per_cubic_inch) - - @property - def femtograms_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtograms_per_cubic_inch) - - @property - def attograms_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attograms_per_cubic_inch) - - @property - def atomic_mass_units_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.atomic_mass_units_per_cubic_inch) - - @property - def pounds_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_per_cubic_inch) - - @property - def ounces_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ounces_per_cubic_inch) - - - -class ForceAccessor[T](QuantityAccessor[T]): +class ForceAccessor[T](Accessor[T]): dimension_name = 'force' @property def newtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.newtons) + return self.quantity.in_units_of(units.newtons) @property def exanewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exanewtons) + return self.quantity.in_units_of(units.exanewtons) @property def petanewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petanewtons) + return self.quantity.in_units_of(units.petanewtons) @property def teranewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teranewtons) + return self.quantity.in_units_of(units.teranewtons) @property def giganewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.giganewtons) + return self.quantity.in_units_of(units.giganewtons) @property def meganewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.meganewtons) + return self.quantity.in_units_of(units.meganewtons) @property def kilonewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilonewtons) + return self.quantity.in_units_of(units.kilonewtons) @property def millinewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millinewtons) + return self.quantity.in_units_of(units.millinewtons) @property def micronewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micronewtons) + return self.quantity.in_units_of(units.micronewtons) @property def nanonewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanonewtons) + return self.quantity.in_units_of(units.nanonewtons) @property def piconewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.piconewtons) + return self.quantity.in_units_of(units.piconewtons) @property def femtonewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtonewtons) + return self.quantity.in_units_of(units.femtonewtons) @property def attonewtons(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attonewtons) - - @property - def kg_force(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kg_force) - - @property - def pounds_force(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_force) + return self.quantity.in_units_of(units.attonewtons) -class PressureAccessor[T](QuantityAccessor[T]): +class PressureAccessor[T](Accessor[T]): dimension_name = 'pressure' @property def pascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pascals) + return self.quantity.in_units_of(units.pascals) @property def exapascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exapascals) + return self.quantity.in_units_of(units.exapascals) @property def petapascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petapascals) + return self.quantity.in_units_of(units.petapascals) @property def terapascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terapascals) + return self.quantity.in_units_of(units.terapascals) @property def gigapascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigapascals) + return self.quantity.in_units_of(units.gigapascals) @property def megapascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megapascals) + return self.quantity.in_units_of(units.megapascals) @property def kilopascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilopascals) + return self.quantity.in_units_of(units.kilopascals) @property def millipascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millipascals) + return self.quantity.in_units_of(units.millipascals) @property def micropascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micropascals) + return self.quantity.in_units_of(units.micropascals) @property def nanopascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanopascals) + return self.quantity.in_units_of(units.nanopascals) @property def picopascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picopascals) + return self.quantity.in_units_of(units.picopascals) @property def femtopascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtopascals) + return self.quantity.in_units_of(units.femtopascals) @property def attopascals(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attopascals) - - @property - def pounds_force_per_square_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.pounds_force_per_square_inch) + return self.quantity.in_units_of(units.attopascals) -class EnergyAccessor[T](QuantityAccessor[T]): +class EnergyAccessor[T](Accessor[T]): dimension_name = 'energy' @property def joules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.joules) + return self.quantity.in_units_of(units.joules) @property def exajoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exajoules) + return self.quantity.in_units_of(units.exajoules) @property def petajoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petajoules) + return self.quantity.in_units_of(units.petajoules) @property def terajoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terajoules) + return self.quantity.in_units_of(units.terajoules) @property def gigajoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigajoules) + return self.quantity.in_units_of(units.gigajoules) @property def megajoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megajoules) + return self.quantity.in_units_of(units.megajoules) @property def kilojoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilojoules) + return self.quantity.in_units_of(units.kilojoules) @property def millijoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millijoules) + return self.quantity.in_units_of(units.millijoules) @property def microjoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microjoules) + return self.quantity.in_units_of(units.microjoules) @property def nanojoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanojoules) + return self.quantity.in_units_of(units.nanojoules) @property def picojoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picojoules) + return self.quantity.in_units_of(units.picojoules) @property def femtojoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtojoules) + return self.quantity.in_units_of(units.femtojoules) @property def attojoules(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attojoules) + return self.quantity.in_units_of(units.attojoules) @property def electronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.electronvolts) + return self.quantity.in_units_of(units.electronvolts) @property def exaelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exaelectronvolts) + return self.quantity.in_units_of(units.exaelectronvolts) @property def petaelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petaelectronvolts) + return self.quantity.in_units_of(units.petaelectronvolts) @property def teraelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teraelectronvolts) + return self.quantity.in_units_of(units.teraelectronvolts) @property def gigaelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigaelectronvolts) + return self.quantity.in_units_of(units.gigaelectronvolts) @property def megaelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megaelectronvolts) + return self.quantity.in_units_of(units.megaelectronvolts) @property def kiloelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kiloelectronvolts) + return self.quantity.in_units_of(units.kiloelectronvolts) @property def millielectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millielectronvolts) + return self.quantity.in_units_of(units.millielectronvolts) @property def microelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microelectronvolts) + return self.quantity.in_units_of(units.microelectronvolts) @property def nanoelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanoelectronvolts) + return self.quantity.in_units_of(units.nanoelectronvolts) @property def picoelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picoelectronvolts) + return self.quantity.in_units_of(units.picoelectronvolts) @property def femtoelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtoelectronvolts) + return self.quantity.in_units_of(units.femtoelectronvolts) @property def attoelectronvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attoelectronvolts) + return self.quantity.in_units_of(units.attoelectronvolts) -class PowerAccessor[T](QuantityAccessor[T]): +class PowerAccessor[T](Accessor[T]): dimension_name = 'power' @property def watts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.watts) + return self.quantity.in_units_of(units.watts) @property def exawatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exawatts) + return self.quantity.in_units_of(units.exawatts) @property def petawatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petawatts) + return self.quantity.in_units_of(units.petawatts) @property def terawatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terawatts) + return self.quantity.in_units_of(units.terawatts) @property def gigawatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigawatts) + return self.quantity.in_units_of(units.gigawatts) @property def megawatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megawatts) + return self.quantity.in_units_of(units.megawatts) @property def kilowatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilowatts) + return self.quantity.in_units_of(units.kilowatts) @property def milliwatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milliwatts) + return self.quantity.in_units_of(units.milliwatts) @property def microwatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microwatts) + return self.quantity.in_units_of(units.microwatts) @property def nanowatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanowatts) + return self.quantity.in_units_of(units.nanowatts) @property def picowatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picowatts) + return self.quantity.in_units_of(units.picowatts) @property def femtowatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtowatts) + return self.quantity.in_units_of(units.femtowatts) @property def attowatts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attowatts) + return self.quantity.in_units_of(units.attowatts) -class ChargeAccessor[T](QuantityAccessor[T]): +class ChargeAccessor[T](Accessor[T]): dimension_name = 'charge' @property def coulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.coulombs) + return self.quantity.in_units_of(units.coulombs) @property def exacoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exacoulombs) + return self.quantity.in_units_of(units.exacoulombs) @property def petacoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petacoulombs) + return self.quantity.in_units_of(units.petacoulombs) @property def teracoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teracoulombs) + return self.quantity.in_units_of(units.teracoulombs) @property def gigacoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigacoulombs) + return self.quantity.in_units_of(units.gigacoulombs) @property def megacoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megacoulombs) + return self.quantity.in_units_of(units.megacoulombs) @property def kilocoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilocoulombs) + return self.quantity.in_units_of(units.kilocoulombs) @property def millicoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millicoulombs) + return self.quantity.in_units_of(units.millicoulombs) @property def microcoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microcoulombs) + return self.quantity.in_units_of(units.microcoulombs) @property def nanocoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanocoulombs) + return self.quantity.in_units_of(units.nanocoulombs) @property def picocoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picocoulombs) + return self.quantity.in_units_of(units.picocoulombs) @property def femtocoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtocoulombs) + return self.quantity.in_units_of(units.femtocoulombs) @property def attocoulombs(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attocoulombs) + return self.quantity.in_units_of(units.attocoulombs) -class PotentialAccessor[T](QuantityAccessor[T]): +class PotentialAccessor[T](Accessor[T]): dimension_name = 'potential' @property def volts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.volts) + return self.quantity.in_units_of(units.volts) @property def exavolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exavolts) + return self.quantity.in_units_of(units.exavolts) @property def petavolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petavolts) + return self.quantity.in_units_of(units.petavolts) @property def teravolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teravolts) + return self.quantity.in_units_of(units.teravolts) @property def gigavolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigavolts) + return self.quantity.in_units_of(units.gigavolts) @property def megavolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megavolts) + return self.quantity.in_units_of(units.megavolts) @property def kilovolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilovolts) + return self.quantity.in_units_of(units.kilovolts) @property def millivolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millivolts) + return self.quantity.in_units_of(units.millivolts) @property def microvolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microvolts) + return self.quantity.in_units_of(units.microvolts) @property def nanovolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanovolts) + return self.quantity.in_units_of(units.nanovolts) @property def picovolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picovolts) + return self.quantity.in_units_of(units.picovolts) @property def femtovolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtovolts) + return self.quantity.in_units_of(units.femtovolts) @property def attovolts(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attovolts) + return self.quantity.in_units_of(units.attovolts) -class ResistanceAccessor[T](QuantityAccessor[T]): +class ResistanceAccessor[T](Accessor[T]): dimension_name = 'resistance' @property def ohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.ohms) + return self.quantity.in_units_of(units.ohms) @property def exaohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exaohms) + return self.quantity.in_units_of(units.exaohms) @property def petaohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petaohms) + return self.quantity.in_units_of(units.petaohms) @property def teraohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teraohms) + return self.quantity.in_units_of(units.teraohms) @property def gigaohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigaohms) + return self.quantity.in_units_of(units.gigaohms) @property def megaohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megaohms) + return self.quantity.in_units_of(units.megaohms) @property def kiloohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kiloohms) + return self.quantity.in_units_of(units.kiloohms) @property def milliohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milliohms) + return self.quantity.in_units_of(units.milliohms) @property def microohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microohms) + return self.quantity.in_units_of(units.microohms) @property def nanoohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanoohms) + return self.quantity.in_units_of(units.nanoohms) @property def picoohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picoohms) + return self.quantity.in_units_of(units.picoohms) @property def femtoohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtoohms) + return self.quantity.in_units_of(units.femtoohms) @property def attoohms(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attoohms) + return self.quantity.in_units_of(units.attoohms) -class CapacitanceAccessor[T](QuantityAccessor[T]): +class CapacitanceAccessor[T](Accessor[T]): dimension_name = 'capacitance' @property def farads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.farads) + return self.quantity.in_units_of(units.farads) @property def exafarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exafarads) + return self.quantity.in_units_of(units.exafarads) @property def petafarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petafarads) + return self.quantity.in_units_of(units.petafarads) @property def terafarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terafarads) + return self.quantity.in_units_of(units.terafarads) @property def gigafarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigafarads) + return self.quantity.in_units_of(units.gigafarads) @property def megafarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megafarads) + return self.quantity.in_units_of(units.megafarads) @property def kilofarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilofarads) + return self.quantity.in_units_of(units.kilofarads) @property def millifarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millifarads) + return self.quantity.in_units_of(units.millifarads) @property def microfarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microfarads) + return self.quantity.in_units_of(units.microfarads) @property def nanofarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanofarads) + return self.quantity.in_units_of(units.nanofarads) @property def picofarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picofarads) + return self.quantity.in_units_of(units.picofarads) @property def femtofarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtofarads) + return self.quantity.in_units_of(units.femtofarads) @property def attofarads(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attofarads) + return self.quantity.in_units_of(units.attofarads) -class ConductanceAccessor[T](QuantityAccessor[T]): +class ConductanceAccessor[T](Accessor[T]): dimension_name = 'conductance' @property def siemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.siemens) + return self.quantity.in_units_of(units.siemens) @property def exasiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exasiemens) + return self.quantity.in_units_of(units.exasiemens) @property def petasiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petasiemens) + return self.quantity.in_units_of(units.petasiemens) @property def terasiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terasiemens) + return self.quantity.in_units_of(units.terasiemens) @property def gigasiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigasiemens) + return self.quantity.in_units_of(units.gigasiemens) @property def megasiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megasiemens) + return self.quantity.in_units_of(units.megasiemens) @property def kilosiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilosiemens) + return self.quantity.in_units_of(units.kilosiemens) @property def millisiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millisiemens) + return self.quantity.in_units_of(units.millisiemens) @property def microsiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microsiemens) + return self.quantity.in_units_of(units.microsiemens) @property def nanosiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanosiemens) + return self.quantity.in_units_of(units.nanosiemens) @property def picosiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picosiemens) + return self.quantity.in_units_of(units.picosiemens) @property def femtosiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtosiemens) + return self.quantity.in_units_of(units.femtosiemens) @property def attosiemens(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attosiemens) + return self.quantity.in_units_of(units.attosiemens) -class MagneticfluxAccessor[T](QuantityAccessor[T]): +class MagneticfluxAccessor[T](Accessor[T]): dimension_name = 'magnetic_flux' @property def webers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.webers) + return self.quantity.in_units_of(units.webers) @property def exawebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exawebers) + return self.quantity.in_units_of(units.exawebers) @property def petawebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petawebers) + return self.quantity.in_units_of(units.petawebers) @property def terawebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terawebers) + return self.quantity.in_units_of(units.terawebers) @property def gigawebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigawebers) + return self.quantity.in_units_of(units.gigawebers) @property def megawebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megawebers) + return self.quantity.in_units_of(units.megawebers) @property def kilowebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilowebers) + return self.quantity.in_units_of(units.kilowebers) @property def milliwebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.milliwebers) + return self.quantity.in_units_of(units.milliwebers) @property def microwebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microwebers) + return self.quantity.in_units_of(units.microwebers) @property def nanowebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanowebers) + return self.quantity.in_units_of(units.nanowebers) @property def picowebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picowebers) + return self.quantity.in_units_of(units.picowebers) @property def femtowebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtowebers) + return self.quantity.in_units_of(units.femtowebers) @property def attowebers(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attowebers) + return self.quantity.in_units_of(units.attowebers) -class MagneticfluxdensityAccessor[T](QuantityAccessor[T]): +class MagneticfluxdensityAccessor[T](Accessor[T]): dimension_name = 'magnetic_flux_density' @property def tesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.tesla) + return self.quantity.in_units_of(units.tesla) @property def exatesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exatesla) + return self.quantity.in_units_of(units.exatesla) @property def petatesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petatesla) + return self.quantity.in_units_of(units.petatesla) @property def teratesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.teratesla) + return self.quantity.in_units_of(units.teratesla) @property def gigatesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigatesla) + return self.quantity.in_units_of(units.gigatesla) @property def megatesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megatesla) + return self.quantity.in_units_of(units.megatesla) @property def kilotesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilotesla) + return self.quantity.in_units_of(units.kilotesla) @property def millitesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millitesla) + return self.quantity.in_units_of(units.millitesla) @property def microtesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microtesla) + return self.quantity.in_units_of(units.microtesla) @property def nanotesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanotesla) + return self.quantity.in_units_of(units.nanotesla) @property def picotesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picotesla) + return self.quantity.in_units_of(units.picotesla) @property def femtotesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtotesla) + return self.quantity.in_units_of(units.femtotesla) @property def attotesla(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attotesla) + return self.quantity.in_units_of(units.attotesla) -class InductanceAccessor[T](QuantityAccessor[T]): +class InductanceAccessor[T](Accessor[T]): dimension_name = 'inductance' @property def henry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.henry) + return self.quantity.in_units_of(units.henry) @property def exahenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exahenry) + return self.quantity.in_units_of(units.exahenry) @property def petahenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petahenry) + return self.quantity.in_units_of(units.petahenry) @property def terahenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terahenry) + return self.quantity.in_units_of(units.terahenry) @property def gigahenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigahenry) + return self.quantity.in_units_of(units.gigahenry) @property def megahenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megahenry) + return self.quantity.in_units_of(units.megahenry) @property def kilohenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilohenry) + return self.quantity.in_units_of(units.kilohenry) @property def millihenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millihenry) + return self.quantity.in_units_of(units.millihenry) @property def microhenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microhenry) + return self.quantity.in_units_of(units.microhenry) @property def nanohenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanohenry) + return self.quantity.in_units_of(units.nanohenry) @property def picohenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picohenry) + return self.quantity.in_units_of(units.picohenry) @property def femtohenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtohenry) + return self.quantity.in_units_of(units.femtohenry) @property def attohenry(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attohenry) + return self.quantity.in_units_of(units.attohenry) -class TemperatureAccessor[T](QuantityAccessor[T]): +class TemperatureAccessor[T](Accessor[T]): dimension_name = 'temperature' @property def kelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kelvin) + return self.quantity.in_units_of(units.kelvin) @property def exakelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.exakelvin) + return self.quantity.in_units_of(units.exakelvin) @property def petakelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.petakelvin) + return self.quantity.in_units_of(units.petakelvin) @property def terakelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.terakelvin) + return self.quantity.in_units_of(units.terakelvin) @property def gigakelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.gigakelvin) + return self.quantity.in_units_of(units.gigakelvin) @property def megakelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.megakelvin) + return self.quantity.in_units_of(units.megakelvin) @property def kilokelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.kilokelvin) + return self.quantity.in_units_of(units.kilokelvin) @property def millikelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millikelvin) + return self.quantity.in_units_of(units.millikelvin) @property def microkelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.microkelvin) + return self.quantity.in_units_of(units.microkelvin) @property def nanokelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanokelvin) + return self.quantity.in_units_of(units.nanokelvin) @property def picokelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picokelvin) + return self.quantity.in_units_of(units.picokelvin) @property def femtokelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtokelvin) + return self.quantity.in_units_of(units.femtokelvin) @property def attokelvin(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attokelvin) + return self.quantity.in_units_of(units.attokelvin) @property def degrees_celsius(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.degrees_celsius) + return self.quantity.in_units_of(units.degrees_celsius) -class DimensionlessAccessor[T](QuantityAccessor[T]): +class DimensionlessAccessor[T](Accessor[T]): dimension_name = 'dimensionless' @property def none(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.none) - - @property - def percent(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.percent) + return self.quantity.in_units_of(units.none) -class AngleAccessor[T](QuantityAccessor[T]): +class AngleAccessor[T](Accessor[T]): dimension_name = 'angle' @property def degrees(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.degrees) + return self.quantity.in_units_of(units.degrees) @property def radians(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.radians) + return self.quantity.in_units_of(units.radians) -class SolidangleAccessor[T](QuantityAccessor[T]): +class SolidangleAccessor[T](Accessor[T]): dimension_name = 'solid_angle' @property def stradians(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.stradians) + return self.quantity.in_units_of(units.stradians) -class AmountAccessor[T](QuantityAccessor[T]): +class AmountAccessor[T](Accessor[T]): dimension_name = 'amount' @property def moles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles) + return self.quantity.in_units_of(units.moles) @property def millimoles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles) + return self.quantity.in_units_of(units.millimoles) @property def micromoles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles) + return self.quantity.in_units_of(units.micromoles) @property def nanomoles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles) + return self.quantity.in_units_of(units.nanomoles) @property def picomoles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles) + return self.quantity.in_units_of(units.picomoles) @property def femtomoles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles) + return self.quantity.in_units_of(units.femtomoles) @property def attomoles(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles) + return self.quantity.in_units_of(units.attomoles) -class ConcentrationAccessor[T](QuantityAccessor[T]): +class ConcentrationAccessor[T](Accessor[T]): dimension_name = 'concentration' @property def moles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_meter) + return self.quantity.in_units_of(units.moles_per_cubic_meter) @property def millimoles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_meter) + return self.quantity.in_units_of(units.millimoles_per_cubic_meter) @property def micromoles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_meter) + return self.quantity.in_units_of(units.micromoles_per_cubic_meter) @property def nanomoles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_meter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_meter) @property def picomoles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_meter) + return self.quantity.in_units_of(units.picomoles_per_cubic_meter) @property def femtomoles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_meter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_meter) @property def attomoles_per_cubic_meter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_meter) + return self.quantity.in_units_of(units.attomoles_per_cubic_meter) @property def moles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_exameter) + return self.quantity.in_units_of(units.moles_per_cubic_exameter) @property def millimoles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_exameter) + return self.quantity.in_units_of(units.millimoles_per_cubic_exameter) @property def micromoles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_exameter) + return self.quantity.in_units_of(units.micromoles_per_cubic_exameter) @property def nanomoles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_exameter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_exameter) @property def picomoles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_exameter) + return self.quantity.in_units_of(units.picomoles_per_cubic_exameter) @property def femtomoles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_exameter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_exameter) @property def attomoles_per_cubic_exameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_exameter) + return self.quantity.in_units_of(units.attomoles_per_cubic_exameter) @property def moles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_petameter) + return self.quantity.in_units_of(units.moles_per_cubic_petameter) @property def millimoles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_petameter) + return self.quantity.in_units_of(units.millimoles_per_cubic_petameter) @property def micromoles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_petameter) + return self.quantity.in_units_of(units.micromoles_per_cubic_petameter) @property def nanomoles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_petameter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_petameter) @property def picomoles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_petameter) + return self.quantity.in_units_of(units.picomoles_per_cubic_petameter) @property def femtomoles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_petameter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_petameter) @property def attomoles_per_cubic_petameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_petameter) + return self.quantity.in_units_of(units.attomoles_per_cubic_petameter) @property def moles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_terameter) + return self.quantity.in_units_of(units.moles_per_cubic_terameter) @property def millimoles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_terameter) + return self.quantity.in_units_of(units.millimoles_per_cubic_terameter) @property def micromoles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_terameter) + return self.quantity.in_units_of(units.micromoles_per_cubic_terameter) @property def nanomoles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_terameter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_terameter) @property def picomoles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_terameter) + return self.quantity.in_units_of(units.picomoles_per_cubic_terameter) @property def femtomoles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_terameter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_terameter) @property def attomoles_per_cubic_terameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_terameter) + return self.quantity.in_units_of(units.attomoles_per_cubic_terameter) @property def moles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_gigameter) + return self.quantity.in_units_of(units.moles_per_cubic_gigameter) @property def millimoles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_gigameter) + return self.quantity.in_units_of(units.millimoles_per_cubic_gigameter) @property def micromoles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_gigameter) + return self.quantity.in_units_of(units.micromoles_per_cubic_gigameter) @property def nanomoles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_gigameter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_gigameter) @property def picomoles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_gigameter) + return self.quantity.in_units_of(units.picomoles_per_cubic_gigameter) @property def femtomoles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_gigameter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_gigameter) @property def attomoles_per_cubic_gigameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_gigameter) + return self.quantity.in_units_of(units.attomoles_per_cubic_gigameter) @property def moles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_megameter) + return self.quantity.in_units_of(units.moles_per_cubic_megameter) @property def millimoles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_megameter) + return self.quantity.in_units_of(units.millimoles_per_cubic_megameter) @property def micromoles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_megameter) + return self.quantity.in_units_of(units.micromoles_per_cubic_megameter) @property def nanomoles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_megameter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_megameter) @property def picomoles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_megameter) + return self.quantity.in_units_of(units.picomoles_per_cubic_megameter) @property def femtomoles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_megameter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_megameter) @property def attomoles_per_cubic_megameter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_megameter) + return self.quantity.in_units_of(units.attomoles_per_cubic_megameter) @property def moles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_kilometer) + return self.quantity.in_units_of(units.moles_per_cubic_kilometer) @property def millimoles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_kilometer) + return self.quantity.in_units_of(units.millimoles_per_cubic_kilometer) @property def micromoles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_kilometer) + return self.quantity.in_units_of(units.micromoles_per_cubic_kilometer) @property def nanomoles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_kilometer) + return self.quantity.in_units_of(units.nanomoles_per_cubic_kilometer) @property def picomoles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_kilometer) + return self.quantity.in_units_of(units.picomoles_per_cubic_kilometer) @property def femtomoles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_kilometer) + return self.quantity.in_units_of(units.femtomoles_per_cubic_kilometer) @property def attomoles_per_cubic_kilometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_kilometer) + return self.quantity.in_units_of(units.attomoles_per_cubic_kilometer) @property def moles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_millimeter) + return self.quantity.in_units_of(units.moles_per_cubic_millimeter) @property def millimoles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_millimeter) + return self.quantity.in_units_of(units.millimoles_per_cubic_millimeter) @property def micromoles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_millimeter) + return self.quantity.in_units_of(units.micromoles_per_cubic_millimeter) @property def nanomoles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_millimeter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_millimeter) @property def picomoles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_millimeter) + return self.quantity.in_units_of(units.picomoles_per_cubic_millimeter) @property def femtomoles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_millimeter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_millimeter) @property def attomoles_per_cubic_millimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_millimeter) + return self.quantity.in_units_of(units.attomoles_per_cubic_millimeter) @property def moles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_micrometer) + return self.quantity.in_units_of(units.moles_per_cubic_micrometer) @property def millimoles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_micrometer) + return self.quantity.in_units_of(units.millimoles_per_cubic_micrometer) @property def micromoles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_micrometer) + return self.quantity.in_units_of(units.micromoles_per_cubic_micrometer) @property def nanomoles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_micrometer) + return self.quantity.in_units_of(units.nanomoles_per_cubic_micrometer) @property def picomoles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_micrometer) + return self.quantity.in_units_of(units.picomoles_per_cubic_micrometer) @property def femtomoles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_micrometer) + return self.quantity.in_units_of(units.femtomoles_per_cubic_micrometer) @property def attomoles_per_cubic_micrometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_micrometer) + return self.quantity.in_units_of(units.attomoles_per_cubic_micrometer) @property def moles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_nanometer) + return self.quantity.in_units_of(units.moles_per_cubic_nanometer) @property def millimoles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_nanometer) + return self.quantity.in_units_of(units.millimoles_per_cubic_nanometer) @property def micromoles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_nanometer) + return self.quantity.in_units_of(units.micromoles_per_cubic_nanometer) @property def nanomoles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_nanometer) + return self.quantity.in_units_of(units.nanomoles_per_cubic_nanometer) @property def picomoles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_nanometer) + return self.quantity.in_units_of(units.picomoles_per_cubic_nanometer) @property def femtomoles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_nanometer) + return self.quantity.in_units_of(units.femtomoles_per_cubic_nanometer) @property def attomoles_per_cubic_nanometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_nanometer) + return self.quantity.in_units_of(units.attomoles_per_cubic_nanometer) @property def moles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_picometer) + return self.quantity.in_units_of(units.moles_per_cubic_picometer) @property def millimoles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_picometer) + return self.quantity.in_units_of(units.millimoles_per_cubic_picometer) @property def micromoles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_picometer) + return self.quantity.in_units_of(units.micromoles_per_cubic_picometer) @property def nanomoles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_picometer) + return self.quantity.in_units_of(units.nanomoles_per_cubic_picometer) @property def picomoles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_picometer) + return self.quantity.in_units_of(units.picomoles_per_cubic_picometer) @property def femtomoles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_picometer) + return self.quantity.in_units_of(units.femtomoles_per_cubic_picometer) @property def attomoles_per_cubic_picometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_picometer) + return self.quantity.in_units_of(units.attomoles_per_cubic_picometer) @property def moles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_femtometer) + return self.quantity.in_units_of(units.moles_per_cubic_femtometer) @property def millimoles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_femtometer) + return self.quantity.in_units_of(units.millimoles_per_cubic_femtometer) @property def micromoles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_femtometer) + return self.quantity.in_units_of(units.micromoles_per_cubic_femtometer) @property def nanomoles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_femtometer) + return self.quantity.in_units_of(units.nanomoles_per_cubic_femtometer) @property def picomoles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_femtometer) + return self.quantity.in_units_of(units.picomoles_per_cubic_femtometer) @property def femtomoles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_femtometer) + return self.quantity.in_units_of(units.femtomoles_per_cubic_femtometer) @property def attomoles_per_cubic_femtometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_femtometer) + return self.quantity.in_units_of(units.attomoles_per_cubic_femtometer) @property def moles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_attometer) + return self.quantity.in_units_of(units.moles_per_cubic_attometer) @property def millimoles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_attometer) + return self.quantity.in_units_of(units.millimoles_per_cubic_attometer) @property def micromoles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_attometer) + return self.quantity.in_units_of(units.micromoles_per_cubic_attometer) @property def nanomoles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_attometer) + return self.quantity.in_units_of(units.nanomoles_per_cubic_attometer) @property def picomoles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_attometer) + return self.quantity.in_units_of(units.picomoles_per_cubic_attometer) @property def femtomoles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_attometer) + return self.quantity.in_units_of(units.femtomoles_per_cubic_attometer) @property def attomoles_per_cubic_attometer(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_attometer) + return self.quantity.in_units_of(units.attomoles_per_cubic_attometer) @property def moles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_decimeter) + return self.quantity.in_units_of(units.moles_per_cubic_decimeter) @property def millimoles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_decimeter) + return self.quantity.in_units_of(units.millimoles_per_cubic_decimeter) @property def micromoles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_decimeter) + return self.quantity.in_units_of(units.micromoles_per_cubic_decimeter) @property def nanomoles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_decimeter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_decimeter) @property def picomoles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_decimeter) + return self.quantity.in_units_of(units.picomoles_per_cubic_decimeter) @property def femtomoles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_decimeter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_decimeter) @property def attomoles_per_cubic_decimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_decimeter) + return self.quantity.in_units_of(units.attomoles_per_cubic_decimeter) @property def moles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_centimeter) + return self.quantity.in_units_of(units.moles_per_cubic_centimeter) @property def millimoles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_centimeter) + return self.quantity.in_units_of(units.millimoles_per_cubic_centimeter) @property def micromoles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_centimeter) + return self.quantity.in_units_of(units.micromoles_per_cubic_centimeter) @property def nanomoles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_centimeter) + return self.quantity.in_units_of(units.nanomoles_per_cubic_centimeter) @property def picomoles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_centimeter) + return self.quantity.in_units_of(units.picomoles_per_cubic_centimeter) @property def femtomoles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_centimeter) + return self.quantity.in_units_of(units.femtomoles_per_cubic_centimeter) @property def attomoles_per_cubic_centimeter(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_centimeter) + return self.quantity.in_units_of(units.attomoles_per_cubic_centimeter) @property def moles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_angstrom) + return self.quantity.in_units_of(units.moles_per_cubic_angstrom) @property def millimoles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_angstrom) + return self.quantity.in_units_of(units.millimoles_per_cubic_angstrom) @property def micromoles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_angstrom) + return self.quantity.in_units_of(units.micromoles_per_cubic_angstrom) @property def nanomoles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_angstrom) + return self.quantity.in_units_of(units.nanomoles_per_cubic_angstrom) @property def picomoles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_angstrom) + return self.quantity.in_units_of(units.picomoles_per_cubic_angstrom) @property def femtomoles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_angstrom) + return self.quantity.in_units_of(units.femtomoles_per_cubic_angstrom) @property def attomoles_per_cubic_angstrom(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_angstrom) - - @property - def moles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_mile) - - @property - def millimoles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_mile) - - @property - def micromoles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_mile) - - @property - def nanomoles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_mile) - - @property - def picomoles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_mile) - - @property - def femtomoles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_mile) - - @property - def attomoles_per_cubic_mile(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_mile) - - @property - def moles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_yard) - - @property - def millimoles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_yard) - - @property - def micromoles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_yard) - - @property - def nanomoles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_yard) - - @property - def picomoles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_yard) - - @property - def femtomoles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_yard) - - @property - def attomoles_per_cubic_yard(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_yard) - - @property - def moles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_foot) - - @property - def millimoles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_foot) - - @property - def micromoles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_foot) - - @property - def nanomoles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_foot) - - @property - def picomoles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_foot) - - @property - def femtomoles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_foot) - - @property - def attomoles_per_cubic_foot(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_foot) - - @property - def moles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.moles_per_cubic_inch) - - @property - def millimoles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.millimoles_per_cubic_inch) - - @property - def micromoles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.micromoles_per_cubic_inch) - - @property - def nanomoles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.nanomoles_per_cubic_inch) - - @property - def picomoles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.picomoles_per_cubic_inch) - - @property - def femtomoles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.femtomoles_per_cubic_inch) - - @property - def attomoles_per_cubic_inch(self) -> T: - quantity = self.quantity - if quantity is None: - return None - else: - return quantity.in_units_of(units.attomoles_per_cubic_inch) + return self.quantity.in_units_of(units.attomoles_per_cubic_angstrom) diff --git a/sasdata/quantities/quantities.py b/sasdata/quantities/quantities.py deleted file mode 100644 index 707ce1590..000000000 --- a/sasdata/quantities/quantities.py +++ /dev/null @@ -1,53 +0,0 @@ -from typing import Collection, Sequence, TypeVar, Generic, Self -from dataclasses import dataclass - -from numpy._typing import ArrayLike - -from sasdata.quantities.units import Unit - - -class UnitError(Exception): - """ Errors caused by unit specification not being correct """ - - -QuantityType = TypeVar("QuantityType") - -class Quantity[QuantityType]: - def __init__(self, value: QuantityType, units: Unit): - self.value = value - self.units = units - - def in_units_of(self, units: Unit) -> QuantityType: - if self.units.equivalent(units): - return (units.scale / self.units.scale) * self.value - else: - raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") - - def __mul__(self: Self, other: ArrayLike | Self ) -> Self: - if isinstance(other, Quantity): - pass - - else: - pass - - def __truediv__(self: Self, other: float | Self) -> Self: - if isinstance(other, Quantity): - pass - - else: - pass - - def __rdiv__(self: Self, other: float | Self) -> Self: - if isinstance(other, Quantity): - pass - - else: - pass - def __add__(self: Self, other: Self) -> Self: - if isinstance(other, Quantity): - pass - - def __sub__(self: Self, other: Self) -> Self: - if isinstance(other, Quantity): - pass - diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 70941d6d4..e265c2762 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,1393 +1,72 @@ -from typing import Self +from typing import Collection, Sequence, TypeVar, Generic, Self +from dataclasses import dataclass -import numpy as np -from docutils.frontend import validate_ternary from numpy._typing import ArrayLike -from sasdata.quantities import units -from sasdata.quantities.numerical_encoding import numerical_decode, numerical_encode -from sasdata.quantities.units import Unit, NamedUnit - -import hashlib - -from typing import Any, TypeVar, Union - -import json - -T = TypeVar("T") - - - - - -################### Quantity based operations, need to be here to avoid cyclic dependencies ##################### - -def transpose(a: Union["Quantity[ArrayLike]", ArrayLike], axes: tuple | None = None): - """ Transpose an array or an array based quantity, can also do reordering of axes""" - if isinstance(a, Quantity): - - if axes is None: - return DerivedQuantity(value=np.transpose(a.value, axes=axes), - units=a.units, - history=QuantityHistory.apply_operation(Transpose, a.history)) - - else: - return DerivedQuantity(value=np.transpose(a.value, axes=axes), - units=a.units, - history=QuantityHistory.apply_operation(Transpose, a.history, axes=axes)) - - else: - return np.transpose(a, axes=axes) - - -def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike]", ArrayLike]): - """ Dot product of two arrays or two array based quantities """ - a_is_quantity = isinstance(a, Quantity) - b_is_quantity = isinstance(b, Quantity) - - if a_is_quantity or b_is_quantity: - - # If its only one of them that is a quantity, convert the other one - - if not a_is_quantity: - a = Quantity(a, units.none) - - if not b_is_quantity: - b = Quantity(b, units.none) - - return DerivedQuantity( - value=np.dot(a.value, b.value), - units=a.units * b.units, - history=QuantityHistory.apply_operation(Dot, a.history, b.history)) - - else: - return np.dot(a, b) - -def tensordot(a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, b: Union["Quantity[ArrayLike]", ArrayLike], a_index: int, b_index: int): - """ Tensor dot product - equivalent to contracting two tensors, such as - - A_{i0, i1, i2, i3...} and B_{j0, j1, j2...} - - e.g. if a_index is 1 and b_index is zero, it will be the sum - - C_{i0, i2, i3 ..., j1, j2 ...} = sum_k A_{i0, k, i2, i3 ...} B_{k, j1, j2 ...} - - (I think, have to check what happens with indices TODO!) - - """ - - a_is_quantity = isinstance(a, Quantity) - b_is_quantity = isinstance(b, Quantity) - - if a_is_quantity or b_is_quantity: - - # If its only one of them that is a quantity, convert the other one - - if not a_is_quantity: - a = Quantity(a, units.none) - - if not b_is_quantity: - b = Quantity(b, units.none) - - return DerivedQuantity( - value=np.tensordot(a.value, b.value, axes=(a_index, b_index)), - units=a.units * b.units, - history=QuantityHistory.apply_operation( - TensorDot, - a.history, - b.history, - a_index=a_index, - b_index=b_index)) - - else: - return np.tensordot(a, b, axes=(a_index, b_index)) - - -################### Operation Definitions ####################################### - -def hash_and_name(hash_or_name: int | str): - """ Infer the name of a variable from a hash, or the hash from the name - - Note: hash_and_name(hash_and_name(number)[1]) is not the identity - however: hash_and_name(hash_and_name(number)) is - """ - - if isinstance(hash_or_name, str): - hash_value = hash(hash_or_name) - name = hash_or_name - - return hash_value, name - - elif isinstance(hash_or_name, int): - hash_value = hash_or_name - name = f"#{hash_or_name}" - - return hash_value, name - - elif isinstance(hash_or_name, tuple): - return hash_or_name - - else: - raise TypeError("Variable name_or_hash_value must be either str or int") - -class Operation: - - serialisation_name = "unknown" - def summary(self, indent_amount: int = 0, indent: str=" "): - """ Summary of the operation tree""" - - s = f"{indent_amount*indent}{self._summary_open()}(\n" - - for chunk in self._summary_components(): - s += chunk.summary(indent_amount+1, indent) + "\n" - - s += f"{indent_amount*indent})" - - return s - def _summary_open(self): - """ First line of summary """ - - def _summary_components(self) -> list["Operation"]: - return [] - def evaluate(self, variables: dict[int, T]) -> T: - - """ Evaluate this operation """ - - def _derivative(self, hash_value: int) -> "Operation": - """ Get the derivative of this operation """ - - def _clean(self): - """ Clean up this operation - i.e. remove silly things like 1*x """ - return self - - def derivative(self, variable: Union[str, int, "Variable"], simplify=True): - if isinstance(variable, Variable): - hash_value = variable.hash_value - else: - hash_value, _ = hash_and_name(variable) - - derivative = self._derivative(hash_value) - - if not simplify: - return derivative - - derivative_string = derivative.serialise() - - # print("---------------") - # print("Base") - # print("---------------") - # print(derivative.summary()) - - # Inefficient way of doing repeated simplification, but it will work - for i in range(100): # set max iterations - - derivative = derivative._clean() - # - # print("-------------------") - # print("Iteration", i+1) - # print("-------------------") - # print(derivative.summary()) - # print("-------------------") - - new_derivative_string = derivative.serialise() - - if derivative_string == new_derivative_string: - break - - derivative_string = new_derivative_string - - return derivative - - @staticmethod - def deserialise(data: str) -> "Operation": - json_data = json.loads(data) - return Operation.deserialise_json(json_data) - - @staticmethod - def deserialise_json(json_data: dict) -> "Operation": - - operation = json_data["operation"] - parameters = json_data["parameters"] - cls = _serialisation_lookup[operation] - - try: - return cls._deserialise(parameters) - - except NotImplementedError: - raise NotImplementedError(f"No method to deserialise {operation} with {parameters} (cls={cls})") - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - raise NotImplementedError(f"Deserialise not implemented for this class") - - def serialise(self) -> str: - return json.dumps(self._serialise_json()) - - def _serialise_json(self) -> dict[str, Any]: - return {"operation": self.serialisation_name, - "parameters": self._serialise_parameters()} - - def _serialise_parameters(self) -> dict[str, Any]: - raise NotImplementedError("_serialise_parameters not implemented") - - def __eq__(self, other: "Operation"): - return NotImplemented - -class ConstantBase(Operation): - pass - -class AdditiveIdentity(ConstantBase): - - serialisation_name = "zero" - def evaluate(self, variables: dict[int, T]) -> T: - return 0 - - def _derivative(self, hash_value: int) -> Operation: - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return AdditiveIdentity() - - def _serialise_parameters(self) -> dict[str, Any]: - return {} - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}0 [Add.Id.]" - - def __eq__(self, other): - if isinstance(other, AdditiveIdentity): - return True - elif isinstance(other, Constant): - if other.value == 0: - return True - - return False - - - -class MultiplicativeIdentity(ConstantBase): - - serialisation_name = "one" - - def evaluate(self, variables: dict[int, T]) -> T: - return 1 - - def _derivative(self, hash_value: int): - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return MultiplicativeIdentity() - - - def _serialise_parameters(self) -> dict[str, Any]: - return {} - - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}1 [Mul.Id.]" - - def __eq__(self, other): - if isinstance(other, MultiplicativeIdentity): - return True - elif isinstance(other, Constant): - if other.value == 1: - return True - - return False - - -class Constant(ConstantBase): - - serialisation_name = "constant" - def __init__(self, value): - self.value = value - - def summary(self, indent_amount: int = 0, indent: str=" "): - return repr(self.value) - - def evaluate(self, variables: dict[int, T]) -> T: - return self.value - - def _derivative(self, hash_value: int): - return AdditiveIdentity() - - def _clean(self): - - if self.value == 0: - return AdditiveIdentity() - - elif self.value == 1: - return MultiplicativeIdentity() - - else: - return self - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - value = numerical_decode(parameters["value"]) - return Constant(value) - - - def _serialise_parameters(self) -> dict[str, Any]: - return {"value": numerical_encode(self.value)} - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}{self.value}" - - def __eq__(self, other): - if isinstance(other, AdditiveIdentity): - return self.value == 0 - - elif isinstance(other, MultiplicativeIdentity): - return self.value == 1 - - elif isinstance(other, Constant): - if other.value == self.value: - return True - - return False - - -class Variable(Operation): - - serialisation_name = "variable" - def __init__(self, name_or_hash_value: int | str | tuple[int, str]): - self.hash_value, self.name = hash_and_name(name_or_hash_value) - - def evaluate(self, variables: dict[int, T]) -> T: - try: - return variables[self.hash_value] - except KeyError: - raise ValueError(f"Variable dictionary didn't have an entry for {self.name} (hash={self.hash_value})") - - def _derivative(self, hash_value: int) -> Operation: - if hash_value == self.hash_value: - return MultiplicativeIdentity() - else: - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - hash_value = parameters["hash_value"] - name = parameters["name"] - - return Variable((hash_value, name)) - - def _serialise_parameters(self) -> dict[str, Any]: - return {"hash_value": self.hash_value, - "name": self.name} - - def summary(self, indent_amount: int = 0, indent: str=" "): - return f"{indent_amount*indent}{self.name}" - - def __eq__(self, other): - if isinstance(other, Variable): - return self.hash_value == other.hash_value - - return False - -class UnaryOperation(Operation): - - def __init__(self, a: Operation): - self.a = a - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": self.a._serialise_json()} - - def _summary_components(self) -> list["Operation"]: - return [self.a] - - - - -class Neg(UnaryOperation): - - serialisation_name = "neg" - def evaluate(self, variables: dict[int, T]) -> T: - return -self.a.evaluate(variables) - - def _derivative(self, hash_value: int): - return Neg(self.a._derivative(hash_value)) - - def _clean(self): - - clean_a = self.a._clean() - - if isinstance(clean_a, Neg): - # Removes double negations - return clean_a.a - - elif isinstance(clean_a, Constant): - return Constant(-clean_a.value)._clean() - - else: - return Neg(clean_a) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Neg(Operation.deserialise_json(parameters["a"])) - - - def _summary_open(self): - return "Neg" - - def __eq__(self, other): - if isinstance(other, Neg): - return other.a == self.a - - -class Inv(UnaryOperation): - - serialisation_name = "reciprocal" - - def evaluate(self, variables: dict[int, T]) -> T: - return 1/self.a.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Neg(Div(self.a._derivative(hash_value), Mul(self.a, self.a))) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, Inv): - # Removes double negations - return clean_a.a - - elif isinstance(clean_a, Neg): - # cannonicalise 1/-a to -(1/a) - # over multiple iterations this should have the effect of ordering and gathering Neg and Inv - return Neg(Inv(clean_a.a)) - - elif isinstance(clean_a, Constant): - return Constant(1/clean_a.value)._clean() - - else: - return Inv(clean_a) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Inv(Operation.deserialise_json(parameters["a"])) - - def _summary_open(self): - return "Inv" - - - def __eq__(self, other): - if isinstance(other, Inv): - return other.a == self.a - -class BinaryOperation(Operation): - def __init__(self, a: Operation, b: Operation): - self.a = a - self.b = b - - def _clean(self): - return self._clean_ab(self.a._clean(), self.b._clean()) - - def _clean_ab(self, a, b): - raise NotImplementedError("_clean_ab not implemented") - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": self.a._serialise_json(), - "b": self.b._serialise_json()} - - @staticmethod - def _deserialise_ab(parameters) -> tuple[Operation, Operation]: - return (Operation.deserialise_json(parameters["a"]), - Operation.deserialise_json(parameters["b"])) - - - def _summary_components(self) -> list["Operation"]: - return [self.a, self.b] - - def _self_cls(self) -> type: - """ Own class""" - def __eq__(self, other): - if isinstance(other, self._self_cls()): - return other.a == self.a and self.b == other.b - -class Add(BinaryOperation): - - serialisation_name = "add" - - def _self_cls(self) -> type: - return Add - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) + self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add(self.a._derivative(hash_value), self.b._derivative(hash_value)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity): - # Convert 0 + b to b - return b - - elif isinstance(b, AdditiveIdentity): - # Convert a + 0 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"+"b" to "a+b" - return Constant(a.evaluate({}) + b.evaluate({}))._clean() - - elif isinstance(a, Neg): - if isinstance(b, Neg): - # Convert (-a)+(-b) to -(a+b) - return Neg(Add(a.a, b.a)) - else: - # Convert (-a) + b to b-a - return Sub(b, a.a) - - elif isinstance(b, Neg): - # Convert a+(-b) to a-b - return Sub(a, b.a) - - elif a == b: - return Mul(Constant(2), a) - - else: - return Add(a, b) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Add(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Add" - -class Sub(BinaryOperation): - - serialisation_name = "sub" - - - def _self_cls(self) -> type: - return Sub - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) - self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Sub(self.a._derivative(hash_value), self.b._derivative(hash_value)) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity): - # Convert 0 - b to -b - return Neg(b) - - elif isinstance(b, AdditiveIdentity): - # Convert a - 0 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant pair "a" - "b" to "a-b" - return Constant(a.evaluate({}) - b.evaluate({}))._clean() - - elif isinstance(a, Neg): - if isinstance(b, Neg): - # Convert (-a)-(-b) to b-a - return Sub(b.a, a.a) - else: - # Convert (-a)-b to -(a+b) - return Neg(Add(a.a, b)) - - elif isinstance(b, Neg): - # Convert a-(-b) to a+b - return Add(a, b.a) - - elif a == b: - return AdditiveIdentity() - - else: - return Sub(a, b) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Sub(*BinaryOperation._deserialise_ab(parameters)) - - - def _summary_open(self): - return "Sub" - -class Mul(BinaryOperation): - - serialisation_name = "mul" - - - def _self_cls(self) -> type: - return Mul - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) * self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add(Mul(self.a, self.b._derivative(hash_value)), Mul(self.a._derivative(hash_value), self.b)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): - # Convert 0*b or a*0 to 0 - return AdditiveIdentity() - - elif isinstance(a, MultiplicativeIdentity): - # Convert 1*b to b - return b - - elif isinstance(b, MultiplicativeIdentity): - # Convert a*1 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"*"b" to "a*b" - return Constant(a.evaluate({}) * b.evaluate({}))._clean() - - elif isinstance(a, Inv) and isinstance(b, Inv): - return Inv(Mul(a.a, b.a)) - - elif isinstance(a, Inv) and not isinstance(b, Inv): - return Div(b, a.a) - - elif not isinstance(a, Inv) and isinstance(b, Inv): - return Div(a, b.a) - - elif isinstance(a, Neg): - return Neg(Mul(a.a, b)) - - elif isinstance(b, Neg): - return Neg(Mul(a, b.a)) - - elif a == b: - return Pow(a, 2) - - elif isinstance(a, Pow) and a.a == b: - return Pow(b, a.power + 1) - - elif isinstance(b, Pow) and b.a == a: - return Pow(a, b.power + 1) - - elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: - return Pow(a.a, a.power + b.power) - - else: - return Mul(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Mul(*BinaryOperation._deserialise_ab(parameters)) - - - def _summary_open(self): - return "Mul" - -class Div(BinaryOperation): - - serialisation_name = "div" - - - def _self_cls(self) -> type: - return Div - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) / self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Sub(Div(self.a.derivative(hash_value), self.b), - Div(Mul(self.a, self.b.derivative(hash_value)), Mul(self.b, self.b))) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity): - # Convert 0/b to 0 - return AdditiveIdentity() - - elif isinstance(a, MultiplicativeIdentity): - # Convert 1/b to inverse of b - return Inv(b) - - elif isinstance(b, MultiplicativeIdentity): - # Convert a/1 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constants "a"/"b" to "a/b" - return Constant(self.a.evaluate({}) / self.b.evaluate({}))._clean() - - - elif isinstance(a, Inv) and isinstance(b, Inv): - return Div(b.a, a.a) - - elif isinstance(a, Inv) and not isinstance(b, Inv): - return Inv(Mul(a.a, b)) - - elif not isinstance(a, Inv) and isinstance(b, Inv): - return Mul(a, b.a) - - elif a == b: - return MultiplicativeIdentity() - - elif isinstance(a, Pow) and a.a == b: - return Pow(b, a.power - 1) - - elif isinstance(b, Pow) and b.a == a: - return Pow(a, 1 - b.power) - - elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: - return Pow(a.a, a.power - b.power) - - else: - return Div(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Div(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Div" - -class Pow(Operation): - - serialisation_name = "pow" - - def __init__(self, a: Operation, power: float): - self.a = a - self.power = power - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) ** self.power - - def _derivative(self, hash_value: int) -> Operation: - if self.power == 0: - return AdditiveIdentity() - - elif self.power == 1: - return self.a._derivative(hash_value) - - else: - return Mul(Constant(self.power), Mul(Pow(self.a, self.power-1), self.a._derivative(hash_value))) - - def _clean(self) -> Operation: - a = self.a._clean() - - if self.power == 1: - return a - - elif self.power == 0: - return MultiplicativeIdentity() - - elif self.power == -1: - return Inv(a) - - else: - return Pow(a, self.power) - - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": Operation._serialise_json(self.a), - "power": self.power} - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Pow(Operation.deserialise_json(parameters["a"]), parameters["power"]) - - def summary(self, indent_amount: int=0, indent=" "): - return (f"{indent_amount*indent}Pow\n" + - self.a.summary(indent_amount+1, indent) + "\n" + - f"{(indent_amount+1)*indent}{self.power}\n" + - f"{indent_amount*indent})") - - def __eq__(self, other): - if isinstance(other, Pow): - return self.a == other.a and self.power == other.power - - - -# -# Matrix operations -# - -class Transpose(Operation): - """ Transpose operation - as per numpy""" - - serialisation_name = "transpose" - - def __init__(self, a: Operation, axes: tuple[int] | None = None): - self.a = a - self.axes = axes - - def evaluate(self, variables: dict[int, T]) -> T: - return np.transpose(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Transpose(self.a.derivative(hash_value)) # TODO: Check! - - def _clean(self): - clean_a = self.a._clean() - return Transpose(clean_a) - - - def _serialise_parameters(self) -> dict[str, Any]: - if self.axes is None: - return { "a": self.a._serialise_json() } - else: - return { - "a": self.a._serialise_json(), - "axes": list(self.axes) - } - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - if "axes" in parameters: - return Transpose( - a=Operation.deserialise_json(parameters["a"]), - axes=tuple(parameters["axes"])) - else: - return Transpose( - a=Operation.deserialise_json(parameters["a"])) - - - def _summary_open(self): - return "Transpose" - - def __eq__(self, other): - if isinstance(other, Transpose): - return other.a == self.a - - -class Dot(BinaryOperation): - """ Dot product - backed by numpy's dot method""" - - serialisation_name = "dot" - - def evaluate(self, variables: dict[int, T]) -> T: - return dot(self.a.evaluate(variables), self.b.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Add( - Dot(self.a, - self.b._derivative(hash_value)), - Dot(self.a._derivative(hash_value), - self.b)) - - def _clean_ab(self, a, b): - return Dot(a, b) # Do nothing for now - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Dot(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Dot" - - -# TODO: Add to base operation class, and to quantities -class MatMul(BinaryOperation): - """ Matrix multiplication, using __matmul__ dunder""" - - serialisation_name = "matmul" - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) @ self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add( - MatMul(self.a, - self.b._derivative(hash_value)), - MatMul(self.a._derivative(hash_value), - self.b)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): - # Convert 0*b or a*0 to 0 - return AdditiveIdentity() - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"@"b" to "a@b" - return Constant(a.evaluate({}) @ b.evaluate({}))._clean() - - elif isinstance(a, Neg): - return Neg(Mul(a.a, b)) - - elif isinstance(b, Neg): - return Neg(Mul(a, b.a)) - - return MatMul(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return MatMul(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "MatMul" - -class TensorDot(Operation): - serialisation_name = "tensor_product" - - def __init__(self, a: Operation, b: Operation, a_index: int, b_index: int): - self.a = a - self.b = b - self.a_index = a_index - self.b_index = b_index - - def evaluate(self, variables: dict[int, T]) -> T: - return tensordot(self.a, self.b, self.a_index, self.b_index) - - - def _serialise_parameters(self) -> dict[str, Any]: - return { - "a": self.a._serialise_json(), - "b": self.b._serialise_json(), - "a_index": self.a_index, - "b_index": self.b_index } - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return TensorDot(a = Operation.deserialise_json(parameters["a"]), - b = Operation.deserialise_json(parameters["b"]), - a_index=int(parameters["a_index"]), - b_index=int(parameters["b_index"])) - - def _summary_open(self): - return "TensorProduct" - - -_serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, - Variable, - Neg, Inv, - Add, Sub, Mul, Div, Pow, - Transpose, Dot, MatMul, TensorDot] - -_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} +from sasdata.quantities.units import Unit class UnitError(Exception): """Errors caused by unit specification not being correct""" -def hash_data_via_numpy(*data: ArrayLike): - - md5_hash = hashlib.md5() - - for datum in data: - data_bytes = np.array(datum).tobytes() - md5_hash.update(data_bytes) - - # Hash function returns a hex string, we want an int - return int(md5_hash.hexdigest(), 16) - - - -##################################### -# # -# # -# # -# Quantities begin here # -# # -# # -# # -##################################### - - QuantityType = TypeVar("QuantityType") -# TODO: change QuantityType serialisation for greater efficiency -def quantity_type_serialisation(var): - if isinstance(var, np.ndarray): - return {"array_contents": var.tobytes(), "shape": var.shape} - else: - return var - -def quantity_type_deserialisation(var): - if isinstance(var, dict): - array = np.frombuffer(var["array_contents"]) - return np.reshape(array, shape=var["shape"]) - else: - return var - - -class QuantityHistory: - """ Class that holds the information for keeping track of operations done on quantities """ - - def __init__(self, operation_tree: Operation, references: dict[int, "Quantity"]): - self.operation_tree = operation_tree - self.references = references - - self.reference_key_list = [key for key in self.references] - self.si_reference_values = {key: self.references[key].in_si() for key in self.references} - - def jacobian(self) -> list[Operation]: - """ Derivative of this quantity's operation history with respect to each of the references """ - - # Use the hash value to specify the variable of differentiation - return [self.operation_tree.derivative(key) for key in self.reference_key_list] - - def _recalculate(self): - """ Recalculate the value of this object - primary use case isfor testing """ - return self.operation_tree.evaluate(self.references) - - def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, int]: "Quantity"] = {}): - """ Do standard error propagation to calculate the uncertainties associated with this quantity - - :param quantity_units: units in which the output should be calculated - :param covariances: off diagonal entries for the covariance matrix - """ - - if covariances: - raise NotImplementedError("User specified covariances not currently implemented") - - jacobian = self.jacobian() - - evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] - - hash_values = [key for key in self.references] - output = None - - for hash_value, jac_component in zip(hash_values, evaluated_jacobian): - if output is None: - output = jac_component * (self.references[hash_value].variance * jac_component) - else: - output += jac_component * (self.references[hash_value].variance * jac_component) - - return output - - - @staticmethod - def variable(quantity: "Quantity"): - """ Create a history that starts with the provided data """ - return QuantityHistory(Variable(quantity.hash_value), {quantity.hash_value: quantity}) - - @staticmethod - def apply_operation(operation: type[Operation], *histories: "QuantityHistory", **extra_parameters) -> "QuantityHistory": - """ Apply an operation to the history - - This is slightly unsafe as it is possible to attempt to apply an n-ary operation to a number of trees other - than n, but it is relatively concise. Because it is concise we'll go with this for now and see if it causes - any problems down the line. It is a private static method to discourage misuse. - - """ - - # Copy references over, even though it overrides on collision, - # this should behave because only data based variables should be represented. - # Should not be a problem any more than losing histories - references = {} - for history in histories: - references.update(history.references) - - return QuantityHistory( - operation(*[history.operation_tree for history in histories], **extra_parameters), - references) - - def has_variance(self): - for key in self.references: - if self.references[key].has_variance: - return True - - return False - - def summary(self): - - variable_strings = [self.references[key].string_repr for key in self.references] - - s = "Variables: "+",".join(variable_strings) - s += "\n" - s += self.operation_tree.summary() - - return s - - @staticmethod - def deserialise_json(json_data: dict) -> "QuantityHistory": - operation_tree = Operation.deserialise(json_data["operation_tree"]) - references = { - key: Quantity.deserialise_json(json_data["references"][key]) - for key in json_data["references"] - } - return QuantityHistory(operation_tree, references) - - def serialise_json(self): - return { - "operation_tree": self.operation_tree.serialise(), - "references": [ - ref.serialise_json_no_history() for ref in self.references.values() - ] - - } - - class Quantity[QuantityType]: - - - def __init__(self, - value: QuantityType, - units: Unit, - standard_error: QuantityType | None = None, - hash_seed = ""): - + def __init__(self, value: QuantityType, units: Unit): self.value = value - """ Numerical value of this data, in the specified units""" - self.units = units - """ Units of this data """ - - self._hash_seed = hash_seed - """ Retain this for copying operations""" - - self.hash_value = -1 - """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ - - self._variance = None - """ Contains the variance if it is data driven """ - - if standard_error is None: - self.hash_value = hash_data_via_numpy(hash_seed, value) - else: - self._variance = standard_error ** 2 - self.hash_value = hash_data_via_numpy(hash_seed, value, standard_error) - - self.history = QuantityHistory.variable(self) - - # TODO: Adding this method as a temporary measure but we need a single - # method that does this. - def with_standard_error(self, standard_error: Quantity): - if standard_error.units.equivalent(self.units): - return NamedQuantity( - value=self.value, - units=self.units, - standard_error=standard_error.in_units_of(self.units),) - else: - raise UnitError(f"Standard error units ({standard_error.units}) " - f"are not compatible with value units ({self.units})") - - @property - def has_variance(self): - return self._variance is not None - - @property - def variance(self) -> "Quantity": - """ Get the variance of this object""" - if self._variance is None: - return Quantity(np.zeros_like(self.value), self.units**2) - else: - return Quantity(self._variance, self.units**2) - - def standard_deviation(self) -> "Quantity": - return self.variance ** 0.5 def in_units_of(self, units: Unit) -> QuantityType: - """ Get this quantity in other units """ if self.units.equivalent(units): - return (self.units.scale / units.scale) * self.value + return (units.scale / self.units.scale) * self.value else: raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") - def to_units_of(self, new_units: Unit) -> "Quantity[QuantityType]": - new_value, new_error = self.in_units_of_with_standard_error(new_units) - return Quantity(value=new_value, - units=new_units, - standard_error=new_error, - hash_seed=self._hash_seed) - - def variance_in_units_of(self, units: Unit) -> QuantityType: - """ Get the variance of quantity in other units """ - variance = self.variance - if variance.units.equivalent(units): - return (variance.units.scale / units.scale) * variance - else: - raise UnitError(f"Target units ({units}) not compatible with existing units ({variance.units}).") - - def in_si(self): - si_units = self.units.si_equivalent() - return self.in_units_of(si_units) - - def in_units_of_with_standard_error(self, units): - variance = self.variance - units_squared = units**2 - - if variance.units.equivalent(units_squared): - - return self.in_units_of(units), np.sqrt(self.variance.in_units_of(units_squared)) - else: - raise UnitError(f"Target units ({units}) not compatible with existing units ({variance.units}).") - - def in_si_with_standard_error(self): - if self.has_variance: - return self.in_units_of_with_standard_error(self.units.si_equivalent()) - else: - return self.in_si(), None - - @staticmethod - def deserialise_json(json_data: dict) -> "Quantity": - value = numerical_decode(json_data["value"]) - units_ = Unit.parse(json_data["units"]) - standard_error = numerical_decode(json_data["variance"]) ** 0.5 - hash_seed = json_data["hash_seed"] - history = QuantityHistory.deserialise_json(json_data["history"]) - quantity = Quantity(value, units_, standard_error, hash_seed) - quantity.history = history - return quantity - - def serialise_json(self): - return { - "value": numerical_encode(self.value), - "units": str(self.units), # Unit serialisation - "variance": numerical_encode(self._variance), - "hash_seed": self._hash_seed, # is this just a string? - "hash_value": self.hash_value, - "history": self.history.serialise_json() - } - - def serialise_json_no_history(self): - return { - "value": numerical_encode(self.value), - "units": str(self.units), # Unit serialisation - "variance": numerical_encode(self._variance), - "hash_seed": self._hash_seed, # is this just a string? - "hash_value": self.hash_value, - "history": {} - } - def __mul__(self: Self, other: ArrayLike | Self ) -> Self: if isinstance(other, Quantity): - return DerivedQuantity( - self.value * other.value, - self.units * other.units, - history=QuantityHistory.apply_operation(Mul, self.history, other.history)) + return Quantity(self.value * other.value, self.units * other.units) else: - return DerivedQuantity(self.value * other, self.units, - QuantityHistory( - Mul( - self.history.operation_tree, - Constant(other)), - self.history.references)) + return Quantity(self.value * other, self.units) def __rmul__(self: Self, other: ArrayLike | Self): if isinstance(other, Quantity): - return DerivedQuantity( - other.value * self.value, - other.units * self.units, - history=QuantityHistory.apply_operation( - Mul, - other.history, - self.history)) - - else: - return DerivedQuantity(other * self.value, self.units, - QuantityHistory( - Mul( - Constant(other), - self.history.operation_tree), - self.history.references)) - - - def __matmul__(self, other: ArrayLike | Self): - if isinstance(other, Quantity): - return DerivedQuantity( - self.value @ other.value, - self.units * other.units, - history=QuantityHistory.apply_operation( - MatMul, - self.history, - other.history)) - else: - return DerivedQuantity( - self.value @ other, - self.units, - QuantityHistory( - MatMul( - self.history.operation_tree, - Constant(other)), - self.history.references)) - - def __rmatmul__(self, other: ArrayLike | Self): - if isinstance(other, Quantity): - return DerivedQuantity( - other.value @ self.value, - other.units * self.units, - history=QuantityHistory.apply_operation( - MatMul, - other.history, - self.history)) + return Quantity(other.value * self.value, other.units * self.units) else: - return DerivedQuantity(other @ self.value, self.units, - QuantityHistory( - MatMul( - Constant(other), - self.history.operation_tree), - self.history.references)) + return Quantity(other * self.value, self.units) def __truediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): - return DerivedQuantity( - self.value / other.value, - self.units / other.units, - history=QuantityHistory.apply_operation( - Div, - self.history, - other.history)) + return Quantity(self.value / other.value, self.units / other.units) else: - return DerivedQuantity(self.value / other, self.units, - QuantityHistory( - Div( - Constant(other), - self.history.operation_tree), - self.history.references)) + return Quantity(self.value / other, self.units) def __rtruediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): - return DerivedQuantity( - other.value / self.value, - other.units / self.units, - history=QuantityHistory.apply_operation( - Div, - other.history, - self.history - )) + return Quantity(self.value / other.value, self.units / other.units) else: - return DerivedQuantity( - other / self.value, - self.units ** -1, - QuantityHistory( - Div( - Constant(other), - self.history.operation_tree), - self.history.references)) + return Quantity(self.value / other, self.units) def __add__(self: Self, other: Self | ArrayLike) -> Self: if isinstance(other, Quantity): if self.units.equivalent(other.units): - return DerivedQuantity( - self.value + (other.value * other.units.scale) / self.units.scale, - self.units, - QuantityHistory.apply_operation( - Add, - self.history, - other.history)) - else: - raise UnitError(f"Units do not have the same dimensionality: {self.units} vs {other.units}") + return Quantity - else: - raise UnitError(f"Cannot perform addition/subtraction non-quantity {type(other)} with quantity") + elif self.units.dimensions.is_dimensionless: + return Quantity(other/self.units.scale, self.units) - # Don't need __radd__ because only quantity/quantity operations should be allowed + else: + raise UnitError(f"Cannot combine type {type(other)} with quantity") def __neg__(self): - return DerivedQuantity(-self.value, self.units, - QuantityHistory.apply_operation( - Neg, - self.history - )) + return Quantity(-self.value, self.units) def __sub__(self: Self, other: Self | ArrayLike) -> Self: return self + (-other) @@ -1395,154 +74,3 @@ def __sub__(self: Self, other: Self | ArrayLike) -> Self: def __rsub__(self: Self, other: Self | ArrayLike) -> Self: return (-self) + other - def __pow__(self: Self, other: int | float): - return DerivedQuantity(self.value ** other, - self.units ** other, - QuantityHistory( - Pow( - self.history.operation_tree, - other), - self.history.references)) - - def __eq__(self, other: object) -> bool: - return isinstance(other, Quantity) and self.hash_value == other.hash_value - - @staticmethod - def _array_repr_format(arr: np.ndarray): - """ Format the array """ - order = len(arr.shape) - reshaped = arr.reshape(-1) - if len(reshaped) <= 2: - numbers = ",".join([f"{n}" for n in reshaped]) - else: - numbers = f"{reshaped[0]} ... {reshaped[-1]}" - - # if len(reshaped) <= 4: - # numbers = ",".join([f"{n}" for n in reshaped]) - # else: - # numbers = f"{reshaped[0]}, {reshaped[1]} ... {reshaped[-2]}, {reshaped[-1]}" - - return "["*order + numbers + "]"*order - - def __repr__(self): - - if isinstance(self.units, NamedUnit): - - value = self.value - error = self.standard_deviation().in_units_of(self.units) - unit_string = self.units.symbol - - else: - value, error = self.in_si_with_standard_error() - unit_string = self.units.dimensions.si_repr() - - if isinstance(self.value, np.ndarray): - # Get the array in short form - numeric_string = self._array_repr_format(value) - - if self.has_variance: - numeric_string += " ± " + self._array_repr_format(error) - - else: - numeric_string = f"{value}" - if self.has_variance: - numeric_string += f" ± {error}" - - return numeric_string + " " + unit_string - - @staticmethod - def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): - pass - - @property - def string_repr(self): - return str(self.hash_value) - - -class NamedQuantity[QuantityType](Quantity[QuantityType]): - def __init__(self, - name: str, - value: QuantityType, - units: Unit, - standard_error: QuantityType | None = None): - - super().__init__(value, units, standard_error=standard_error, hash_seed=name) - self.name = name - - def __repr__(self): - return f"[{self.name}] " + super().__repr__() - - def to_units_of(self, new_units: Unit) -> "NamedQuantity[QuantityType]": - new_value, new_error = self.in_units_of_with_standard_error(new_units) - return NamedQuantity(value=new_value, - units=new_units, - standard_error=new_error, - name=self.name) - - def with_standard_error(self, standard_error: Quantity): - if standard_error.units.equivalent(self.units): - return NamedQuantity( - value=self.value, - units=self.units, - standard_error=standard_error.in_units_of(self.units), - name=self.name) - - else: - raise UnitError(f"Standard error units ({standard_error.units}) " - f"are not compatible with value units ({self.units})") - - @staticmethod - def deserialise_json(json_data: dict) -> "NamedQuantity": - name = json_data["name"] - value = numerical_decode(json_data["value"]) - units_ = Unit.parse(json_data["units"]) - standard_error = numerical_decode(json_data["variance"]) ** 0.5 - history = QuantityHistory.deserialise_json(json_data["history"]) - quantity = NamedQuantity(name, value, units_, standard_error) - quantity.history = history - return quantity - - def serialise_json(self): - quantity = super().serialise_json() - quantity["name"] = self.name - return quantity - - @property - def string_repr(self): - return self.name - -class DerivedQuantity[QuantityType](Quantity[QuantityType]): - def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): - super().__init__(value, units, standard_error=None) - - self.history = history - self._variance_cache = None - self._has_variance = history.has_variance() - - - def to_units_of(self, new_units: Unit) -> "Quantity[QuantityType]": - # TODO: Lots of tests needed for this - return DerivedQuantity( - value=self.in_units_of(new_units), - units=new_units, - history=self.history) - - @property - def has_variance(self): - return self._has_variance - - @property - def variance(self) -> Quantity: - if self._variance_cache is None: - self._variance_cache = self.history.variance_propagate(self.units) - - return self._variance_cache - - - @staticmethod - def deserialise_json(json_data: dict) -> "DerivedQuantity": - value = numerical_decode(json_data["value"]) - units_ = Unit.parse(json_data["units"]) - history = QuantityHistory.deserialise_json(json_data["history"]) - quantity = DerivedQuantity(value, units_, history) - return quantity diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index c68f6f676..f15243536 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -1,11 +1,79 @@ """ -Autogenerated file by _units_table.py - - - - ******** DO NOT EDIT BY HAND ******** - +This file is autogenerated! + +Do not edit by hand, instead edit the files that build it (_build_tables.py, _units_base.py) + + + + +DDDDDDDDDDDDD NNNNNNNN NNNNNNNN tttt +D::::::::::::DDD N:::::::N N::::::N ttt:::t +D:::::::::::::::DD N::::::::N N::::::N t:::::t +DDD:::::DDDDD:::::D N:::::::::N N::::::N t:::::t + D:::::D D:::::D ooooooooooo N::::::::::N N::::::N ooooooooooo ttttttt:::::ttttttt + D:::::D D:::::D oo:::::::::::oo N:::::::::::N N::::::N oo:::::::::::oo t:::::::::::::::::t + D:::::D D:::::Do:::::::::::::::o N:::::::N::::N N::::::No:::::::::::::::ot:::::::::::::::::t + D:::::D D:::::Do:::::ooooo:::::o N::::::N N::::N N::::::No:::::ooooo:::::otttttt:::::::tttttt + D:::::D D:::::Do::::o o::::o N::::::N N::::N:::::::No::::o o::::o t:::::t + D:::::D D:::::Do::::o o::::o N::::::N N:::::::::::No::::o o::::o t:::::t + D:::::D D:::::Do::::o o::::o N::::::N N::::::::::No::::o o::::o t:::::t + D:::::D D:::::D o::::o o::::o N::::::N N:::::::::No::::o o::::o t:::::t tttttt +DDD:::::DDDDD:::::D o:::::ooooo:::::o N::::::N N::::::::No:::::ooooo:::::o t::::::tttt:::::t +D:::::::::::::::DD o:::::::::::::::o N::::::N N:::::::No:::::::::::::::o tt::::::::::::::t +D::::::::::::DDD oo:::::::::::oo N::::::N N::::::N oo:::::::::::oo tt:::::::::::tt +DDDDDDDDDDDDD ooooooooooo NNNNNNNN NNNNNNN ooooooooooo ttttttttttt + + + + + + + + + dddddddd +EEEEEEEEEEEEEEEEEEEEEE d::::::d iiii tttt BBBBBBBBBBBBBBBBB +E::::::::::::::::::::E d::::::d i::::i ttt:::t B::::::::::::::::B +E::::::::::::::::::::E d::::::d iiii t:::::t B::::::BBBBBB:::::B +EE::::::EEEEEEEEE::::E d:::::d t:::::t BB:::::B B:::::B + E:::::E EEEEEE ddddddddd:::::d iiiiiiittttttt:::::ttttttt B::::B B:::::Byyyyyyy yyyyyyy + E:::::E dd::::::::::::::d i:::::it:::::::::::::::::t B::::B B:::::B y:::::y y:::::y + E::::::EEEEEEEEEE d::::::::::::::::d i::::it:::::::::::::::::t B::::BBBBBB:::::B y:::::y y:::::y + E:::::::::::::::E d:::::::ddddd:::::d i::::itttttt:::::::tttttt B:::::::::::::BB y:::::y y:::::y + E:::::::::::::::E d::::::d d:::::d i::::i t:::::t B::::BBBBBB:::::B y:::::y y:::::y + E::::::EEEEEEEEEE d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y y:::::y + E:::::E d:::::d d:::::d i::::i t:::::t B::::B B:::::B y:::::y:::::y + E:::::E EEEEEEd:::::d d:::::d i::::i t:::::t tttttt B::::B B:::::B y:::::::::y +EE::::::EEEEEEEE:::::Ed::::::ddddd::::::ddi::::::i t::::::tttt:::::t BB:::::BBBBBB::::::B y:::::::y +E::::::::::::::::::::E d:::::::::::::::::di::::::i tt::::::::::::::t B:::::::::::::::::B y:::::y +E::::::::::::::::::::E d:::::::::ddd::::di::::::i tt:::::::::::tt B::::::::::::::::B y:::::y +EEEEEEEEEEEEEEEEEEEEEE ddddddddd dddddiiiiiiii ttttttttttt BBBBBBBBBBBBBBBBB y:::::y + y:::::y + y:::::y + y:::::y + y:::::y + yyyyyyy + + + + dddddddd +HHHHHHHHH HHHHHHHHH d::::::d +H:::::::H H:::::::H d::::::d +H:::::::H H:::::::H d::::::d +HH::::::H H::::::HH d:::::d + H:::::H H:::::H aaaaaaaaaaaaa nnnn nnnnnnnn ddddddddd:::::d + H:::::H H:::::H a::::::::::::a n:::nn::::::::nn dd::::::::::::::d + H::::::HHHHH::::::H aaaaaaaaa:::::an::::::::::::::nn d::::::::::::::::d + H:::::::::::::::::H a::::ann:::::::::::::::nd:::::::ddddd:::::d + H:::::::::::::::::H aaaaaaa:::::a n:::::nnnn:::::nd::::::d d:::::d + H::::::HHHHH::::::H aa::::::::::::a n::::n n::::nd:::::d d:::::d + H:::::H H:::::H a::::aaaa::::::a n::::n n::::nd:::::d d:::::d + H:::::H H:::::H a::::a a:::::a n::::n n::::nd:::::d d:::::d +HH::::::H H::::::HHa::::a a:::::a n::::n n::::nd::::::ddddd::::::dd +H:::::::H H:::::::Ha:::::aaaa::::::a n::::n n::::n d:::::::::::::::::d +H:::::::H H:::::::H a::::::::::aa:::a n::::n n::::n d:::::::::ddd::::d +HHHHHHHHH HHHHHHHHH aaaaaaaaaa aaaa nnnnnn nnnnnn ddddddddd ddddd + """ @@ -48,6 +116,11 @@ def __init__(self, self.moles_hint = moles_hint self.angle_hint = angle_hint + @property + def is_dimensionless(self): + """ Is this dimension dimensionless (ignores moles_hint and angle_hint) """ + return self.length == 0 and self.time == 0 and self.mass == 0 and self.current == 0 and self.temperature == 0 + def __mul__(self: Self, other: Self): if not isinstance(other, Dimensions): @@ -208,6 +281,11 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): for processor in format_process: pass + +class NamedUnit: + # TODO: Add named unit class + pass + # # Parsing plan: # Require unknown amounts of units to be explicitly positive or negative? @@ -504,27 +582,27 @@ def __init__(self, name: str, units: list[Unit]): stradians = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') none = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') litres = Unit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') -electronvolts = Unit(1.602e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') -exaelectronvolts = Unit(0.1602, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') -petaelectronvolts = Unit(0.0001602, Dimensions(2, -2, 1, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') -teraelectronvolts = Unit(1.602e-07, Dimensions(2, -2, 1, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') -gigaelectronvolts = Unit(1.602e-10, Dimensions(2, -2, 1, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') -megaelectronvolts = Unit(1.602e-13, Dimensions(2, -2, 1, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') -kiloelectronvolts = Unit(1.602e-16, Dimensions(2, -2, 1, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') -millielectronvolts = Unit(1.602e-22, Dimensions(2, -2, 1, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') -microelectronvolts = Unit(1.6019999999999998e-25, Dimensions(2, -2, 1, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') -nanoelectronvolts = Unit(1.602e-28, Dimensions(2, -2, 1, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') -picoelectronvolts = Unit(1.6019999999999998e-31, Dimensions(2, -2, 1, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') -femtoelectronvolts = Unit(1.602e-34, Dimensions(2, -2, 1, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') -attoelectronvolts = Unit(1.6020000000000002e-37, Dimensions(2, -2, 1, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') -atomic_mass_units = Unit(1.661e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') -moles = Unit(6.022e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') -millimoles = Unit(6.022e+20, Dimensions(0, 0, 0, 0, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') -micromoles = Unit(6.022e+17, Dimensions(0, 0, 0, 0, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') -nanomoles = Unit(602200000000000.1, Dimensions(0, 0, 0, 0, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') -picomoles = Unit(602200000000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') -femtomoles = Unit(602200000.0000001, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') -attomoles = Unit(602200.0000000001, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') +electronvolts = Unit(1.602176634e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') +exaelectronvolts = Unit(0.1602176634, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') +petaelectronvolts = Unit(0.0001602176634, Dimensions(2, -2, 1, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') +teraelectronvolts = Unit(1.602176634e-07, Dimensions(2, -2, 1, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') +gigaelectronvolts = Unit(1.6021766339999998e-10, Dimensions(2, -2, 1, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') +megaelectronvolts = Unit(1.6021766339999998e-13, Dimensions(2, -2, 1, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') +kiloelectronvolts = Unit(1.602176634e-16, Dimensions(2, -2, 1, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') +millielectronvolts = Unit(1.6021766339999998e-22, Dimensions(2, -2, 1, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') +microelectronvolts = Unit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') +nanoelectronvolts = Unit(1.602176634e-28, Dimensions(2, -2, 1, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') +picoelectronvolts = Unit(1.6021766339999998e-31, Dimensions(2, -2, 1, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') +femtoelectronvolts = Unit(1.602176634e-34, Dimensions(2, -2, 1, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') +attoelectronvolts = Unit(1.602176634e-37, Dimensions(2, -2, 1, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') +atomic_mass_units = Unit(1.660538921e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') +moles = Unit(6.02214076e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') +millimoles = Unit(6.02214076e+20, Dimensions(0, 0, 0, 0, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') +micromoles = Unit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') +nanomoles = Unit(602214076000000.0, Dimensions(0, 0, 0, 0, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') +picomoles = Unit(602214076000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') +femtomoles = Unit(602214076.0, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') +attomoles = Unit(602214.076, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') square_meters = Unit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = Unit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = Unit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -970,7 +1048,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_meter = Unit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') femtograms_per_cubic_meter = Unit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') attograms_per_cubic_meter = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_meter = Unit(1.661e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='NoneNone⁻³') +atomic_mass_units_per_cubic_meter = Unit(1.660538921e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='NoneNone⁻³') grams_per_cubic_exameter = Unit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') exagrams_per_cubic_exameter = Unit(1e-39, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') petagrams_per_cubic_exameter = Unit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') @@ -984,7 +1062,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_exameter = Unit(1e-69, Dimensions(length=-3, mass=1), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') femtograms_per_cubic_exameter = Unit(1e-72, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') attograms_per_cubic_exameter = Unit(1e-75, Dimensions(length=-3, mass=1), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -atomic_mass_units_per_cubic_exameter = Unit(1.661e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='NoneEm⁻³') +atomic_mass_units_per_cubic_exameter = Unit(1.6605389209999996e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='NoneEm⁻³') grams_per_cubic_petameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') exagrams_per_cubic_petameter = Unit(1e-30, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') petagrams_per_cubic_petameter = Unit(1e-33, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') @@ -998,7 +1076,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_petameter = Unit(1.0000000000000001e-60, Dimensions(length=-3, mass=1), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') femtograms_per_cubic_petameter = Unit(1.0000000000000002e-63, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') attograms_per_cubic_petameter = Unit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -atomic_mass_units_per_cubic_petameter = Unit(1.6610000000000003e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='NonePm⁻³') +atomic_mass_units_per_cubic_petameter = Unit(1.660538921e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='NonePm⁻³') grams_per_cubic_terameter = Unit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') exagrams_per_cubic_terameter = Unit(1e-21, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') petagrams_per_cubic_terameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') @@ -1012,7 +1090,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_terameter = Unit(1e-51, Dimensions(length=-3, mass=1), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') femtograms_per_cubic_terameter = Unit(1e-54, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') attograms_per_cubic_terameter = Unit(1.0000000000000001e-57, Dimensions(length=-3, mass=1), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -atomic_mass_units_per_cubic_terameter = Unit(1.6610000000000002e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='NoneTm⁻³') +atomic_mass_units_per_cubic_terameter = Unit(1.6605389209999997e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='NoneTm⁻³') grams_per_cubic_gigameter = Unit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') exagrams_per_cubic_gigameter = Unit(1e-12, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') petagrams_per_cubic_gigameter = Unit(1e-15, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') @@ -1026,7 +1104,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_gigameter = Unit(1e-42, Dimensions(length=-3, mass=1), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') femtograms_per_cubic_gigameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') attograms_per_cubic_gigameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -atomic_mass_units_per_cubic_gigameter = Unit(1.6610000000000002e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='NoneGm⁻³') +atomic_mass_units_per_cubic_gigameter = Unit(1.660538921e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='NoneGm⁻³') grams_per_cubic_megameter = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') exagrams_per_cubic_megameter = Unit(0.001, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') petagrams_per_cubic_megameter = Unit(1e-06, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') @@ -1040,7 +1118,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_megameter = Unit(1e-33, Dimensions(length=-3, mass=1), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') femtograms_per_cubic_megameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') attograms_per_cubic_megameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -atomic_mass_units_per_cubic_megameter = Unit(1.661e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='NoneMm⁻³') +atomic_mass_units_per_cubic_megameter = Unit(1.6605389209999997e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='NoneMm⁻³') grams_per_cubic_kilometer = Unit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') exagrams_per_cubic_kilometer = Unit(1000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') petagrams_per_cubic_kilometer = Unit(1000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') @@ -1054,7 +1132,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_kilometer = Unit(1.0000000000000001e-24, Dimensions(length=-3, mass=1), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') femtograms_per_cubic_kilometer = Unit(1e-27, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') attograms_per_cubic_kilometer = Unit(1e-30, Dimensions(length=-3, mass=1), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') -atomic_mass_units_per_cubic_kilometer = Unit(1.661e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='Nonekm⁻³') +atomic_mass_units_per_cubic_kilometer = Unit(1.6605389209999997e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='Nonekm⁻³') grams_per_cubic_millimeter = Unit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') exagrams_per_cubic_millimeter = Unit(1e+24, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') petagrams_per_cubic_millimeter = Unit(1e+21, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') @@ -1068,7 +1146,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_millimeter = Unit(1e-06, Dimensions(length=-3, mass=1), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') femtograms_per_cubic_millimeter = Unit(1e-09, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') attograms_per_cubic_millimeter = Unit(1e-12, Dimensions(length=-3, mass=1), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -atomic_mass_units_per_cubic_millimeter = Unit(1.661e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='Nonemm⁻³') +atomic_mass_units_per_cubic_millimeter = Unit(1.6605389209999997e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='Nonemm⁻³') grams_per_cubic_micrometer = Unit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') exagrams_per_cubic_micrometer = Unit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') petagrams_per_cubic_micrometer = Unit(1.0000000000000002e+30, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') @@ -1082,7 +1160,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_micrometer = Unit(1000.0000000000002, Dimensions(length=-3, mass=1), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') femtograms_per_cubic_micrometer = Unit(1.0000000000000002, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') attograms_per_cubic_micrometer = Unit(0.0010000000000000002, Dimensions(length=-3, mass=1), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') -atomic_mass_units_per_cubic_micrometer = Unit(1.6610000000000004e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='Noneµm⁻³') +atomic_mass_units_per_cubic_micrometer = Unit(1.660538921e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='Noneµm⁻³') grams_per_cubic_nanometer = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') exagrams_per_cubic_nanometer = Unit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') petagrams_per_cubic_nanometer = Unit(9.999999999999998e+38, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') @@ -1096,7 +1174,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_nanometer = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') femtograms_per_cubic_nanometer = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') attograms_per_cubic_nanometer = Unit(999999.9999999999, Dimensions(length=-3, mass=1), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -atomic_mass_units_per_cubic_nanometer = Unit(1.6609999999999998, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='Nonenm⁻³') +atomic_mass_units_per_cubic_nanometer = Unit(1.6605389209999994, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='Nonenm⁻³') grams_per_cubic_picometer = Unit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') exagrams_per_cubic_picometer = Unit(1e+51, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') petagrams_per_cubic_picometer = Unit(1e+48, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') @@ -1110,7 +1188,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_picometer = Unit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') femtograms_per_cubic_picometer = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') attograms_per_cubic_picometer = Unit(1000000000000000.1, Dimensions(length=-3, mass=1), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -atomic_mass_units_per_cubic_picometer = Unit(1661000000.0000002, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='Nonepm⁻³') +atomic_mass_units_per_cubic_picometer = Unit(1660538921.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='Nonepm⁻³') grams_per_cubic_femtometer = Unit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') exagrams_per_cubic_femtometer = Unit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') petagrams_per_cubic_femtometer = Unit(9.999999999999997e+56, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') @@ -1124,7 +1202,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_femtometer = Unit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') femtograms_per_cubic_femtometer = Unit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') attograms_per_cubic_femtometer = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -atomic_mass_units_per_cubic_femtometer = Unit(1.6609999999999997e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='Nonefm⁻³') +atomic_mass_units_per_cubic_femtometer = Unit(1.6605389209999992e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='Nonefm⁻³') grams_per_cubic_attometer = Unit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') exagrams_per_cubic_attometer = Unit(9.999999999999999e+68, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') petagrams_per_cubic_attometer = Unit(9.999999999999998e+65, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') @@ -1138,7 +1216,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_attometer = Unit(1e+39, Dimensions(length=-3, mass=1), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') femtograms_per_cubic_attometer = Unit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') attograms_per_cubic_attometer = Unit(1e+33, Dimensions(length=-3, mass=1), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') -atomic_mass_units_per_cubic_attometer = Unit(1.661e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='Noneam⁻³') +atomic_mass_units_per_cubic_attometer = Unit(1.6605389209999997e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='Noneam⁻³') grams_per_cubic_decimeter = Unit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='Nonedm⁻³') exagrams_per_cubic_decimeter = Unit(9.999999999999997e+17, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_decimeter', ascii_symbol='Eg dm^-3', symbol='Egdm⁻³') petagrams_per_cubic_decimeter = Unit(999999999999999.8, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_decimeter', ascii_symbol='Pg dm^-3', symbol='Pgdm⁻³') @@ -1152,7 +1230,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_decimeter = Unit(9.999999999999998e-13, Dimensions(length=-3, mass=1), name='picograms_per_cubic_decimeter', ascii_symbol='pg dm^-3', symbol='pgdm⁻³') femtograms_per_cubic_decimeter = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_decimeter', ascii_symbol='fg dm^-3', symbol='fgdm⁻³') attograms_per_cubic_decimeter = Unit(9.999999999999999e-19, Dimensions(length=-3, mass=1), name='attograms_per_cubic_decimeter', ascii_symbol='ag dm^-3', symbol='agdm⁻³') -atomic_mass_units_per_cubic_decimeter = Unit(1.661e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='Nonedm⁻³') +atomic_mass_units_per_cubic_decimeter = Unit(1.6605389209999993e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='Nonedm⁻³') grams_per_cubic_centimeter = Unit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='Nonecm⁻³') exagrams_per_cubic_centimeter = Unit(9.999999999999999e+20, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_centimeter', ascii_symbol='Eg cm^-3', symbol='Egcm⁻³') petagrams_per_cubic_centimeter = Unit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_centimeter', ascii_symbol='Pg cm^-3', symbol='Pgcm⁻³') @@ -1166,7 +1244,7 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_centimeter = Unit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='picograms_per_cubic_centimeter', ascii_symbol='pg cm^-3', symbol='pgcm⁻³') femtograms_per_cubic_centimeter = Unit(1e-12, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_centimeter', ascii_symbol='fg cm^-3', symbol='fgcm⁻³') attograms_per_cubic_centimeter = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='attograms_per_cubic_centimeter', ascii_symbol='ag cm^-3', symbol='agcm⁻³') -atomic_mass_units_per_cubic_centimeter = Unit(1.6609999999999998e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='Nonecm⁻³') +atomic_mass_units_per_cubic_centimeter = Unit(1.6605389209999996e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='Nonecm⁻³') grams_per_cubic_angstrom = Unit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') exagrams_per_cubic_angstrom = Unit(1e+45, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') petagrams_per_cubic_angstrom = Unit(9.999999999999999e+41, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') @@ -1180,119 +1258,119 @@ def __init__(self, name: str, units: list[Unit]): picograms_per_cubic_angstrom = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') femtograms_per_cubic_angstrom = Unit(1000000000000.0, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') attograms_per_cubic_angstrom = Unit(1000000000.0, Dimensions(length=-3, mass=1), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') -atomic_mass_units_per_cubic_angstrom = Unit(1661.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='NoneÅ⁻³') -moles_per_cubic_meter = Unit(6.022e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_meter = Unit(6.022e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_meter = Unit(6.022e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_meter = Unit(602200000000000.1, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_meter = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_meter = Unit(602200000.0000001, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_meter = Unit(602200.0000000001, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolNone⁻³') -moles_per_cubic_exameter = Unit(6.022e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='NoneEm⁻³') -millimoles_per_cubic_exameter = Unit(6.021999999999999e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') -micromoles_per_cubic_exameter = Unit(6.022e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') -nanomoles_per_cubic_exameter = Unit(6.022000000000001e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') -picomoles_per_cubic_exameter = Unit(6.022e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') -femtomoles_per_cubic_exameter = Unit(6.022e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') -attomoles_per_cubic_exameter = Unit(6.0220000000000006e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') -moles_per_cubic_petameter = Unit(6.022e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='NonePm⁻³') -millimoles_per_cubic_petameter = Unit(6.022e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') -micromoles_per_cubic_petameter = Unit(6.022e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') -nanomoles_per_cubic_petameter = Unit(6.0220000000000015e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') -picomoles_per_cubic_petameter = Unit(6.022e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') -femtomoles_per_cubic_petameter = Unit(6.022000000000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') -attomoles_per_cubic_petameter = Unit(6.0220000000000015e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') -moles_per_cubic_terameter = Unit(6.022e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='NoneTm⁻³') -millimoles_per_cubic_terameter = Unit(6.022e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') -micromoles_per_cubic_terameter = Unit(6.022e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') -nanomoles_per_cubic_terameter = Unit(6.022000000000001e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') -picomoles_per_cubic_terameter = Unit(6.0219999999999995e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') -femtomoles_per_cubic_terameter = Unit(6.022000000000001e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') -attomoles_per_cubic_terameter = Unit(6.022000000000001e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') -moles_per_cubic_gigameter = Unit(0.0006022, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='NoneGm⁻³') -millimoles_per_cubic_gigameter = Unit(6.022e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') -micromoles_per_cubic_gigameter = Unit(6.021999999999999e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') -nanomoles_per_cubic_gigameter = Unit(6.022000000000001e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') -picomoles_per_cubic_gigameter = Unit(6.022e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') -femtomoles_per_cubic_gigameter = Unit(6.022000000000002e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') -attomoles_per_cubic_gigameter = Unit(6.022000000000001e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') -moles_per_cubic_megameter = Unit(602200.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='NoneMm⁻³') -millimoles_per_cubic_megameter = Unit(602.2, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') -micromoles_per_cubic_megameter = Unit(0.6022, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') -nanomoles_per_cubic_megameter = Unit(0.0006022000000000001, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') -picomoles_per_cubic_megameter = Unit(6.022e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') -femtomoles_per_cubic_megameter = Unit(6.022000000000002e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') -attomoles_per_cubic_megameter = Unit(6.022000000000001e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') -moles_per_cubic_kilometer = Unit(602200000000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='Nonekm⁻³') -millimoles_per_cubic_kilometer = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') -micromoles_per_cubic_kilometer = Unit(602200000.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') -nanomoles_per_cubic_kilometer = Unit(602200.0000000001, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') -picomoles_per_cubic_kilometer = Unit(602.2, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') -femtomoles_per_cubic_kilometer = Unit(0.6022000000000001, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') -attomoles_per_cubic_kilometer = Unit(0.0006022000000000001, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') -moles_per_cubic_millimeter = Unit(6.022e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='Nonemm⁻³') -millimoles_per_cubic_millimeter = Unit(6.0219999999999996e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') -micromoles_per_cubic_millimeter = Unit(6.022e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') -nanomoles_per_cubic_millimeter = Unit(6.022000000000001e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') -picomoles_per_cubic_millimeter = Unit(6.022e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') -femtomoles_per_cubic_millimeter = Unit(6.022000000000001e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') -attomoles_per_cubic_millimeter = Unit(602200000000000.1, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') -moles_per_cubic_micrometer = Unit(6.022000000000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='Noneµm⁻³') -millimoles_per_cubic_micrometer = Unit(6.0220000000000006e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') -micromoles_per_cubic_micrometer = Unit(6.022000000000001e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') -nanomoles_per_cubic_micrometer = Unit(6.022000000000002e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') -picomoles_per_cubic_micrometer = Unit(6.022000000000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') -femtomoles_per_cubic_micrometer = Unit(6.022000000000002e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') -attomoles_per_cubic_micrometer = Unit(6.0220000000000016e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') -moles_per_cubic_nanometer = Unit(6.021999999999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='Nonenm⁻³') -millimoles_per_cubic_nanometer = Unit(6.0219999999999984e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') -micromoles_per_cubic_nanometer = Unit(6.0219999999999985e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') -nanomoles_per_cubic_nanometer = Unit(6.022e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') -picomoles_per_cubic_nanometer = Unit(6.021999999999999e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') -femtomoles_per_cubic_nanometer = Unit(6.0219999999999995e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') -attomoles_per_cubic_nanometer = Unit(6.022e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') -moles_per_cubic_picometer = Unit(6.022e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='Nonepm⁻³') -millimoles_per_cubic_picometer = Unit(6.0220000000000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') -micromoles_per_cubic_picometer = Unit(6.0220000000000005e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') -nanomoles_per_cubic_picometer = Unit(6.022000000000002e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') -picomoles_per_cubic_picometer = Unit(6.022e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') -femtomoles_per_cubic_picometer = Unit(6.022000000000002e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') -attomoles_per_cubic_picometer = Unit(6.022000000000002e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') -moles_per_cubic_femtometer = Unit(6.021999999999999e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='Nonefm⁻³') -millimoles_per_cubic_femtometer = Unit(6.021999999999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') -micromoles_per_cubic_femtometer = Unit(6.021999999999998e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') -nanomoles_per_cubic_femtometer = Unit(6.021999999999999e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') -picomoles_per_cubic_femtometer = Unit(6.021999999999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') -femtomoles_per_cubic_femtometer = Unit(6.022e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') -attomoles_per_cubic_femtometer = Unit(6.021999999999999e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') -moles_per_cubic_attometer = Unit(6.021999999999999e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='Noneam⁻³') -millimoles_per_cubic_attometer = Unit(6.021999999999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') -micromoles_per_cubic_attometer = Unit(6.021999999999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') -nanomoles_per_cubic_attometer = Unit(6.022000000000001e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') -picomoles_per_cubic_attometer = Unit(6.021999999999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') -femtomoles_per_cubic_attometer = Unit(6.022e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') -attomoles_per_cubic_attometer = Unit(6.022e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') -moles_per_cubic_decimeter = Unit(6.0219999999999985e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='Nonedm⁻³') -millimoles_per_cubic_decimeter = Unit(6.021999999999998e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') -micromoles_per_cubic_decimeter = Unit(6.021999999999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') -nanomoles_per_cubic_decimeter = Unit(6.022e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') -picomoles_per_cubic_decimeter = Unit(602199999999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') -femtomoles_per_cubic_decimeter = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') -attomoles_per_cubic_decimeter = Unit(602200000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') -moles_per_cubic_centimeter = Unit(6.0219999999999996e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='Nonecm⁻³') -millimoles_per_cubic_centimeter = Unit(6.021999999999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') -micromoles_per_cubic_centimeter = Unit(6.021999999999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') -nanomoles_per_cubic_centimeter = Unit(6.022e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') -picomoles_per_cubic_centimeter = Unit(6.021999999999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') -femtomoles_per_cubic_centimeter = Unit(602200000000000.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') -attomoles_per_cubic_centimeter = Unit(602200000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') -moles_per_cubic_angstrom = Unit(6.022e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='NoneÅ⁻³') -millimoles_per_cubic_angstrom = Unit(6.021999999999999e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') -micromoles_per_cubic_angstrom = Unit(6.021999999999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') -nanomoles_per_cubic_angstrom = Unit(6.022000000000001e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') -picomoles_per_cubic_angstrom = Unit(6.0219999999999994e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') -femtomoles_per_cubic_angstrom = Unit(6.0220000000000006e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') -attomoles_per_cubic_angstrom = Unit(6.022000000000001e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') +atomic_mass_units_per_cubic_angstrom = Unit(1660.5389209999996, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='NoneÅ⁻³') +moles_per_cubic_meter = Unit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_meter = Unit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_meter = Unit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_meter = Unit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_meter = Unit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_meter = Unit(602214076.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_meter = Unit(602214.076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolNone⁻³') +moles_per_cubic_exameter = Unit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='NoneEm⁻³') +millimoles_per_cubic_exameter = Unit(6.02214076e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') +micromoles_per_cubic_exameter = Unit(6.02214076e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') +nanomoles_per_cubic_exameter = Unit(6.022140759999999e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') +picomoles_per_cubic_exameter = Unit(6.022140759999999e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') +femtomoles_per_cubic_exameter = Unit(6.02214076e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') +attomoles_per_cubic_exameter = Unit(6.022140759999999e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') +moles_per_cubic_petameter = Unit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='NonePm⁻³') +millimoles_per_cubic_petameter = Unit(6.0221407600000005e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') +micromoles_per_cubic_petameter = Unit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') +nanomoles_per_cubic_petameter = Unit(6.02214076e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') +picomoles_per_cubic_petameter = Unit(6.0221407600000005e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') +femtomoles_per_cubic_petameter = Unit(6.022140760000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') +attomoles_per_cubic_petameter = Unit(6.022140760000001e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') +moles_per_cubic_terameter = Unit(6.022140759999999e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='NoneTm⁻³') +millimoles_per_cubic_terameter = Unit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') +micromoles_per_cubic_terameter = Unit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') +nanomoles_per_cubic_terameter = Unit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') +picomoles_per_cubic_terameter = Unit(6.02214076e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') +femtomoles_per_cubic_terameter = Unit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') +attomoles_per_cubic_terameter = Unit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') +moles_per_cubic_gigameter = Unit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='NoneGm⁻³') +millimoles_per_cubic_gigameter = Unit(6.022140760000001e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') +micromoles_per_cubic_gigameter = Unit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') +nanomoles_per_cubic_gigameter = Unit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') +picomoles_per_cubic_gigameter = Unit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') +femtomoles_per_cubic_gigameter = Unit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') +attomoles_per_cubic_gigameter = Unit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') +moles_per_cubic_megameter = Unit(602214.076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='NoneMm⁻³') +millimoles_per_cubic_megameter = Unit(602.214076, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') +micromoles_per_cubic_megameter = Unit(0.602214076, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') +nanomoles_per_cubic_megameter = Unit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') +picomoles_per_cubic_megameter = Unit(6.02214076e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') +femtomoles_per_cubic_megameter = Unit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') +attomoles_per_cubic_megameter = Unit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') +moles_per_cubic_kilometer = Unit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='Nonekm⁻³') +millimoles_per_cubic_kilometer = Unit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') +micromoles_per_cubic_kilometer = Unit(602214076.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') +nanomoles_per_cubic_kilometer = Unit(602214.076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') +picomoles_per_cubic_kilometer = Unit(602.214076, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') +femtomoles_per_cubic_kilometer = Unit(0.602214076, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') +attomoles_per_cubic_kilometer = Unit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') +moles_per_cubic_millimeter = Unit(6.0221407599999995e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='Nonemm⁻³') +millimoles_per_cubic_millimeter = Unit(6.02214076e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') +micromoles_per_cubic_millimeter = Unit(6.0221407599999996e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') +nanomoles_per_cubic_millimeter = Unit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') +picomoles_per_cubic_millimeter = Unit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') +femtomoles_per_cubic_millimeter = Unit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') +attomoles_per_cubic_millimeter = Unit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') +moles_per_cubic_micrometer = Unit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='Noneµm⁻³') +millimoles_per_cubic_micrometer = Unit(6.022140760000001e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') +micromoles_per_cubic_micrometer = Unit(6.0221407600000004e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') +nanomoles_per_cubic_micrometer = Unit(6.022140760000001e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') +picomoles_per_cubic_micrometer = Unit(6.022140760000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') +femtomoles_per_cubic_micrometer = Unit(6.022140760000001e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') +attomoles_per_cubic_micrometer = Unit(6.0221407600000005e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') +moles_per_cubic_nanometer = Unit(6.022140759999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='Nonenm⁻³') +millimoles_per_cubic_nanometer = Unit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') +micromoles_per_cubic_nanometer = Unit(6.022140759999999e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') +nanomoles_per_cubic_nanometer = Unit(6.022140759999998e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') +picomoles_per_cubic_nanometer = Unit(6.0221407599999985e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') +femtomoles_per_cubic_nanometer = Unit(6.022140759999999e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') +attomoles_per_cubic_nanometer = Unit(6.022140759999999e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') +moles_per_cubic_picometer = Unit(6.0221407600000005e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='Nonepm⁻³') +millimoles_per_cubic_picometer = Unit(6.0221407600000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') +micromoles_per_cubic_picometer = Unit(6.022140760000001e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') +nanomoles_per_cubic_picometer = Unit(6.0221407600000005e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') +picomoles_per_cubic_picometer = Unit(6.02214076e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') +femtomoles_per_cubic_picometer = Unit(6.022140760000001e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') +attomoles_per_cubic_picometer = Unit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') +moles_per_cubic_femtometer = Unit(6.022140759999998e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='Nonefm⁻³') +millimoles_per_cubic_femtometer = Unit(6.022140759999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') +micromoles_per_cubic_femtometer = Unit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') +nanomoles_per_cubic_femtometer = Unit(6.022140759999998e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') +picomoles_per_cubic_femtometer = Unit(6.022140759999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') +femtomoles_per_cubic_femtometer = Unit(6.022140759999998e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') +attomoles_per_cubic_femtometer = Unit(6.022140759999998e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') +moles_per_cubic_attometer = Unit(6.022140759999998e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='Noneam⁻³') +millimoles_per_cubic_attometer = Unit(6.022140759999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') +micromoles_per_cubic_attometer = Unit(6.022140759999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') +nanomoles_per_cubic_attometer = Unit(6.022140759999999e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') +picomoles_per_cubic_attometer = Unit(6.022140759999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') +femtomoles_per_cubic_attometer = Unit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') +attomoles_per_cubic_attometer = Unit(6.022140759999999e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') +moles_per_cubic_decimeter = Unit(6.022140759999998e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='Nonedm⁻³') +millimoles_per_cubic_decimeter = Unit(6.0221407599999985e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') +micromoles_per_cubic_decimeter = Unit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') +nanomoles_per_cubic_decimeter = Unit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') +picomoles_per_cubic_decimeter = Unit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') +femtomoles_per_cubic_decimeter = Unit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') +attomoles_per_cubic_decimeter = Unit(602214075.9999999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') +moles_per_cubic_centimeter = Unit(6.022140759999999e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='Nonecm⁻³') +millimoles_per_cubic_centimeter = Unit(6.022140759999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') +micromoles_per_cubic_centimeter = Unit(6.022140759999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') +nanomoles_per_cubic_centimeter = Unit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') +picomoles_per_cubic_centimeter = Unit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') +femtomoles_per_cubic_centimeter = Unit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') +attomoles_per_cubic_centimeter = Unit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') +moles_per_cubic_angstrom = Unit(6.022140759999999e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='NoneÅ⁻³') +millimoles_per_cubic_angstrom = Unit(6.02214076e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') +micromoles_per_cubic_angstrom = Unit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') +nanomoles_per_cubic_angstrom = Unit(6.02214076e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') +picomoles_per_cubic_angstrom = Unit(6.02214076e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') +femtomoles_per_cubic_angstrom = Unit(6.022140759999999e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') +attomoles_per_cubic_angstrom = Unit(6.02214076e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') # # Lookup table from symbols to units diff --git a/sasdata/transforms/operation.py b/sasdata/transforms/operation.py index 3ff7b0614..c06bb379a 100644 --- a/sasdata/transforms/operation.py +++ b/sasdata/transforms/operation.py @@ -1,5 +1,5 @@ import numpy as np -from sasdata.quantities.quantities import Quantity +from sasdata.quantities.quantity import Quantity class Operation: """ Sketch of what model post-processing classes might look like """ From a82e88d5a44f9c8709bdd1b050555b928e041ecc Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 15:09:31 +0100 Subject: [PATCH 496/675] Some tests --- sasdata/quantities/_build_tables.py | 5 +- sasdata/quantities/accessors.py | 4 + sasdata/quantities/quantities_tests.py | 101 ++----------------------- sasdata/quantities/quantity.py | 4 +- sasdata/quantities/units.py | 55 +++++++------- sasdata/quantities/units_tests.py | 4 +- 6 files changed, 48 insertions(+), 125 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index bd7495d01..a29d1115a 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -35,7 +35,7 @@ ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, 0, 0, all_magnitudes + unusual_magnitudes), ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, 0, 0, smaller_magnitudes), ("g", None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, 0, 0, all_magnitudes), - ("A", None, "amp", "amps", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), + ("A", None, "ampere", "amperes", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, 0, 0, all_magnitudes) ] derived_si_units = [ @@ -68,7 +68,8 @@ ("l", None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), ("eV", None, "electronvolt", "electronvolts", 1.602176634e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), ("au", None, "atomic mass unit", "atomic mass units", 1.660538921e-27, 0, 0, 1, 0, 0, 0, 0, []), - ("mol", None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes) + ("mol", None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes), + ("kgForce", None, "kg force", "kg force", 9.80665, 1, -2, 1, 0, 0, 0, 0, []) ] aliases = { diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 575298eb9..53cfca732 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -2994,6 +2994,10 @@ def femtonewtons(self) -> T: def attonewtons(self) -> T: return self.quantity.in_units_of(units.attonewtons) + @property + def kg_force(self) -> T: + return self.quantity.in_units_of(units.kg_force) + class PressureAccessor[T](Accessor[T]): diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index 8ab0a41ce..b9d3b7d8e 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -2,123 +2,36 @@ from sasdata.quantities.quantity import Quantity, UnitError import sasdata.quantities.units as units -import sasdata.quantities.si as si import pytest def test_in_units_of_calculation(): """ Just a couple of unit conversions """ assert Quantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 assert Quantity(10, units.minutes).in_units_of(units.seconds) == 600 - assert Quantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000 / 9.81, abs=1) + assert Quantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000/9.81, abs=1) assert Quantity(0, units.meters).in_units_of(units.exameters) == 0 def test_unit_compounding_pow(): - """ Test units compound correctly when __pow__ is used""" - assert (Quantity(1, units.millimeters) ** 2).in_units_of(units.square_meters) == 1e-6 - assert (Quantity(1, units.minutes) ** 3).in_units_of(units.seconds ** 3) == 60 ** 3 - -def test_pow_scaling(): - q2 = Quantity(1000, units.millimeters)**2 - assert q2.units.scale == 1e-6 - assert q2.value == 1e6 - + assert (Quantity(1, units.millimeters)**2).in_units_of(units.square_meters) == 1e-6 + assert (Quantity(1, units.minutes)**3).in_units_of(units.seconds**3) == 60**3 def test_unit_compounding_mul(): - """ Test units compound correctly when __mul__ is used""" assert (Quantity(4, units.minutes) * Quantity(0.25, units.hertz)).in_units_of(units.none) == 60 assert (Quantity(250, units.volts) * Quantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 def test_unit_compounding_div(): - """ Test units compound correctly when __truediv__ is used""" assert (Quantity(10, units.kilometers) / Quantity(2, units.minutes) ).in_units_of(units.meters_per_second) == pytest.approx(250/3, abs=1e-6) - assert (Quantity(1, units.nanowebers) / (Quantity(1, units.millimeters) ** 2)).in_units_of(units.millitesla) == 1 + assert (Quantity(1, units.nanowebers) / (Quantity(1, units.millimeters)**2)).in_units_of(units.millitesla) == 1 def test_value_mul(): - """ Test value part of quantities multiply correctly""" assert (Quantity(1j, units.seconds) * Quantity(1j, units.watts)).in_units_of(units.joules) == -1 -def test_scalar_mul(): - assert (Quantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 - assert (10 * Quantity(1, units.seconds)).in_units_of(units.seconds) == 10 - assert (1000 * Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 - -def test_scalar_div(): - - assert (Quantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 - assert (10 / Quantity(1, units.seconds)).in_units_of(units.hertz) == 10 - assert (0.001 / Quantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 - -def test_good_add_sub(): - """ Test that adding and subtracting units works """ - assert (Quantity(1, units.seconds) + Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 - assert (Quantity(1, units.seconds) - Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 - - assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == pytest.approx(13, abs=1e-8) - -@pytest.mark.parametrize("unit_in, power, unit_out", [ - (units.meters**2, 1/2, units.meters), - (units.meters**3, 1/3, units.meters), - (units.meters**3, 2/3, units.meters**2), - (units.meters**3, -5/3, units.meters**-5), - (units.none, 1/10, units.none), - (units.none, 19/17, units.none), - (units.none, np.pi, units.none) -]) -def test_good_non_integer_unit_powers(unit_in, power, unit_out): - """ Check that we can do various square and cube root stuff if we need to, - If dimensionless, we should be able to do arbitrary powers - """ - assert unit_in**power == unit_out - -@pytest.mark.parametrize("unit, power", [ - (units.meters, 1/2), - (units.milliohms, 1/3), - (units.meters, 3/2), - (units.meters**2, 2/3) -]) -def test_bad_non_integer_unit_powers(unit, power): - """ Check that we get an error if we try and do something silly with powers""" - with pytest.raises(units.DimensionError): - x = unit**power - - -@pytest.mark.parametrize("unit_1", si.all_si) -@pytest.mark.parametrize("unit_2", si.all_si) -def test_mixed_quantity_add_sub(unit_1, unit_2): - if unit_1.equivalent(unit_2): - assert (Quantity(0, unit_1) + Quantity(0, unit_2)).in_units_of(unit_1) == 0 - - else: - with pytest.raises(UnitError): - Quantity(1, unit_1) + Quantity(1, unit_2) - -def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float, abs=1e-9): - """ Helper function for testing units that are multiples of each other """ - - assert u1.equivalent(u2), "Units should be compatible for this test" - assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) - - -def test_american_units(): - assert_unit_ratio(units.feet, units.inches, 12) - assert_unit_ratio(units.yards, units.inches, 36) - assert_unit_ratio(units.miles, units.inches, 63360) - assert_unit_ratio(units.pounds_force_per_square_inch, units.pounds_force / (units.inches**2), 1, abs=1e-5) - -def test_percent(): - assert Quantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) -@pytest.mark.parametrize("unit_1", si.all_si) -@pytest.mark.parametrize("unit_2", si.all_si) -def test_conversion_errors(unit_1, unit_2): - """ Test conversion errors are thrown when units are not compatible """ +def test_conversion_errors(): - if unit_1 == unit_2: - assert Quantity(1, unit_1).in_units_of(unit_2) == 1 - else: - with pytest.raises(UnitError): - Quantity(1, units.seconds).in_units_of(units.meters) + with pytest.raises(UnitError): + Quantity(1, units.seconds).in_units_of(units.meters) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index e265c2762..b7efb1c8c 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -21,7 +21,7 @@ def __init__(self, value: QuantityType, units: Unit): def in_units_of(self, units: Unit) -> QuantityType: if self.units.equivalent(units): - return (units.scale / self.units.scale) * self.value + return (self.units.scale / units.scale) * self.value else: raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") @@ -74,3 +74,5 @@ def __sub__(self: Self, other: Self | ArrayLike) -> Self: def __rsub__(self: Self, other: Self | ArrayLike) -> Self: return (-self) + other + def __pow__(self: Self, other: int): + return Quantity(self.value**other, self.units**other) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index f15243536..24ca20761 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -376,19 +376,19 @@ def __init__(self, name: str, units: list[Unit]): picograms = Unit(1e-15, Dimensions(0, 0, 1, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') femtograms = Unit(1e-18, Dimensions(0, 0, 1, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') attograms = Unit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') -amps = Unit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amps',ascii_symbol='A',symbol='A') -exaamps = Unit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamps',ascii_symbol='EA',symbol='EA') -petaamps = Unit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamps',ascii_symbol='PA',symbol='PA') -teraamps = Unit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamps',ascii_symbol='TA',symbol='TA') -gigaamps = Unit(1000000000.0, Dimensions(0, 0, 0, 1, 0),name='gigaamps',ascii_symbol='GA',symbol='GA') -megaamps = Unit(1000000.0, Dimensions(0, 0, 0, 1, 0),name='megaamps',ascii_symbol='MA',symbol='MA') -kiloamps = Unit(1000.0, Dimensions(0, 0, 0, 1, 0),name='kiloamps',ascii_symbol='kA',symbol='kA') -milliamps = Unit(0.001, Dimensions(0, 0, 0, 1, 0),name='milliamps',ascii_symbol='mA',symbol='mA') -microamps = Unit(1e-06, Dimensions(0, 0, 0, 1, 0),name='microamps',ascii_symbol='uA',symbol='µA') -nanoamps = Unit(1e-09, Dimensions(0, 0, 0, 1, 0),name='nanoamps',ascii_symbol='nA',symbol='nA') -picoamps = Unit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamps',ascii_symbol='pA',symbol='pA') -femtoamps = Unit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamps',ascii_symbol='fA',symbol='fA') -attoamps = Unit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamps',ascii_symbol='aA',symbol='aA') +amperes = Unit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amperes',ascii_symbol='A',symbol='A') +exaamperes = Unit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamperes',ascii_symbol='EA',symbol='EA') +petaamperes = Unit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamperes',ascii_symbol='PA',symbol='PA') +teraamperes = Unit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamperes',ascii_symbol='TA',symbol='TA') +gigaamperes = Unit(1000000000.0, Dimensions(0, 0, 0, 1, 0),name='gigaamperes',ascii_symbol='GA',symbol='GA') +megaamperes = Unit(1000000.0, Dimensions(0, 0, 0, 1, 0),name='megaamperes',ascii_symbol='MA',symbol='MA') +kiloamperes = Unit(1000.0, Dimensions(0, 0, 0, 1, 0),name='kiloamperes',ascii_symbol='kA',symbol='kA') +milliamperes = Unit(0.001, Dimensions(0, 0, 0, 1, 0),name='milliamperes',ascii_symbol='mA',symbol='mA') +microamperes = Unit(1e-06, Dimensions(0, 0, 0, 1, 0),name='microamperes',ascii_symbol='uA',symbol='µA') +nanoamperes = Unit(1e-09, Dimensions(0, 0, 0, 1, 0),name='nanoamperes',ascii_symbol='nA',symbol='nA') +picoamperes = Unit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamperes',ascii_symbol='pA',symbol='pA') +femtoamperes = Unit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamperes',ascii_symbol='fA',symbol='fA') +attoamperes = Unit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamperes',ascii_symbol='aA',symbol='aA') kelvin = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kelvin',ascii_symbol='K',symbol='K') exakelvin = Unit(1e+18, Dimensions(0, 0, 0, 0, 1),name='exakelvin',ascii_symbol='EK',symbol='EK') petakelvin = Unit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1),name='petakelvin',ascii_symbol='PK',symbol='PK') @@ -603,6 +603,7 @@ def __init__(self, name: str, units: list[Unit]): picomoles = Unit(602214076000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') femtomoles = Unit(602214076.0, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') attomoles = Unit(602214.076, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') +kg_force = Unit(9.80665, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kg_force',ascii_symbol='kgForce',symbol='kgForce') square_meters = Unit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = Unit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = Unit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -1416,19 +1417,19 @@ def __init__(self, name: str, units: list[Unit]): "fg": femtograms, "ag": attograms, "A": angstroms, - "EA": exaamps, - "PA": petaamps, - "TA": teraamps, - "GA": gigaamps, - "MA": megaamps, - "kA": kiloamps, - "mA": milliamps, - "uA": microamps, - "µA": microamps, - "nA": nanoamps, - "pA": picoamps, - "fA": femtoamps, - "aA": attoamps, + "EA": exaamperes, + "PA": petaamperes, + "TA": teraamperes, + "GA": gigaamperes, + "MA": megaamperes, + "kA": kiloamperes, + "mA": milliamperes, + "uA": microamperes, + "µA": microamperes, + "nA": nanoamperes, + "pA": picoamperes, + "fA": femtoamperes, + "aA": attoamperes, "K": kelvin, "EK": exakelvin, "PK": petakelvin, @@ -1671,6 +1672,7 @@ def __init__(self, name: str, units: list[Unit]): "pmol": picomoles, "fmol": femtomoles, "amol": attomoles, + "kgForce": kg_force, "yr": years, "year": years, "day": days, @@ -2455,6 +2457,7 @@ def __init__(self, name: str, units: list[Unit]): piconewtons, femtonewtons, attonewtons, + kg_force, ]) pressure = UnitGroup( diff --git a/sasdata/quantities/units_tests.py b/sasdata/quantities/units_tests.py index 05636153f..d0d090934 100644 --- a/sasdata/quantities/units_tests.py +++ b/sasdata/quantities/units_tests.py @@ -34,7 +34,7 @@ def run_test(self): EqualUnits("Resistance", units.ohms, - units.volts / units.amps, + units.volts / units.amperes, 1e-3/units.millisiemens) @@ -43,4 +43,4 @@ def run_test(self): for test in tests: print(test.test_name) - test.run_test() + test.run_test() \ No newline at end of file From 5903d8e1d1abb86bc3d1fde5b3def099b4a6d73a Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 15:16:51 +0100 Subject: [PATCH 497/675] SI unit module --- sasdata/quantities/_build_tables.py | 12 ++++++++++-- sasdata/quantities/si.py | 21 --------------------- sasdata/quantities/units.py | 2 +- 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index a29d1115a..1cc57360d 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -52,7 +52,6 @@ ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, 0, 0, all_magnitudes), ("T", None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, 0, 0, all_magnitudes), ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), - ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []) ] non_si_units = [ @@ -69,7 +68,8 @@ ("eV", None, "electronvolt", "electronvolts", 1.602176634e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), ("au", None, "atomic mass unit", "atomic mass units", 1.660538921e-27, 0, 0, 1, 0, 0, 0, 0, []), ("mol", None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes), - ("kgForce", None, "kg force", "kg force", 9.80665, 1, -2, 1, 0, 0, 0, 0, []) + ("kgForce", None, "kg force", "kg force", 9.80665, 1, -2, 1, 0, 0, 0, 0, []), + ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []) ] aliases = { @@ -347,4 +347,12 @@ def format_name(name: str): f"\n") fid.write("\n") +with open("si.py", 'w') as fid: + + fid.write('"""'+(warning_text%"_build_tables.py")+'"""\n\n') + si_unit_names = [values[3] for values in base_si_units + derived_si_units if values[3] != "grams"] + ["kilograms"] + + for name in si_unit_names: + + fid.write(f"from sasdata.quantities.units import {name}\n") diff --git a/sasdata/quantities/si.py b/sasdata/quantities/si.py index 871b6ee26..039cdb8e8 100644 --- a/sasdata/quantities/si.py +++ b/sasdata/quantities/si.py @@ -96,24 +96,3 @@ from sasdata.quantities.units import tesla from sasdata.quantities.units import henry from sasdata.quantities.units import kilograms - -all_si = [ - meters, - seconds, - amperes, - kelvin, - hertz, - newtons, - pascals, - joules, - watts, - coulombs, - volts, - ohms, - farads, - siemens, - webers, - tesla, - henry, - kilograms, -] diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 24ca20761..04907bfc6 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -571,7 +571,6 @@ def __init__(self, name: str, units: list[Unit]): picohenry = Unit(1e-12, Dimensions(2, -2, 1, -2, 0),name='picohenry',ascii_symbol='pH',symbol='pH') femtohenry = Unit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') attohenry = Unit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') -degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') minutes = Unit(60, Dimensions(0, 1, 0, 0, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') hours = Unit(360, Dimensions(0, 1, 0, 0, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') @@ -604,6 +603,7 @@ def __init__(self, name: str, units: list[Unit]): femtomoles = Unit(602214076.0, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') attomoles = Unit(602214.076, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') kg_force = Unit(9.80665, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kg_force',ascii_symbol='kgForce',symbol='kgForce') +degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') square_meters = Unit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = Unit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = Unit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') From 4cc3d2cc7bfac0d7c25453b0961b5a9553524d1d Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 15:21:34 +0100 Subject: [PATCH 498/675] si unit list --- sasdata/quantities/_build_tables.py | 4 ++++ sasdata/quantities/si.py | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 1cc57360d..e455027e4 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -355,4 +355,8 @@ def format_name(name: str): for name in si_unit_names: fid.write(f"from sasdata.quantities.units import {name}\n") + fid.write("\nall_si = [\n") + for name in si_unit_names: + fid.write(f" {name},\n") + fid.write("]\n") diff --git a/sasdata/quantities/si.py b/sasdata/quantities/si.py index 039cdb8e8..871b6ee26 100644 --- a/sasdata/quantities/si.py +++ b/sasdata/quantities/si.py @@ -96,3 +96,24 @@ from sasdata.quantities.units import tesla from sasdata.quantities.units import henry from sasdata.quantities.units import kilograms + +all_si = [ + meters, + seconds, + amperes, + kelvin, + hertz, + newtons, + pascals, + joules, + watts, + coulombs, + volts, + ohms, + farads, + siemens, + webers, + tesla, + henry, + kilograms, +] From f10d810065201493788864ba4d189c72d7c2a408 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 16:37:17 +0100 Subject: [PATCH 499/675] More tests, added names --- sasdata/quantities/_build_tables.py | 35 +- sasdata/quantities/_units_base.py | 39 +- sasdata/quantities/accessors.py | 948 +++++++++ sasdata/quantities/quantities_tests.py | 58 +- sasdata/quantities/quantity.py | 11 +- sasdata/quantities/units.py | 2580 ++++++++++++++---------- 6 files changed, 2591 insertions(+), 1080 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index e455027e4..ba0db8bb2 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -54,7 +54,7 @@ ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), ] -non_si_units = [ +non_si_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, 0, 0, []), ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, 0, 0, []), ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, 0, 0, []), @@ -69,7 +69,14 @@ ("au", None, "atomic mass unit", "atomic mass units", 1.660538921e-27, 0, 0, 1, 0, 0, 0, 0, []), ("mol", None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes), ("kgForce", None, "kg force", "kg force", 9.80665, 1, -2, 1, 0, 0, 0, 0, []), - ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []) + ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []), + ("miles", None, "mile", "miles", 1760*3*0.3048, 1, 0, 0, 0, 0, 0, 0, []), + ("yrd", None, "yard", "yards", 3*0.3048, 1, 0, 0, 0, 0, 0, 0, []), + ("ft", None, "foot", "feet", 0.3048, 1, 0, 0, 0, 0, 0, 0, []), + ("in", None, "inch", "inches", 0.0254, 1, 0, 0, 0, 0, 0, 0, []), + ("lb", None, "pound", "pounds", 0.45359237, 0, 0, 1, 0, 0, 0, 0, []), + ("oz", None, "ounce", "ounces", 0.45359237/16, 0, 0, 1, 0, 0, 0, 0, []), + ("psi", None, "pound force per square inch", "pound force per square inch", 6.894757e3, -1, -2, 1, 0, 0, 0, 0, []), ] aliases = { @@ -113,13 +120,20 @@ def format_name(name: str): unit_types_temp = defaultdict(list) # Keep track of unit types unit_types = defaultdict(list) - for symbol, special_symbol, singular, plural, scale, length, time, mass, current, temperature, moles_hint, angle_hint, magnitudes in all_units: + for unit_def in all_units: + + try: + symbol, special_symbol, singular, plural, scale, length, time, \ + mass, current, temperature, moles_hint, angle_hint, magnitudes = unit_def + except Exception as e: + print(unit_def) + raise e formatted_plural = format_name(plural) formatted_singular = format_name(singular) dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) - fid.write(f"{formatted_plural} = Unit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," + fid.write(f"{formatted_plural} = NamedUnit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," f"name='{formatted_plural}'," f"ascii_symbol='{symbol}'," f"symbol='{symbol if special_symbol is None else special_symbol}')\n") @@ -149,7 +163,7 @@ def format_name(name: str): # Units dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) - fid.write(f"{combined_name_plural} = Unit({combined_scale}, " + fid.write(f"{combined_name_plural} = NamedUnit({combined_scale}, " f"Dimensions({length}, {time}, {mass}, {current}, {temperature})," f"name='{combined_name_plural}'," f"ascii_symbol='{combined_symbol}'," @@ -186,7 +200,7 @@ def format_name(name: str): unit_name = prefix + name unit_special_symbol = (symbol if special_symbol is None else special_symbol) + unicode_suffix unit_symbol = symbol + f"^{power}" - fid.write(f"{unit_name} = Unit({scale**power}, Dimensions(length={power}), " + fid.write(f"{unit_name} = NamedUnit({scale**power}, Dimensions(length={power}), " f"name='{unit_name}', " f"ascii_symbol='{unit_symbol}', " f"symbol='{unit_special_symbol}')\n") @@ -203,13 +217,13 @@ def format_name(name: str): accel_dimensions = Dimensions(length=1, time=-2) fid.write(f"{speed_name} " - f"= Unit({length_scale / time_scale}, " + f"= NamedUnit({length_scale / time_scale}, " f"Dimensions(length=1, time=-1), " f"name='{speed_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}', " f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") - fid.write(f"{accel_name} = Unit({length_scale / time_scale}, " + fid.write(f"{accel_name} = NamedUnit({length_scale / time_scale}, " f"Dimensions(length=1, time=-2), " f"name='{accel_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}^2', " @@ -227,7 +241,7 @@ def format_name(name: str): dimensions = Dimensions(length=-3, mass=1) fid.write(f"{name} " - f"= Unit({mass_scale / length_scale**3}, " + f"= NamedUnit({mass_scale / length_scale**3}, " f"Dimensions(length=-3, mass=1), " f"name='{name}', " f"ascii_symbol='{mass_symbol} {length_symbol}^-3', " @@ -244,7 +258,7 @@ def format_name(name: str): dimensions = Dimensions(length=-3, moles_hint=1) fid.write(f"{name} " - f"= Unit({amount_scale / length_scale**3}, " + f"= NamedUnit({amount_scale / length_scale**3}, " f"Dimensions(length=-3, moles_hint=1), " f"name='{name}', " f"ascii_symbol='{amount_symbol} {length_symbol}^-3', " @@ -359,4 +373,3 @@ def format_name(name: str): for name in si_unit_names: fid.write(f" {name},\n") fid.write("]\n") - diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index a8c713d30..995c62ce7 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -143,33 +143,27 @@ def __repr__(self): class Unit: def __init__(self, si_scaling_factor: float, - dimensions: Dimensions, - name: str | None = None, - ascii_symbol: str | None = None, - symbol: str | None = None): + dimensions: Dimensions): 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): + def __mul__(self: Self, other: "Unit"): if not isinstance(other, Unit): return NotImplemented return Unit(self.scale * other.scale, self.dimensions * other.dimensions) - def __truediv__(self: Self, other: Self): + def __truediv__(self: Self, other: "Unit"): if not isinstance(other, Unit): return NotImplemented return Unit(self.scale / other.scale, self.dimensions / other.dimensions) - def __rtruediv__(self: Self, other: Self): + def __rtruediv__(self: Self, other: "Unit"): if isinstance(other, Unit): return Unit(other.scale / self.scale, other.dimensions / self.dimensions) elif isinstance(other, (int, float)): @@ -183,10 +177,10 @@ def __pow__(self, power: int): return Unit(self.scale**power, self.dimensions**power) - def equivalent(self: Self, other: Self): + def equivalent(self: Self, other: "Unit"): return self.dimensions == other.dimensions - def __eq__(self: Self, other: Self): + def __eq__(self: Self, other: "Unit"): return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 def si_equivalent(self): @@ -197,10 +191,25 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): for processor in format_process: pass + def __repr__(self): + return f"Unit[{self.scale}, {self.dimensions}]" -class NamedUnit: - # TODO: Add named unit class - pass +class NamedUnit(Unit): + """ Units, but they have a name, and a symbol""" + def __init__(self, + si_scaling_factor: float, + dimensions: Dimensions, + name: str | None = None, + ascii_symbol: str | None = None, + symbol: str | None = None): + + super().__init__(si_scaling_factor, dimensions) + self.name = name + self.ascii_symbol = ascii_symbol + self.symbol = symbol + + def __repr__(self): + return self.name # # Parsing plan: diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 53cfca732..5882a9795 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -163,6 +163,22 @@ def centimeters(self) -> T: def angstroms(self) -> T: return self.quantity.in_units_of(units.angstroms) + @property + def miles(self) -> T: + return self.quantity.in_units_of(units.miles) + + @property + def yards(self) -> T: + return self.quantity.in_units_of(units.yards) + + @property + def feet(self) -> T: + return self.quantity.in_units_of(units.feet) + + @property + def inches(self) -> T: + return self.quantity.in_units_of(units.inches) + class AreaAccessor[T](Accessor[T]): @@ -232,6 +248,22 @@ def square_centimeters(self) -> T: def square_angstroms(self) -> T: return self.quantity.in_units_of(units.square_angstroms) + @property + def square_miles(self) -> T: + return self.quantity.in_units_of(units.square_miles) + + @property + def square_yards(self) -> T: + return self.quantity.in_units_of(units.square_yards) + + @property + def square_feet(self) -> T: + return self.quantity.in_units_of(units.square_feet) + + @property + def square_inches(self) -> T: + return self.quantity.in_units_of(units.square_inches) + class VolumeAccessor[T](Accessor[T]): @@ -305,6 +337,22 @@ def cubic_centimeters(self) -> T: def cubic_angstroms(self) -> T: return self.quantity.in_units_of(units.cubic_angstroms) + @property + def cubic_miles(self) -> T: + return self.quantity.in_units_of(units.cubic_miles) + + @property + def cubic_yards(self) -> T: + return self.quantity.in_units_of(units.cubic_yards) + + @property + def cubic_feet(self) -> T: + return self.quantity.in_units_of(units.cubic_feet) + + @property + def cubic_inches(self) -> T: + return self.quantity.in_units_of(units.cubic_inches) + class InverselengthAccessor[T](Accessor[T]): @@ -374,6 +422,22 @@ def per_centimeter(self) -> T: def per_angstrom(self) -> T: return self.quantity.in_units_of(units.per_angstrom) + @property + def per_mile(self) -> T: + return self.quantity.in_units_of(units.per_mile) + + @property + def per_yard(self) -> T: + return self.quantity.in_units_of(units.per_yard) + + @property + def per_foot(self) -> T: + return self.quantity.in_units_of(units.per_foot) + + @property + def per_inch(self) -> T: + return self.quantity.in_units_of(units.per_inch) + class InverseareaAccessor[T](Accessor[T]): @@ -443,6 +507,22 @@ def per_square_centimeter(self) -> T: def per_square_angstrom(self) -> T: return self.quantity.in_units_of(units.per_square_angstrom) + @property + def per_square_mile(self) -> T: + return self.quantity.in_units_of(units.per_square_mile) + + @property + def per_square_yard(self) -> T: + return self.quantity.in_units_of(units.per_square_yard) + + @property + def per_square_foot(self) -> T: + return self.quantity.in_units_of(units.per_square_foot) + + @property + def per_square_inch(self) -> T: + return self.quantity.in_units_of(units.per_square_inch) + class InversevolumeAccessor[T](Accessor[T]): @@ -512,6 +592,22 @@ def per_cubic_centimeter(self) -> T: def per_cubic_angstrom(self) -> T: return self.quantity.in_units_of(units.per_cubic_angstrom) + @property + def per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.per_cubic_mile) + + @property + def per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.per_cubic_yard) + + @property + def per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.per_cubic_foot) + + @property + def per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.per_cubic_inch) + class TimeAccessor[T](Accessor[T]): @@ -1327,6 +1423,182 @@ def angstroms_per_day(self) -> T: def angstroms_per_year(self) -> T: return self.quantity.in_units_of(units.angstroms_per_year) + @property + def miles_per_second(self) -> T: + return self.quantity.in_units_of(units.miles_per_second) + + @property + def miles_per_millisecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_millisecond) + + @property + def miles_per_microsecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_microsecond) + + @property + def miles_per_nanosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_nanosecond) + + @property + def miles_per_picosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_picosecond) + + @property + def miles_per_femtosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_femtosecond) + + @property + def miles_per_attosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_attosecond) + + @property + def miles_per_minute(self) -> T: + return self.quantity.in_units_of(units.miles_per_minute) + + @property + def miles_per_hour(self) -> T: + return self.quantity.in_units_of(units.miles_per_hour) + + @property + def miles_per_day(self) -> T: + return self.quantity.in_units_of(units.miles_per_day) + + @property + def miles_per_year(self) -> T: + return self.quantity.in_units_of(units.miles_per_year) + + @property + def yards_per_second(self) -> T: + return self.quantity.in_units_of(units.yards_per_second) + + @property + def yards_per_millisecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_millisecond) + + @property + def yards_per_microsecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_microsecond) + + @property + def yards_per_nanosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_nanosecond) + + @property + def yards_per_picosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_picosecond) + + @property + def yards_per_femtosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_femtosecond) + + @property + def yards_per_attosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_attosecond) + + @property + def yards_per_minute(self) -> T: + return self.quantity.in_units_of(units.yards_per_minute) + + @property + def yards_per_hour(self) -> T: + return self.quantity.in_units_of(units.yards_per_hour) + + @property + def yards_per_day(self) -> T: + return self.quantity.in_units_of(units.yards_per_day) + + @property + def yards_per_year(self) -> T: + return self.quantity.in_units_of(units.yards_per_year) + + @property + def feet_per_second(self) -> T: + return self.quantity.in_units_of(units.feet_per_second) + + @property + def feet_per_millisecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_millisecond) + + @property + def feet_per_microsecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_microsecond) + + @property + def feet_per_nanosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_nanosecond) + + @property + def feet_per_picosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_picosecond) + + @property + def feet_per_femtosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_femtosecond) + + @property + def feet_per_attosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_attosecond) + + @property + def feet_per_minute(self) -> T: + return self.quantity.in_units_of(units.feet_per_minute) + + @property + def feet_per_hour(self) -> T: + return self.quantity.in_units_of(units.feet_per_hour) + + @property + def feet_per_day(self) -> T: + return self.quantity.in_units_of(units.feet_per_day) + + @property + def feet_per_year(self) -> T: + return self.quantity.in_units_of(units.feet_per_year) + + @property + def inches_per_second(self) -> T: + return self.quantity.in_units_of(units.inches_per_second) + + @property + def inches_per_millisecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_millisecond) + + @property + def inches_per_microsecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_microsecond) + + @property + def inches_per_nanosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_nanosecond) + + @property + def inches_per_picosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_picosecond) + + @property + def inches_per_femtosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_femtosecond) + + @property + def inches_per_attosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_attosecond) + + @property + def inches_per_minute(self) -> T: + return self.quantity.in_units_of(units.inches_per_minute) + + @property + def inches_per_hour(self) -> T: + return self.quantity.in_units_of(units.inches_per_hour) + + @property + def inches_per_day(self) -> T: + return self.quantity.in_units_of(units.inches_per_day) + + @property + def inches_per_year(self) -> T: + return self.quantity.in_units_of(units.inches_per_year) + class AccelerationAccessor[T](Accessor[T]): @@ -2036,6 +2308,182 @@ def angstroms_per_square_day(self) -> T: def angstroms_per_square_year(self) -> T: return self.quantity.in_units_of(units.angstroms_per_square_year) + @property + def miles_per_square_second(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_second) + + @property + def miles_per_square_millisecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_millisecond) + + @property + def miles_per_square_microsecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_microsecond) + + @property + def miles_per_square_nanosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_nanosecond) + + @property + def miles_per_square_picosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_picosecond) + + @property + def miles_per_square_femtosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_femtosecond) + + @property + def miles_per_square_attosecond(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_attosecond) + + @property + def miles_per_square_minute(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_minute) + + @property + def miles_per_square_hour(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_hour) + + @property + def miles_per_square_day(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_day) + + @property + def miles_per_square_year(self) -> T: + return self.quantity.in_units_of(units.miles_per_square_year) + + @property + def yards_per_square_second(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_second) + + @property + def yards_per_square_millisecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_millisecond) + + @property + def yards_per_square_microsecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_microsecond) + + @property + def yards_per_square_nanosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_nanosecond) + + @property + def yards_per_square_picosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_picosecond) + + @property + def yards_per_square_femtosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_femtosecond) + + @property + def yards_per_square_attosecond(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_attosecond) + + @property + def yards_per_square_minute(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_minute) + + @property + def yards_per_square_hour(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_hour) + + @property + def yards_per_square_day(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_day) + + @property + def yards_per_square_year(self) -> T: + return self.quantity.in_units_of(units.yards_per_square_year) + + @property + def feet_per_square_second(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_second) + + @property + def feet_per_square_millisecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_millisecond) + + @property + def feet_per_square_microsecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_microsecond) + + @property + def feet_per_square_nanosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_nanosecond) + + @property + def feet_per_square_picosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_picosecond) + + @property + def feet_per_square_femtosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_femtosecond) + + @property + def feet_per_square_attosecond(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_attosecond) + + @property + def feet_per_square_minute(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_minute) + + @property + def feet_per_square_hour(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_hour) + + @property + def feet_per_square_day(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_day) + + @property + def feet_per_square_year(self) -> T: + return self.quantity.in_units_of(units.feet_per_square_year) + + @property + def inches_per_square_second(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_second) + + @property + def inches_per_square_millisecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_millisecond) + + @property + def inches_per_square_microsecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_microsecond) + + @property + def inches_per_square_nanosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_nanosecond) + + @property + def inches_per_square_picosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_picosecond) + + @property + def inches_per_square_femtosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_femtosecond) + + @property + def inches_per_square_attosecond(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_attosecond) + + @property + def inches_per_square_minute(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_minute) + + @property + def inches_per_square_hour(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_hour) + + @property + def inches_per_square_day(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_day) + + @property + def inches_per_square_year(self) -> T: + return self.quantity.in_units_of(units.inches_per_square_year) + class DensityAccessor[T](Accessor[T]): @@ -2097,6 +2545,14 @@ def attograms_per_cubic_meter(self) -> T: def atomic_mass_units_per_cubic_meter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_meter) + @property + def pounds_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_meter) + + @property + def ounces_per_cubic_meter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_meter) + @property def grams_per_cubic_exameter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_exameter) @@ -2153,6 +2609,14 @@ def attograms_per_cubic_exameter(self) -> T: def atomic_mass_units_per_cubic_exameter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_exameter) + @property + def pounds_per_cubic_exameter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_exameter) + + @property + def ounces_per_cubic_exameter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_exameter) + @property def grams_per_cubic_petameter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_petameter) @@ -2209,6 +2673,14 @@ def attograms_per_cubic_petameter(self) -> T: def atomic_mass_units_per_cubic_petameter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_petameter) + @property + def pounds_per_cubic_petameter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_petameter) + + @property + def ounces_per_cubic_petameter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_petameter) + @property def grams_per_cubic_terameter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_terameter) @@ -2265,6 +2737,14 @@ def attograms_per_cubic_terameter(self) -> T: def atomic_mass_units_per_cubic_terameter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_terameter) + @property + def pounds_per_cubic_terameter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_terameter) + + @property + def ounces_per_cubic_terameter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_terameter) + @property def grams_per_cubic_gigameter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_gigameter) @@ -2321,6 +2801,14 @@ def attograms_per_cubic_gigameter(self) -> T: def atomic_mass_units_per_cubic_gigameter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_gigameter) + @property + def pounds_per_cubic_gigameter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_gigameter) + + @property + def ounces_per_cubic_gigameter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_gigameter) + @property def grams_per_cubic_megameter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_megameter) @@ -2377,6 +2865,14 @@ def attograms_per_cubic_megameter(self) -> T: def atomic_mass_units_per_cubic_megameter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_megameter) + @property + def pounds_per_cubic_megameter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_megameter) + + @property + def ounces_per_cubic_megameter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_megameter) + @property def grams_per_cubic_kilometer(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_kilometer) @@ -2433,6 +2929,14 @@ def attograms_per_cubic_kilometer(self) -> T: def atomic_mass_units_per_cubic_kilometer(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_kilometer) + @property + def pounds_per_cubic_kilometer(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_kilometer) + + @property + def ounces_per_cubic_kilometer(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_kilometer) + @property def grams_per_cubic_millimeter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_millimeter) @@ -2489,6 +2993,14 @@ def attograms_per_cubic_millimeter(self) -> T: def atomic_mass_units_per_cubic_millimeter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_millimeter) + @property + def pounds_per_cubic_millimeter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_millimeter) + + @property + def ounces_per_cubic_millimeter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_millimeter) + @property def grams_per_cubic_micrometer(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_micrometer) @@ -2545,6 +3057,14 @@ def attograms_per_cubic_micrometer(self) -> T: def atomic_mass_units_per_cubic_micrometer(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_micrometer) + @property + def pounds_per_cubic_micrometer(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_micrometer) + + @property + def ounces_per_cubic_micrometer(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_micrometer) + @property def grams_per_cubic_nanometer(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_nanometer) @@ -2601,6 +3121,14 @@ def attograms_per_cubic_nanometer(self) -> T: def atomic_mass_units_per_cubic_nanometer(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_nanometer) + @property + def pounds_per_cubic_nanometer(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_nanometer) + + @property + def ounces_per_cubic_nanometer(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_nanometer) + @property def grams_per_cubic_picometer(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_picometer) @@ -2657,6 +3185,14 @@ def attograms_per_cubic_picometer(self) -> T: def atomic_mass_units_per_cubic_picometer(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_picometer) + @property + def pounds_per_cubic_picometer(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_picometer) + + @property + def ounces_per_cubic_picometer(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_picometer) + @property def grams_per_cubic_femtometer(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_femtometer) @@ -2713,6 +3249,14 @@ def attograms_per_cubic_femtometer(self) -> T: def atomic_mass_units_per_cubic_femtometer(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_femtometer) + @property + def pounds_per_cubic_femtometer(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_femtometer) + + @property + def ounces_per_cubic_femtometer(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_femtometer) + @property def grams_per_cubic_attometer(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_attometer) @@ -2769,6 +3313,14 @@ def attograms_per_cubic_attometer(self) -> T: def atomic_mass_units_per_cubic_attometer(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_attometer) + @property + def pounds_per_cubic_attometer(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_attometer) + + @property + def ounces_per_cubic_attometer(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_attometer) + @property def grams_per_cubic_decimeter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_decimeter) @@ -2825,6 +3377,14 @@ def attograms_per_cubic_decimeter(self) -> T: def atomic_mass_units_per_cubic_decimeter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_decimeter) + @property + def pounds_per_cubic_decimeter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_decimeter) + + @property + def ounces_per_cubic_decimeter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_decimeter) + @property def grams_per_cubic_centimeter(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_centimeter) @@ -2881,6 +3441,14 @@ def attograms_per_cubic_centimeter(self) -> T: def atomic_mass_units_per_cubic_centimeter(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_centimeter) + @property + def pounds_per_cubic_centimeter(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_centimeter) + + @property + def ounces_per_cubic_centimeter(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_centimeter) + @property def grams_per_cubic_angstrom(self) -> T: return self.quantity.in_units_of(units.grams_per_cubic_angstrom) @@ -2937,6 +3505,270 @@ def attograms_per_cubic_angstrom(self) -> T: def atomic_mass_units_per_cubic_angstrom(self) -> T: return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_angstrom) + @property + def pounds_per_cubic_angstrom(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_angstrom) + + @property + def ounces_per_cubic_angstrom(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_angstrom) + + @property + def grams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.grams_per_cubic_mile) + + @property + def exagrams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.exagrams_per_cubic_mile) + + @property + def petagrams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.petagrams_per_cubic_mile) + + @property + def teragrams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.teragrams_per_cubic_mile) + + @property + def gigagrams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.gigagrams_per_cubic_mile) + + @property + def megagrams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.megagrams_per_cubic_mile) + + @property + def kilograms_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.kilograms_per_cubic_mile) + + @property + def milligrams_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.milligrams_per_cubic_mile) + + @property + def micrograms_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.micrograms_per_cubic_mile) + + @property + def nanograms_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.nanograms_per_cubic_mile) + + @property + def picograms_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.picograms_per_cubic_mile) + + @property + def femtograms_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.femtograms_per_cubic_mile) + + @property + def attograms_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.attograms_per_cubic_mile) + + @property + def atomic_mass_units_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_mile) + + @property + def pounds_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_mile) + + @property + def ounces_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_mile) + + @property + def grams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.grams_per_cubic_yard) + + @property + def exagrams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.exagrams_per_cubic_yard) + + @property + def petagrams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.petagrams_per_cubic_yard) + + @property + def teragrams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.teragrams_per_cubic_yard) + + @property + def gigagrams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.gigagrams_per_cubic_yard) + + @property + def megagrams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.megagrams_per_cubic_yard) + + @property + def kilograms_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.kilograms_per_cubic_yard) + + @property + def milligrams_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.milligrams_per_cubic_yard) + + @property + def micrograms_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.micrograms_per_cubic_yard) + + @property + def nanograms_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.nanograms_per_cubic_yard) + + @property + def picograms_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.picograms_per_cubic_yard) + + @property + def femtograms_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.femtograms_per_cubic_yard) + + @property + def attograms_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.attograms_per_cubic_yard) + + @property + def atomic_mass_units_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_yard) + + @property + def pounds_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_yard) + + @property + def ounces_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_yard) + + @property + def grams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.grams_per_cubic_foot) + + @property + def exagrams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.exagrams_per_cubic_foot) + + @property + def petagrams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.petagrams_per_cubic_foot) + + @property + def teragrams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.teragrams_per_cubic_foot) + + @property + def gigagrams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.gigagrams_per_cubic_foot) + + @property + def megagrams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.megagrams_per_cubic_foot) + + @property + def kilograms_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.kilograms_per_cubic_foot) + + @property + def milligrams_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.milligrams_per_cubic_foot) + + @property + def micrograms_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.micrograms_per_cubic_foot) + + @property + def nanograms_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.nanograms_per_cubic_foot) + + @property + def picograms_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.picograms_per_cubic_foot) + + @property + def femtograms_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.femtograms_per_cubic_foot) + + @property + def attograms_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.attograms_per_cubic_foot) + + @property + def atomic_mass_units_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_foot) + + @property + def pounds_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_foot) + + @property + def ounces_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_foot) + + @property + def grams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.grams_per_cubic_inch) + + @property + def exagrams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.exagrams_per_cubic_inch) + + @property + def petagrams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.petagrams_per_cubic_inch) + + @property + def teragrams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.teragrams_per_cubic_inch) + + @property + def gigagrams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.gigagrams_per_cubic_inch) + + @property + def megagrams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.megagrams_per_cubic_inch) + + @property + def kilograms_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.kilograms_per_cubic_inch) + + @property + def milligrams_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.milligrams_per_cubic_inch) + + @property + def micrograms_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.micrograms_per_cubic_inch) + + @property + def nanograms_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.nanograms_per_cubic_inch) + + @property + def picograms_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.picograms_per_cubic_inch) + + @property + def femtograms_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.femtograms_per_cubic_inch) + + @property + def attograms_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.attograms_per_cubic_inch) + + @property + def atomic_mass_units_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_inch) + + @property + def pounds_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.pounds_per_cubic_inch) + + @property + def ounces_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.ounces_per_cubic_inch) + class ForceAccessor[T](Accessor[T]): @@ -3055,6 +3887,10 @@ def femtopascals(self) -> T: def attopascals(self) -> T: return self.quantity.in_units_of(units.attopascals) + @property + def pound_force_per_square_inch(self) -> T: + return self.quantity.in_units_of(units.pound_force_per_square_inch) + class EnergyAccessor[T](Accessor[T]): @@ -4255,4 +5091,116 @@ def femtomoles_per_cubic_angstrom(self) -> T: def attomoles_per_cubic_angstrom(self) -> T: return self.quantity.in_units_of(units.attomoles_per_cubic_angstrom) + @property + def moles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.moles_per_cubic_mile) + + @property + def millimoles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.millimoles_per_cubic_mile) + + @property + def micromoles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.micromoles_per_cubic_mile) + + @property + def nanomoles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.nanomoles_per_cubic_mile) + + @property + def picomoles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.picomoles_per_cubic_mile) + + @property + def femtomoles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.femtomoles_per_cubic_mile) + + @property + def attomoles_per_cubic_mile(self) -> T: + return self.quantity.in_units_of(units.attomoles_per_cubic_mile) + + @property + def moles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.moles_per_cubic_yard) + + @property + def millimoles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.millimoles_per_cubic_yard) + + @property + def micromoles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.micromoles_per_cubic_yard) + + @property + def nanomoles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.nanomoles_per_cubic_yard) + + @property + def picomoles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.picomoles_per_cubic_yard) + + @property + def femtomoles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.femtomoles_per_cubic_yard) + + @property + def attomoles_per_cubic_yard(self) -> T: + return self.quantity.in_units_of(units.attomoles_per_cubic_yard) + + @property + def moles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.moles_per_cubic_foot) + + @property + def millimoles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.millimoles_per_cubic_foot) + + @property + def micromoles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.micromoles_per_cubic_foot) + + @property + def nanomoles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.nanomoles_per_cubic_foot) + + @property + def picomoles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.picomoles_per_cubic_foot) + + @property + def femtomoles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.femtomoles_per_cubic_foot) + + @property + def attomoles_per_cubic_foot(self) -> T: + return self.quantity.in_units_of(units.attomoles_per_cubic_foot) + + @property + def moles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.moles_per_cubic_inch) + + @property + def millimoles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.millimoles_per_cubic_inch) + + @property + def micromoles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.micromoles_per_cubic_inch) + + @property + def nanomoles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.nanomoles_per_cubic_inch) + + @property + def picomoles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.picomoles_per_cubic_inch) + + @property + def femtomoles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.femtomoles_per_cubic_inch) + + @property + def attomoles_per_cubic_inch(self) -> T: + return self.quantity.in_units_of(units.attomoles_per_cubic_inch) + diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index b9d3b7d8e..28bab065d 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -2,6 +2,7 @@ from sasdata.quantities.quantity import Quantity, UnitError import sasdata.quantities.units as units +import sasdata.quantities.si as si import pytest def test_in_units_of_calculation(): """ Just a couple of unit conversions """ @@ -12,26 +13,77 @@ def test_in_units_of_calculation(): def test_unit_compounding_pow(): + """ Test units compound correctly when __pow__ is used""" assert (Quantity(1, units.millimeters)**2).in_units_of(units.square_meters) == 1e-6 assert (Quantity(1, units.minutes)**3).in_units_of(units.seconds**3) == 60**3 def test_unit_compounding_mul(): + """ Test units compound correctly when __mul__ is used""" assert (Quantity(4, units.minutes) * Quantity(0.25, units.hertz)).in_units_of(units.none) == 60 assert (Quantity(250, units.volts) * Quantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 def test_unit_compounding_div(): + """ Test units compound correctly when __truediv__ is used""" assert (Quantity(10, units.kilometers) / Quantity(2, units.minutes) ).in_units_of(units.meters_per_second) == pytest.approx(250/3, abs=1e-6) assert (Quantity(1, units.nanowebers) / (Quantity(1, units.millimeters)**2)).in_units_of(units.millitesla) == 1 def test_value_mul(): + """ Test value part of quantities multiply correctly""" assert (Quantity(1j, units.seconds) * Quantity(1j, units.watts)).in_units_of(units.joules) == -1 +def test_scalar_mul(): + assert (Quantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 + assert (10 * Quantity(1, units.seconds)).in_units_of(units.seconds) == 10 + assert (1000 * Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 -def test_conversion_errors(): +def test_scalar_div(): + assert (Quantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 + assert (10 / Quantity(1, units.seconds)).in_units_of(units.hertz) == 10 + assert (0.001 / Quantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 +def test_good_add_sub(): + """ Test that adding and subtracting units works """ + assert (Quantity(1, units.seconds) + Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 + assert (Quantity(1, units.seconds) - Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 + + assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == 13 + + +@pytest.mark.parametrize("unit_1", si.all_si) +@pytest.mark.parametrize("unit_2", si.all_si) +def test_mixed_quantity_add_sub(unit_1, unit_2): + if unit_1.equivalent(unit_2): + assert (Quantity(0, unit_1) + Quantity(0, unit_2)).in_units_of(unit_1) == 0 + + else: + with pytest.raises(UnitError): + Quantity(1, unit_1) + Quantity(1, unit_2) + +def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float): + """ Helper function for testing units that are multiples of each other """ + + assert u1.equivalent(u2), "Units should be compatible for this test" + assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=1e-9) + + +def test_american_units(): + assert_unit_ratio(units.feet, units.inches, 12) + assert_unit_ratio(units.yards, units.inches, 36) + assert_unit_ratio(units.miles, units.inches, 63360) + + +@pytest.mark.parametrize("unit_1", si.all_si) +@pytest.mark.parametrize("unit_2", si.all_si) +def test_conversion_errors(unit_1, unit_2): + """ Test conversion errors are thrown when units are not compatible """ + + if unit_1 == unit_2: + assert Quantity(1, unit_1).in_units_of(unit_2) == 1 + + else: + with pytest.raises(UnitError): + Quantity(1, units.seconds).in_units_of(units.meters) - with pytest.raises(UnitError): - Quantity(1, units.seconds).in_units_of(units.meters) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index b7efb1c8c..0c98c6cce 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -57,13 +57,14 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: def __add__(self: Self, other: Self | ArrayLike) -> Self: if isinstance(other, Quantity): if self.units.equivalent(other.units): - return Quantity - - elif self.units.dimensions.is_dimensionless: - return Quantity(other/self.units.scale, self.units) + return Quantity(self.value + (other.value * other.scale)/self.scale, self.units) + else: + raise UnitError(f"Units do not have the same dimensionality: {self.units} vs {other.units}") else: - raise UnitError(f"Cannot combine type {type(other)} with quantity") + raise UnitError(f"Cannot perform addition/subtraction non-quantity {type(other)} with quantity") + + # Don't need __radd__ because only quantity/quantity operations should be allowed def __neg__(self): return Quantity(-self.value, self.units) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 04907bfc6..ba3d8ae18 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -227,33 +227,27 @@ def __repr__(self): class Unit: def __init__(self, si_scaling_factor: float, - dimensions: Dimensions, - name: str | None = None, - ascii_symbol: str | None = None, - symbol: str | None = None): + dimensions: Dimensions): 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): + def __mul__(self: Self, other: "Unit"): if not isinstance(other, Unit): return NotImplemented return Unit(self.scale * other.scale, self.dimensions * other.dimensions) - def __truediv__(self: Self, other: Self): + def __truediv__(self: Self, other: "Unit"): if not isinstance(other, Unit): return NotImplemented return Unit(self.scale / other.scale, self.dimensions / other.dimensions) - def __rtruediv__(self: Self, other: Self): + def __rtruediv__(self: Self, other: "Unit"): if isinstance(other, Unit): return Unit(other.scale / self.scale, other.dimensions / self.dimensions) elif isinstance(other, (int, float)): @@ -267,10 +261,10 @@ def __pow__(self, power: int): return Unit(self.scale**power, self.dimensions**power) - def equivalent(self: Self, other: Self): + def equivalent(self: Self, other: "Unit"): return self.dimensions == other.dimensions - def __eq__(self: Self, other: Self): + def __eq__(self: Self, other: "Unit"): return self.equivalent(other) and np.abs(np.log(self.scale/other.scale)) < 1e-5 def si_equivalent(self): @@ -281,10 +275,21 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): for processor in format_process: pass + def __repr__(self): + return f"Unit[{self.scale}, {self.dimensions}]" + +class NamedUnit(Unit): + def __init__(self, + si_scaling_factor: float, + dimensions: Dimensions, + name: str | None = None, + ascii_symbol: str | None = None, + symbol: str | None = None): -class NamedUnit: - # TODO: Add named unit class - pass + super().__init__(si_scaling_factor, dimensions) + self.name = name + self.ascii_symbol = ascii_symbol + self.symbol = symbol # # Parsing plan: @@ -341,1037 +346,1276 @@ def __init__(self, name: str, units: list[Unit]): # Specific units # -meters = Unit(1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') -exameters = Unit(1e+18, Dimensions(1, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') -petameters = Unit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') -terameters = Unit(1000000000000.0, Dimensions(1, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') -gigameters = Unit(1000000000.0, Dimensions(1, 0, 0, 0, 0),name='gigameters',ascii_symbol='Gm',symbol='Gm') -megameters = Unit(1000000.0, Dimensions(1, 0, 0, 0, 0),name='megameters',ascii_symbol='Mm',symbol='Mm') -kilometers = Unit(1000.0, Dimensions(1, 0, 0, 0, 0),name='kilometers',ascii_symbol='km',symbol='km') -millimeters = Unit(0.001, Dimensions(1, 0, 0, 0, 0),name='millimeters',ascii_symbol='mm',symbol='mm') -micrometers = Unit(1e-06, Dimensions(1, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',symbol='µm') -nanometers = Unit(1e-09, Dimensions(1, 0, 0, 0, 0),name='nanometers',ascii_symbol='nm',symbol='nm') -picometers = Unit(1e-12, Dimensions(1, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') -femtometers = Unit(1e-15, Dimensions(1, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') -attometers = Unit(1e-18, Dimensions(1, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') -decimeters = Unit(0.1, Dimensions(1, 0, 0, 0, 0),name='decimeters',ascii_symbol='dm',symbol='dm') -centimeters = Unit(0.01, Dimensions(1, 0, 0, 0, 0),name='centimeters',ascii_symbol='cm',symbol='cm') -seconds = Unit(1, Dimensions(0, 1, 0, 0, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') -milliseconds = Unit(0.001, Dimensions(0, 1, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') -microseconds = Unit(1e-06, Dimensions(0, 1, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') -nanoseconds = Unit(1e-09, Dimensions(0, 1, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') -picoseconds = Unit(1e-12, Dimensions(0, 1, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') -femtoseconds = Unit(1e-15, Dimensions(0, 1, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') -attoseconds = Unit(1e-18, Dimensions(0, 1, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') -grams = Unit(0.001, Dimensions(0, 0, 1, 0, 0, 0, 0),name='grams',ascii_symbol='g',symbol='g') -exagrams = Unit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') -petagrams = Unit(1000000000000.0, Dimensions(0, 0, 1, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') -teragrams = Unit(1000000000.0, Dimensions(0, 0, 1, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') -gigagrams = Unit(1000000.0, Dimensions(0, 0, 1, 0, 0),name='gigagrams',ascii_symbol='Gg',symbol='Gg') -megagrams = Unit(1000.0, Dimensions(0, 0, 1, 0, 0),name='megagrams',ascii_symbol='Mg',symbol='Mg') -kilograms = Unit(1.0, Dimensions(0, 0, 1, 0, 0),name='kilograms',ascii_symbol='kg',symbol='kg') -milligrams = Unit(1e-06, Dimensions(0, 0, 1, 0, 0),name='milligrams',ascii_symbol='mg',symbol='mg') -micrograms = Unit(1e-09, Dimensions(0, 0, 1, 0, 0),name='micrograms',ascii_symbol='ug',symbol='µg') -nanograms = Unit(1.0000000000000002e-12, Dimensions(0, 0, 1, 0, 0),name='nanograms',ascii_symbol='ng',symbol='ng') -picograms = Unit(1e-15, Dimensions(0, 0, 1, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') -femtograms = Unit(1e-18, Dimensions(0, 0, 1, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') -attograms = Unit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') -amperes = Unit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amperes',ascii_symbol='A',symbol='A') -exaamperes = Unit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamperes',ascii_symbol='EA',symbol='EA') -petaamperes = Unit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamperes',ascii_symbol='PA',symbol='PA') -teraamperes = Unit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamperes',ascii_symbol='TA',symbol='TA') -gigaamperes = Unit(1000000000.0, Dimensions(0, 0, 0, 1, 0),name='gigaamperes',ascii_symbol='GA',symbol='GA') -megaamperes = Unit(1000000.0, Dimensions(0, 0, 0, 1, 0),name='megaamperes',ascii_symbol='MA',symbol='MA') -kiloamperes = Unit(1000.0, Dimensions(0, 0, 0, 1, 0),name='kiloamperes',ascii_symbol='kA',symbol='kA') -milliamperes = Unit(0.001, Dimensions(0, 0, 0, 1, 0),name='milliamperes',ascii_symbol='mA',symbol='mA') -microamperes = Unit(1e-06, Dimensions(0, 0, 0, 1, 0),name='microamperes',ascii_symbol='uA',symbol='µA') -nanoamperes = Unit(1e-09, Dimensions(0, 0, 0, 1, 0),name='nanoamperes',ascii_symbol='nA',symbol='nA') -picoamperes = Unit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamperes',ascii_symbol='pA',symbol='pA') -femtoamperes = Unit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamperes',ascii_symbol='fA',symbol='fA') -attoamperes = Unit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamperes',ascii_symbol='aA',symbol='aA') -kelvin = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kelvin',ascii_symbol='K',symbol='K') -exakelvin = Unit(1e+18, Dimensions(0, 0, 0, 0, 1),name='exakelvin',ascii_symbol='EK',symbol='EK') -petakelvin = Unit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1),name='petakelvin',ascii_symbol='PK',symbol='PK') -terakelvin = Unit(1000000000000.0, Dimensions(0, 0, 0, 0, 1),name='terakelvin',ascii_symbol='TK',symbol='TK') -gigakelvin = Unit(1000000000.0, Dimensions(0, 0, 0, 0, 1),name='gigakelvin',ascii_symbol='GK',symbol='GK') -megakelvin = Unit(1000000.0, Dimensions(0, 0, 0, 0, 1),name='megakelvin',ascii_symbol='MK',symbol='MK') -kilokelvin = Unit(1000.0, Dimensions(0, 0, 0, 0, 1),name='kilokelvin',ascii_symbol='kK',symbol='kK') -millikelvin = Unit(0.001, Dimensions(0, 0, 0, 0, 1),name='millikelvin',ascii_symbol='mK',symbol='mK') -microkelvin = Unit(1e-06, Dimensions(0, 0, 0, 0, 1),name='microkelvin',ascii_symbol='uK',symbol='µK') -nanokelvin = Unit(1e-09, Dimensions(0, 0, 0, 0, 1),name='nanokelvin',ascii_symbol='nK',symbol='nK') -picokelvin = Unit(1e-12, Dimensions(0, 0, 0, 0, 1),name='picokelvin',ascii_symbol='pK',symbol='pK') -femtokelvin = Unit(1e-15, Dimensions(0, 0, 0, 0, 1),name='femtokelvin',ascii_symbol='fK',symbol='fK') -attokelvin = Unit(1e-18, Dimensions(0, 0, 0, 0, 1),name='attokelvin',ascii_symbol='aK',symbol='aK') -hertz = Unit(1, Dimensions(0, -1, 0, 0, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') -exahertz = Unit(1e+18, Dimensions(0, -1, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') -petahertz = Unit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') -terahertz = Unit(1000000000000.0, Dimensions(0, -1, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') -gigahertz = Unit(1000000000.0, Dimensions(0, -1, 0, 0, 0),name='gigahertz',ascii_symbol='GHz',symbol='GHz') -megahertz = Unit(1000000.0, Dimensions(0, -1, 0, 0, 0),name='megahertz',ascii_symbol='MHz',symbol='MHz') -kilohertz = Unit(1000.0, Dimensions(0, -1, 0, 0, 0),name='kilohertz',ascii_symbol='kHz',symbol='kHz') -millihertz = Unit(0.001, Dimensions(0, -1, 0, 0, 0),name='millihertz',ascii_symbol='mHz',symbol='mHz') -microhertz = Unit(1e-06, Dimensions(0, -1, 0, 0, 0),name='microhertz',ascii_symbol='uHz',symbol='µHz') -nanohertz = Unit(1e-09, Dimensions(0, -1, 0, 0, 0),name='nanohertz',ascii_symbol='nHz',symbol='nHz') -picohertz = Unit(1e-12, Dimensions(0, -1, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') -femtohertz = Unit(1e-15, Dimensions(0, -1, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') -attohertz = Unit(1e-18, Dimensions(0, -1, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') -newtons = Unit(1, Dimensions(1, -2, 1, 0, 0, 0, 0),name='newtons',ascii_symbol='N',symbol='N') -exanewtons = Unit(1e+18, Dimensions(1, -2, 1, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') -petanewtons = Unit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') -teranewtons = Unit(1000000000000.0, Dimensions(1, -2, 1, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') -giganewtons = Unit(1000000000.0, Dimensions(1, -2, 1, 0, 0),name='giganewtons',ascii_symbol='GN',symbol='GN') -meganewtons = Unit(1000000.0, Dimensions(1, -2, 1, 0, 0),name='meganewtons',ascii_symbol='MN',symbol='MN') -kilonewtons = Unit(1000.0, Dimensions(1, -2, 1, 0, 0),name='kilonewtons',ascii_symbol='kN',symbol='kN') -millinewtons = Unit(0.001, Dimensions(1, -2, 1, 0, 0),name='millinewtons',ascii_symbol='mN',symbol='mN') -micronewtons = Unit(1e-06, Dimensions(1, -2, 1, 0, 0),name='micronewtons',ascii_symbol='uN',symbol='µN') -nanonewtons = Unit(1e-09, Dimensions(1, -2, 1, 0, 0),name='nanonewtons',ascii_symbol='nN',symbol='nN') -piconewtons = Unit(1e-12, Dimensions(1, -2, 1, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') -femtonewtons = Unit(1e-15, Dimensions(1, -2, 1, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') -attonewtons = Unit(1e-18, Dimensions(1, -2, 1, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') -pascals = Unit(1, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') -exapascals = Unit(1e+18, Dimensions(-1, -2, 1, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') -petapascals = Unit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') -terapascals = Unit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') -gigapascals = Unit(1000000000.0, Dimensions(-1, -2, 1, 0, 0),name='gigapascals',ascii_symbol='GPa',symbol='GPa') -megapascals = Unit(1000000.0, Dimensions(-1, -2, 1, 0, 0),name='megapascals',ascii_symbol='MPa',symbol='MPa') -kilopascals = Unit(1000.0, Dimensions(-1, -2, 1, 0, 0),name='kilopascals',ascii_symbol='kPa',symbol='kPa') -millipascals = Unit(0.001, Dimensions(-1, -2, 1, 0, 0),name='millipascals',ascii_symbol='mPa',symbol='mPa') -micropascals = Unit(1e-06, Dimensions(-1, -2, 1, 0, 0),name='micropascals',ascii_symbol='uPa',symbol='µPa') -nanopascals = Unit(1e-09, Dimensions(-1, -2, 1, 0, 0),name='nanopascals',ascii_symbol='nPa',symbol='nPa') -picopascals = Unit(1e-12, Dimensions(-1, -2, 1, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') -femtopascals = Unit(1e-15, Dimensions(-1, -2, 1, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') -attopascals = Unit(1e-18, Dimensions(-1, -2, 1, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') -joules = Unit(1, Dimensions(2, -2, 1, 0, 0, 0, 0),name='joules',ascii_symbol='J',symbol='J') -exajoules = Unit(1e+18, Dimensions(2, -2, 1, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') -petajoules = Unit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') -terajoules = Unit(1000000000000.0, Dimensions(2, -2, 1, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') -gigajoules = Unit(1000000000.0, Dimensions(2, -2, 1, 0, 0),name='gigajoules',ascii_symbol='GJ',symbol='GJ') -megajoules = Unit(1000000.0, Dimensions(2, -2, 1, 0, 0),name='megajoules',ascii_symbol='MJ',symbol='MJ') -kilojoules = Unit(1000.0, Dimensions(2, -2, 1, 0, 0),name='kilojoules',ascii_symbol='kJ',symbol='kJ') -millijoules = Unit(0.001, Dimensions(2, -2, 1, 0, 0),name='millijoules',ascii_symbol='mJ',symbol='mJ') -microjoules = Unit(1e-06, Dimensions(2, -2, 1, 0, 0),name='microjoules',ascii_symbol='uJ',symbol='µJ') -nanojoules = Unit(1e-09, Dimensions(2, -2, 1, 0, 0),name='nanojoules',ascii_symbol='nJ',symbol='nJ') -picojoules = Unit(1e-12, Dimensions(2, -2, 1, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') -femtojoules = Unit(1e-15, Dimensions(2, -2, 1, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') -attojoules = Unit(1e-18, Dimensions(2, -2, 1, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') -watts = Unit(1, Dimensions(2, -3, 1, 0, 0, 0, 0),name='watts',ascii_symbol='W',symbol='W') -exawatts = Unit(1e+18, Dimensions(2, -3, 1, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') -petawatts = Unit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') -terawatts = Unit(1000000000000.0, Dimensions(2, -3, 1, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') -gigawatts = Unit(1000000000.0, Dimensions(2, -3, 1, 0, 0),name='gigawatts',ascii_symbol='GW',symbol='GW') -megawatts = Unit(1000000.0, Dimensions(2, -3, 1, 0, 0),name='megawatts',ascii_symbol='MW',symbol='MW') -kilowatts = Unit(1000.0, Dimensions(2, -3, 1, 0, 0),name='kilowatts',ascii_symbol='kW',symbol='kW') -milliwatts = Unit(0.001, Dimensions(2, -3, 1, 0, 0),name='milliwatts',ascii_symbol='mW',symbol='mW') -microwatts = Unit(1e-06, Dimensions(2, -3, 1, 0, 0),name='microwatts',ascii_symbol='uW',symbol='µW') -nanowatts = Unit(1e-09, Dimensions(2, -3, 1, 0, 0),name='nanowatts',ascii_symbol='nW',symbol='nW') -picowatts = Unit(1e-12, Dimensions(2, -3, 1, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') -femtowatts = Unit(1e-15, Dimensions(2, -3, 1, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') -attowatts = Unit(1e-18, Dimensions(2, -3, 1, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') -coulombs = Unit(1, Dimensions(0, 1, 0, 1, 0, 0, 0),name='coulombs',ascii_symbol='C',symbol='C') -exacoulombs = Unit(1e+18, Dimensions(0, 1, 0, 1, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') -petacoulombs = Unit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') -teracoulombs = Unit(1000000000000.0, Dimensions(0, 1, 0, 1, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') -gigacoulombs = Unit(1000000000.0, Dimensions(0, 1, 0, 1, 0),name='gigacoulombs',ascii_symbol='GC',symbol='GC') -megacoulombs = Unit(1000000.0, Dimensions(0, 1, 0, 1, 0),name='megacoulombs',ascii_symbol='MC',symbol='MC') -kilocoulombs = Unit(1000.0, Dimensions(0, 1, 0, 1, 0),name='kilocoulombs',ascii_symbol='kC',symbol='kC') -millicoulombs = Unit(0.001, Dimensions(0, 1, 0, 1, 0),name='millicoulombs',ascii_symbol='mC',symbol='mC') -microcoulombs = Unit(1e-06, Dimensions(0, 1, 0, 1, 0),name='microcoulombs',ascii_symbol='uC',symbol='µC') -nanocoulombs = Unit(1e-09, Dimensions(0, 1, 0, 1, 0),name='nanocoulombs',ascii_symbol='nC',symbol='nC') -picocoulombs = Unit(1e-12, Dimensions(0, 1, 0, 1, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') -femtocoulombs = Unit(1e-15, Dimensions(0, 1, 0, 1, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') -attocoulombs = Unit(1e-18, Dimensions(0, 1, 0, 1, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') -volts = Unit(1, Dimensions(2, -3, 1, -1, 0, 0, 0),name='volts',ascii_symbol='V',symbol='V') -exavolts = Unit(1e+18, Dimensions(2, -3, 1, -1, 0),name='exavolts',ascii_symbol='EV',symbol='EV') -petavolts = Unit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0),name='petavolts',ascii_symbol='PV',symbol='PV') -teravolts = Unit(1000000000000.0, Dimensions(2, -3, 1, -1, 0),name='teravolts',ascii_symbol='TV',symbol='TV') -gigavolts = Unit(1000000000.0, Dimensions(2, -3, 1, -1, 0),name='gigavolts',ascii_symbol='GV',symbol='GV') -megavolts = Unit(1000000.0, Dimensions(2, -3, 1, -1, 0),name='megavolts',ascii_symbol='MV',symbol='MV') -kilovolts = Unit(1000.0, Dimensions(2, -3, 1, -1, 0),name='kilovolts',ascii_symbol='kV',symbol='kV') -millivolts = Unit(0.001, Dimensions(2, -3, 1, -1, 0),name='millivolts',ascii_symbol='mV',symbol='mV') -microvolts = Unit(1e-06, Dimensions(2, -3, 1, -1, 0),name='microvolts',ascii_symbol='uV',symbol='µV') -nanovolts = Unit(1e-09, Dimensions(2, -3, 1, -1, 0),name='nanovolts',ascii_symbol='nV',symbol='nV') -picovolts = Unit(1e-12, Dimensions(2, -3, 1, -1, 0),name='picovolts',ascii_symbol='pV',symbol='pV') -femtovolts = Unit(1e-15, Dimensions(2, -3, 1, -1, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') -attovolts = Unit(1e-18, Dimensions(2, -3, 1, -1, 0),name='attovolts',ascii_symbol='aV',symbol='aV') -ohms = Unit(1, Dimensions(2, -3, 1, -2, 0, 0, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') -exaohms = Unit(1e+18, Dimensions(2, -3, 1, -2, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') -petaohms = Unit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') -teraohms = Unit(1000000000000.0, Dimensions(2, -3, 1, -2, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') -gigaohms = Unit(1000000000.0, Dimensions(2, -3, 1, -2, 0),name='gigaohms',ascii_symbol='GOhm',symbol='GΩ') -megaohms = Unit(1000000.0, Dimensions(2, -3, 1, -2, 0),name='megaohms',ascii_symbol='MOhm',symbol='MΩ') -kiloohms = Unit(1000.0, Dimensions(2, -3, 1, -2, 0),name='kiloohms',ascii_symbol='kOhm',symbol='kΩ') -milliohms = Unit(0.001, Dimensions(2, -3, 1, -2, 0),name='milliohms',ascii_symbol='mOhm',symbol='mΩ') -microohms = Unit(1e-06, Dimensions(2, -3, 1, -2, 0),name='microohms',ascii_symbol='uOhm',symbol='µΩ') -nanoohms = Unit(1e-09, Dimensions(2, -3, 1, -2, 0),name='nanoohms',ascii_symbol='nOhm',symbol='nΩ') -picoohms = Unit(1e-12, Dimensions(2, -3, 1, -2, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') -femtoohms = Unit(1e-15, Dimensions(2, -3, 1, -2, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') -attoohms = Unit(1e-18, Dimensions(2, -3, 1, -2, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') -farads = Unit(1, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='farads',ascii_symbol='F',symbol='F') -exafarads = Unit(1e+18, Dimensions(-2, 4, -1, 2, 0),name='exafarads',ascii_symbol='EF',symbol='EF') -petafarads = Unit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='petafarads',ascii_symbol='PF',symbol='PF') -terafarads = Unit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='terafarads',ascii_symbol='TF',symbol='TF') -gigafarads = Unit(1000000000.0, Dimensions(-2, 4, -1, 2, 0),name='gigafarads',ascii_symbol='GF',symbol='GF') -megafarads = Unit(1000000.0, Dimensions(-2, 4, -1, 2, 0),name='megafarads',ascii_symbol='MF',symbol='MF') -kilofarads = Unit(1000.0, Dimensions(-2, 4, -1, 2, 0),name='kilofarads',ascii_symbol='kF',symbol='kF') -millifarads = Unit(0.001, Dimensions(-2, 4, -1, 2, 0),name='millifarads',ascii_symbol='mF',symbol='mF') -microfarads = Unit(1e-06, Dimensions(-2, 4, -1, 2, 0),name='microfarads',ascii_symbol='uF',symbol='µF') -nanofarads = Unit(1e-09, Dimensions(-2, 4, -1, 2, 0),name='nanofarads',ascii_symbol='nF',symbol='nF') -picofarads = Unit(1e-12, Dimensions(-2, 4, -1, 2, 0),name='picofarads',ascii_symbol='pF',symbol='pF') -femtofarads = Unit(1e-15, Dimensions(-2, 4, -1, 2, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') -attofarads = Unit(1e-18, Dimensions(-2, 4, -1, 2, 0),name='attofarads',ascii_symbol='aF',symbol='aF') -siemens = Unit(1, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='siemens',ascii_symbol='S',symbol='S') -exasiemens = Unit(1e+18, Dimensions(-2, 3, -1, 2, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') -petasiemens = Unit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') -terasiemens = Unit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') -gigasiemens = Unit(1000000000.0, Dimensions(-2, 3, -1, 2, 0),name='gigasiemens',ascii_symbol='GS',symbol='GS') -megasiemens = Unit(1000000.0, Dimensions(-2, 3, -1, 2, 0),name='megasiemens',ascii_symbol='MS',symbol='MS') -kilosiemens = Unit(1000.0, Dimensions(-2, 3, -1, 2, 0),name='kilosiemens',ascii_symbol='kS',symbol='kS') -millisiemens = Unit(0.001, Dimensions(-2, 3, -1, 2, 0),name='millisiemens',ascii_symbol='mS',symbol='mS') -microsiemens = Unit(1e-06, Dimensions(-2, 3, -1, 2, 0),name='microsiemens',ascii_symbol='uS',symbol='µS') -nanosiemens = Unit(1e-09, Dimensions(-2, 3, -1, 2, 0),name='nanosiemens',ascii_symbol='nS',symbol='nS') -picosiemens = Unit(1e-12, Dimensions(-2, 3, -1, 2, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') -femtosiemens = Unit(1e-15, Dimensions(-2, 3, -1, 2, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') -attosiemens = Unit(1e-18, Dimensions(-2, 3, -1, 2, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') -webers = Unit(1, Dimensions(2, -2, 1, -1, 0, 0, 0),name='webers',ascii_symbol='Wb',symbol='Wb') -exawebers = Unit(1e+18, Dimensions(2, -2, 1, -1, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') -petawebers = Unit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') -terawebers = Unit(1000000000000.0, Dimensions(2, -2, 1, -1, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') -gigawebers = Unit(1000000000.0, Dimensions(2, -2, 1, -1, 0),name='gigawebers',ascii_symbol='GWb',symbol='GWb') -megawebers = Unit(1000000.0, Dimensions(2, -2, 1, -1, 0),name='megawebers',ascii_symbol='MWb',symbol='MWb') -kilowebers = Unit(1000.0, Dimensions(2, -2, 1, -1, 0),name='kilowebers',ascii_symbol='kWb',symbol='kWb') -milliwebers = Unit(0.001, Dimensions(2, -2, 1, -1, 0),name='milliwebers',ascii_symbol='mWb',symbol='mWb') -microwebers = Unit(1e-06, Dimensions(2, -2, 1, -1, 0),name='microwebers',ascii_symbol='uWb',symbol='µWb') -nanowebers = Unit(1e-09, Dimensions(2, -2, 1, -1, 0),name='nanowebers',ascii_symbol='nWb',symbol='nWb') -picowebers = Unit(1e-12, Dimensions(2, -2, 1, -1, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') -femtowebers = Unit(1e-15, Dimensions(2, -2, 1, -1, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') -attowebers = Unit(1e-18, Dimensions(2, -2, 1, -1, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') -tesla = Unit(1, Dimensions(0, -2, 1, -1, 0, 0, 0),name='tesla',ascii_symbol='T',symbol='T') -exatesla = Unit(1e+18, Dimensions(0, -2, 1, -1, 0),name='exatesla',ascii_symbol='ET',symbol='ET') -petatesla = Unit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0),name='petatesla',ascii_symbol='PT',symbol='PT') -teratesla = Unit(1000000000000.0, Dimensions(0, -2, 1, -1, 0),name='teratesla',ascii_symbol='TT',symbol='TT') -gigatesla = Unit(1000000000.0, Dimensions(0, -2, 1, -1, 0),name='gigatesla',ascii_symbol='GT',symbol='GT') -megatesla = Unit(1000000.0, Dimensions(0, -2, 1, -1, 0),name='megatesla',ascii_symbol='MT',symbol='MT') -kilotesla = Unit(1000.0, Dimensions(0, -2, 1, -1, 0),name='kilotesla',ascii_symbol='kT',symbol='kT') -millitesla = Unit(0.001, Dimensions(0, -2, 1, -1, 0),name='millitesla',ascii_symbol='mT',symbol='mT') -microtesla = Unit(1e-06, Dimensions(0, -2, 1, -1, 0),name='microtesla',ascii_symbol='uT',symbol='µT') -nanotesla = Unit(1e-09, Dimensions(0, -2, 1, -1, 0),name='nanotesla',ascii_symbol='nT',symbol='nT') -picotesla = Unit(1e-12, Dimensions(0, -2, 1, -1, 0),name='picotesla',ascii_symbol='pT',symbol='pT') -femtotesla = Unit(1e-15, Dimensions(0, -2, 1, -1, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') -attotesla = Unit(1e-18, Dimensions(0, -2, 1, -1, 0),name='attotesla',ascii_symbol='aT',symbol='aT') -henry = Unit(1, Dimensions(2, -2, 1, -2, 0, 0, 0),name='henry',ascii_symbol='H',symbol='H') -exahenry = Unit(1e+18, Dimensions(2, -2, 1, -2, 0),name='exahenry',ascii_symbol='EH',symbol='EH') -petahenry = Unit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0),name='petahenry',ascii_symbol='PH',symbol='PH') -terahenry = Unit(1000000000000.0, Dimensions(2, -2, 1, -2, 0),name='terahenry',ascii_symbol='TH',symbol='TH') -gigahenry = Unit(1000000000.0, Dimensions(2, -2, 1, -2, 0),name='gigahenry',ascii_symbol='GH',symbol='GH') -megahenry = Unit(1000000.0, Dimensions(2, -2, 1, -2, 0),name='megahenry',ascii_symbol='MH',symbol='MH') -kilohenry = Unit(1000.0, Dimensions(2, -2, 1, -2, 0),name='kilohenry',ascii_symbol='kH',symbol='kH') -millihenry = Unit(0.001, Dimensions(2, -2, 1, -2, 0),name='millihenry',ascii_symbol='mH',symbol='mH') -microhenry = Unit(1e-06, Dimensions(2, -2, 1, -2, 0),name='microhenry',ascii_symbol='uH',symbol='µH') -nanohenry = Unit(1e-09, Dimensions(2, -2, 1, -2, 0),name='nanohenry',ascii_symbol='nH',symbol='nH') -picohenry = Unit(1e-12, Dimensions(2, -2, 1, -2, 0),name='picohenry',ascii_symbol='pH',symbol='pH') -femtohenry = Unit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') -attohenry = Unit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') -angstroms = Unit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') -minutes = Unit(60, Dimensions(0, 1, 0, 0, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') -hours = Unit(360, Dimensions(0, 1, 0, 0, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') -days = Unit(8640, Dimensions(0, 1, 0, 0, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') -years = Unit(3155695.2, Dimensions(0, 1, 0, 0, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') -degrees = Unit(57.29577951308232, Dimensions(0, 0, 0, 0, 0, 0, 1),name='degrees',ascii_symbol='deg',symbol='deg') -radians = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 1),name='radians',ascii_symbol='rad',symbol='rad') -stradians = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') -none = Unit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') -litres = Unit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') -electronvolts = Unit(1.602176634e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') -exaelectronvolts = Unit(0.1602176634, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') -petaelectronvolts = Unit(0.0001602176634, Dimensions(2, -2, 1, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') -teraelectronvolts = Unit(1.602176634e-07, Dimensions(2, -2, 1, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') -gigaelectronvolts = Unit(1.6021766339999998e-10, Dimensions(2, -2, 1, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') -megaelectronvolts = Unit(1.6021766339999998e-13, Dimensions(2, -2, 1, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') -kiloelectronvolts = Unit(1.602176634e-16, Dimensions(2, -2, 1, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') -millielectronvolts = Unit(1.6021766339999998e-22, Dimensions(2, -2, 1, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') -microelectronvolts = Unit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') -nanoelectronvolts = Unit(1.602176634e-28, Dimensions(2, -2, 1, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') -picoelectronvolts = Unit(1.6021766339999998e-31, Dimensions(2, -2, 1, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') -femtoelectronvolts = Unit(1.602176634e-34, Dimensions(2, -2, 1, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') -attoelectronvolts = Unit(1.602176634e-37, Dimensions(2, -2, 1, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') -atomic_mass_units = Unit(1.660538921e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') -moles = Unit(6.02214076e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') -millimoles = Unit(6.02214076e+20, Dimensions(0, 0, 0, 0, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') -micromoles = Unit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') -nanomoles = Unit(602214076000000.0, Dimensions(0, 0, 0, 0, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') -picomoles = Unit(602214076000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') -femtomoles = Unit(602214076.0, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') -attomoles = Unit(602214.076, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') -kg_force = Unit(9.80665, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kg_force',ascii_symbol='kgForce',symbol='kgForce') -degrees_celsius = Unit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') -square_meters = Unit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') -cubic_meters = Unit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') -per_meter = Unit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') -per_square_meter = Unit(1.0, Dimensions(length=-2), name='per_square_meter', ascii_symbol='m^-2', symbol='m⁻²') -per_cubic_meter = Unit(1.0, Dimensions(length=-3), name='per_cubic_meter', ascii_symbol='m^-3', symbol='m⁻³') -square_exameters = Unit(1e+36, Dimensions(length=2), name='square_exameters', ascii_symbol='Em^2', symbol='Em²') -cubic_exameters = Unit(1e+54, Dimensions(length=3), name='cubic_exameters', ascii_symbol='Em^3', symbol='Em³') -per_exameter = Unit(1e-18, Dimensions(length=-1), name='per_exameter', ascii_symbol='Em^-1', symbol='Em⁻¹') -per_square_exameter = Unit(1e-36, Dimensions(length=-2), name='per_square_exameter', ascii_symbol='Em^-2', symbol='Em⁻²') -per_cubic_exameter = Unit(1e-54, Dimensions(length=-3), name='per_cubic_exameter', ascii_symbol='Em^-3', symbol='Em⁻³') -square_petameters = Unit(1e+30, Dimensions(length=2), name='square_petameters', ascii_symbol='Pm^2', symbol='Pm²') -cubic_petameters = Unit(1e+45, Dimensions(length=3), name='cubic_petameters', ascii_symbol='Pm^3', symbol='Pm³') -per_petameter = Unit(1e-15, Dimensions(length=-1), name='per_petameter', ascii_symbol='Pm^-1', symbol='Pm⁻¹') -per_square_petameter = Unit(1e-30, Dimensions(length=-2), name='per_square_petameter', ascii_symbol='Pm^-2', symbol='Pm⁻²') -per_cubic_petameter = Unit(1e-45, Dimensions(length=-3), name='per_cubic_petameter', ascii_symbol='Pm^-3', symbol='Pm⁻³') -square_terameters = Unit(1e+24, Dimensions(length=2), name='square_terameters', ascii_symbol='Tm^2', symbol='Tm²') -cubic_terameters = Unit(1e+36, Dimensions(length=3), name='cubic_terameters', ascii_symbol='Tm^3', symbol='Tm³') -per_terameter = Unit(1e-12, Dimensions(length=-1), name='per_terameter', ascii_symbol='Tm^-1', symbol='Tm⁻¹') -per_square_terameter = Unit(1e-24, Dimensions(length=-2), name='per_square_terameter', ascii_symbol='Tm^-2', symbol='Tm⁻²') -per_cubic_terameter = Unit(1e-36, Dimensions(length=-3), name='per_cubic_terameter', ascii_symbol='Tm^-3', symbol='Tm⁻³') -square_gigameters = Unit(1e+18, Dimensions(length=2), name='square_gigameters', ascii_symbol='Gm^2', symbol='Gm²') -cubic_gigameters = Unit(1e+27, Dimensions(length=3), name='cubic_gigameters', ascii_symbol='Gm^3', symbol='Gm³') -per_gigameter = Unit(1e-09, Dimensions(length=-1), name='per_gigameter', ascii_symbol='Gm^-1', symbol='Gm⁻¹') -per_square_gigameter = Unit(1e-18, Dimensions(length=-2), name='per_square_gigameter', ascii_symbol='Gm^-2', symbol='Gm⁻²') -per_cubic_gigameter = Unit(1e-27, Dimensions(length=-3), name='per_cubic_gigameter', ascii_symbol='Gm^-3', symbol='Gm⁻³') -square_megameters = Unit(1000000000000.0, Dimensions(length=2), name='square_megameters', ascii_symbol='Mm^2', symbol='Mm²') -cubic_megameters = Unit(1e+18, Dimensions(length=3), name='cubic_megameters', ascii_symbol='Mm^3', symbol='Mm³') -per_megameter = Unit(1e-06, Dimensions(length=-1), name='per_megameter', ascii_symbol='Mm^-1', symbol='Mm⁻¹') -per_square_megameter = Unit(1e-12, Dimensions(length=-2), name='per_square_megameter', ascii_symbol='Mm^-2', symbol='Mm⁻²') -per_cubic_megameter = Unit(1e-18, Dimensions(length=-3), name='per_cubic_megameter', ascii_symbol='Mm^-3', symbol='Mm⁻³') -square_kilometers = Unit(1000000.0, Dimensions(length=2), name='square_kilometers', ascii_symbol='km^2', symbol='km²') -cubic_kilometers = Unit(1000000000.0, Dimensions(length=3), name='cubic_kilometers', ascii_symbol='km^3', symbol='km³') -per_kilometer = Unit(0.001, Dimensions(length=-1), name='per_kilometer', ascii_symbol='km^-1', symbol='km⁻¹') -per_square_kilometer = Unit(1e-06, Dimensions(length=-2), name='per_square_kilometer', ascii_symbol='km^-2', symbol='km⁻²') -per_cubic_kilometer = Unit(1e-09, Dimensions(length=-3), name='per_cubic_kilometer', ascii_symbol='km^-3', symbol='km⁻³') -square_millimeters = Unit(1e-06, Dimensions(length=2), name='square_millimeters', ascii_symbol='mm^2', symbol='mm²') -cubic_millimeters = Unit(1e-09, Dimensions(length=3), name='cubic_millimeters', ascii_symbol='mm^3', symbol='mm³') -per_millimeter = Unit(1000.0, Dimensions(length=-1), name='per_millimeter', ascii_symbol='mm^-1', symbol='mm⁻¹') -per_square_millimeter = Unit(1000000.0, Dimensions(length=-2), name='per_square_millimeter', ascii_symbol='mm^-2', symbol='mm⁻²') -per_cubic_millimeter = Unit(999999999.9999999, Dimensions(length=-3), name='per_cubic_millimeter', ascii_symbol='mm^-3', symbol='mm⁻³') -square_micrometers = Unit(1e-12, Dimensions(length=2), name='square_micrometers', ascii_symbol='um^2', symbol='µm²') -cubic_micrometers = Unit(9.999999999999999e-19, Dimensions(length=3), name='cubic_micrometers', ascii_symbol='um^3', symbol='µm³') -per_micrometer = Unit(1000000.0, Dimensions(length=-1), name='per_micrometer', ascii_symbol='um^-1', symbol='µm⁻¹') -per_square_micrometer = Unit(1000000000000.0001, Dimensions(length=-2), name='per_square_micrometer', ascii_symbol='um^-2', symbol='µm⁻²') -per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(length=-3), name='per_cubic_micrometer', ascii_symbol='um^-3', symbol='µm⁻³') -square_nanometers = Unit(1e-18, Dimensions(length=2), name='square_nanometers', ascii_symbol='nm^2', symbol='nm²') -cubic_nanometers = Unit(1.0000000000000002e-27, Dimensions(length=3), name='cubic_nanometers', ascii_symbol='nm^3', symbol='nm³') -per_nanometer = Unit(999999999.9999999, Dimensions(length=-1), name='per_nanometer', ascii_symbol='nm^-1', symbol='nm⁻¹') -per_square_nanometer = Unit(9.999999999999999e+17, Dimensions(length=-2), name='per_square_nanometer', ascii_symbol='nm^-2', symbol='nm⁻²') -per_cubic_nanometer = Unit(9.999999999999999e+26, Dimensions(length=-3), name='per_cubic_nanometer', ascii_symbol='nm^-3', symbol='nm⁻³') -square_picometers = Unit(1e-24, Dimensions(length=2), name='square_picometers', ascii_symbol='pm^2', symbol='pm²') -cubic_picometers = Unit(1e-36, Dimensions(length=3), name='cubic_picometers', ascii_symbol='pm^3', symbol='pm³') -per_picometer = Unit(1000000000000.0, Dimensions(length=-1), name='per_picometer', ascii_symbol='pm^-1', symbol='pm⁻¹') -per_square_picometer = Unit(1e+24, Dimensions(length=-2), name='per_square_picometer', ascii_symbol='pm^-2', symbol='pm⁻²') -per_cubic_picometer = Unit(1e+36, Dimensions(length=-3), name='per_cubic_picometer', ascii_symbol='pm^-3', symbol='pm⁻³') -square_femtometers = Unit(1e-30, Dimensions(length=2), name='square_femtometers', ascii_symbol='fm^2', symbol='fm²') -cubic_femtometers = Unit(1.0000000000000003e-45, Dimensions(length=3), name='cubic_femtometers', ascii_symbol='fm^3', symbol='fm³') -per_femtometer = Unit(999999999999999.9, Dimensions(length=-1), name='per_femtometer', ascii_symbol='fm^-1', symbol='fm⁻¹') -per_square_femtometer = Unit(9.999999999999999e+29, Dimensions(length=-2), name='per_square_femtometer', ascii_symbol='fm^-2', symbol='fm⁻²') -per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(length=-3), name='per_cubic_femtometer', ascii_symbol='fm^-3', symbol='fm⁻³') -square_attometers = Unit(1.0000000000000001e-36, Dimensions(length=2), name='square_attometers', ascii_symbol='am^2', symbol='am²') -cubic_attometers = Unit(1.0000000000000002e-54, Dimensions(length=3), name='cubic_attometers', ascii_symbol='am^3', symbol='am³') -per_attometer = Unit(9.999999999999999e+17, Dimensions(length=-1), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') -per_square_attometer = Unit(9.999999999999999e+35, Dimensions(length=-2), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') -per_cubic_attometer = Unit(9.999999999999997e+53, Dimensions(length=-3), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') -square_decimeters = Unit(0.010000000000000002, Dimensions(length=2), name='square_decimeters', ascii_symbol='dm^2', symbol='dm²') -cubic_decimeters = Unit(0.0010000000000000002, Dimensions(length=3), name='cubic_decimeters', ascii_symbol='dm^3', symbol='dm³') -per_decimeter = Unit(10.0, Dimensions(length=-1), name='per_decimeter', ascii_symbol='dm^-1', symbol='dm⁻¹') -per_square_decimeter = Unit(99.99999999999999, Dimensions(length=-2), name='per_square_decimeter', ascii_symbol='dm^-2', symbol='dm⁻²') -per_cubic_decimeter = Unit(999.9999999999999, Dimensions(length=-3), name='per_cubic_decimeter', ascii_symbol='dm^-3', symbol='dm⁻³') -square_centimeters = Unit(0.0001, Dimensions(length=2), name='square_centimeters', ascii_symbol='cm^2', symbol='cm²') -cubic_centimeters = Unit(1.0000000000000002e-06, Dimensions(length=3), name='cubic_centimeters', ascii_symbol='cm^3', symbol='cm³') -per_centimeter = Unit(100.0, Dimensions(length=-1), name='per_centimeter', ascii_symbol='cm^-1', symbol='cm⁻¹') -per_square_centimeter = Unit(10000.0, Dimensions(length=-2), name='per_square_centimeter', ascii_symbol='cm^-2', symbol='cm⁻²') -per_cubic_centimeter = Unit(999999.9999999999, Dimensions(length=-3), name='per_cubic_centimeter', ascii_symbol='cm^-3', symbol='cm⁻³') -square_angstroms = Unit(1.0000000000000001e-20, Dimensions(length=2), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ų') -cubic_angstroms = Unit(1e-30, Dimensions(length=3), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='ų') -per_angstrom = Unit(10000000000.0, Dimensions(length=-1), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Å⁻¹') -per_square_angstrom = Unit(1e+20, Dimensions(length=-2), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Å⁻²') -per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(length=-3), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Å⁻³') -meters_per_second = Unit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') -meters_per_square_second = Unit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') -meters_per_millisecond = Unit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') -meters_per_square_millisecond = Unit(1000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') -meters_per_microsecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') -meters_per_square_microsecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') -meters_per_nanosecond = Unit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') -meters_per_square_nanosecond = Unit(999999999.9999999, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') -meters_per_picosecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') -meters_per_square_picosecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') -meters_per_femtosecond = Unit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') -meters_per_square_femtosecond = Unit(999999999999999.9, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') -meters_per_attosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') -meters_per_square_attosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') -meters_per_minute = Unit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') -meters_per_square_minute = Unit(0.016666666666666666, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') -meters_per_hour = Unit(0.002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') -meters_per_square_hour = Unit(0.002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') -meters_per_day = Unit(0.00011574074074074075, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') -meters_per_square_day = Unit(0.00011574074074074075, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') -meters_per_year = Unit(3.168873850681143e-07, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') -meters_per_square_year = Unit(3.168873850681143e-07, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') -exameters_per_second = Unit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') -exameters_per_square_second = Unit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') -exameters_per_millisecond = Unit(1e+21, Dimensions(length=1, time=-1), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') -exameters_per_square_millisecond = Unit(1e+21, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') -exameters_per_microsecond = Unit(1e+24, Dimensions(length=1, time=-1), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') -exameters_per_square_microsecond = Unit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') -exameters_per_nanosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') -exameters_per_square_nanosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') -exameters_per_picosecond = Unit(1e+30, Dimensions(length=1, time=-1), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') -exameters_per_square_picosecond = Unit(1e+30, Dimensions(length=1, time=-2), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') -exameters_per_femtosecond = Unit(1e+33, Dimensions(length=1, time=-1), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') -exameters_per_square_femtosecond = Unit(1e+33, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') -exameters_per_attosecond = Unit(9.999999999999999e+35, Dimensions(length=1, time=-1), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') -exameters_per_square_attosecond = Unit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') -exameters_per_minute = Unit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') -exameters_per_square_minute = Unit(1.6666666666666666e+16, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') -exameters_per_hour = Unit(2777777777777778.0, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') -exameters_per_square_hour = Unit(2777777777777778.0, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') -exameters_per_day = Unit(115740740740740.73, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') -exameters_per_square_day = Unit(115740740740740.73, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') -exameters_per_year = Unit(316887385068.1143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') -exameters_per_square_year = Unit(316887385068.1143, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') -petameters_per_second = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') -petameters_per_square_second = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') -petameters_per_millisecond = Unit(1e+18, Dimensions(length=1, time=-1), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') -petameters_per_square_millisecond = Unit(1e+18, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') -petameters_per_microsecond = Unit(1e+21, Dimensions(length=1, time=-1), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') -petameters_per_square_microsecond = Unit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') -petameters_per_nanosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') -petameters_per_square_nanosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') -petameters_per_picosecond = Unit(1e+27, Dimensions(length=1, time=-1), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') -petameters_per_square_picosecond = Unit(1e+27, Dimensions(length=1, time=-2), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') -petameters_per_femtosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') -petameters_per_square_femtosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') -petameters_per_attosecond = Unit(1e+33, Dimensions(length=1, time=-1), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') -petameters_per_square_attosecond = Unit(1e+33, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') -petameters_per_minute = Unit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') -petameters_per_square_minute = Unit(16666666666666.666, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') -petameters_per_hour = Unit(2777777777777.778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') -petameters_per_square_hour = Unit(2777777777777.778, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') -petameters_per_day = Unit(115740740740.74074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') -petameters_per_square_day = Unit(115740740740.74074, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') -petameters_per_year = Unit(316887385.0681143, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') -petameters_per_square_year = Unit(316887385.0681143, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') -terameters_per_second = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') -terameters_per_square_second = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') -terameters_per_millisecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') -terameters_per_square_millisecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') -terameters_per_microsecond = Unit(1e+18, Dimensions(length=1, time=-1), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') -terameters_per_square_microsecond = Unit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') -terameters_per_nanosecond = Unit(1e+21, Dimensions(length=1, time=-1), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') -terameters_per_square_nanosecond = Unit(1e+21, Dimensions(length=1, time=-2), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') -terameters_per_picosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') -terameters_per_square_picosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') -terameters_per_femtosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') -terameters_per_square_femtosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') -terameters_per_attosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') -terameters_per_square_attosecond = Unit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') -terameters_per_minute = Unit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') -terameters_per_square_minute = Unit(16666666666.666666, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') -terameters_per_hour = Unit(2777777777.7777777, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') -terameters_per_square_hour = Unit(2777777777.7777777, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') -terameters_per_day = Unit(115740740.74074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') -terameters_per_square_day = Unit(115740740.74074075, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') -terameters_per_year = Unit(316887.38506811426, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') -terameters_per_square_year = Unit(316887.38506811426, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') -gigameters_per_second = Unit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') -gigameters_per_square_second = Unit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') -gigameters_per_millisecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') -gigameters_per_square_millisecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') -gigameters_per_microsecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') -gigameters_per_square_microsecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') -gigameters_per_nanosecond = Unit(1e+18, Dimensions(length=1, time=-1), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') -gigameters_per_square_nanosecond = Unit(1e+18, Dimensions(length=1, time=-2), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') -gigameters_per_picosecond = Unit(1e+21, Dimensions(length=1, time=-1), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') -gigameters_per_square_picosecond = Unit(1e+21, Dimensions(length=1, time=-2), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') -gigameters_per_femtosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') -gigameters_per_square_femtosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') -gigameters_per_attosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') -gigameters_per_square_attosecond = Unit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') -gigameters_per_minute = Unit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') -gigameters_per_square_minute = Unit(16666666.666666666, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') -gigameters_per_hour = Unit(2777777.777777778, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') -gigameters_per_square_hour = Unit(2777777.777777778, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') -gigameters_per_day = Unit(115740.74074074074, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') -gigameters_per_square_day = Unit(115740.74074074074, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') -gigameters_per_year = Unit(316.88738506811427, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') -gigameters_per_square_year = Unit(316.88738506811427, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') -megameters_per_second = Unit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') -megameters_per_square_second = Unit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') -megameters_per_millisecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') -megameters_per_square_millisecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') -megameters_per_microsecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') -megameters_per_square_microsecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') -megameters_per_nanosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') -megameters_per_square_nanosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') -megameters_per_picosecond = Unit(1e+18, Dimensions(length=1, time=-1), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') -megameters_per_square_picosecond = Unit(1e+18, Dimensions(length=1, time=-2), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') -megameters_per_femtosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') -megameters_per_square_femtosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') -megameters_per_attosecond = Unit(1e+24, Dimensions(length=1, time=-1), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') -megameters_per_square_attosecond = Unit(1e+24, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') -megameters_per_minute = Unit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') -megameters_per_square_minute = Unit(16666.666666666668, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') -megameters_per_hour = Unit(2777.777777777778, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') -megameters_per_square_hour = Unit(2777.777777777778, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') -megameters_per_day = Unit(115.74074074074075, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') -megameters_per_square_day = Unit(115.74074074074075, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') -megameters_per_year = Unit(0.3168873850681143, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') -megameters_per_square_year = Unit(0.3168873850681143, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') -kilometers_per_second = Unit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') -kilometers_per_square_second = Unit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') -kilometers_per_millisecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') -kilometers_per_square_millisecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') -kilometers_per_microsecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') -kilometers_per_square_microsecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') -kilometers_per_nanosecond = Unit(999999999999.9999, Dimensions(length=1, time=-1), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') -kilometers_per_square_nanosecond = Unit(999999999999.9999, Dimensions(length=1, time=-2), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') -kilometers_per_picosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') -kilometers_per_square_picosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') -kilometers_per_femtosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') -kilometers_per_square_femtosecond = Unit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') -kilometers_per_attosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') -kilometers_per_square_attosecond = Unit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') -kilometers_per_minute = Unit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') -kilometers_per_square_minute = Unit(16.666666666666668, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') -kilometers_per_hour = Unit(2.7777777777777777, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') -kilometers_per_square_hour = Unit(2.7777777777777777, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') -kilometers_per_day = Unit(0.11574074074074074, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') -kilometers_per_square_day = Unit(0.11574074074074074, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') -kilometers_per_year = Unit(0.0003168873850681143, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') -kilometers_per_square_year = Unit(0.0003168873850681143, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') -millimeters_per_second = Unit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') -millimeters_per_square_second = Unit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') -millimeters_per_millisecond = Unit(1.0, Dimensions(length=1, time=-1), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') -millimeters_per_square_millisecond = Unit(1.0, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') -millimeters_per_microsecond = Unit(1000.0000000000001, Dimensions(length=1, time=-1), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') -millimeters_per_square_microsecond = Unit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') -millimeters_per_nanosecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') -millimeters_per_square_nanosecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') -millimeters_per_picosecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') -millimeters_per_square_picosecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') -millimeters_per_femtosecond = Unit(1000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') -millimeters_per_square_femtosecond = Unit(1000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') -millimeters_per_attosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') -millimeters_per_square_attosecond = Unit(1000000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') -millimeters_per_minute = Unit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') -millimeters_per_square_minute = Unit(1.6666666666666667e-05, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') -millimeters_per_hour = Unit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') -millimeters_per_square_hour = Unit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') -millimeters_per_day = Unit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') -millimeters_per_square_day = Unit(1.1574074074074074e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') -millimeters_per_year = Unit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') -millimeters_per_square_year = Unit(3.168873850681143e-10, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') -micrometers_per_second = Unit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') -micrometers_per_square_second = Unit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') -micrometers_per_millisecond = Unit(0.001, Dimensions(length=1, time=-1), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') -micrometers_per_square_millisecond = Unit(0.001, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') -micrometers_per_microsecond = Unit(1.0, Dimensions(length=1, time=-1), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') -micrometers_per_square_microsecond = Unit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') -micrometers_per_nanosecond = Unit(999.9999999999999, Dimensions(length=1, time=-1), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') -micrometers_per_square_nanosecond = Unit(999.9999999999999, Dimensions(length=1, time=-2), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') -micrometers_per_picosecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') -micrometers_per_square_picosecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') -micrometers_per_femtosecond = Unit(999999999.9999999, Dimensions(length=1, time=-1), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') -micrometers_per_square_femtosecond = Unit(999999999.9999999, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') -micrometers_per_attosecond = Unit(999999999999.9999, Dimensions(length=1, time=-1), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') -micrometers_per_square_attosecond = Unit(999999999999.9999, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') -micrometers_per_minute = Unit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') -micrometers_per_square_minute = Unit(1.6666666666666667e-08, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') -micrometers_per_hour = Unit(2.7777777777777776e-09, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') -micrometers_per_square_hour = Unit(2.7777777777777776e-09, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') -micrometers_per_day = Unit(1.1574074074074074e-10, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') -micrometers_per_square_day = Unit(1.1574074074074074e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') -micrometers_per_year = Unit(3.168873850681143e-13, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') -micrometers_per_square_year = Unit(3.168873850681143e-13, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') -nanometers_per_second = Unit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') -nanometers_per_square_second = Unit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') -nanometers_per_millisecond = Unit(1e-06, Dimensions(length=1, time=-1), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') -nanometers_per_square_millisecond = Unit(1e-06, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') -nanometers_per_microsecond = Unit(0.001, Dimensions(length=1, time=-1), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') -nanometers_per_square_microsecond = Unit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') -nanometers_per_nanosecond = Unit(1.0, Dimensions(length=1, time=-1), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') -nanometers_per_square_nanosecond = Unit(1.0, Dimensions(length=1, time=-2), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') -nanometers_per_picosecond = Unit(1000.0000000000001, Dimensions(length=1, time=-1), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') -nanometers_per_square_picosecond = Unit(1000.0000000000001, Dimensions(length=1, time=-2), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') -nanometers_per_femtosecond = Unit(1000000.0, Dimensions(length=1, time=-1), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') -nanometers_per_square_femtosecond = Unit(1000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') -nanometers_per_attosecond = Unit(1000000000.0, Dimensions(length=1, time=-1), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') -nanometers_per_square_attosecond = Unit(1000000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') -nanometers_per_minute = Unit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') -nanometers_per_square_minute = Unit(1.6666666666666667e-11, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') -nanometers_per_hour = Unit(2.777777777777778e-12, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') -nanometers_per_square_hour = Unit(2.777777777777778e-12, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') -nanometers_per_day = Unit(1.1574074074074076e-13, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') -nanometers_per_square_day = Unit(1.1574074074074076e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') -nanometers_per_year = Unit(3.1688738506811433e-16, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') -nanometers_per_square_year = Unit(3.1688738506811433e-16, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') -picometers_per_second = Unit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') -picometers_per_square_second = Unit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') -picometers_per_millisecond = Unit(1e-09, Dimensions(length=1, time=-1), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') -picometers_per_square_millisecond = Unit(1e-09, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') -picometers_per_microsecond = Unit(1e-06, Dimensions(length=1, time=-1), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') -picometers_per_square_microsecond = Unit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') -picometers_per_nanosecond = Unit(0.001, Dimensions(length=1, time=-1), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') -picometers_per_square_nanosecond = Unit(0.001, Dimensions(length=1, time=-2), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') -picometers_per_picosecond = Unit(1.0, Dimensions(length=1, time=-1), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') -picometers_per_square_picosecond = Unit(1.0, Dimensions(length=1, time=-2), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') -picometers_per_femtosecond = Unit(999.9999999999999, Dimensions(length=1, time=-1), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') -picometers_per_square_femtosecond = Unit(999.9999999999999, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') -picometers_per_attosecond = Unit(999999.9999999999, Dimensions(length=1, time=-1), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') -picometers_per_square_attosecond = Unit(999999.9999999999, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') -picometers_per_minute = Unit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') -picometers_per_square_minute = Unit(1.6666666666666667e-14, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') -picometers_per_hour = Unit(2.7777777777777776e-15, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') -picometers_per_square_hour = Unit(2.7777777777777776e-15, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') -picometers_per_day = Unit(1.1574074074074073e-16, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') -picometers_per_square_day = Unit(1.1574074074074073e-16, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') -picometers_per_year = Unit(3.168873850681143e-19, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') -picometers_per_square_year = Unit(3.168873850681143e-19, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') -femtometers_per_second = Unit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') -femtometers_per_square_second = Unit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') -femtometers_per_millisecond = Unit(1e-12, Dimensions(length=1, time=-1), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') -femtometers_per_square_millisecond = Unit(1e-12, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') -femtometers_per_microsecond = Unit(1e-09, Dimensions(length=1, time=-1), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') -femtometers_per_square_microsecond = Unit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') -femtometers_per_nanosecond = Unit(1e-06, Dimensions(length=1, time=-1), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') -femtometers_per_square_nanosecond = Unit(1e-06, Dimensions(length=1, time=-2), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') -femtometers_per_picosecond = Unit(0.001, Dimensions(length=1, time=-1), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') -femtometers_per_square_picosecond = Unit(0.001, Dimensions(length=1, time=-2), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') -femtometers_per_femtosecond = Unit(1.0, Dimensions(length=1, time=-1), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') -femtometers_per_square_femtosecond = Unit(1.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') -femtometers_per_attosecond = Unit(1000.0, Dimensions(length=1, time=-1), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') -femtometers_per_square_attosecond = Unit(1000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') -femtometers_per_minute = Unit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') -femtometers_per_square_minute = Unit(1.6666666666666667e-17, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') -femtometers_per_hour = Unit(2.777777777777778e-18, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') -femtometers_per_square_hour = Unit(2.777777777777778e-18, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') -femtometers_per_day = Unit(1.1574074074074075e-19, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') -femtometers_per_square_day = Unit(1.1574074074074075e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') -femtometers_per_year = Unit(3.168873850681143e-22, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') -femtometers_per_square_year = Unit(3.168873850681143e-22, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') -attometers_per_second = Unit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') -attometers_per_square_second = Unit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') -attometers_per_millisecond = Unit(1e-15, Dimensions(length=1, time=-1), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') -attometers_per_square_millisecond = Unit(1e-15, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') -attometers_per_microsecond = Unit(1.0000000000000002e-12, Dimensions(length=1, time=-1), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') -attometers_per_square_microsecond = Unit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') -attometers_per_nanosecond = Unit(1e-09, Dimensions(length=1, time=-1), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') -attometers_per_square_nanosecond = Unit(1e-09, Dimensions(length=1, time=-2), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') -attometers_per_picosecond = Unit(1.0000000000000002e-06, Dimensions(length=1, time=-1), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') -attometers_per_square_picosecond = Unit(1.0000000000000002e-06, Dimensions(length=1, time=-2), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') -attometers_per_femtosecond = Unit(0.001, Dimensions(length=1, time=-1), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') -attometers_per_square_femtosecond = Unit(0.001, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') -attometers_per_attosecond = Unit(1.0, Dimensions(length=1, time=-1), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') -attometers_per_square_attosecond = Unit(1.0, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') -attometers_per_minute = Unit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') -attometers_per_square_minute = Unit(1.6666666666666668e-20, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') -attometers_per_hour = Unit(2.7777777777777778e-21, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') -attometers_per_square_hour = Unit(2.7777777777777778e-21, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') -attometers_per_day = Unit(1.1574074074074074e-22, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') -attometers_per_square_day = Unit(1.1574074074074074e-22, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') -attometers_per_year = Unit(3.1688738506811433e-25, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') -attometers_per_square_year = Unit(3.1688738506811433e-25, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') -decimeters_per_second = Unit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dmNone⁻¹') -decimeters_per_square_second = Unit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dmNone⁻²') -decimeters_per_millisecond = Unit(100.0, Dimensions(length=1, time=-1), name='decimeters_per_millisecond', ascii_symbol='dm/ms', symbol='dmms⁻¹') -decimeters_per_square_millisecond = Unit(100.0, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') -decimeters_per_microsecond = Unit(100000.00000000001, Dimensions(length=1, time=-1), name='decimeters_per_microsecond', ascii_symbol='dm/us', symbol='dmµs⁻¹') -decimeters_per_square_microsecond = Unit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_microsecond', ascii_symbol='dm/us^2', symbol='dmµs⁻²') -decimeters_per_nanosecond = Unit(100000000.0, Dimensions(length=1, time=-1), name='decimeters_per_nanosecond', ascii_symbol='dm/ns', symbol='dmns⁻¹') -decimeters_per_square_nanosecond = Unit(100000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_nanosecond', ascii_symbol='dm/ns^2', symbol='dmns⁻²') -decimeters_per_picosecond = Unit(100000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_picosecond', ascii_symbol='dm/ps', symbol='dmps⁻¹') -decimeters_per_square_picosecond = Unit(100000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_picosecond', ascii_symbol='dm/ps^2', symbol='dmps⁻²') -decimeters_per_femtosecond = Unit(100000000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_femtosecond', ascii_symbol='dm/fs', symbol='dmfs⁻¹') -decimeters_per_square_femtosecond = Unit(100000000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') -decimeters_per_attosecond = Unit(1e+17, Dimensions(length=1, time=-1), name='decimeters_per_attosecond', ascii_symbol='dm/as', symbol='dmas⁻¹') -decimeters_per_square_attosecond = Unit(1e+17, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') -decimeters_per_minute = Unit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmNone⁻¹') -decimeters_per_square_minute = Unit(0.0016666666666666668, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmNone⁻²') -decimeters_per_hour = Unit(0.0002777777777777778, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmNone⁻¹') -decimeters_per_square_hour = Unit(0.0002777777777777778, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmNone⁻²') -decimeters_per_day = Unit(1.1574074074074075e-05, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmNone⁻¹') -decimeters_per_square_day = Unit(1.1574074074074075e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmNone⁻²') -decimeters_per_year = Unit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmNone⁻¹') -decimeters_per_square_year = Unit(3.168873850681143e-08, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmNone⁻²') -centimeters_per_second = Unit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cmNone⁻¹') -centimeters_per_square_second = Unit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cmNone⁻²') -centimeters_per_millisecond = Unit(10.0, Dimensions(length=1, time=-1), name='centimeters_per_millisecond', ascii_symbol='cm/ms', symbol='cmms⁻¹') -centimeters_per_square_millisecond = Unit(10.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') -centimeters_per_microsecond = Unit(10000.0, Dimensions(length=1, time=-1), name='centimeters_per_microsecond', ascii_symbol='cm/us', symbol='cmµs⁻¹') -centimeters_per_square_microsecond = Unit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_microsecond', ascii_symbol='cm/us^2', symbol='cmµs⁻²') -centimeters_per_nanosecond = Unit(10000000.0, Dimensions(length=1, time=-1), name='centimeters_per_nanosecond', ascii_symbol='cm/ns', symbol='cmns⁻¹') -centimeters_per_square_nanosecond = Unit(10000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_nanosecond', ascii_symbol='cm/ns^2', symbol='cmns⁻²') -centimeters_per_picosecond = Unit(10000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_picosecond', ascii_symbol='cm/ps', symbol='cmps⁻¹') -centimeters_per_square_picosecond = Unit(10000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_picosecond', ascii_symbol='cm/ps^2', symbol='cmps⁻²') -centimeters_per_femtosecond = Unit(10000000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_femtosecond', ascii_symbol='cm/fs', symbol='cmfs⁻¹') -centimeters_per_square_femtosecond = Unit(10000000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') -centimeters_per_attosecond = Unit(1e+16, Dimensions(length=1, time=-1), name='centimeters_per_attosecond', ascii_symbol='cm/as', symbol='cmas⁻¹') -centimeters_per_square_attosecond = Unit(1e+16, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') -centimeters_per_minute = Unit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmNone⁻¹') -centimeters_per_square_minute = Unit(0.00016666666666666666, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmNone⁻²') -centimeters_per_hour = Unit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmNone⁻¹') -centimeters_per_square_hour = Unit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmNone⁻²') -centimeters_per_day = Unit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmNone⁻¹') -centimeters_per_square_day = Unit(1.1574074074074074e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmNone⁻²') -centimeters_per_year = Unit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmNone⁻¹') -centimeters_per_square_year = Unit(3.168873850681143e-09, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmNone⁻²') -angstroms_per_second = Unit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') -angstroms_per_square_second = Unit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') -angstroms_per_millisecond = Unit(1e-07, Dimensions(length=1, time=-1), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') -angstroms_per_square_millisecond = Unit(1e-07, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') -angstroms_per_microsecond = Unit(0.0001, Dimensions(length=1, time=-1), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') -angstroms_per_square_microsecond = Unit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') -angstroms_per_nanosecond = Unit(0.09999999999999999, Dimensions(length=1, time=-1), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') -angstroms_per_square_nanosecond = Unit(0.09999999999999999, Dimensions(length=1, time=-2), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') -angstroms_per_picosecond = Unit(100.0, Dimensions(length=1, time=-1), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') -angstroms_per_square_picosecond = Unit(100.0, Dimensions(length=1, time=-2), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') -angstroms_per_femtosecond = Unit(100000.0, Dimensions(length=1, time=-1), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') -angstroms_per_square_femtosecond = Unit(100000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') -angstroms_per_attosecond = Unit(100000000.0, Dimensions(length=1, time=-1), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') -angstroms_per_square_attosecond = Unit(100000000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') -angstroms_per_minute = Unit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') -angstroms_per_square_minute = Unit(1.6666666666666668e-12, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') -angstroms_per_hour = Unit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') -angstroms_per_square_hour = Unit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') -angstroms_per_day = Unit(1.1574074074074074e-14, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') -angstroms_per_square_day = Unit(1.1574074074074074e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') -angstroms_per_year = Unit(3.168873850681143e-17, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') -angstroms_per_square_year = Unit(3.168873850681143e-17, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') -grams_per_cubic_meter = Unit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_meter = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') -petagrams_per_cubic_meter = Unit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') -teragrams_per_cubic_meter = Unit(1000000000.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_meter = Unit(1000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') -megagrams_per_cubic_meter = Unit(1000.0, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') -kilograms_per_cubic_meter = Unit(1.0, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgNone⁻³') -milligrams_per_cubic_meter = Unit(1e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgNone⁻³') -micrograms_per_cubic_meter = Unit(1e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgNone⁻³') -nanograms_per_cubic_meter = Unit(1.0000000000000002e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngNone⁻³') -picograms_per_cubic_meter = Unit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') -femtograms_per_cubic_meter = Unit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') -attograms_per_cubic_meter = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_meter = Unit(1.660538921e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='NoneNone⁻³') -grams_per_cubic_exameter = Unit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') -exagrams_per_cubic_exameter = Unit(1e-39, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') -petagrams_per_cubic_exameter = Unit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') -teragrams_per_cubic_exameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') -gigagrams_per_cubic_exameter = Unit(1e-48, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_exameter', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') -megagrams_per_cubic_exameter = Unit(9.999999999999999e-52, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_exameter', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') -kilograms_per_cubic_exameter = Unit(9.999999999999999e-55, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_exameter', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') -milligrams_per_cubic_exameter = Unit(9.999999999999998e-61, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_exameter', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') -micrograms_per_cubic_exameter = Unit(9.999999999999999e-64, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_exameter', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') -nanograms_per_cubic_exameter = Unit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_exameter', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') -picograms_per_cubic_exameter = Unit(1e-69, Dimensions(length=-3, mass=1), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') -femtograms_per_cubic_exameter = Unit(1e-72, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') -attograms_per_cubic_exameter = Unit(1e-75, Dimensions(length=-3, mass=1), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -atomic_mass_units_per_cubic_exameter = Unit(1.6605389209999996e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='NoneEm⁻³') -grams_per_cubic_petameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') -exagrams_per_cubic_petameter = Unit(1e-30, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') -petagrams_per_cubic_petameter = Unit(1e-33, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') -teragrams_per_cubic_petameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') -gigagrams_per_cubic_petameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_petameter', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') -megagrams_per_cubic_petameter = Unit(1e-42, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_petameter', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') -kilograms_per_cubic_petameter = Unit(1.0000000000000001e-45, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_petameter', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') -milligrams_per_cubic_petameter = Unit(1e-51, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_petameter', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') -micrograms_per_cubic_petameter = Unit(1.0000000000000002e-54, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_petameter', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') -nanograms_per_cubic_petameter = Unit(1.0000000000000002e-57, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_petameter', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') -picograms_per_cubic_petameter = Unit(1.0000000000000001e-60, Dimensions(length=-3, mass=1), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') -femtograms_per_cubic_petameter = Unit(1.0000000000000002e-63, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') -attograms_per_cubic_petameter = Unit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -atomic_mass_units_per_cubic_petameter = Unit(1.660538921e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='NonePm⁻³') -grams_per_cubic_terameter = Unit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') -exagrams_per_cubic_terameter = Unit(1e-21, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') -petagrams_per_cubic_terameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') -teragrams_per_cubic_terameter = Unit(1e-27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') -gigagrams_per_cubic_terameter = Unit(9.999999999999999e-31, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_terameter', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') -megagrams_per_cubic_terameter = Unit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_terameter', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') -kilograms_per_cubic_terameter = Unit(1e-36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_terameter', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') -milligrams_per_cubic_terameter = Unit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_terameter', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') -micrograms_per_cubic_terameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_terameter', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') -nanograms_per_cubic_terameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_terameter', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') -picograms_per_cubic_terameter = Unit(1e-51, Dimensions(length=-3, mass=1), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') -femtograms_per_cubic_terameter = Unit(1e-54, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') -attograms_per_cubic_terameter = Unit(1.0000000000000001e-57, Dimensions(length=-3, mass=1), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -atomic_mass_units_per_cubic_terameter = Unit(1.6605389209999997e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='NoneTm⁻³') -grams_per_cubic_gigameter = Unit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') -exagrams_per_cubic_gigameter = Unit(1e-12, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') -petagrams_per_cubic_gigameter = Unit(1e-15, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') -teragrams_per_cubic_gigameter = Unit(1e-18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') -gigagrams_per_cubic_gigameter = Unit(1e-21, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_gigameter', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') -megagrams_per_cubic_gigameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_gigameter', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') -kilograms_per_cubic_gigameter = Unit(1e-27, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_gigameter', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') -milligrams_per_cubic_gigameter = Unit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_gigameter', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') -micrograms_per_cubic_gigameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_gigameter', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') -nanograms_per_cubic_gigameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_gigameter', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') -picograms_per_cubic_gigameter = Unit(1e-42, Dimensions(length=-3, mass=1), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') -femtograms_per_cubic_gigameter = Unit(1e-45, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') -attograms_per_cubic_gigameter = Unit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -atomic_mass_units_per_cubic_gigameter = Unit(1.660538921e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='NoneGm⁻³') -grams_per_cubic_megameter = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') -exagrams_per_cubic_megameter = Unit(0.001, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') -petagrams_per_cubic_megameter = Unit(1e-06, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') -teragrams_per_cubic_megameter = Unit(1e-09, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') -gigagrams_per_cubic_megameter = Unit(1e-12, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_megameter', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') -megagrams_per_cubic_megameter = Unit(1e-15, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_megameter', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') -kilograms_per_cubic_megameter = Unit(1e-18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_megameter', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') -milligrams_per_cubic_megameter = Unit(1e-24, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_megameter', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') -micrograms_per_cubic_megameter = Unit(1e-27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_megameter', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') -nanograms_per_cubic_megameter = Unit(1.0000000000000003e-30, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_megameter', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') -picograms_per_cubic_megameter = Unit(1e-33, Dimensions(length=-3, mass=1), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') -femtograms_per_cubic_megameter = Unit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') -attograms_per_cubic_megameter = Unit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -atomic_mass_units_per_cubic_megameter = Unit(1.6605389209999997e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='NoneMm⁻³') -grams_per_cubic_kilometer = Unit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') -exagrams_per_cubic_kilometer = Unit(1000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') -petagrams_per_cubic_kilometer = Unit(1000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') -teragrams_per_cubic_kilometer = Unit(1.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') -gigagrams_per_cubic_kilometer = Unit(0.001, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_kilometer', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') -megagrams_per_cubic_kilometer = Unit(1e-06, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_kilometer', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') -kilograms_per_cubic_kilometer = Unit(1e-09, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_kilometer', ascii_symbol='kg km^-3', symbol='kgkm⁻³') -milligrams_per_cubic_kilometer = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_kilometer', ascii_symbol='mg km^-3', symbol='mgkm⁻³') -micrograms_per_cubic_kilometer = Unit(1e-18, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_kilometer', ascii_symbol='ug km^-3', symbol='µgkm⁻³') -nanograms_per_cubic_kilometer = Unit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_kilometer', ascii_symbol='ng km^-3', symbol='ngkm⁻³') -picograms_per_cubic_kilometer = Unit(1.0000000000000001e-24, Dimensions(length=-3, mass=1), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') -femtograms_per_cubic_kilometer = Unit(1e-27, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') -attograms_per_cubic_kilometer = Unit(1e-30, Dimensions(length=-3, mass=1), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') -atomic_mass_units_per_cubic_kilometer = Unit(1.6605389209999997e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='Nonekm⁻³') -grams_per_cubic_millimeter = Unit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') -exagrams_per_cubic_millimeter = Unit(1e+24, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') -petagrams_per_cubic_millimeter = Unit(1e+21, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') -teragrams_per_cubic_millimeter = Unit(1e+18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') -gigagrams_per_cubic_millimeter = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_millimeter', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') -megagrams_per_cubic_millimeter = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_millimeter', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') -kilograms_per_cubic_millimeter = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_millimeter', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') -milligrams_per_cubic_millimeter = Unit(999.9999999999999, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_millimeter', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') -micrograms_per_cubic_millimeter = Unit(1.0, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_millimeter', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') -nanograms_per_cubic_millimeter = Unit(0.001, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_millimeter', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') -picograms_per_cubic_millimeter = Unit(1e-06, Dimensions(length=-3, mass=1), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') -femtograms_per_cubic_millimeter = Unit(1e-09, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') -attograms_per_cubic_millimeter = Unit(1e-12, Dimensions(length=-3, mass=1), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -atomic_mass_units_per_cubic_millimeter = Unit(1.6605389209999997e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='Nonemm⁻³') -grams_per_cubic_micrometer = Unit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') -exagrams_per_cubic_micrometer = Unit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') -petagrams_per_cubic_micrometer = Unit(1.0000000000000002e+30, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') -teragrams_per_cubic_micrometer = Unit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') -gigagrams_per_cubic_micrometer = Unit(1.0000000000000001e+24, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_micrometer', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') -megagrams_per_cubic_micrometer = Unit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_micrometer', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') -kilograms_per_cubic_micrometer = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_micrometer', ascii_symbol='kg um^-3', symbol='kgµm⁻³') -milligrams_per_cubic_micrometer = Unit(1000000000000.0001, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_micrometer', ascii_symbol='mg um^-3', symbol='mgµm⁻³') -micrograms_per_cubic_micrometer = Unit(1000000000.0000002, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_micrometer', ascii_symbol='ug um^-3', symbol='µgµm⁻³') -nanograms_per_cubic_micrometer = Unit(1000000.0000000003, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_micrometer', ascii_symbol='ng um^-3', symbol='ngµm⁻³') -picograms_per_cubic_micrometer = Unit(1000.0000000000002, Dimensions(length=-3, mass=1), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') -femtograms_per_cubic_micrometer = Unit(1.0000000000000002, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') -attograms_per_cubic_micrometer = Unit(0.0010000000000000002, Dimensions(length=-3, mass=1), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') -atomic_mass_units_per_cubic_micrometer = Unit(1.660538921e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='Noneµm⁻³') -grams_per_cubic_nanometer = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') -exagrams_per_cubic_nanometer = Unit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') -petagrams_per_cubic_nanometer = Unit(9.999999999999998e+38, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') -teragrams_per_cubic_nanometer = Unit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') -gigagrams_per_cubic_nanometer = Unit(9.999999999999998e+32, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_nanometer', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') -megagrams_per_cubic_nanometer = Unit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_nanometer', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') -kilograms_per_cubic_nanometer = Unit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_nanometer', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') -milligrams_per_cubic_nanometer = Unit(9.999999999999997e+20, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_nanometer', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') -micrograms_per_cubic_nanometer = Unit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_nanometer', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') -nanograms_per_cubic_nanometer = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_nanometer', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') -picograms_per_cubic_nanometer = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') -femtograms_per_cubic_nanometer = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') -attograms_per_cubic_nanometer = Unit(999999.9999999999, Dimensions(length=-3, mass=1), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -atomic_mass_units_per_cubic_nanometer = Unit(1.6605389209999994, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='Nonenm⁻³') -grams_per_cubic_picometer = Unit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') -exagrams_per_cubic_picometer = Unit(1e+51, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') -petagrams_per_cubic_picometer = Unit(1e+48, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') -teragrams_per_cubic_picometer = Unit(1.0000000000000001e+45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') -gigagrams_per_cubic_picometer = Unit(1e+42, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_picometer', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') -megagrams_per_cubic_picometer = Unit(1.0000000000000001e+39, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_picometer', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') -kilograms_per_cubic_picometer = Unit(1e+36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_picometer', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') -milligrams_per_cubic_picometer = Unit(1e+30, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_picometer', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') -micrograms_per_cubic_picometer = Unit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_picometer', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') -nanograms_per_cubic_picometer = Unit(1.0000000000000003e+24, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_picometer', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') -picograms_per_cubic_picometer = Unit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') -femtograms_per_cubic_picometer = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') -attograms_per_cubic_picometer = Unit(1000000000000000.1, Dimensions(length=-3, mass=1), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -atomic_mass_units_per_cubic_picometer = Unit(1660538921.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='Nonepm⁻³') -grams_per_cubic_femtometer = Unit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') -exagrams_per_cubic_femtometer = Unit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') -petagrams_per_cubic_femtometer = Unit(9.999999999999997e+56, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') -teragrams_per_cubic_femtometer = Unit(9.999999999999997e+53, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') -gigagrams_per_cubic_femtometer = Unit(9.999999999999997e+50, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_femtometer', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') -megagrams_per_cubic_femtometer = Unit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_femtometer', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') -kilograms_per_cubic_femtometer = Unit(9.999999999999998e+44, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_femtometer', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') -milligrams_per_cubic_femtometer = Unit(9.999999999999996e+38, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_femtometer', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') -micrograms_per_cubic_femtometer = Unit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_femtometer', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') -nanograms_per_cubic_femtometer = Unit(1e+33, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_femtometer', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') -picograms_per_cubic_femtometer = Unit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') -femtograms_per_cubic_femtometer = Unit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') -attograms_per_cubic_femtometer = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -atomic_mass_units_per_cubic_femtometer = Unit(1.6605389209999992e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='Nonefm⁻³') -grams_per_cubic_attometer = Unit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') -exagrams_per_cubic_attometer = Unit(9.999999999999999e+68, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') -petagrams_per_cubic_attometer = Unit(9.999999999999998e+65, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') -teragrams_per_cubic_attometer = Unit(9.999999999999999e+62, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') -gigagrams_per_cubic_attometer = Unit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_attometer', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') -megagrams_per_cubic_attometer = Unit(9.999999999999999e+56, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_attometer', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') -kilograms_per_cubic_attometer = Unit(9.999999999999999e+53, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_attometer', ascii_symbol='kg am^-3', symbol='kgam⁻³') -milligrams_per_cubic_attometer = Unit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_attometer', ascii_symbol='mg am^-3', symbol='mgam⁻³') -micrograms_per_cubic_attometer = Unit(1e+45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_attometer', ascii_symbol='ug am^-3', symbol='µgam⁻³') -nanograms_per_cubic_attometer = Unit(1e+42, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_attometer', ascii_symbol='ng am^-3', symbol='ngam⁻³') -picograms_per_cubic_attometer = Unit(1e+39, Dimensions(length=-3, mass=1), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') -femtograms_per_cubic_attometer = Unit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') -attograms_per_cubic_attometer = Unit(1e+33, Dimensions(length=-3, mass=1), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') -atomic_mass_units_per_cubic_attometer = Unit(1.6605389209999997e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='Noneam⁻³') -grams_per_cubic_decimeter = Unit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='Nonedm⁻³') -exagrams_per_cubic_decimeter = Unit(9.999999999999997e+17, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_decimeter', ascii_symbol='Eg dm^-3', symbol='Egdm⁻³') -petagrams_per_cubic_decimeter = Unit(999999999999999.8, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_decimeter', ascii_symbol='Pg dm^-3', symbol='Pgdm⁻³') -teragrams_per_cubic_decimeter = Unit(999999999999.9998, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_decimeter', ascii_symbol='Tg dm^-3', symbol='Tgdm⁻³') -gigagrams_per_cubic_decimeter = Unit(999999999.9999998, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_decimeter', ascii_symbol='Gg dm^-3', symbol='Ggdm⁻³') -megagrams_per_cubic_decimeter = Unit(999999.9999999998, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_decimeter', ascii_symbol='Mg dm^-3', symbol='Mgdm⁻³') -kilograms_per_cubic_decimeter = Unit(999.9999999999998, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_decimeter', ascii_symbol='kg dm^-3', symbol='kgdm⁻³') -milligrams_per_cubic_decimeter = Unit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_decimeter', ascii_symbol='mg dm^-3', symbol='mgdm⁻³') -micrograms_per_cubic_decimeter = Unit(9.999999999999997e-07, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_decimeter', ascii_symbol='ug dm^-3', symbol='µgdm⁻³') -nanograms_per_cubic_decimeter = Unit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_decimeter', ascii_symbol='ng dm^-3', symbol='ngdm⁻³') -picograms_per_cubic_decimeter = Unit(9.999999999999998e-13, Dimensions(length=-3, mass=1), name='picograms_per_cubic_decimeter', ascii_symbol='pg dm^-3', symbol='pgdm⁻³') -femtograms_per_cubic_decimeter = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_decimeter', ascii_symbol='fg dm^-3', symbol='fgdm⁻³') -attograms_per_cubic_decimeter = Unit(9.999999999999999e-19, Dimensions(length=-3, mass=1), name='attograms_per_cubic_decimeter', ascii_symbol='ag dm^-3', symbol='agdm⁻³') -atomic_mass_units_per_cubic_decimeter = Unit(1.6605389209999993e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='Nonedm⁻³') -grams_per_cubic_centimeter = Unit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='Nonecm⁻³') -exagrams_per_cubic_centimeter = Unit(9.999999999999999e+20, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_centimeter', ascii_symbol='Eg cm^-3', symbol='Egcm⁻³') -petagrams_per_cubic_centimeter = Unit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_centimeter', ascii_symbol='Pg cm^-3', symbol='Pgcm⁻³') -teragrams_per_cubic_centimeter = Unit(999999999999999.9, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_centimeter', ascii_symbol='Tg cm^-3', symbol='Tgcm⁻³') -gigagrams_per_cubic_centimeter = Unit(999999999999.9999, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_centimeter', ascii_symbol='Gg cm^-3', symbol='Ggcm⁻³') -megagrams_per_cubic_centimeter = Unit(999999999.9999999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_centimeter', ascii_symbol='Mg cm^-3', symbol='Mgcm⁻³') -kilograms_per_cubic_centimeter = Unit(999999.9999999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_centimeter', ascii_symbol='kg cm^-3', symbol='kgcm⁻³') -milligrams_per_cubic_centimeter = Unit(0.9999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_centimeter', ascii_symbol='mg cm^-3', symbol='mgcm⁻³') -micrograms_per_cubic_centimeter = Unit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_centimeter', ascii_symbol='ug cm^-3', symbol='µgcm⁻³') -nanograms_per_cubic_centimeter = Unit(1e-06, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_centimeter', ascii_symbol='ng cm^-3', symbol='ngcm⁻³') -picograms_per_cubic_centimeter = Unit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='picograms_per_cubic_centimeter', ascii_symbol='pg cm^-3', symbol='pgcm⁻³') -femtograms_per_cubic_centimeter = Unit(1e-12, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_centimeter', ascii_symbol='fg cm^-3', symbol='fgcm⁻³') -attograms_per_cubic_centimeter = Unit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='attograms_per_cubic_centimeter', ascii_symbol='ag cm^-3', symbol='agcm⁻³') -atomic_mass_units_per_cubic_centimeter = Unit(1.6605389209999996e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='Nonecm⁻³') -grams_per_cubic_angstrom = Unit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') -exagrams_per_cubic_angstrom = Unit(1e+45, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') -petagrams_per_cubic_angstrom = Unit(9.999999999999999e+41, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') -teragrams_per_cubic_angstrom = Unit(1e+39, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') -gigagrams_per_cubic_angstrom = Unit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_angstrom', ascii_symbol='Gg Ang^-3', symbol='GgÅ⁻³') -megagrams_per_cubic_angstrom = Unit(1e+33, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_angstrom', ascii_symbol='Mg Ang^-3', symbol='MgÅ⁻³') -kilograms_per_cubic_angstrom = Unit(9.999999999999999e+29, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_angstrom', ascii_symbol='kg Ang^-3', symbol='kgÅ⁻³') -milligrams_per_cubic_angstrom = Unit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_angstrom', ascii_symbol='mg Ang^-3', symbol='mgÅ⁻³') -micrograms_per_cubic_angstrom = Unit(1e+21, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_angstrom', ascii_symbol='ug Ang^-3', symbol='µgÅ⁻³') -nanograms_per_cubic_angstrom = Unit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_angstrom', ascii_symbol='ng Ang^-3', symbol='ngÅ⁻³') -picograms_per_cubic_angstrom = Unit(1000000000000000.0, Dimensions(length=-3, mass=1), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') -femtograms_per_cubic_angstrom = Unit(1000000000000.0, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') -attograms_per_cubic_angstrom = Unit(1000000000.0, Dimensions(length=-3, mass=1), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') -atomic_mass_units_per_cubic_angstrom = Unit(1660.5389209999996, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='NoneÅ⁻³') -moles_per_cubic_meter = Unit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_meter = Unit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_meter = Unit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_meter = Unit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_meter = Unit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_meter = Unit(602214076.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_meter = Unit(602214.076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolNone⁻³') -moles_per_cubic_exameter = Unit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='NoneEm⁻³') -millimoles_per_cubic_exameter = Unit(6.02214076e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') -micromoles_per_cubic_exameter = Unit(6.02214076e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') -nanomoles_per_cubic_exameter = Unit(6.022140759999999e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') -picomoles_per_cubic_exameter = Unit(6.022140759999999e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') -femtomoles_per_cubic_exameter = Unit(6.02214076e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') -attomoles_per_cubic_exameter = Unit(6.022140759999999e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') -moles_per_cubic_petameter = Unit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='NonePm⁻³') -millimoles_per_cubic_petameter = Unit(6.0221407600000005e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') -micromoles_per_cubic_petameter = Unit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') -nanomoles_per_cubic_petameter = Unit(6.02214076e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') -picomoles_per_cubic_petameter = Unit(6.0221407600000005e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') -femtomoles_per_cubic_petameter = Unit(6.022140760000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') -attomoles_per_cubic_petameter = Unit(6.022140760000001e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') -moles_per_cubic_terameter = Unit(6.022140759999999e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='NoneTm⁻³') -millimoles_per_cubic_terameter = Unit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') -micromoles_per_cubic_terameter = Unit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') -nanomoles_per_cubic_terameter = Unit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') -picomoles_per_cubic_terameter = Unit(6.02214076e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') -femtomoles_per_cubic_terameter = Unit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') -attomoles_per_cubic_terameter = Unit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') -moles_per_cubic_gigameter = Unit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='NoneGm⁻³') -millimoles_per_cubic_gigameter = Unit(6.022140760000001e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') -micromoles_per_cubic_gigameter = Unit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') -nanomoles_per_cubic_gigameter = Unit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') -picomoles_per_cubic_gigameter = Unit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') -femtomoles_per_cubic_gigameter = Unit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') -attomoles_per_cubic_gigameter = Unit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') -moles_per_cubic_megameter = Unit(602214.076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='NoneMm⁻³') -millimoles_per_cubic_megameter = Unit(602.214076, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') -micromoles_per_cubic_megameter = Unit(0.602214076, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') -nanomoles_per_cubic_megameter = Unit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') -picomoles_per_cubic_megameter = Unit(6.02214076e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') -femtomoles_per_cubic_megameter = Unit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') -attomoles_per_cubic_megameter = Unit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') -moles_per_cubic_kilometer = Unit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='Nonekm⁻³') -millimoles_per_cubic_kilometer = Unit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') -micromoles_per_cubic_kilometer = Unit(602214076.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') -nanomoles_per_cubic_kilometer = Unit(602214.076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') -picomoles_per_cubic_kilometer = Unit(602.214076, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') -femtomoles_per_cubic_kilometer = Unit(0.602214076, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') -attomoles_per_cubic_kilometer = Unit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') -moles_per_cubic_millimeter = Unit(6.0221407599999995e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='Nonemm⁻³') -millimoles_per_cubic_millimeter = Unit(6.02214076e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') -micromoles_per_cubic_millimeter = Unit(6.0221407599999996e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') -nanomoles_per_cubic_millimeter = Unit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') -picomoles_per_cubic_millimeter = Unit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') -femtomoles_per_cubic_millimeter = Unit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') -attomoles_per_cubic_millimeter = Unit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') -moles_per_cubic_micrometer = Unit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='Noneµm⁻³') -millimoles_per_cubic_micrometer = Unit(6.022140760000001e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') -micromoles_per_cubic_micrometer = Unit(6.0221407600000004e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') -nanomoles_per_cubic_micrometer = Unit(6.022140760000001e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') -picomoles_per_cubic_micrometer = Unit(6.022140760000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') -femtomoles_per_cubic_micrometer = Unit(6.022140760000001e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') -attomoles_per_cubic_micrometer = Unit(6.0221407600000005e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') -moles_per_cubic_nanometer = Unit(6.022140759999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='Nonenm⁻³') -millimoles_per_cubic_nanometer = Unit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') -micromoles_per_cubic_nanometer = Unit(6.022140759999999e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') -nanomoles_per_cubic_nanometer = Unit(6.022140759999998e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') -picomoles_per_cubic_nanometer = Unit(6.0221407599999985e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') -femtomoles_per_cubic_nanometer = Unit(6.022140759999999e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') -attomoles_per_cubic_nanometer = Unit(6.022140759999999e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') -moles_per_cubic_picometer = Unit(6.0221407600000005e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='Nonepm⁻³') -millimoles_per_cubic_picometer = Unit(6.0221407600000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') -micromoles_per_cubic_picometer = Unit(6.022140760000001e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') -nanomoles_per_cubic_picometer = Unit(6.0221407600000005e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') -picomoles_per_cubic_picometer = Unit(6.02214076e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') -femtomoles_per_cubic_picometer = Unit(6.022140760000001e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') -attomoles_per_cubic_picometer = Unit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') -moles_per_cubic_femtometer = Unit(6.022140759999998e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='Nonefm⁻³') -millimoles_per_cubic_femtometer = Unit(6.022140759999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') -micromoles_per_cubic_femtometer = Unit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') -nanomoles_per_cubic_femtometer = Unit(6.022140759999998e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') -picomoles_per_cubic_femtometer = Unit(6.022140759999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') -femtomoles_per_cubic_femtometer = Unit(6.022140759999998e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') -attomoles_per_cubic_femtometer = Unit(6.022140759999998e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') -moles_per_cubic_attometer = Unit(6.022140759999998e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='Noneam⁻³') -millimoles_per_cubic_attometer = Unit(6.022140759999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') -micromoles_per_cubic_attometer = Unit(6.022140759999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') -nanomoles_per_cubic_attometer = Unit(6.022140759999999e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') -picomoles_per_cubic_attometer = Unit(6.022140759999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') -femtomoles_per_cubic_attometer = Unit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') -attomoles_per_cubic_attometer = Unit(6.022140759999999e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') -moles_per_cubic_decimeter = Unit(6.022140759999998e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='Nonedm⁻³') -millimoles_per_cubic_decimeter = Unit(6.0221407599999985e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') -micromoles_per_cubic_decimeter = Unit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') -nanomoles_per_cubic_decimeter = Unit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') -picomoles_per_cubic_decimeter = Unit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') -femtomoles_per_cubic_decimeter = Unit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') -attomoles_per_cubic_decimeter = Unit(602214075.9999999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') -moles_per_cubic_centimeter = Unit(6.022140759999999e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='Nonecm⁻³') -millimoles_per_cubic_centimeter = Unit(6.022140759999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') -micromoles_per_cubic_centimeter = Unit(6.022140759999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') -nanomoles_per_cubic_centimeter = Unit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') -picomoles_per_cubic_centimeter = Unit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') -femtomoles_per_cubic_centimeter = Unit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') -attomoles_per_cubic_centimeter = Unit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') -moles_per_cubic_angstrom = Unit(6.022140759999999e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='NoneÅ⁻³') -millimoles_per_cubic_angstrom = Unit(6.02214076e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') -micromoles_per_cubic_angstrom = Unit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') -nanomoles_per_cubic_angstrom = Unit(6.02214076e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') -picomoles_per_cubic_angstrom = Unit(6.02214076e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') -femtomoles_per_cubic_angstrom = Unit(6.022140759999999e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') -attomoles_per_cubic_angstrom = Unit(6.02214076e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') +meters = NamedUnit(1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') +exameters = NamedUnit(1e+18, Dimensions(1, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') +petameters = NamedUnit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') +terameters = NamedUnit(1000000000000.0, Dimensions(1, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') +gigameters = NamedUnit(1000000000.0, Dimensions(1, 0, 0, 0, 0),name='gigameters',ascii_symbol='Gm',symbol='Gm') +megameters = NamedUnit(1000000.0, Dimensions(1, 0, 0, 0, 0),name='megameters',ascii_symbol='Mm',symbol='Mm') +kilometers = NamedUnit(1000.0, Dimensions(1, 0, 0, 0, 0),name='kilometers',ascii_symbol='km',symbol='km') +millimeters = NamedUnit(0.001, Dimensions(1, 0, 0, 0, 0),name='millimeters',ascii_symbol='mm',symbol='mm') +micrometers = NamedUnit(1e-06, Dimensions(1, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',symbol='µm') +nanometers = NamedUnit(1e-09, Dimensions(1, 0, 0, 0, 0),name='nanometers',ascii_symbol='nm',symbol='nm') +picometers = NamedUnit(1e-12, Dimensions(1, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') +femtometers = NamedUnit(1e-15, Dimensions(1, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') +attometers = NamedUnit(1e-18, Dimensions(1, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') +decimeters = NamedUnit(0.1, Dimensions(1, 0, 0, 0, 0),name='decimeters',ascii_symbol='dm',symbol='dm') +centimeters = NamedUnit(0.01, Dimensions(1, 0, 0, 0, 0),name='centimeters',ascii_symbol='cm',symbol='cm') +seconds = NamedUnit(1, Dimensions(0, 1, 0, 0, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') +milliseconds = NamedUnit(0.001, Dimensions(0, 1, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') +microseconds = NamedUnit(1e-06, Dimensions(0, 1, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') +nanoseconds = NamedUnit(1e-09, Dimensions(0, 1, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') +picoseconds = NamedUnit(1e-12, Dimensions(0, 1, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') +femtoseconds = NamedUnit(1e-15, Dimensions(0, 1, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') +attoseconds = NamedUnit(1e-18, Dimensions(0, 1, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') +grams = NamedUnit(0.001, Dimensions(0, 0, 1, 0, 0, 0, 0),name='grams',ascii_symbol='g',symbol='g') +exagrams = NamedUnit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') +petagrams = NamedUnit(1000000000000.0, Dimensions(0, 0, 1, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') +teragrams = NamedUnit(1000000000.0, Dimensions(0, 0, 1, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') +gigagrams = NamedUnit(1000000.0, Dimensions(0, 0, 1, 0, 0),name='gigagrams',ascii_symbol='Gg',symbol='Gg') +megagrams = NamedUnit(1000.0, Dimensions(0, 0, 1, 0, 0),name='megagrams',ascii_symbol='Mg',symbol='Mg') +kilograms = NamedUnit(1.0, Dimensions(0, 0, 1, 0, 0),name='kilograms',ascii_symbol='kg',symbol='kg') +milligrams = NamedUnit(1e-06, Dimensions(0, 0, 1, 0, 0),name='milligrams',ascii_symbol='mg',symbol='mg') +micrograms = NamedUnit(1e-09, Dimensions(0, 0, 1, 0, 0),name='micrograms',ascii_symbol='ug',symbol='µg') +nanograms = NamedUnit(1.0000000000000002e-12, Dimensions(0, 0, 1, 0, 0),name='nanograms',ascii_symbol='ng',symbol='ng') +picograms = NamedUnit(1e-15, Dimensions(0, 0, 1, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') +femtograms = NamedUnit(1e-18, Dimensions(0, 0, 1, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') +attograms = NamedUnit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') +amperes = NamedUnit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amperes',ascii_symbol='A',symbol='A') +exaamperes = NamedUnit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamperes',ascii_symbol='EA',symbol='EA') +petaamperes = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamperes',ascii_symbol='PA',symbol='PA') +teraamperes = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamperes',ascii_symbol='TA',symbol='TA') +gigaamperes = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 1, 0),name='gigaamperes',ascii_symbol='GA',symbol='GA') +megaamperes = NamedUnit(1000000.0, Dimensions(0, 0, 0, 1, 0),name='megaamperes',ascii_symbol='MA',symbol='MA') +kiloamperes = NamedUnit(1000.0, Dimensions(0, 0, 0, 1, 0),name='kiloamperes',ascii_symbol='kA',symbol='kA') +milliamperes = NamedUnit(0.001, Dimensions(0, 0, 0, 1, 0),name='milliamperes',ascii_symbol='mA',symbol='mA') +microamperes = NamedUnit(1e-06, Dimensions(0, 0, 0, 1, 0),name='microamperes',ascii_symbol='uA',symbol='µA') +nanoamperes = NamedUnit(1e-09, Dimensions(0, 0, 0, 1, 0),name='nanoamperes',ascii_symbol='nA',symbol='nA') +picoamperes = NamedUnit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamperes',ascii_symbol='pA',symbol='pA') +femtoamperes = NamedUnit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamperes',ascii_symbol='fA',symbol='fA') +attoamperes = NamedUnit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamperes',ascii_symbol='aA',symbol='aA') +kelvin = NamedUnit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kelvin',ascii_symbol='K',symbol='K') +exakelvin = NamedUnit(1e+18, Dimensions(0, 0, 0, 0, 1),name='exakelvin',ascii_symbol='EK',symbol='EK') +petakelvin = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1),name='petakelvin',ascii_symbol='PK',symbol='PK') +terakelvin = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 0, 1),name='terakelvin',ascii_symbol='TK',symbol='TK') +gigakelvin = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 0, 1),name='gigakelvin',ascii_symbol='GK',symbol='GK') +megakelvin = NamedUnit(1000000.0, Dimensions(0, 0, 0, 0, 1),name='megakelvin',ascii_symbol='MK',symbol='MK') +kilokelvin = NamedUnit(1000.0, Dimensions(0, 0, 0, 0, 1),name='kilokelvin',ascii_symbol='kK',symbol='kK') +millikelvin = NamedUnit(0.001, Dimensions(0, 0, 0, 0, 1),name='millikelvin',ascii_symbol='mK',symbol='mK') +microkelvin = NamedUnit(1e-06, Dimensions(0, 0, 0, 0, 1),name='microkelvin',ascii_symbol='uK',symbol='µK') +nanokelvin = NamedUnit(1e-09, Dimensions(0, 0, 0, 0, 1),name='nanokelvin',ascii_symbol='nK',symbol='nK') +picokelvin = NamedUnit(1e-12, Dimensions(0, 0, 0, 0, 1),name='picokelvin',ascii_symbol='pK',symbol='pK') +femtokelvin = NamedUnit(1e-15, Dimensions(0, 0, 0, 0, 1),name='femtokelvin',ascii_symbol='fK',symbol='fK') +attokelvin = NamedUnit(1e-18, Dimensions(0, 0, 0, 0, 1),name='attokelvin',ascii_symbol='aK',symbol='aK') +hertz = NamedUnit(1, Dimensions(0, -1, 0, 0, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') +exahertz = NamedUnit(1e+18, Dimensions(0, -1, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') +petahertz = NamedUnit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') +terahertz = NamedUnit(1000000000000.0, Dimensions(0, -1, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') +gigahertz = NamedUnit(1000000000.0, Dimensions(0, -1, 0, 0, 0),name='gigahertz',ascii_symbol='GHz',symbol='GHz') +megahertz = NamedUnit(1000000.0, Dimensions(0, -1, 0, 0, 0),name='megahertz',ascii_symbol='MHz',symbol='MHz') +kilohertz = NamedUnit(1000.0, Dimensions(0, -1, 0, 0, 0),name='kilohertz',ascii_symbol='kHz',symbol='kHz') +millihertz = NamedUnit(0.001, Dimensions(0, -1, 0, 0, 0),name='millihertz',ascii_symbol='mHz',symbol='mHz') +microhertz = NamedUnit(1e-06, Dimensions(0, -1, 0, 0, 0),name='microhertz',ascii_symbol='uHz',symbol='µHz') +nanohertz = NamedUnit(1e-09, Dimensions(0, -1, 0, 0, 0),name='nanohertz',ascii_symbol='nHz',symbol='nHz') +picohertz = NamedUnit(1e-12, Dimensions(0, -1, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') +femtohertz = NamedUnit(1e-15, Dimensions(0, -1, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') +attohertz = NamedUnit(1e-18, Dimensions(0, -1, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') +newtons = NamedUnit(1, Dimensions(1, -2, 1, 0, 0, 0, 0),name='newtons',ascii_symbol='N',symbol='N') +exanewtons = NamedUnit(1e+18, Dimensions(1, -2, 1, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') +petanewtons = NamedUnit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') +teranewtons = NamedUnit(1000000000000.0, Dimensions(1, -2, 1, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') +giganewtons = NamedUnit(1000000000.0, Dimensions(1, -2, 1, 0, 0),name='giganewtons',ascii_symbol='GN',symbol='GN') +meganewtons = NamedUnit(1000000.0, Dimensions(1, -2, 1, 0, 0),name='meganewtons',ascii_symbol='MN',symbol='MN') +kilonewtons = NamedUnit(1000.0, Dimensions(1, -2, 1, 0, 0),name='kilonewtons',ascii_symbol='kN',symbol='kN') +millinewtons = NamedUnit(0.001, Dimensions(1, -2, 1, 0, 0),name='millinewtons',ascii_symbol='mN',symbol='mN') +micronewtons = NamedUnit(1e-06, Dimensions(1, -2, 1, 0, 0),name='micronewtons',ascii_symbol='uN',symbol='µN') +nanonewtons = NamedUnit(1e-09, Dimensions(1, -2, 1, 0, 0),name='nanonewtons',ascii_symbol='nN',symbol='nN') +piconewtons = NamedUnit(1e-12, Dimensions(1, -2, 1, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') +femtonewtons = NamedUnit(1e-15, Dimensions(1, -2, 1, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') +attonewtons = NamedUnit(1e-18, Dimensions(1, -2, 1, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') +pascals = NamedUnit(1, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') +exapascals = NamedUnit(1e+18, Dimensions(-1, -2, 1, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') +petapascals = NamedUnit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') +terapascals = NamedUnit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') +gigapascals = NamedUnit(1000000000.0, Dimensions(-1, -2, 1, 0, 0),name='gigapascals',ascii_symbol='GPa',symbol='GPa') +megapascals = NamedUnit(1000000.0, Dimensions(-1, -2, 1, 0, 0),name='megapascals',ascii_symbol='MPa',symbol='MPa') +kilopascals = NamedUnit(1000.0, Dimensions(-1, -2, 1, 0, 0),name='kilopascals',ascii_symbol='kPa',symbol='kPa') +millipascals = NamedUnit(0.001, Dimensions(-1, -2, 1, 0, 0),name='millipascals',ascii_symbol='mPa',symbol='mPa') +micropascals = NamedUnit(1e-06, Dimensions(-1, -2, 1, 0, 0),name='micropascals',ascii_symbol='uPa',symbol='µPa') +nanopascals = NamedUnit(1e-09, Dimensions(-1, -2, 1, 0, 0),name='nanopascals',ascii_symbol='nPa',symbol='nPa') +picopascals = NamedUnit(1e-12, Dimensions(-1, -2, 1, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') +femtopascals = NamedUnit(1e-15, Dimensions(-1, -2, 1, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') +attopascals = NamedUnit(1e-18, Dimensions(-1, -2, 1, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') +joules = NamedUnit(1, Dimensions(2, -2, 1, 0, 0, 0, 0),name='joules',ascii_symbol='J',symbol='J') +exajoules = NamedUnit(1e+18, Dimensions(2, -2, 1, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') +petajoules = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') +terajoules = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') +gigajoules = NamedUnit(1000000000.0, Dimensions(2, -2, 1, 0, 0),name='gigajoules',ascii_symbol='GJ',symbol='GJ') +megajoules = NamedUnit(1000000.0, Dimensions(2, -2, 1, 0, 0),name='megajoules',ascii_symbol='MJ',symbol='MJ') +kilojoules = NamedUnit(1000.0, Dimensions(2, -2, 1, 0, 0),name='kilojoules',ascii_symbol='kJ',symbol='kJ') +millijoules = NamedUnit(0.001, Dimensions(2, -2, 1, 0, 0),name='millijoules',ascii_symbol='mJ',symbol='mJ') +microjoules = NamedUnit(1e-06, Dimensions(2, -2, 1, 0, 0),name='microjoules',ascii_symbol='uJ',symbol='µJ') +nanojoules = NamedUnit(1e-09, Dimensions(2, -2, 1, 0, 0),name='nanojoules',ascii_symbol='nJ',symbol='nJ') +picojoules = NamedUnit(1e-12, Dimensions(2, -2, 1, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') +femtojoules = NamedUnit(1e-15, Dimensions(2, -2, 1, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') +attojoules = NamedUnit(1e-18, Dimensions(2, -2, 1, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') +watts = NamedUnit(1, Dimensions(2, -3, 1, 0, 0, 0, 0),name='watts',ascii_symbol='W',symbol='W') +exawatts = NamedUnit(1e+18, Dimensions(2, -3, 1, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') +petawatts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') +terawatts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') +gigawatts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, 0, 0),name='gigawatts',ascii_symbol='GW',symbol='GW') +megawatts = NamedUnit(1000000.0, Dimensions(2, -3, 1, 0, 0),name='megawatts',ascii_symbol='MW',symbol='MW') +kilowatts = NamedUnit(1000.0, Dimensions(2, -3, 1, 0, 0),name='kilowatts',ascii_symbol='kW',symbol='kW') +milliwatts = NamedUnit(0.001, Dimensions(2, -3, 1, 0, 0),name='milliwatts',ascii_symbol='mW',symbol='mW') +microwatts = NamedUnit(1e-06, Dimensions(2, -3, 1, 0, 0),name='microwatts',ascii_symbol='uW',symbol='µW') +nanowatts = NamedUnit(1e-09, Dimensions(2, -3, 1, 0, 0),name='nanowatts',ascii_symbol='nW',symbol='nW') +picowatts = NamedUnit(1e-12, Dimensions(2, -3, 1, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') +femtowatts = NamedUnit(1e-15, Dimensions(2, -3, 1, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') +attowatts = NamedUnit(1e-18, Dimensions(2, -3, 1, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') +coulombs = NamedUnit(1, Dimensions(0, 1, 0, 1, 0, 0, 0),name='coulombs',ascii_symbol='C',symbol='C') +exacoulombs = NamedUnit(1e+18, Dimensions(0, 1, 0, 1, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') +petacoulombs = NamedUnit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') +teracoulombs = NamedUnit(1000000000000.0, Dimensions(0, 1, 0, 1, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') +gigacoulombs = NamedUnit(1000000000.0, Dimensions(0, 1, 0, 1, 0),name='gigacoulombs',ascii_symbol='GC',symbol='GC') +megacoulombs = NamedUnit(1000000.0, Dimensions(0, 1, 0, 1, 0),name='megacoulombs',ascii_symbol='MC',symbol='MC') +kilocoulombs = NamedUnit(1000.0, Dimensions(0, 1, 0, 1, 0),name='kilocoulombs',ascii_symbol='kC',symbol='kC') +millicoulombs = NamedUnit(0.001, Dimensions(0, 1, 0, 1, 0),name='millicoulombs',ascii_symbol='mC',symbol='mC') +microcoulombs = NamedUnit(1e-06, Dimensions(0, 1, 0, 1, 0),name='microcoulombs',ascii_symbol='uC',symbol='µC') +nanocoulombs = NamedUnit(1e-09, Dimensions(0, 1, 0, 1, 0),name='nanocoulombs',ascii_symbol='nC',symbol='nC') +picocoulombs = NamedUnit(1e-12, Dimensions(0, 1, 0, 1, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') +femtocoulombs = NamedUnit(1e-15, Dimensions(0, 1, 0, 1, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') +attocoulombs = NamedUnit(1e-18, Dimensions(0, 1, 0, 1, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') +volts = NamedUnit(1, Dimensions(2, -3, 1, -1, 0, 0, 0),name='volts',ascii_symbol='V',symbol='V') +exavolts = NamedUnit(1e+18, Dimensions(2, -3, 1, -1, 0),name='exavolts',ascii_symbol='EV',symbol='EV') +petavolts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0),name='petavolts',ascii_symbol='PV',symbol='PV') +teravolts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -1, 0),name='teravolts',ascii_symbol='TV',symbol='TV') +gigavolts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -1, 0),name='gigavolts',ascii_symbol='GV',symbol='GV') +megavolts = NamedUnit(1000000.0, Dimensions(2, -3, 1, -1, 0),name='megavolts',ascii_symbol='MV',symbol='MV') +kilovolts = NamedUnit(1000.0, Dimensions(2, -3, 1, -1, 0),name='kilovolts',ascii_symbol='kV',symbol='kV') +millivolts = NamedUnit(0.001, Dimensions(2, -3, 1, -1, 0),name='millivolts',ascii_symbol='mV',symbol='mV') +microvolts = NamedUnit(1e-06, Dimensions(2, -3, 1, -1, 0),name='microvolts',ascii_symbol='uV',symbol='µV') +nanovolts = NamedUnit(1e-09, Dimensions(2, -3, 1, -1, 0),name='nanovolts',ascii_symbol='nV',symbol='nV') +picovolts = NamedUnit(1e-12, Dimensions(2, -3, 1, -1, 0),name='picovolts',ascii_symbol='pV',symbol='pV') +femtovolts = NamedUnit(1e-15, Dimensions(2, -3, 1, -1, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') +attovolts = NamedUnit(1e-18, Dimensions(2, -3, 1, -1, 0),name='attovolts',ascii_symbol='aV',symbol='aV') +ohms = NamedUnit(1, Dimensions(2, -3, 1, -2, 0, 0, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') +exaohms = NamedUnit(1e+18, Dimensions(2, -3, 1, -2, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') +petaohms = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') +teraohms = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -2, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') +gigaohms = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -2, 0),name='gigaohms',ascii_symbol='GOhm',symbol='GΩ') +megaohms = NamedUnit(1000000.0, Dimensions(2, -3, 1, -2, 0),name='megaohms',ascii_symbol='MOhm',symbol='MΩ') +kiloohms = NamedUnit(1000.0, Dimensions(2, -3, 1, -2, 0),name='kiloohms',ascii_symbol='kOhm',symbol='kΩ') +milliohms = NamedUnit(0.001, Dimensions(2, -3, 1, -2, 0),name='milliohms',ascii_symbol='mOhm',symbol='mΩ') +microohms = NamedUnit(1e-06, Dimensions(2, -3, 1, -2, 0),name='microohms',ascii_symbol='uOhm',symbol='µΩ') +nanoohms = NamedUnit(1e-09, Dimensions(2, -3, 1, -2, 0),name='nanoohms',ascii_symbol='nOhm',symbol='nΩ') +picoohms = NamedUnit(1e-12, Dimensions(2, -3, 1, -2, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') +femtoohms = NamedUnit(1e-15, Dimensions(2, -3, 1, -2, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') +attoohms = NamedUnit(1e-18, Dimensions(2, -3, 1, -2, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') +farads = NamedUnit(1, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='farads',ascii_symbol='F',symbol='F') +exafarads = NamedUnit(1e+18, Dimensions(-2, 4, -1, 2, 0),name='exafarads',ascii_symbol='EF',symbol='EF') +petafarads = NamedUnit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='petafarads',ascii_symbol='PF',symbol='PF') +terafarads = NamedUnit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='terafarads',ascii_symbol='TF',symbol='TF') +gigafarads = NamedUnit(1000000000.0, Dimensions(-2, 4, -1, 2, 0),name='gigafarads',ascii_symbol='GF',symbol='GF') +megafarads = NamedUnit(1000000.0, Dimensions(-2, 4, -1, 2, 0),name='megafarads',ascii_symbol='MF',symbol='MF') +kilofarads = NamedUnit(1000.0, Dimensions(-2, 4, -1, 2, 0),name='kilofarads',ascii_symbol='kF',symbol='kF') +millifarads = NamedUnit(0.001, Dimensions(-2, 4, -1, 2, 0),name='millifarads',ascii_symbol='mF',symbol='mF') +microfarads = NamedUnit(1e-06, Dimensions(-2, 4, -1, 2, 0),name='microfarads',ascii_symbol='uF',symbol='µF') +nanofarads = NamedUnit(1e-09, Dimensions(-2, 4, -1, 2, 0),name='nanofarads',ascii_symbol='nF',symbol='nF') +picofarads = NamedUnit(1e-12, Dimensions(-2, 4, -1, 2, 0),name='picofarads',ascii_symbol='pF',symbol='pF') +femtofarads = NamedUnit(1e-15, Dimensions(-2, 4, -1, 2, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') +attofarads = NamedUnit(1e-18, Dimensions(-2, 4, -1, 2, 0),name='attofarads',ascii_symbol='aF',symbol='aF') +siemens = NamedUnit(1, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='siemens',ascii_symbol='S',symbol='S') +exasiemens = NamedUnit(1e+18, Dimensions(-2, 3, -1, 2, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') +petasiemens = NamedUnit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') +terasiemens = NamedUnit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') +gigasiemens = NamedUnit(1000000000.0, Dimensions(-2, 3, -1, 2, 0),name='gigasiemens',ascii_symbol='GS',symbol='GS') +megasiemens = NamedUnit(1000000.0, Dimensions(-2, 3, -1, 2, 0),name='megasiemens',ascii_symbol='MS',symbol='MS') +kilosiemens = NamedUnit(1000.0, Dimensions(-2, 3, -1, 2, 0),name='kilosiemens',ascii_symbol='kS',symbol='kS') +millisiemens = NamedUnit(0.001, Dimensions(-2, 3, -1, 2, 0),name='millisiemens',ascii_symbol='mS',symbol='mS') +microsiemens = NamedUnit(1e-06, Dimensions(-2, 3, -1, 2, 0),name='microsiemens',ascii_symbol='uS',symbol='µS') +nanosiemens = NamedUnit(1e-09, Dimensions(-2, 3, -1, 2, 0),name='nanosiemens',ascii_symbol='nS',symbol='nS') +picosiemens = NamedUnit(1e-12, Dimensions(-2, 3, -1, 2, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') +femtosiemens = NamedUnit(1e-15, Dimensions(-2, 3, -1, 2, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') +attosiemens = NamedUnit(1e-18, Dimensions(-2, 3, -1, 2, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') +webers = NamedUnit(1, Dimensions(2, -2, 1, -1, 0, 0, 0),name='webers',ascii_symbol='Wb',symbol='Wb') +exawebers = NamedUnit(1e+18, Dimensions(2, -2, 1, -1, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') +petawebers = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') +terawebers = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -1, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') +gigawebers = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -1, 0),name='gigawebers',ascii_symbol='GWb',symbol='GWb') +megawebers = NamedUnit(1000000.0, Dimensions(2, -2, 1, -1, 0),name='megawebers',ascii_symbol='MWb',symbol='MWb') +kilowebers = NamedUnit(1000.0, Dimensions(2, -2, 1, -1, 0),name='kilowebers',ascii_symbol='kWb',symbol='kWb') +milliwebers = NamedUnit(0.001, Dimensions(2, -2, 1, -1, 0),name='milliwebers',ascii_symbol='mWb',symbol='mWb') +microwebers = NamedUnit(1e-06, Dimensions(2, -2, 1, -1, 0),name='microwebers',ascii_symbol='uWb',symbol='µWb') +nanowebers = NamedUnit(1e-09, Dimensions(2, -2, 1, -1, 0),name='nanowebers',ascii_symbol='nWb',symbol='nWb') +picowebers = NamedUnit(1e-12, Dimensions(2, -2, 1, -1, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') +femtowebers = NamedUnit(1e-15, Dimensions(2, -2, 1, -1, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') +attowebers = NamedUnit(1e-18, Dimensions(2, -2, 1, -1, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') +tesla = NamedUnit(1, Dimensions(0, -2, 1, -1, 0, 0, 0),name='tesla',ascii_symbol='T',symbol='T') +exatesla = NamedUnit(1e+18, Dimensions(0, -2, 1, -1, 0),name='exatesla',ascii_symbol='ET',symbol='ET') +petatesla = NamedUnit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0),name='petatesla',ascii_symbol='PT',symbol='PT') +teratesla = NamedUnit(1000000000000.0, Dimensions(0, -2, 1, -1, 0),name='teratesla',ascii_symbol='TT',symbol='TT') +gigatesla = NamedUnit(1000000000.0, Dimensions(0, -2, 1, -1, 0),name='gigatesla',ascii_symbol='GT',symbol='GT') +megatesla = NamedUnit(1000000.0, Dimensions(0, -2, 1, -1, 0),name='megatesla',ascii_symbol='MT',symbol='MT') +kilotesla = NamedUnit(1000.0, Dimensions(0, -2, 1, -1, 0),name='kilotesla',ascii_symbol='kT',symbol='kT') +millitesla = NamedUnit(0.001, Dimensions(0, -2, 1, -1, 0),name='millitesla',ascii_symbol='mT',symbol='mT') +microtesla = NamedUnit(1e-06, Dimensions(0, -2, 1, -1, 0),name='microtesla',ascii_symbol='uT',symbol='µT') +nanotesla = NamedUnit(1e-09, Dimensions(0, -2, 1, -1, 0),name='nanotesla',ascii_symbol='nT',symbol='nT') +picotesla = NamedUnit(1e-12, Dimensions(0, -2, 1, -1, 0),name='picotesla',ascii_symbol='pT',symbol='pT') +femtotesla = NamedUnit(1e-15, Dimensions(0, -2, 1, -1, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') +attotesla = NamedUnit(1e-18, Dimensions(0, -2, 1, -1, 0),name='attotesla',ascii_symbol='aT',symbol='aT') +henry = NamedUnit(1, Dimensions(2, -2, 1, -2, 0, 0, 0),name='henry',ascii_symbol='H',symbol='H') +exahenry = NamedUnit(1e+18, Dimensions(2, -2, 1, -2, 0),name='exahenry',ascii_symbol='EH',symbol='EH') +petahenry = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0),name='petahenry',ascii_symbol='PH',symbol='PH') +terahenry = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -2, 0),name='terahenry',ascii_symbol='TH',symbol='TH') +gigahenry = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -2, 0),name='gigahenry',ascii_symbol='GH',symbol='GH') +megahenry = NamedUnit(1000000.0, Dimensions(2, -2, 1, -2, 0),name='megahenry',ascii_symbol='MH',symbol='MH') +kilohenry = NamedUnit(1000.0, Dimensions(2, -2, 1, -2, 0),name='kilohenry',ascii_symbol='kH',symbol='kH') +millihenry = NamedUnit(0.001, Dimensions(2, -2, 1, -2, 0),name='millihenry',ascii_symbol='mH',symbol='mH') +microhenry = NamedUnit(1e-06, Dimensions(2, -2, 1, -2, 0),name='microhenry',ascii_symbol='uH',symbol='µH') +nanohenry = NamedUnit(1e-09, Dimensions(2, -2, 1, -2, 0),name='nanohenry',ascii_symbol='nH',symbol='nH') +picohenry = NamedUnit(1e-12, Dimensions(2, -2, 1, -2, 0),name='picohenry',ascii_symbol='pH',symbol='pH') +femtohenry = NamedUnit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') +attohenry = NamedUnit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') +angstroms = NamedUnit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') +minutes = NamedUnit(60, Dimensions(0, 1, 0, 0, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') +hours = NamedUnit(360, Dimensions(0, 1, 0, 0, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') +days = NamedUnit(8640, Dimensions(0, 1, 0, 0, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') +years = NamedUnit(3155695.2, Dimensions(0, 1, 0, 0, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') +degrees = NamedUnit(57.29577951308232, Dimensions(0, 0, 0, 0, 0, 0, 1),name='degrees',ascii_symbol='deg',symbol='deg') +radians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 1),name='radians',ascii_symbol='rad',symbol='rad') +stradians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') +none = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') +litres = NamedUnit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') +electronvolts = NamedUnit(1.602176634e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') +exaelectronvolts = NamedUnit(0.1602176634, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') +petaelectronvolts = NamedUnit(0.0001602176634, Dimensions(2, -2, 1, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') +teraelectronvolts = NamedUnit(1.602176634e-07, Dimensions(2, -2, 1, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') +gigaelectronvolts = NamedUnit(1.6021766339999998e-10, Dimensions(2, -2, 1, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') +megaelectronvolts = NamedUnit(1.6021766339999998e-13, Dimensions(2, -2, 1, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') +kiloelectronvolts = NamedUnit(1.602176634e-16, Dimensions(2, -2, 1, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') +millielectronvolts = NamedUnit(1.6021766339999998e-22, Dimensions(2, -2, 1, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') +microelectronvolts = NamedUnit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') +nanoelectronvolts = NamedUnit(1.602176634e-28, Dimensions(2, -2, 1, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') +picoelectronvolts = NamedUnit(1.6021766339999998e-31, Dimensions(2, -2, 1, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') +femtoelectronvolts = NamedUnit(1.602176634e-34, Dimensions(2, -2, 1, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') +attoelectronvolts = NamedUnit(1.602176634e-37, Dimensions(2, -2, 1, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') +atomic_mass_units = NamedUnit(1.660538921e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') +moles = NamedUnit(6.02214076e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') +millimoles = NamedUnit(6.02214076e+20, Dimensions(0, 0, 0, 0, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') +micromoles = NamedUnit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') +nanomoles = NamedUnit(602214076000000.0, Dimensions(0, 0, 0, 0, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') +picomoles = NamedUnit(602214076000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') +femtomoles = NamedUnit(602214076.0, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') +attomoles = NamedUnit(602214.076, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') +kg_force = NamedUnit(9.80665, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kg_force',ascii_symbol='kgForce',symbol='kgForce') +degrees_celsius = NamedUnit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') +miles = NamedUnit(1609.344, Dimensions(1, 0, 0, 0, 0, 0, 0),name='miles',ascii_symbol='miles',symbol='miles') +yards = NamedUnit(0.9144000000000001, Dimensions(1, 0, 0, 0, 0, 0, 0),name='yards',ascii_symbol='yrd',symbol='yrd') +feet = NamedUnit(0.3048, Dimensions(1, 0, 0, 0, 0, 0, 0),name='feet',ascii_symbol='ft',symbol='ft') +inches = NamedUnit(0.0254, Dimensions(1, 0, 0, 0, 0, 0, 0),name='inches',ascii_symbol='in',symbol='in') +pounds = NamedUnit(0.45359237, Dimensions(0, 0, 1, 0, 0, 0, 0),name='pounds',ascii_symbol='lb',symbol='lb') +ounces = NamedUnit(0.028349523125, Dimensions(0, 0, 1, 0, 0, 0, 0),name='ounces',ascii_symbol='oz',symbol='oz') +pound_force_per_square_inch = NamedUnit(6894.757, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pound_force_per_square_inch',ascii_symbol='psi',symbol='psi') +square_meters = NamedUnit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') +cubic_meters = NamedUnit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') +per_meter = NamedUnit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') +per_square_meter = NamedUnit(1.0, Dimensions(length=-2), name='per_square_meter', ascii_symbol='m^-2', symbol='m⁻²') +per_cubic_meter = NamedUnit(1.0, Dimensions(length=-3), name='per_cubic_meter', ascii_symbol='m^-3', symbol='m⁻³') +square_exameters = NamedUnit(1e+36, Dimensions(length=2), name='square_exameters', ascii_symbol='Em^2', symbol='Em²') +cubic_exameters = NamedUnit(1e+54, Dimensions(length=3), name='cubic_exameters', ascii_symbol='Em^3', symbol='Em³') +per_exameter = NamedUnit(1e-18, Dimensions(length=-1), name='per_exameter', ascii_symbol='Em^-1', symbol='Em⁻¹') +per_square_exameter = NamedUnit(1e-36, Dimensions(length=-2), name='per_square_exameter', ascii_symbol='Em^-2', symbol='Em⁻²') +per_cubic_exameter = NamedUnit(1e-54, Dimensions(length=-3), name='per_cubic_exameter', ascii_symbol='Em^-3', symbol='Em⁻³') +square_petameters = NamedUnit(1e+30, Dimensions(length=2), name='square_petameters', ascii_symbol='Pm^2', symbol='Pm²') +cubic_petameters = NamedUnit(1e+45, Dimensions(length=3), name='cubic_petameters', ascii_symbol='Pm^3', symbol='Pm³') +per_petameter = NamedUnit(1e-15, Dimensions(length=-1), name='per_petameter', ascii_symbol='Pm^-1', symbol='Pm⁻¹') +per_square_petameter = NamedUnit(1e-30, Dimensions(length=-2), name='per_square_petameter', ascii_symbol='Pm^-2', symbol='Pm⁻²') +per_cubic_petameter = NamedUnit(1e-45, Dimensions(length=-3), name='per_cubic_petameter', ascii_symbol='Pm^-3', symbol='Pm⁻³') +square_terameters = NamedUnit(1e+24, Dimensions(length=2), name='square_terameters', ascii_symbol='Tm^2', symbol='Tm²') +cubic_terameters = NamedUnit(1e+36, Dimensions(length=3), name='cubic_terameters', ascii_symbol='Tm^3', symbol='Tm³') +per_terameter = NamedUnit(1e-12, Dimensions(length=-1), name='per_terameter', ascii_symbol='Tm^-1', symbol='Tm⁻¹') +per_square_terameter = NamedUnit(1e-24, Dimensions(length=-2), name='per_square_terameter', ascii_symbol='Tm^-2', symbol='Tm⁻²') +per_cubic_terameter = NamedUnit(1e-36, Dimensions(length=-3), name='per_cubic_terameter', ascii_symbol='Tm^-3', symbol='Tm⁻³') +square_gigameters = NamedUnit(1e+18, Dimensions(length=2), name='square_gigameters', ascii_symbol='Gm^2', symbol='Gm²') +cubic_gigameters = NamedUnit(1e+27, Dimensions(length=3), name='cubic_gigameters', ascii_symbol='Gm^3', symbol='Gm³') +per_gigameter = NamedUnit(1e-09, Dimensions(length=-1), name='per_gigameter', ascii_symbol='Gm^-1', symbol='Gm⁻¹') +per_square_gigameter = NamedUnit(1e-18, Dimensions(length=-2), name='per_square_gigameter', ascii_symbol='Gm^-2', symbol='Gm⁻²') +per_cubic_gigameter = NamedUnit(1e-27, Dimensions(length=-3), name='per_cubic_gigameter', ascii_symbol='Gm^-3', symbol='Gm⁻³') +square_megameters = NamedUnit(1000000000000.0, Dimensions(length=2), name='square_megameters', ascii_symbol='Mm^2', symbol='Mm²') +cubic_megameters = NamedUnit(1e+18, Dimensions(length=3), name='cubic_megameters', ascii_symbol='Mm^3', symbol='Mm³') +per_megameter = NamedUnit(1e-06, Dimensions(length=-1), name='per_megameter', ascii_symbol='Mm^-1', symbol='Mm⁻¹') +per_square_megameter = NamedUnit(1e-12, Dimensions(length=-2), name='per_square_megameter', ascii_symbol='Mm^-2', symbol='Mm⁻²') +per_cubic_megameter = NamedUnit(1e-18, Dimensions(length=-3), name='per_cubic_megameter', ascii_symbol='Mm^-3', symbol='Mm⁻³') +square_kilometers = NamedUnit(1000000.0, Dimensions(length=2), name='square_kilometers', ascii_symbol='km^2', symbol='km²') +cubic_kilometers = NamedUnit(1000000000.0, Dimensions(length=3), name='cubic_kilometers', ascii_symbol='km^3', symbol='km³') +per_kilometer = NamedUnit(0.001, Dimensions(length=-1), name='per_kilometer', ascii_symbol='km^-1', symbol='km⁻¹') +per_square_kilometer = NamedUnit(1e-06, Dimensions(length=-2), name='per_square_kilometer', ascii_symbol='km^-2', symbol='km⁻²') +per_cubic_kilometer = NamedUnit(1e-09, Dimensions(length=-3), name='per_cubic_kilometer', ascii_symbol='km^-3', symbol='km⁻³') +square_millimeters = NamedUnit(1e-06, Dimensions(length=2), name='square_millimeters', ascii_symbol='mm^2', symbol='mm²') +cubic_millimeters = NamedUnit(1e-09, Dimensions(length=3), name='cubic_millimeters', ascii_symbol='mm^3', symbol='mm³') +per_millimeter = NamedUnit(1000.0, Dimensions(length=-1), name='per_millimeter', ascii_symbol='mm^-1', symbol='mm⁻¹') +per_square_millimeter = NamedUnit(1000000.0, Dimensions(length=-2), name='per_square_millimeter', ascii_symbol='mm^-2', symbol='mm⁻²') +per_cubic_millimeter = NamedUnit(999999999.9999999, Dimensions(length=-3), name='per_cubic_millimeter', ascii_symbol='mm^-3', symbol='mm⁻³') +square_micrometers = NamedUnit(1e-12, Dimensions(length=2), name='square_micrometers', ascii_symbol='um^2', symbol='µm²') +cubic_micrometers = NamedUnit(9.999999999999999e-19, Dimensions(length=3), name='cubic_micrometers', ascii_symbol='um^3', symbol='µm³') +per_micrometer = NamedUnit(1000000.0, Dimensions(length=-1), name='per_micrometer', ascii_symbol='um^-1', symbol='µm⁻¹') +per_square_micrometer = NamedUnit(1000000000000.0001, Dimensions(length=-2), name='per_square_micrometer', ascii_symbol='um^-2', symbol='µm⁻²') +per_cubic_micrometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3), name='per_cubic_micrometer', ascii_symbol='um^-3', symbol='µm⁻³') +square_nanometers = NamedUnit(1e-18, Dimensions(length=2), name='square_nanometers', ascii_symbol='nm^2', symbol='nm²') +cubic_nanometers = NamedUnit(1.0000000000000002e-27, Dimensions(length=3), name='cubic_nanometers', ascii_symbol='nm^3', symbol='nm³') +per_nanometer = NamedUnit(999999999.9999999, Dimensions(length=-1), name='per_nanometer', ascii_symbol='nm^-1', symbol='nm⁻¹') +per_square_nanometer = NamedUnit(9.999999999999999e+17, Dimensions(length=-2), name='per_square_nanometer', ascii_symbol='nm^-2', symbol='nm⁻²') +per_cubic_nanometer = NamedUnit(9.999999999999999e+26, Dimensions(length=-3), name='per_cubic_nanometer', ascii_symbol='nm^-3', symbol='nm⁻³') +square_picometers = NamedUnit(1e-24, Dimensions(length=2), name='square_picometers', ascii_symbol='pm^2', symbol='pm²') +cubic_picometers = NamedUnit(1e-36, Dimensions(length=3), name='cubic_picometers', ascii_symbol='pm^3', symbol='pm³') +per_picometer = NamedUnit(1000000000000.0, Dimensions(length=-1), name='per_picometer', ascii_symbol='pm^-1', symbol='pm⁻¹') +per_square_picometer = NamedUnit(1e+24, Dimensions(length=-2), name='per_square_picometer', ascii_symbol='pm^-2', symbol='pm⁻²') +per_cubic_picometer = NamedUnit(1e+36, Dimensions(length=-3), name='per_cubic_picometer', ascii_symbol='pm^-3', symbol='pm⁻³') +square_femtometers = NamedUnit(1e-30, Dimensions(length=2), name='square_femtometers', ascii_symbol='fm^2', symbol='fm²') +cubic_femtometers = NamedUnit(1.0000000000000003e-45, Dimensions(length=3), name='cubic_femtometers', ascii_symbol='fm^3', symbol='fm³') +per_femtometer = NamedUnit(999999999999999.9, Dimensions(length=-1), name='per_femtometer', ascii_symbol='fm^-1', symbol='fm⁻¹') +per_square_femtometer = NamedUnit(9.999999999999999e+29, Dimensions(length=-2), name='per_square_femtometer', ascii_symbol='fm^-2', symbol='fm⁻²') +per_cubic_femtometer = NamedUnit(9.999999999999998e+44, Dimensions(length=-3), name='per_cubic_femtometer', ascii_symbol='fm^-3', symbol='fm⁻³') +square_attometers = NamedUnit(1.0000000000000001e-36, Dimensions(length=2), name='square_attometers', ascii_symbol='am^2', symbol='am²') +cubic_attometers = NamedUnit(1.0000000000000002e-54, Dimensions(length=3), name='cubic_attometers', ascii_symbol='am^3', symbol='am³') +per_attometer = NamedUnit(9.999999999999999e+17, Dimensions(length=-1), name='per_attometer', ascii_symbol='am^-1', symbol='am⁻¹') +per_square_attometer = NamedUnit(9.999999999999999e+35, Dimensions(length=-2), name='per_square_attometer', ascii_symbol='am^-2', symbol='am⁻²') +per_cubic_attometer = NamedUnit(9.999999999999997e+53, Dimensions(length=-3), name='per_cubic_attometer', ascii_symbol='am^-3', symbol='am⁻³') +square_decimeters = NamedUnit(0.010000000000000002, Dimensions(length=2), name='square_decimeters', ascii_symbol='dm^2', symbol='dm²') +cubic_decimeters = NamedUnit(0.0010000000000000002, Dimensions(length=3), name='cubic_decimeters', ascii_symbol='dm^3', symbol='dm³') +per_decimeter = NamedUnit(10.0, Dimensions(length=-1), name='per_decimeter', ascii_symbol='dm^-1', symbol='dm⁻¹') +per_square_decimeter = NamedUnit(99.99999999999999, Dimensions(length=-2), name='per_square_decimeter', ascii_symbol='dm^-2', symbol='dm⁻²') +per_cubic_decimeter = NamedUnit(999.9999999999999, Dimensions(length=-3), name='per_cubic_decimeter', ascii_symbol='dm^-3', symbol='dm⁻³') +square_centimeters = NamedUnit(0.0001, Dimensions(length=2), name='square_centimeters', ascii_symbol='cm^2', symbol='cm²') +cubic_centimeters = NamedUnit(1.0000000000000002e-06, Dimensions(length=3), name='cubic_centimeters', ascii_symbol='cm^3', symbol='cm³') +per_centimeter = NamedUnit(100.0, Dimensions(length=-1), name='per_centimeter', ascii_symbol='cm^-1', symbol='cm⁻¹') +per_square_centimeter = NamedUnit(10000.0, Dimensions(length=-2), name='per_square_centimeter', ascii_symbol='cm^-2', symbol='cm⁻²') +per_cubic_centimeter = NamedUnit(999999.9999999999, Dimensions(length=-3), name='per_cubic_centimeter', ascii_symbol='cm^-3', symbol='cm⁻³') +square_angstroms = NamedUnit(1.0000000000000001e-20, Dimensions(length=2), name='square_angstroms', ascii_symbol='Ang^2', symbol='Ų') +cubic_angstroms = NamedUnit(1e-30, Dimensions(length=3), name='cubic_angstroms', ascii_symbol='Ang^3', symbol='ų') +per_angstrom = NamedUnit(10000000000.0, Dimensions(length=-1), name='per_angstrom', ascii_symbol='Ang^-1', symbol='Å⁻¹') +per_square_angstrom = NamedUnit(1e+20, Dimensions(length=-2), name='per_square_angstrom', ascii_symbol='Ang^-2', symbol='Å⁻²') +per_cubic_angstrom = NamedUnit(9.999999999999999e+29, Dimensions(length=-3), name='per_cubic_angstrom', ascii_symbol='Ang^-3', symbol='Å⁻³') +square_miles = NamedUnit(2589988.110336, Dimensions(length=2), name='square_miles', ascii_symbol='miles^2', symbol='miles²') +cubic_miles = NamedUnit(4168181825.44058, Dimensions(length=3), name='cubic_miles', ascii_symbol='miles^3', symbol='miles³') +per_mile = NamedUnit(0.0006213711922373339, Dimensions(length=-1), name='per_mile', ascii_symbol='miles^-1', symbol='miles⁻¹') +per_square_mile = NamedUnit(3.861021585424458e-07, Dimensions(length=-2), name='per_square_mile', ascii_symbol='miles^-2', symbol='miles⁻²') +per_cubic_mile = NamedUnit(2.399127585789277e-10, Dimensions(length=-3), name='per_cubic_mile', ascii_symbol='miles^-3', symbol='miles⁻³') +square_yards = NamedUnit(0.8361273600000002, Dimensions(length=2), name='square_yards', ascii_symbol='yrd^2', symbol='yrd²') +cubic_yards = NamedUnit(0.7645548579840002, Dimensions(length=3), name='cubic_yards', ascii_symbol='yrd^3', symbol='yrd³') +per_yard = NamedUnit(1.0936132983377076, Dimensions(length=-1), name='per_yard', ascii_symbol='yrd^-1', symbol='yrd⁻¹') +per_square_yard = NamedUnit(1.19599004630108, Dimensions(length=-2), name='per_square_yard', ascii_symbol='yrd^-2', symbol='yrd⁻²') +per_cubic_yard = NamedUnit(1.3079506193143917, Dimensions(length=-3), name='per_cubic_yard', ascii_symbol='yrd^-3', symbol='yrd⁻³') +square_feet = NamedUnit(0.09290304, Dimensions(length=2), name='square_feet', ascii_symbol='ft^2', symbol='ft²') +cubic_feet = NamedUnit(0.028316846592000004, Dimensions(length=3), name='cubic_feet', ascii_symbol='ft^3', symbol='ft³') +per_foot = NamedUnit(3.280839895013123, Dimensions(length=-1), name='per_foot', ascii_symbol='ft^-1', symbol='ft⁻¹') +per_square_foot = NamedUnit(10.763910416709722, Dimensions(length=-2), name='per_square_foot', ascii_symbol='ft^-2', symbol='ft⁻²') +per_cubic_foot = NamedUnit(35.314666721488585, Dimensions(length=-3), name='per_cubic_foot', ascii_symbol='ft^-3', symbol='ft⁻³') +square_inches = NamedUnit(0.00064516, Dimensions(length=2), name='square_inches', ascii_symbol='in^2', symbol='in²') +cubic_inches = NamedUnit(1.6387064e-05, Dimensions(length=3), name='cubic_inches', ascii_symbol='in^3', symbol='in³') +per_inch = NamedUnit(39.37007874015748, Dimensions(length=-1), name='per_inch', ascii_symbol='in^-1', symbol='in⁻¹') +per_square_inch = NamedUnit(1550.0031000062002, Dimensions(length=-2), name='per_square_inch', ascii_symbol='in^-2', symbol='in⁻²') +per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3), name='per_cubic_inch', ascii_symbol='in^-3', symbol='in⁻³') +meters_per_second = NamedUnit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') +meters_per_square_second = NamedUnit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') +meters_per_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') +meters_per_square_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') +meters_per_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') +meters_per_square_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') +meters_per_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') +meters_per_square_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') +meters_per_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') +meters_per_square_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') +meters_per_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') +meters_per_square_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') +meters_per_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') +meters_per_square_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') +meters_per_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') +meters_per_square_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') +meters_per_hour = NamedUnit(0.002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') +meters_per_square_hour = NamedUnit(0.002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') +meters_per_day = NamedUnit(0.00011574074074074075, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') +meters_per_square_day = NamedUnit(0.00011574074074074075, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') +meters_per_year = NamedUnit(3.168873850681143e-07, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') +meters_per_square_year = NamedUnit(3.168873850681143e-07, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') +exameters_per_second = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') +exameters_per_square_second = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') +exameters_per_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') +exameters_per_square_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') +exameters_per_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') +exameters_per_square_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') +exameters_per_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') +exameters_per_square_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') +exameters_per_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-1), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') +exameters_per_square_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-2), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') +exameters_per_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') +exameters_per_square_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') +exameters_per_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-1), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') +exameters_per_square_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') +exameters_per_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') +exameters_per_square_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') +exameters_per_hour = NamedUnit(2777777777777778.0, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') +exameters_per_square_hour = NamedUnit(2777777777777778.0, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') +exameters_per_day = NamedUnit(115740740740740.73, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') +exameters_per_square_day = NamedUnit(115740740740740.73, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') +exameters_per_year = NamedUnit(316887385068.1143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') +exameters_per_square_year = NamedUnit(316887385068.1143, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') +petameters_per_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') +petameters_per_square_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') +petameters_per_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') +petameters_per_square_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') +petameters_per_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') +petameters_per_square_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') +petameters_per_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') +petameters_per_square_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') +petameters_per_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-1), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') +petameters_per_square_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') +petameters_per_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') +petameters_per_square_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') +petameters_per_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') +petameters_per_square_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') +petameters_per_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') +petameters_per_square_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') +petameters_per_hour = NamedUnit(2777777777777.778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') +petameters_per_square_hour = NamedUnit(2777777777777.778, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') +petameters_per_day = NamedUnit(115740740740.74074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') +petameters_per_square_day = NamedUnit(115740740740.74074, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') +petameters_per_year = NamedUnit(316887385.0681143, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') +petameters_per_square_year = NamedUnit(316887385.0681143, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') +terameters_per_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') +terameters_per_square_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') +terameters_per_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') +terameters_per_square_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') +terameters_per_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') +terameters_per_square_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') +terameters_per_nanosecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') +terameters_per_square_nanosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') +terameters_per_picosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') +terameters_per_square_picosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') +terameters_per_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') +terameters_per_square_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') +terameters_per_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') +terameters_per_square_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') +terameters_per_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') +terameters_per_square_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') +terameters_per_hour = NamedUnit(2777777777.7777777, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') +terameters_per_square_hour = NamedUnit(2777777777.7777777, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') +terameters_per_day = NamedUnit(115740740.74074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') +terameters_per_square_day = NamedUnit(115740740.74074075, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') +terameters_per_year = NamedUnit(316887.38506811426, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') +terameters_per_square_year = NamedUnit(316887.38506811426, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') +gigameters_per_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') +gigameters_per_square_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') +gigameters_per_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') +gigameters_per_square_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') +gigameters_per_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') +gigameters_per_square_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') +gigameters_per_nanosecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') +gigameters_per_square_nanosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') +gigameters_per_picosecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') +gigameters_per_square_picosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') +gigameters_per_femtosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') +gigameters_per_square_femtosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') +gigameters_per_attosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') +gigameters_per_square_attosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') +gigameters_per_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') +gigameters_per_square_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') +gigameters_per_hour = NamedUnit(2777777.777777778, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') +gigameters_per_square_hour = NamedUnit(2777777.777777778, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') +gigameters_per_day = NamedUnit(115740.74074074074, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') +gigameters_per_square_day = NamedUnit(115740.74074074074, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') +gigameters_per_year = NamedUnit(316.88738506811427, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') +gigameters_per_square_year = NamedUnit(316.88738506811427, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') +megameters_per_second = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') +megameters_per_square_second = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') +megameters_per_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') +megameters_per_square_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') +megameters_per_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') +megameters_per_square_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') +megameters_per_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') +megameters_per_square_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') +megameters_per_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') +megameters_per_square_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') +megameters_per_femtosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') +megameters_per_square_femtosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') +megameters_per_attosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') +megameters_per_square_attosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') +megameters_per_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') +megameters_per_square_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') +megameters_per_hour = NamedUnit(2777.777777777778, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') +megameters_per_square_hour = NamedUnit(2777.777777777778, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') +megameters_per_day = NamedUnit(115.74074074074075, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') +megameters_per_square_day = NamedUnit(115.74074074074075, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') +megameters_per_year = NamedUnit(0.3168873850681143, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') +megameters_per_square_year = NamedUnit(0.3168873850681143, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') +kilometers_per_second = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') +kilometers_per_square_second = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') +kilometers_per_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') +kilometers_per_square_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') +kilometers_per_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') +kilometers_per_square_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') +kilometers_per_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') +kilometers_per_square_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-2), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') +kilometers_per_picosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') +kilometers_per_square_picosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') +kilometers_per_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') +kilometers_per_square_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') +kilometers_per_attosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') +kilometers_per_square_attosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') +kilometers_per_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') +kilometers_per_square_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') +kilometers_per_hour = NamedUnit(2.7777777777777777, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') +kilometers_per_square_hour = NamedUnit(2.7777777777777777, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') +kilometers_per_day = NamedUnit(0.11574074074074074, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') +kilometers_per_square_day = NamedUnit(0.11574074074074074, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') +kilometers_per_year = NamedUnit(0.0003168873850681143, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') +kilometers_per_square_year = NamedUnit(0.0003168873850681143, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') +millimeters_per_second = NamedUnit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') +millimeters_per_square_second = NamedUnit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') +millimeters_per_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') +millimeters_per_square_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') +millimeters_per_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') +millimeters_per_square_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') +millimeters_per_nanosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') +millimeters_per_square_nanosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') +millimeters_per_picosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') +millimeters_per_square_picosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') +millimeters_per_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') +millimeters_per_square_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') +millimeters_per_attosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') +millimeters_per_square_attosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') +millimeters_per_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') +millimeters_per_square_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') +millimeters_per_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') +millimeters_per_square_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') +millimeters_per_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') +millimeters_per_square_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') +millimeters_per_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') +millimeters_per_square_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') +micrometers_per_second = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') +micrometers_per_square_second = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') +micrometers_per_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') +micrometers_per_square_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') +micrometers_per_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') +micrometers_per_square_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') +micrometers_per_nanosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-1), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') +micrometers_per_square_nanosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-2), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') +micrometers_per_picosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') +micrometers_per_square_picosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') +micrometers_per_femtosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') +micrometers_per_square_femtosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') +micrometers_per_attosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') +micrometers_per_square_attosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') +micrometers_per_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') +micrometers_per_square_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') +micrometers_per_hour = NamedUnit(2.7777777777777776e-09, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') +micrometers_per_square_hour = NamedUnit(2.7777777777777776e-09, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') +micrometers_per_day = NamedUnit(1.1574074074074074e-10, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') +micrometers_per_square_day = NamedUnit(1.1574074074074074e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') +micrometers_per_year = NamedUnit(3.168873850681143e-13, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') +micrometers_per_square_year = NamedUnit(3.168873850681143e-13, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') +nanometers_per_second = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') +nanometers_per_square_second = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') +nanometers_per_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') +nanometers_per_square_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') +nanometers_per_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') +nanometers_per_square_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') +nanometers_per_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') +nanometers_per_square_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') +nanometers_per_picosecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') +nanometers_per_square_picosecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') +nanometers_per_femtosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') +nanometers_per_square_femtosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') +nanometers_per_attosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') +nanometers_per_square_attosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') +nanometers_per_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') +nanometers_per_square_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') +nanometers_per_hour = NamedUnit(2.777777777777778e-12, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') +nanometers_per_square_hour = NamedUnit(2.777777777777778e-12, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') +nanometers_per_day = NamedUnit(1.1574074074074076e-13, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') +nanometers_per_square_day = NamedUnit(1.1574074074074076e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') +nanometers_per_year = NamedUnit(3.1688738506811433e-16, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') +nanometers_per_square_year = NamedUnit(3.1688738506811433e-16, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') +picometers_per_second = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') +picometers_per_square_second = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') +picometers_per_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') +picometers_per_square_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') +picometers_per_microsecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') +picometers_per_square_microsecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') +picometers_per_nanosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') +picometers_per_square_nanosecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') +picometers_per_picosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') +picometers_per_square_picosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') +picometers_per_femtosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-1), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') +picometers_per_square_femtosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') +picometers_per_attosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-1), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') +picometers_per_square_attosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') +picometers_per_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') +picometers_per_square_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') +picometers_per_hour = NamedUnit(2.7777777777777776e-15, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') +picometers_per_square_hour = NamedUnit(2.7777777777777776e-15, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') +picometers_per_day = NamedUnit(1.1574074074074073e-16, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') +picometers_per_square_day = NamedUnit(1.1574074074074073e-16, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') +picometers_per_year = NamedUnit(3.168873850681143e-19, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') +picometers_per_square_year = NamedUnit(3.168873850681143e-19, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') +femtometers_per_second = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') +femtometers_per_square_second = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') +femtometers_per_millisecond = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') +femtometers_per_square_millisecond = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') +femtometers_per_microsecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') +femtometers_per_square_microsecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') +femtometers_per_nanosecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') +femtometers_per_square_nanosecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') +femtometers_per_picosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') +femtometers_per_square_picosecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') +femtometers_per_femtosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') +femtometers_per_square_femtosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') +femtometers_per_attosecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') +femtometers_per_square_attosecond = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') +femtometers_per_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') +femtometers_per_square_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') +femtometers_per_hour = NamedUnit(2.777777777777778e-18, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') +femtometers_per_square_hour = NamedUnit(2.777777777777778e-18, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') +femtometers_per_day = NamedUnit(1.1574074074074075e-19, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') +femtometers_per_square_day = NamedUnit(1.1574074074074075e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') +femtometers_per_year = NamedUnit(3.168873850681143e-22, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') +femtometers_per_square_year = NamedUnit(3.168873850681143e-22, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') +attometers_per_second = NamedUnit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') +attometers_per_square_second = NamedUnit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') +attometers_per_millisecond = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') +attometers_per_square_millisecond = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') +attometers_per_microsecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-1), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') +attometers_per_square_microsecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') +attometers_per_nanosecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') +attometers_per_square_nanosecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') +attometers_per_picosecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-1), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') +attometers_per_square_picosecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-2), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') +attometers_per_femtosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') +attometers_per_square_femtosecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') +attometers_per_attosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') +attometers_per_square_attosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') +attometers_per_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') +attometers_per_square_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') +attometers_per_hour = NamedUnit(2.7777777777777778e-21, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') +attometers_per_square_hour = NamedUnit(2.7777777777777778e-21, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') +attometers_per_day = NamedUnit(1.1574074074074074e-22, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') +attometers_per_square_day = NamedUnit(1.1574074074074074e-22, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') +attometers_per_year = NamedUnit(3.1688738506811433e-25, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') +attometers_per_square_year = NamedUnit(3.1688738506811433e-25, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') +decimeters_per_second = NamedUnit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dmNone⁻¹') +decimeters_per_square_second = NamedUnit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dmNone⁻²') +decimeters_per_millisecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='decimeters_per_millisecond', ascii_symbol='dm/ms', symbol='dmms⁻¹') +decimeters_per_square_millisecond = NamedUnit(100.0, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') +decimeters_per_microsecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-1), name='decimeters_per_microsecond', ascii_symbol='dm/us', symbol='dmµs⁻¹') +decimeters_per_square_microsecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_microsecond', ascii_symbol='dm/us^2', symbol='dmµs⁻²') +decimeters_per_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='decimeters_per_nanosecond', ascii_symbol='dm/ns', symbol='dmns⁻¹') +decimeters_per_square_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_nanosecond', ascii_symbol='dm/ns^2', symbol='dmns⁻²') +decimeters_per_picosecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_picosecond', ascii_symbol='dm/ps', symbol='dmps⁻¹') +decimeters_per_square_picosecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_picosecond', ascii_symbol='dm/ps^2', symbol='dmps⁻²') +decimeters_per_femtosecond = NamedUnit(100000000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_femtosecond', ascii_symbol='dm/fs', symbol='dmfs⁻¹') +decimeters_per_square_femtosecond = NamedUnit(100000000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') +decimeters_per_attosecond = NamedUnit(1e+17, Dimensions(length=1, time=-1), name='decimeters_per_attosecond', ascii_symbol='dm/as', symbol='dmas⁻¹') +decimeters_per_square_attosecond = NamedUnit(1e+17, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') +decimeters_per_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmNone⁻¹') +decimeters_per_square_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmNone⁻²') +decimeters_per_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmNone⁻¹') +decimeters_per_square_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmNone⁻²') +decimeters_per_day = NamedUnit(1.1574074074074075e-05, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmNone⁻¹') +decimeters_per_square_day = NamedUnit(1.1574074074074075e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmNone⁻²') +decimeters_per_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmNone⁻¹') +decimeters_per_square_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmNone⁻²') +centimeters_per_second = NamedUnit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cmNone⁻¹') +centimeters_per_square_second = NamedUnit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cmNone⁻²') +centimeters_per_millisecond = NamedUnit(10.0, Dimensions(length=1, time=-1), name='centimeters_per_millisecond', ascii_symbol='cm/ms', symbol='cmms⁻¹') +centimeters_per_square_millisecond = NamedUnit(10.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') +centimeters_per_microsecond = NamedUnit(10000.0, Dimensions(length=1, time=-1), name='centimeters_per_microsecond', ascii_symbol='cm/us', symbol='cmµs⁻¹') +centimeters_per_square_microsecond = NamedUnit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_microsecond', ascii_symbol='cm/us^2', symbol='cmµs⁻²') +centimeters_per_nanosecond = NamedUnit(10000000.0, Dimensions(length=1, time=-1), name='centimeters_per_nanosecond', ascii_symbol='cm/ns', symbol='cmns⁻¹') +centimeters_per_square_nanosecond = NamedUnit(10000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_nanosecond', ascii_symbol='cm/ns^2', symbol='cmns⁻²') +centimeters_per_picosecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_picosecond', ascii_symbol='cm/ps', symbol='cmps⁻¹') +centimeters_per_square_picosecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_picosecond', ascii_symbol='cm/ps^2', symbol='cmps⁻²') +centimeters_per_femtosecond = NamedUnit(10000000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_femtosecond', ascii_symbol='cm/fs', symbol='cmfs⁻¹') +centimeters_per_square_femtosecond = NamedUnit(10000000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') +centimeters_per_attosecond = NamedUnit(1e+16, Dimensions(length=1, time=-1), name='centimeters_per_attosecond', ascii_symbol='cm/as', symbol='cmas⁻¹') +centimeters_per_square_attosecond = NamedUnit(1e+16, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') +centimeters_per_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmNone⁻¹') +centimeters_per_square_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmNone⁻²') +centimeters_per_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmNone⁻¹') +centimeters_per_square_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmNone⁻²') +centimeters_per_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmNone⁻¹') +centimeters_per_square_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmNone⁻²') +centimeters_per_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmNone⁻¹') +centimeters_per_square_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmNone⁻²') +angstroms_per_second = NamedUnit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') +angstroms_per_square_second = NamedUnit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') +angstroms_per_millisecond = NamedUnit(1e-07, Dimensions(length=1, time=-1), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') +angstroms_per_square_millisecond = NamedUnit(1e-07, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') +angstroms_per_microsecond = NamedUnit(0.0001, Dimensions(length=1, time=-1), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') +angstroms_per_square_microsecond = NamedUnit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') +angstroms_per_nanosecond = NamedUnit(0.09999999999999999, Dimensions(length=1, time=-1), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') +angstroms_per_square_nanosecond = NamedUnit(0.09999999999999999, Dimensions(length=1, time=-2), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') +angstroms_per_picosecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') +angstroms_per_square_picosecond = NamedUnit(100.0, Dimensions(length=1, time=-2), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') +angstroms_per_femtosecond = NamedUnit(100000.0, Dimensions(length=1, time=-1), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') +angstroms_per_square_femtosecond = NamedUnit(100000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') +angstroms_per_attosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') +angstroms_per_square_attosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') +angstroms_per_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') +angstroms_per_square_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') +angstroms_per_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') +angstroms_per_square_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') +angstroms_per_day = NamedUnit(1.1574074074074074e-14, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') +angstroms_per_square_day = NamedUnit(1.1574074074074074e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') +angstroms_per_year = NamedUnit(3.168873850681143e-17, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') +angstroms_per_square_year = NamedUnit(3.168873850681143e-17, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') +miles_per_second = NamedUnit(1609.344, Dimensions(length=1, time=-1), name='miles_per_second', ascii_symbol='miles/s', symbol='NoneNone⁻¹') +miles_per_square_second = NamedUnit(1609.344, Dimensions(length=1, time=-2), name='miles_per_square_second', ascii_symbol='miles/s^2', symbol='NoneNone⁻²') +miles_per_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-1), name='miles_per_millisecond', ascii_symbol='miles/ms', symbol='Nonems⁻¹') +miles_per_square_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-2), name='miles_per_square_millisecond', ascii_symbol='miles/ms^2', symbol='Nonems⁻²') +miles_per_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-1), name='miles_per_microsecond', ascii_symbol='miles/us', symbol='Noneµs⁻¹') +miles_per_square_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-2), name='miles_per_square_microsecond', ascii_symbol='miles/us^2', symbol='Noneµs⁻²') +miles_per_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-1), name='miles_per_nanosecond', ascii_symbol='miles/ns', symbol='Nonens⁻¹') +miles_per_square_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-2), name='miles_per_square_nanosecond', ascii_symbol='miles/ns^2', symbol='Nonens⁻²') +miles_per_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-1), name='miles_per_picosecond', ascii_symbol='miles/ps', symbol='Noneps⁻¹') +miles_per_square_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-2), name='miles_per_square_picosecond', ascii_symbol='miles/ps^2', symbol='Noneps⁻²') +miles_per_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-1), name='miles_per_femtosecond', ascii_symbol='miles/fs', symbol='Nonefs⁻¹') +miles_per_square_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-2), name='miles_per_square_femtosecond', ascii_symbol='miles/fs^2', symbol='Nonefs⁻²') +miles_per_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-1), name='miles_per_attosecond', ascii_symbol='miles/as', symbol='Noneas⁻¹') +miles_per_square_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-2), name='miles_per_square_attosecond', ascii_symbol='miles/as^2', symbol='Noneas⁻²') +miles_per_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-1), name='miles_per_minute', ascii_symbol='miles/min', symbol='NoneNone⁻¹') +miles_per_square_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-2), name='miles_per_square_minute', ascii_symbol='miles/min^2', symbol='NoneNone⁻²') +miles_per_hour = NamedUnit(4.4704, Dimensions(length=1, time=-1), name='miles_per_hour', ascii_symbol='miles/h', symbol='NoneNone⁻¹') +miles_per_square_hour = NamedUnit(4.4704, Dimensions(length=1, time=-2), name='miles_per_square_hour', ascii_symbol='miles/h^2', symbol='NoneNone⁻²') +miles_per_day = NamedUnit(0.18626666666666666, Dimensions(length=1, time=-1), name='miles_per_day', ascii_symbol='miles/d', symbol='NoneNone⁻¹') +miles_per_square_day = NamedUnit(0.18626666666666666, Dimensions(length=1, time=-2), name='miles_per_square_day', ascii_symbol='miles/d^2', symbol='NoneNone⁻²') +miles_per_year = NamedUnit(0.0005099808118350593, Dimensions(length=1, time=-1), name='miles_per_year', ascii_symbol='miles/y', symbol='NoneNone⁻¹') +miles_per_square_year = NamedUnit(0.0005099808118350593, Dimensions(length=1, time=-2), name='miles_per_square_year', ascii_symbol='miles/y^2', symbol='NoneNone⁻²') +yards_per_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-1), name='yards_per_second', ascii_symbol='yrd/s', symbol='NoneNone⁻¹') +yards_per_square_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-2), name='yards_per_square_second', ascii_symbol='yrd/s^2', symbol='NoneNone⁻²') +yards_per_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-1), name='yards_per_millisecond', ascii_symbol='yrd/ms', symbol='Nonems⁻¹') +yards_per_square_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-2), name='yards_per_square_millisecond', ascii_symbol='yrd/ms^2', symbol='Nonems⁻²') +yards_per_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-1), name='yards_per_microsecond', ascii_symbol='yrd/us', symbol='Noneµs⁻¹') +yards_per_square_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-2), name='yards_per_square_microsecond', ascii_symbol='yrd/us^2', symbol='Noneµs⁻²') +yards_per_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-1), name='yards_per_nanosecond', ascii_symbol='yrd/ns', symbol='Nonens⁻¹') +yards_per_square_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-2), name='yards_per_square_nanosecond', ascii_symbol='yrd/ns^2', symbol='Nonens⁻²') +yards_per_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-1), name='yards_per_picosecond', ascii_symbol='yrd/ps', symbol='Noneps⁻¹') +yards_per_square_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-2), name='yards_per_square_picosecond', ascii_symbol='yrd/ps^2', symbol='Noneps⁻²') +yards_per_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-1), name='yards_per_femtosecond', ascii_symbol='yrd/fs', symbol='Nonefs⁻¹') +yards_per_square_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-2), name='yards_per_square_femtosecond', ascii_symbol='yrd/fs^2', symbol='Nonefs⁻²') +yards_per_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-1), name='yards_per_attosecond', ascii_symbol='yrd/as', symbol='Noneas⁻¹') +yards_per_square_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-2), name='yards_per_square_attosecond', ascii_symbol='yrd/as^2', symbol='Noneas⁻²') +yards_per_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-1), name='yards_per_minute', ascii_symbol='yrd/min', symbol='NoneNone⁻¹') +yards_per_square_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-2), name='yards_per_square_minute', ascii_symbol='yrd/min^2', symbol='NoneNone⁻²') +yards_per_hour = NamedUnit(0.00254, Dimensions(length=1, time=-1), name='yards_per_hour', ascii_symbol='yrd/h', symbol='NoneNone⁻¹') +yards_per_square_hour = NamedUnit(0.00254, Dimensions(length=1, time=-2), name='yards_per_square_hour', ascii_symbol='yrd/h^2', symbol='NoneNone⁻²') +yards_per_day = NamedUnit(0.00010583333333333335, Dimensions(length=1, time=-1), name='yards_per_day', ascii_symbol='yrd/d', symbol='NoneNone⁻¹') +yards_per_square_day = NamedUnit(0.00010583333333333335, Dimensions(length=1, time=-2), name='yards_per_square_day', ascii_symbol='yrd/d^2', symbol='NoneNone⁻²') +yards_per_year = NamedUnit(2.897618249062837e-07, Dimensions(length=1, time=-1), name='yards_per_year', ascii_symbol='yrd/y', symbol='NoneNone⁻¹') +yards_per_square_year = NamedUnit(2.897618249062837e-07, Dimensions(length=1, time=-2), name='yards_per_square_year', ascii_symbol='yrd/y^2', symbol='NoneNone⁻²') +feet_per_second = NamedUnit(0.3048, Dimensions(length=1, time=-1), name='feet_per_second', ascii_symbol='ft/s', symbol='NoneNone⁻¹') +feet_per_square_second = NamedUnit(0.3048, Dimensions(length=1, time=-2), name='feet_per_square_second', ascii_symbol='ft/s^2', symbol='NoneNone⁻²') +feet_per_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-1), name='feet_per_millisecond', ascii_symbol='ft/ms', symbol='Nonems⁻¹') +feet_per_square_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-2), name='feet_per_square_millisecond', ascii_symbol='ft/ms^2', symbol='Nonems⁻²') +feet_per_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-1), name='feet_per_microsecond', ascii_symbol='ft/us', symbol='Noneµs⁻¹') +feet_per_square_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-2), name='feet_per_square_microsecond', ascii_symbol='ft/us^2', symbol='Noneµs⁻²') +feet_per_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-1), name='feet_per_nanosecond', ascii_symbol='ft/ns', symbol='Nonens⁻¹') +feet_per_square_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-2), name='feet_per_square_nanosecond', ascii_symbol='ft/ns^2', symbol='Nonens⁻²') +feet_per_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-1), name='feet_per_picosecond', ascii_symbol='ft/ps', symbol='Noneps⁻¹') +feet_per_square_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-2), name='feet_per_square_picosecond', ascii_symbol='ft/ps^2', symbol='Noneps⁻²') +feet_per_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-1), name='feet_per_femtosecond', ascii_symbol='ft/fs', symbol='Nonefs⁻¹') +feet_per_square_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-2), name='feet_per_square_femtosecond', ascii_symbol='ft/fs^2', symbol='Nonefs⁻²') +feet_per_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-1), name='feet_per_attosecond', ascii_symbol='ft/as', symbol='Noneas⁻¹') +feet_per_square_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-2), name='feet_per_square_attosecond', ascii_symbol='ft/as^2', symbol='Noneas⁻²') +feet_per_minute = NamedUnit(0.00508, Dimensions(length=1, time=-1), name='feet_per_minute', ascii_symbol='ft/min', symbol='NoneNone⁻¹') +feet_per_square_minute = NamedUnit(0.00508, Dimensions(length=1, time=-2), name='feet_per_square_minute', ascii_symbol='ft/min^2', symbol='NoneNone⁻²') +feet_per_hour = NamedUnit(0.0008466666666666667, Dimensions(length=1, time=-1), name='feet_per_hour', ascii_symbol='ft/h', symbol='NoneNone⁻¹') +feet_per_square_hour = NamedUnit(0.0008466666666666667, Dimensions(length=1, time=-2), name='feet_per_square_hour', ascii_symbol='ft/h^2', symbol='NoneNone⁻²') +feet_per_day = NamedUnit(3.527777777777778e-05, Dimensions(length=1, time=-1), name='feet_per_day', ascii_symbol='ft/d', symbol='NoneNone⁻¹') +feet_per_square_day = NamedUnit(3.527777777777778e-05, Dimensions(length=1, time=-2), name='feet_per_square_day', ascii_symbol='ft/d^2', symbol='NoneNone⁻²') +feet_per_year = NamedUnit(9.658727496876123e-08, Dimensions(length=1, time=-1), name='feet_per_year', ascii_symbol='ft/y', symbol='NoneNone⁻¹') +feet_per_square_year = NamedUnit(9.658727496876123e-08, Dimensions(length=1, time=-2), name='feet_per_square_year', ascii_symbol='ft/y^2', symbol='NoneNone⁻²') +inches_per_second = NamedUnit(0.0254, Dimensions(length=1, time=-1), name='inches_per_second', ascii_symbol='in/s', symbol='NoneNone⁻¹') +inches_per_square_second = NamedUnit(0.0254, Dimensions(length=1, time=-2), name='inches_per_square_second', ascii_symbol='in/s^2', symbol='NoneNone⁻²') +inches_per_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-1), name='inches_per_millisecond', ascii_symbol='in/ms', symbol='Nonems⁻¹') +inches_per_square_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-2), name='inches_per_square_millisecond', ascii_symbol='in/ms^2', symbol='Nonems⁻²') +inches_per_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-1), name='inches_per_microsecond', ascii_symbol='in/us', symbol='Noneµs⁻¹') +inches_per_square_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-2), name='inches_per_square_microsecond', ascii_symbol='in/us^2', symbol='Noneµs⁻²') +inches_per_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-1), name='inches_per_nanosecond', ascii_symbol='in/ns', symbol='Nonens⁻¹') +inches_per_square_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-2), name='inches_per_square_nanosecond', ascii_symbol='in/ns^2', symbol='Nonens⁻²') +inches_per_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-1), name='inches_per_picosecond', ascii_symbol='in/ps', symbol='Noneps⁻¹') +inches_per_square_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-2), name='inches_per_square_picosecond', ascii_symbol='in/ps^2', symbol='Noneps⁻²') +inches_per_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-1), name='inches_per_femtosecond', ascii_symbol='in/fs', symbol='Nonefs⁻¹') +inches_per_square_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-2), name='inches_per_square_femtosecond', ascii_symbol='in/fs^2', symbol='Nonefs⁻²') +inches_per_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-1), name='inches_per_attosecond', ascii_symbol='in/as', symbol='Noneas⁻¹') +inches_per_square_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-2), name='inches_per_square_attosecond', ascii_symbol='in/as^2', symbol='Noneas⁻²') +inches_per_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-1), name='inches_per_minute', ascii_symbol='in/min', symbol='NoneNone⁻¹') +inches_per_square_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-2), name='inches_per_square_minute', ascii_symbol='in/min^2', symbol='NoneNone⁻²') +inches_per_hour = NamedUnit(7.055555555555556e-05, Dimensions(length=1, time=-1), name='inches_per_hour', ascii_symbol='in/h', symbol='NoneNone⁻¹') +inches_per_square_hour = NamedUnit(7.055555555555556e-05, Dimensions(length=1, time=-2), name='inches_per_square_hour', ascii_symbol='in/h^2', symbol='NoneNone⁻²') +inches_per_day = NamedUnit(2.9398148148148147e-06, Dimensions(length=1, time=-1), name='inches_per_day', ascii_symbol='in/d', symbol='NoneNone⁻¹') +inches_per_square_day = NamedUnit(2.9398148148148147e-06, Dimensions(length=1, time=-2), name='inches_per_square_day', ascii_symbol='in/d^2', symbol='NoneNone⁻²') +inches_per_year = NamedUnit(8.048939580730103e-09, Dimensions(length=1, time=-1), name='inches_per_year', ascii_symbol='in/y', symbol='NoneNone⁻¹') +inches_per_square_year = NamedUnit(8.048939580730103e-09, Dimensions(length=1, time=-2), name='inches_per_square_year', ascii_symbol='in/y^2', symbol='NoneNone⁻²') +grams_per_cubic_meter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_meter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') +petagrams_per_cubic_meter = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') +teragrams_per_cubic_meter = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_meter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') +megagrams_per_cubic_meter = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') +kilograms_per_cubic_meter = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgNone⁻³') +milligrams_per_cubic_meter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgNone⁻³') +micrograms_per_cubic_meter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgNone⁻³') +nanograms_per_cubic_meter = NamedUnit(1.0000000000000002e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngNone⁻³') +picograms_per_cubic_meter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') +femtograms_per_cubic_meter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') +attograms_per_cubic_meter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') +atomic_mass_units_per_cubic_meter = NamedUnit(1.660538921e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='NoneNone⁻³') +pounds_per_cubic_meter = NamedUnit(0.45359237, Dimensions(length=-3, mass=1), name='pounds_per_cubic_meter', ascii_symbol='lb m^-3', symbol='NoneNone⁻³') +ounces_per_cubic_meter = NamedUnit(0.028349523125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_meter', ascii_symbol='oz m^-3', symbol='NoneNone⁻³') +grams_per_cubic_exameter = NamedUnit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') +exagrams_per_cubic_exameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') +petagrams_per_cubic_exameter = NamedUnit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') +teragrams_per_cubic_exameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') +gigagrams_per_cubic_exameter = NamedUnit(1e-48, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_exameter', ascii_symbol='Gg Em^-3', symbol='GgEm⁻³') +megagrams_per_cubic_exameter = NamedUnit(9.999999999999999e-52, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_exameter', ascii_symbol='Mg Em^-3', symbol='MgEm⁻³') +kilograms_per_cubic_exameter = NamedUnit(9.999999999999999e-55, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_exameter', ascii_symbol='kg Em^-3', symbol='kgEm⁻³') +milligrams_per_cubic_exameter = NamedUnit(9.999999999999998e-61, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_exameter', ascii_symbol='mg Em^-3', symbol='mgEm⁻³') +micrograms_per_cubic_exameter = NamedUnit(9.999999999999999e-64, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_exameter', ascii_symbol='ug Em^-3', symbol='µgEm⁻³') +nanograms_per_cubic_exameter = NamedUnit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_exameter', ascii_symbol='ng Em^-3', symbol='ngEm⁻³') +picograms_per_cubic_exameter = NamedUnit(1e-69, Dimensions(length=-3, mass=1), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') +femtograms_per_cubic_exameter = NamedUnit(1e-72, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') +attograms_per_cubic_exameter = NamedUnit(1e-75, Dimensions(length=-3, mass=1), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') +atomic_mass_units_per_cubic_exameter = NamedUnit(1.6605389209999996e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='NoneEm⁻³') +pounds_per_cubic_exameter = NamedUnit(4.5359237e-55, Dimensions(length=-3, mass=1), name='pounds_per_cubic_exameter', ascii_symbol='lb Em^-3', symbol='NoneEm⁻³') +ounces_per_cubic_exameter = NamedUnit(2.8349523125e-56, Dimensions(length=-3, mass=1), name='ounces_per_cubic_exameter', ascii_symbol='oz Em^-3', symbol='NoneEm⁻³') +grams_per_cubic_petameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') +exagrams_per_cubic_petameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') +petagrams_per_cubic_petameter = NamedUnit(1e-33, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') +teragrams_per_cubic_petameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') +gigagrams_per_cubic_petameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_petameter', ascii_symbol='Gg Pm^-3', symbol='GgPm⁻³') +megagrams_per_cubic_petameter = NamedUnit(1e-42, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_petameter', ascii_symbol='Mg Pm^-3', symbol='MgPm⁻³') +kilograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-45, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_petameter', ascii_symbol='kg Pm^-3', symbol='kgPm⁻³') +milligrams_per_cubic_petameter = NamedUnit(1e-51, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_petameter', ascii_symbol='mg Pm^-3', symbol='mgPm⁻³') +micrograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-54, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_petameter', ascii_symbol='ug Pm^-3', symbol='µgPm⁻³') +nanograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-57, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_petameter', ascii_symbol='ng Pm^-3', symbol='ngPm⁻³') +picograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-60, Dimensions(length=-3, mass=1), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') +femtograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-63, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') +attograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') +atomic_mass_units_per_cubic_petameter = NamedUnit(1.660538921e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='NonePm⁻³') +pounds_per_cubic_petameter = NamedUnit(4.5359237000000005e-46, Dimensions(length=-3, mass=1), name='pounds_per_cubic_petameter', ascii_symbol='lb Pm^-3', symbol='NonePm⁻³') +ounces_per_cubic_petameter = NamedUnit(2.8349523125000003e-47, Dimensions(length=-3, mass=1), name='ounces_per_cubic_petameter', ascii_symbol='oz Pm^-3', symbol='NonePm⁻³') +grams_per_cubic_terameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') +exagrams_per_cubic_terameter = NamedUnit(1e-21, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') +petagrams_per_cubic_terameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') +teragrams_per_cubic_terameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') +gigagrams_per_cubic_terameter = NamedUnit(9.999999999999999e-31, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_terameter', ascii_symbol='Gg Tm^-3', symbol='GgTm⁻³') +megagrams_per_cubic_terameter = NamedUnit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_terameter', ascii_symbol='Mg Tm^-3', symbol='MgTm⁻³') +kilograms_per_cubic_terameter = NamedUnit(1e-36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_terameter', ascii_symbol='kg Tm^-3', symbol='kgTm⁻³') +milligrams_per_cubic_terameter = NamedUnit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_terameter', ascii_symbol='mg Tm^-3', symbol='mgTm⁻³') +micrograms_per_cubic_terameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_terameter', ascii_symbol='ug Tm^-3', symbol='µgTm⁻³') +nanograms_per_cubic_terameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_terameter', ascii_symbol='ng Tm^-3', symbol='ngTm⁻³') +picograms_per_cubic_terameter = NamedUnit(1e-51, Dimensions(length=-3, mass=1), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') +femtograms_per_cubic_terameter = NamedUnit(1e-54, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') +attograms_per_cubic_terameter = NamedUnit(1.0000000000000001e-57, Dimensions(length=-3, mass=1), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') +atomic_mass_units_per_cubic_terameter = NamedUnit(1.6605389209999997e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='NoneTm⁻³') +pounds_per_cubic_terameter = NamedUnit(4.5359237e-37, Dimensions(length=-3, mass=1), name='pounds_per_cubic_terameter', ascii_symbol='lb Tm^-3', symbol='NoneTm⁻³') +ounces_per_cubic_terameter = NamedUnit(2.8349523125e-38, Dimensions(length=-3, mass=1), name='ounces_per_cubic_terameter', ascii_symbol='oz Tm^-3', symbol='NoneTm⁻³') +grams_per_cubic_gigameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') +exagrams_per_cubic_gigameter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') +petagrams_per_cubic_gigameter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') +teragrams_per_cubic_gigameter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') +gigagrams_per_cubic_gigameter = NamedUnit(1e-21, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_gigameter', ascii_symbol='Gg Gm^-3', symbol='GgGm⁻³') +megagrams_per_cubic_gigameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_gigameter', ascii_symbol='Mg Gm^-3', symbol='MgGm⁻³') +kilograms_per_cubic_gigameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_gigameter', ascii_symbol='kg Gm^-3', symbol='kgGm⁻³') +milligrams_per_cubic_gigameter = NamedUnit(9.999999999999999e-34, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_gigameter', ascii_symbol='mg Gm^-3', symbol='mgGm⁻³') +micrograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_gigameter', ascii_symbol='ug Gm^-3', symbol='µgGm⁻³') +nanograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_gigameter', ascii_symbol='ng Gm^-3', symbol='ngGm⁻³') +picograms_per_cubic_gigameter = NamedUnit(1e-42, Dimensions(length=-3, mass=1), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') +femtograms_per_cubic_gigameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') +attograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') +atomic_mass_units_per_cubic_gigameter = NamedUnit(1.660538921e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='NoneGm⁻³') +pounds_per_cubic_gigameter = NamedUnit(4.5359237e-28, Dimensions(length=-3, mass=1), name='pounds_per_cubic_gigameter', ascii_symbol='lb Gm^-3', symbol='NoneGm⁻³') +ounces_per_cubic_gigameter = NamedUnit(2.8349523125e-29, Dimensions(length=-3, mass=1), name='ounces_per_cubic_gigameter', ascii_symbol='oz Gm^-3', symbol='NoneGm⁻³') +grams_per_cubic_megameter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') +exagrams_per_cubic_megameter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') +petagrams_per_cubic_megameter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') +teragrams_per_cubic_megameter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') +gigagrams_per_cubic_megameter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_megameter', ascii_symbol='Gg Mm^-3', symbol='GgMm⁻³') +megagrams_per_cubic_megameter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_megameter', ascii_symbol='Mg Mm^-3', symbol='MgMm⁻³') +kilograms_per_cubic_megameter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_megameter', ascii_symbol='kg Mm^-3', symbol='kgMm⁻³') +milligrams_per_cubic_megameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_megameter', ascii_symbol='mg Mm^-3', symbol='mgMm⁻³') +micrograms_per_cubic_megameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_megameter', ascii_symbol='ug Mm^-3', symbol='µgMm⁻³') +nanograms_per_cubic_megameter = NamedUnit(1.0000000000000003e-30, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_megameter', ascii_symbol='ng Mm^-3', symbol='ngMm⁻³') +picograms_per_cubic_megameter = NamedUnit(1e-33, Dimensions(length=-3, mass=1), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') +femtograms_per_cubic_megameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') +attograms_per_cubic_megameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') +atomic_mass_units_per_cubic_megameter = NamedUnit(1.6605389209999997e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='NoneMm⁻³') +pounds_per_cubic_megameter = NamedUnit(4.535923700000001e-19, Dimensions(length=-3, mass=1), name='pounds_per_cubic_megameter', ascii_symbol='lb Mm^-3', symbol='NoneMm⁻³') +ounces_per_cubic_megameter = NamedUnit(2.8349523125000004e-20, Dimensions(length=-3, mass=1), name='ounces_per_cubic_megameter', ascii_symbol='oz Mm^-3', symbol='NoneMm⁻³') +grams_per_cubic_kilometer = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') +exagrams_per_cubic_kilometer = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') +petagrams_per_cubic_kilometer = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') +teragrams_per_cubic_kilometer = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') +gigagrams_per_cubic_kilometer = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_kilometer', ascii_symbol='Gg km^-3', symbol='Ggkm⁻³') +megagrams_per_cubic_kilometer = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_kilometer', ascii_symbol='Mg km^-3', symbol='Mgkm⁻³') +kilograms_per_cubic_kilometer = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_kilometer', ascii_symbol='kg km^-3', symbol='kgkm⁻³') +milligrams_per_cubic_kilometer = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_kilometer', ascii_symbol='mg km^-3', symbol='mgkm⁻³') +micrograms_per_cubic_kilometer = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_kilometer', ascii_symbol='ug km^-3', symbol='µgkm⁻³') +nanograms_per_cubic_kilometer = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_kilometer', ascii_symbol='ng km^-3', symbol='ngkm⁻³') +picograms_per_cubic_kilometer = NamedUnit(1.0000000000000001e-24, Dimensions(length=-3, mass=1), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') +femtograms_per_cubic_kilometer = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') +attograms_per_cubic_kilometer = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') +atomic_mass_units_per_cubic_kilometer = NamedUnit(1.6605389209999997e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='Nonekm⁻³') +pounds_per_cubic_kilometer = NamedUnit(4.5359237000000004e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_kilometer', ascii_symbol='lb km^-3', symbol='Nonekm⁻³') +ounces_per_cubic_kilometer = NamedUnit(2.8349523125000003e-11, Dimensions(length=-3, mass=1), name='ounces_per_cubic_kilometer', ascii_symbol='oz km^-3', symbol='Nonekm⁻³') +grams_per_cubic_millimeter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') +exagrams_per_cubic_millimeter = NamedUnit(1e+24, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') +petagrams_per_cubic_millimeter = NamedUnit(1e+21, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') +teragrams_per_cubic_millimeter = NamedUnit(1e+18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') +gigagrams_per_cubic_millimeter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_millimeter', ascii_symbol='Gg mm^-3', symbol='Ggmm⁻³') +megagrams_per_cubic_millimeter = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_millimeter', ascii_symbol='Mg mm^-3', symbol='Mgmm⁻³') +kilograms_per_cubic_millimeter = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_millimeter', ascii_symbol='kg mm^-3', symbol='kgmm⁻³') +milligrams_per_cubic_millimeter = NamedUnit(999.9999999999999, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_millimeter', ascii_symbol='mg mm^-3', symbol='mgmm⁻³') +micrograms_per_cubic_millimeter = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_millimeter', ascii_symbol='ug mm^-3', symbol='µgmm⁻³') +nanograms_per_cubic_millimeter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_millimeter', ascii_symbol='ng mm^-3', symbol='ngmm⁻³') +picograms_per_cubic_millimeter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') +femtograms_per_cubic_millimeter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') +attograms_per_cubic_millimeter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') +atomic_mass_units_per_cubic_millimeter = NamedUnit(1.6605389209999997e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='Nonemm⁻³') +pounds_per_cubic_millimeter = NamedUnit(453592370.0, Dimensions(length=-3, mass=1), name='pounds_per_cubic_millimeter', ascii_symbol='lb mm^-3', symbol='Nonemm⁻³') +ounces_per_cubic_millimeter = NamedUnit(28349523.125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_millimeter', ascii_symbol='oz mm^-3', symbol='Nonemm⁻³') +grams_per_cubic_micrometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') +exagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') +petagrams_per_cubic_micrometer = NamedUnit(1.0000000000000002e+30, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') +teragrams_per_cubic_micrometer = NamedUnit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') +gigagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+24, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_micrometer', ascii_symbol='Gg um^-3', symbol='Ggµm⁻³') +megagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_micrometer', ascii_symbol='Mg um^-3', symbol='Mgµm⁻³') +kilograms_per_cubic_micrometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_micrometer', ascii_symbol='kg um^-3', symbol='kgµm⁻³') +milligrams_per_cubic_micrometer = NamedUnit(1000000000000.0001, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_micrometer', ascii_symbol='mg um^-3', symbol='mgµm⁻³') +micrograms_per_cubic_micrometer = NamedUnit(1000000000.0000002, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_micrometer', ascii_symbol='ug um^-3', symbol='µgµm⁻³') +nanograms_per_cubic_micrometer = NamedUnit(1000000.0000000003, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_micrometer', ascii_symbol='ng um^-3', symbol='ngµm⁻³') +picograms_per_cubic_micrometer = NamedUnit(1000.0000000000002, Dimensions(length=-3, mass=1), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') +femtograms_per_cubic_micrometer = NamedUnit(1.0000000000000002, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') +attograms_per_cubic_micrometer = NamedUnit(0.0010000000000000002, Dimensions(length=-3, mass=1), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') +atomic_mass_units_per_cubic_micrometer = NamedUnit(1.660538921e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='Noneµm⁻³') +pounds_per_cubic_micrometer = NamedUnit(4.5359237000000006e+17, Dimensions(length=-3, mass=1), name='pounds_per_cubic_micrometer', ascii_symbol='lb um^-3', symbol='Noneµm⁻³') +ounces_per_cubic_micrometer = NamedUnit(2.8349523125000004e+16, Dimensions(length=-3, mass=1), name='ounces_per_cubic_micrometer', ascii_symbol='oz um^-3', symbol='Noneµm⁻³') +grams_per_cubic_nanometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') +exagrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') +petagrams_per_cubic_nanometer = NamedUnit(9.999999999999998e+38, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') +teragrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') +gigagrams_per_cubic_nanometer = NamedUnit(9.999999999999998e+32, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_nanometer', ascii_symbol='Gg nm^-3', symbol='Ggnm⁻³') +megagrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_nanometer', ascii_symbol='Mg nm^-3', symbol='Mgnm⁻³') +kilograms_per_cubic_nanometer = NamedUnit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_nanometer', ascii_symbol='kg nm^-3', symbol='kgnm⁻³') +milligrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+20, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_nanometer', ascii_symbol='mg nm^-3', symbol='mgnm⁻³') +micrograms_per_cubic_nanometer = NamedUnit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_nanometer', ascii_symbol='ug nm^-3', symbol='µgnm⁻³') +nanograms_per_cubic_nanometer = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_nanometer', ascii_symbol='ng nm^-3', symbol='ngnm⁻³') +picograms_per_cubic_nanometer = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') +femtograms_per_cubic_nanometer = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') +attograms_per_cubic_nanometer = NamedUnit(999999.9999999999, Dimensions(length=-3, mass=1), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') +atomic_mass_units_per_cubic_nanometer = NamedUnit(1.6605389209999994, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='Nonenm⁻³') +pounds_per_cubic_nanometer = NamedUnit(4.535923699999999e+26, Dimensions(length=-3, mass=1), name='pounds_per_cubic_nanometer', ascii_symbol='lb nm^-3', symbol='Nonenm⁻³') +ounces_per_cubic_nanometer = NamedUnit(2.8349523124999993e+25, Dimensions(length=-3, mass=1), name='ounces_per_cubic_nanometer', ascii_symbol='oz nm^-3', symbol='Nonenm⁻³') +grams_per_cubic_picometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') +exagrams_per_cubic_picometer = NamedUnit(1e+51, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') +petagrams_per_cubic_picometer = NamedUnit(1e+48, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') +teragrams_per_cubic_picometer = NamedUnit(1.0000000000000001e+45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') +gigagrams_per_cubic_picometer = NamedUnit(1e+42, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_picometer', ascii_symbol='Gg pm^-3', symbol='Ggpm⁻³') +megagrams_per_cubic_picometer = NamedUnit(1.0000000000000001e+39, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_picometer', ascii_symbol='Mg pm^-3', symbol='Mgpm⁻³') +kilograms_per_cubic_picometer = NamedUnit(1e+36, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_picometer', ascii_symbol='kg pm^-3', symbol='kgpm⁻³') +milligrams_per_cubic_picometer = NamedUnit(1e+30, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_picometer', ascii_symbol='mg pm^-3', symbol='mgpm⁻³') +micrograms_per_cubic_picometer = NamedUnit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_picometer', ascii_symbol='ug pm^-3', symbol='µgpm⁻³') +nanograms_per_cubic_picometer = NamedUnit(1.0000000000000003e+24, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_picometer', ascii_symbol='ng pm^-3', symbol='ngpm⁻³') +picograms_per_cubic_picometer = NamedUnit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') +femtograms_per_cubic_picometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') +attograms_per_cubic_picometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') +atomic_mass_units_per_cubic_picometer = NamedUnit(1660538921.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='Nonepm⁻³') +pounds_per_cubic_picometer = NamedUnit(4.5359237000000005e+35, Dimensions(length=-3, mass=1), name='pounds_per_cubic_picometer', ascii_symbol='lb pm^-3', symbol='Nonepm⁻³') +ounces_per_cubic_picometer = NamedUnit(2.8349523125000003e+34, Dimensions(length=-3, mass=1), name='ounces_per_cubic_picometer', ascii_symbol='oz pm^-3', symbol='Nonepm⁻³') +grams_per_cubic_femtometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') +exagrams_per_cubic_femtometer = NamedUnit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') +petagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+56, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') +teragrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+53, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') +gigagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+50, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_femtometer', ascii_symbol='Gg fm^-3', symbol='Ggfm⁻³') +megagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_femtometer', ascii_symbol='Mg fm^-3', symbol='Mgfm⁻³') +kilograms_per_cubic_femtometer = NamedUnit(9.999999999999998e+44, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_femtometer', ascii_symbol='kg fm^-3', symbol='kgfm⁻³') +milligrams_per_cubic_femtometer = NamedUnit(9.999999999999996e+38, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_femtometer', ascii_symbol='mg fm^-3', symbol='mgfm⁻³') +micrograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_femtometer', ascii_symbol='ug fm^-3', symbol='µgfm⁻³') +nanograms_per_cubic_femtometer = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_femtometer', ascii_symbol='ng fm^-3', symbol='ngfm⁻³') +picograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') +femtograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') +attograms_per_cubic_femtometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') +atomic_mass_units_per_cubic_femtometer = NamedUnit(1.6605389209999992e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='Nonefm⁻³') +pounds_per_cubic_femtometer = NamedUnit(4.5359236999999985e+44, Dimensions(length=-3, mass=1), name='pounds_per_cubic_femtometer', ascii_symbol='lb fm^-3', symbol='Nonefm⁻³') +ounces_per_cubic_femtometer = NamedUnit(2.834952312499999e+43, Dimensions(length=-3, mass=1), name='ounces_per_cubic_femtometer', ascii_symbol='oz fm^-3', symbol='Nonefm⁻³') +grams_per_cubic_attometer = NamedUnit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') +exagrams_per_cubic_attometer = NamedUnit(9.999999999999999e+68, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') +petagrams_per_cubic_attometer = NamedUnit(9.999999999999998e+65, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') +teragrams_per_cubic_attometer = NamedUnit(9.999999999999999e+62, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') +gigagrams_per_cubic_attometer = NamedUnit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_attometer', ascii_symbol='Gg am^-3', symbol='Ggam⁻³') +megagrams_per_cubic_attometer = NamedUnit(9.999999999999999e+56, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_attometer', ascii_symbol='Mg am^-3', symbol='Mgam⁻³') +kilograms_per_cubic_attometer = NamedUnit(9.999999999999999e+53, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_attometer', ascii_symbol='kg am^-3', symbol='kgam⁻³') +milligrams_per_cubic_attometer = NamedUnit(9.999999999999997e+47, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_attometer', ascii_symbol='mg am^-3', symbol='mgam⁻³') +micrograms_per_cubic_attometer = NamedUnit(1e+45, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_attometer', ascii_symbol='ug am^-3', symbol='µgam⁻³') +nanograms_per_cubic_attometer = NamedUnit(1e+42, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_attometer', ascii_symbol='ng am^-3', symbol='ngam⁻³') +picograms_per_cubic_attometer = NamedUnit(1e+39, Dimensions(length=-3, mass=1), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') +femtograms_per_cubic_attometer = NamedUnit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') +attograms_per_cubic_attometer = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') +atomic_mass_units_per_cubic_attometer = NamedUnit(1.6605389209999997e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='Noneam⁻³') +pounds_per_cubic_attometer = NamedUnit(4.5359237e+53, Dimensions(length=-3, mass=1), name='pounds_per_cubic_attometer', ascii_symbol='lb am^-3', symbol='Noneam⁻³') +ounces_per_cubic_attometer = NamedUnit(2.8349523125e+52, Dimensions(length=-3, mass=1), name='ounces_per_cubic_attometer', ascii_symbol='oz am^-3', symbol='Noneam⁻³') +grams_per_cubic_decimeter = NamedUnit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='Nonedm⁻³') +exagrams_per_cubic_decimeter = NamedUnit(9.999999999999997e+17, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_decimeter', ascii_symbol='Eg dm^-3', symbol='Egdm⁻³') +petagrams_per_cubic_decimeter = NamedUnit(999999999999999.8, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_decimeter', ascii_symbol='Pg dm^-3', symbol='Pgdm⁻³') +teragrams_per_cubic_decimeter = NamedUnit(999999999999.9998, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_decimeter', ascii_symbol='Tg dm^-3', symbol='Tgdm⁻³') +gigagrams_per_cubic_decimeter = NamedUnit(999999999.9999998, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_decimeter', ascii_symbol='Gg dm^-3', symbol='Ggdm⁻³') +megagrams_per_cubic_decimeter = NamedUnit(999999.9999999998, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_decimeter', ascii_symbol='Mg dm^-3', symbol='Mgdm⁻³') +kilograms_per_cubic_decimeter = NamedUnit(999.9999999999998, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_decimeter', ascii_symbol='kg dm^-3', symbol='kgdm⁻³') +milligrams_per_cubic_decimeter = NamedUnit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_decimeter', ascii_symbol='mg dm^-3', symbol='mgdm⁻³') +micrograms_per_cubic_decimeter = NamedUnit(9.999999999999997e-07, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_decimeter', ascii_symbol='ug dm^-3', symbol='µgdm⁻³') +nanograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_decimeter', ascii_symbol='ng dm^-3', symbol='ngdm⁻³') +picograms_per_cubic_decimeter = NamedUnit(9.999999999999998e-13, Dimensions(length=-3, mass=1), name='picograms_per_cubic_decimeter', ascii_symbol='pg dm^-3', symbol='pgdm⁻³') +femtograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_decimeter', ascii_symbol='fg dm^-3', symbol='fgdm⁻³') +attograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-19, Dimensions(length=-3, mass=1), name='attograms_per_cubic_decimeter', ascii_symbol='ag dm^-3', symbol='agdm⁻³') +atomic_mass_units_per_cubic_decimeter = NamedUnit(1.6605389209999993e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='Nonedm⁻³') +pounds_per_cubic_decimeter = NamedUnit(453.5923699999999, Dimensions(length=-3, mass=1), name='pounds_per_cubic_decimeter', ascii_symbol='lb dm^-3', symbol='Nonedm⁻³') +ounces_per_cubic_decimeter = NamedUnit(28.349523124999994, Dimensions(length=-3, mass=1), name='ounces_per_cubic_decimeter', ascii_symbol='oz dm^-3', symbol='Nonedm⁻³') +grams_per_cubic_centimeter = NamedUnit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='Nonecm⁻³') +exagrams_per_cubic_centimeter = NamedUnit(9.999999999999999e+20, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_centimeter', ascii_symbol='Eg cm^-3', symbol='Egcm⁻³') +petagrams_per_cubic_centimeter = NamedUnit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_centimeter', ascii_symbol='Pg cm^-3', symbol='Pgcm⁻³') +teragrams_per_cubic_centimeter = NamedUnit(999999999999999.9, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_centimeter', ascii_symbol='Tg cm^-3', symbol='Tgcm⁻³') +gigagrams_per_cubic_centimeter = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_centimeter', ascii_symbol='Gg cm^-3', symbol='Ggcm⁻³') +megagrams_per_cubic_centimeter = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_centimeter', ascii_symbol='Mg cm^-3', symbol='Mgcm⁻³') +kilograms_per_cubic_centimeter = NamedUnit(999999.9999999999, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_centimeter', ascii_symbol='kg cm^-3', symbol='kgcm⁻³') +milligrams_per_cubic_centimeter = NamedUnit(0.9999999999999998, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_centimeter', ascii_symbol='mg cm^-3', symbol='mgcm⁻³') +micrograms_per_cubic_centimeter = NamedUnit(0.0009999999999999998, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_centimeter', ascii_symbol='ug cm^-3', symbol='µgcm⁻³') +nanograms_per_cubic_centimeter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_centimeter', ascii_symbol='ng cm^-3', symbol='ngcm⁻³') +picograms_per_cubic_centimeter = NamedUnit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='picograms_per_cubic_centimeter', ascii_symbol='pg cm^-3', symbol='pgcm⁻³') +femtograms_per_cubic_centimeter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_centimeter', ascii_symbol='fg cm^-3', symbol='fgcm⁻³') +attograms_per_cubic_centimeter = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='attograms_per_cubic_centimeter', ascii_symbol='ag cm^-3', symbol='agcm⁻³') +atomic_mass_units_per_cubic_centimeter = NamedUnit(1.6605389209999996e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='Nonecm⁻³') +pounds_per_cubic_centimeter = NamedUnit(453592.36999999994, Dimensions(length=-3, mass=1), name='pounds_per_cubic_centimeter', ascii_symbol='lb cm^-3', symbol='Nonecm⁻³') +ounces_per_cubic_centimeter = NamedUnit(28349.523124999996, Dimensions(length=-3, mass=1), name='ounces_per_cubic_centimeter', ascii_symbol='oz cm^-3', symbol='Nonecm⁻³') +grams_per_cubic_angstrom = NamedUnit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') +exagrams_per_cubic_angstrom = NamedUnit(1e+45, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') +petagrams_per_cubic_angstrom = NamedUnit(9.999999999999999e+41, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') +teragrams_per_cubic_angstrom = NamedUnit(1e+39, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') +gigagrams_per_cubic_angstrom = NamedUnit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_angstrom', ascii_symbol='Gg Ang^-3', symbol='GgÅ⁻³') +megagrams_per_cubic_angstrom = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_angstrom', ascii_symbol='Mg Ang^-3', symbol='MgÅ⁻³') +kilograms_per_cubic_angstrom = NamedUnit(9.999999999999999e+29, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_angstrom', ascii_symbol='kg Ang^-3', symbol='kgÅ⁻³') +milligrams_per_cubic_angstrom = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_angstrom', ascii_symbol='mg Ang^-3', symbol='mgÅ⁻³') +micrograms_per_cubic_angstrom = NamedUnit(1e+21, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_angstrom', ascii_symbol='ug Ang^-3', symbol='µgÅ⁻³') +nanograms_per_cubic_angstrom = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_angstrom', ascii_symbol='ng Ang^-3', symbol='ngÅ⁻³') +picograms_per_cubic_angstrom = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') +femtograms_per_cubic_angstrom = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') +attograms_per_cubic_angstrom = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') +atomic_mass_units_per_cubic_angstrom = NamedUnit(1660.5389209999996, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='NoneÅ⁻³') +pounds_per_cubic_angstrom = NamedUnit(4.5359237e+29, Dimensions(length=-3, mass=1), name='pounds_per_cubic_angstrom', ascii_symbol='lb Ang^-3', symbol='NoneÅ⁻³') +ounces_per_cubic_angstrom = NamedUnit(2.8349523125e+28, Dimensions(length=-3, mass=1), name='ounces_per_cubic_angstrom', ascii_symbol='oz Ang^-3', symbol='NoneÅ⁻³') +grams_per_cubic_mile = NamedUnit(2.399127585789277e-13, Dimensions(length=-3, mass=1), name='grams_per_cubic_mile', ascii_symbol='g miles^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_mile = NamedUnit(239912.7585789277, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_mile', ascii_symbol='Eg miles^-3', symbol='EgNone⁻³') +petagrams_per_cubic_mile = NamedUnit(239.9127585789277, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_mile', ascii_symbol='Pg miles^-3', symbol='PgNone⁻³') +teragrams_per_cubic_mile = NamedUnit(0.2399127585789277, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_mile', ascii_symbol='Tg miles^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_mile = NamedUnit(0.0002399127585789277, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_mile', ascii_symbol='Gg miles^-3', symbol='GgNone⁻³') +megagrams_per_cubic_mile = NamedUnit(2.399127585789277e-07, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_mile', ascii_symbol='Mg miles^-3', symbol='MgNone⁻³') +kilograms_per_cubic_mile = NamedUnit(2.399127585789277e-10, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_mile', ascii_symbol='kg miles^-3', symbol='kgNone⁻³') +milligrams_per_cubic_mile = NamedUnit(2.399127585789277e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_mile', ascii_symbol='mg miles^-3', symbol='mgNone⁻³') +micrograms_per_cubic_mile = NamedUnit(2.3991275857892774e-19, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_mile', ascii_symbol='ug miles^-3', symbol='µgNone⁻³') +nanograms_per_cubic_mile = NamedUnit(2.3991275857892774e-22, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_mile', ascii_symbol='ng miles^-3', symbol='ngNone⁻³') +picograms_per_cubic_mile = NamedUnit(2.399127585789277e-25, Dimensions(length=-3, mass=1), name='picograms_per_cubic_mile', ascii_symbol='pg miles^-3', symbol='pgNone⁻³') +femtograms_per_cubic_mile = NamedUnit(2.3991275857892772e-28, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_mile', ascii_symbol='fg miles^-3', symbol='fgNone⁻³') +attograms_per_cubic_mile = NamedUnit(2.399127585789277e-31, Dimensions(length=-3, mass=1), name='attograms_per_cubic_mile', ascii_symbol='ag miles^-3', symbol='agNone⁻³') +atomic_mass_units_per_cubic_mile = NamedUnit(3.98384473264786e-37, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_mile', ascii_symbol='au miles^-3', symbol='NoneNone⁻³') +pounds_per_cubic_mile = NamedUnit(1.0882259675705365e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_mile', ascii_symbol='lb miles^-3', symbol='NoneNone⁻³') +ounces_per_cubic_mile = NamedUnit(6.801412297315853e-12, Dimensions(length=-3, mass=1), name='ounces_per_cubic_mile', ascii_symbol='oz miles^-3', symbol='NoneNone⁻³') +grams_per_cubic_yard = NamedUnit(0.0013079506193143919, Dimensions(length=-3, mass=1), name='grams_per_cubic_yard', ascii_symbol='g yrd^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_yard = NamedUnit(1307950619314391.8, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_yard', ascii_symbol='Eg yrd^-3', symbol='EgNone⁻³') +petagrams_per_cubic_yard = NamedUnit(1307950619314.3918, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_yard', ascii_symbol='Pg yrd^-3', symbol='PgNone⁻³') +teragrams_per_cubic_yard = NamedUnit(1307950619.3143919, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_yard', ascii_symbol='Tg yrd^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_yard = NamedUnit(1307950.6193143919, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_yard', ascii_symbol='Gg yrd^-3', symbol='GgNone⁻³') +megagrams_per_cubic_yard = NamedUnit(1307.9506193143918, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_yard', ascii_symbol='Mg yrd^-3', symbol='MgNone⁻³') +kilograms_per_cubic_yard = NamedUnit(1.3079506193143917, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_yard', ascii_symbol='kg yrd^-3', symbol='kgNone⁻³') +milligrams_per_cubic_yard = NamedUnit(1.3079506193143917e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_yard', ascii_symbol='mg yrd^-3', symbol='mgNone⁻³') +micrograms_per_cubic_yard = NamedUnit(1.3079506193143919e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_yard', ascii_symbol='ug yrd^-3', symbol='µgNone⁻³') +nanograms_per_cubic_yard = NamedUnit(1.307950619314392e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_yard', ascii_symbol='ng yrd^-3', symbol='ngNone⁻³') +picograms_per_cubic_yard = NamedUnit(1.3079506193143919e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_yard', ascii_symbol='pg yrd^-3', symbol='pgNone⁻³') +femtograms_per_cubic_yard = NamedUnit(1.3079506193143918e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_yard', ascii_symbol='fg yrd^-3', symbol='fgNone⁻³') +attograms_per_cubic_yard = NamedUnit(1.307950619314392e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_yard', ascii_symbol='ag yrd^-3', symbol='agNone⁻³') +atomic_mass_units_per_cubic_yard = NamedUnit(2.1719029101176016e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_yard', ascii_symbol='au yrd^-3', symbol='NoneNone⁻³') +pounds_per_cubic_yard = NamedUnit(0.5932764212577828, Dimensions(length=-3, mass=1), name='pounds_per_cubic_yard', ascii_symbol='lb yrd^-3', symbol='NoneNone⁻³') +ounces_per_cubic_yard = NamedUnit(0.037079776328611425, Dimensions(length=-3, mass=1), name='ounces_per_cubic_yard', ascii_symbol='oz yrd^-3', symbol='NoneNone⁻³') +grams_per_cubic_foot = NamedUnit(0.035314666721488586, Dimensions(length=-3, mass=1), name='grams_per_cubic_foot', ascii_symbol='g ft^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_foot = NamedUnit(3.5314666721488584e+16, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_foot', ascii_symbol='Eg ft^-3', symbol='EgNone⁻³') +petagrams_per_cubic_foot = NamedUnit(35314666721488.586, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_foot', ascii_symbol='Pg ft^-3', symbol='PgNone⁻³') +teragrams_per_cubic_foot = NamedUnit(35314666721.48859, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_foot', ascii_symbol='Tg ft^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_foot = NamedUnit(35314666.72148859, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_foot', ascii_symbol='Gg ft^-3', symbol='GgNone⁻³') +megagrams_per_cubic_foot = NamedUnit(35314.66672148858, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_foot', ascii_symbol='Mg ft^-3', symbol='MgNone⁻³') +kilograms_per_cubic_foot = NamedUnit(35.314666721488585, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_foot', ascii_symbol='kg ft^-3', symbol='kgNone⁻³') +milligrams_per_cubic_foot = NamedUnit(3.5314666721488586e-05, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_foot', ascii_symbol='mg ft^-3', symbol='mgNone⁻³') +micrograms_per_cubic_foot = NamedUnit(3.5314666721488584e-08, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_foot', ascii_symbol='ug ft^-3', symbol='µgNone⁻³') +nanograms_per_cubic_foot = NamedUnit(3.531466672148859e-11, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_foot', ascii_symbol='ng ft^-3', symbol='ngNone⁻³') +picograms_per_cubic_foot = NamedUnit(3.531466672148859e-14, Dimensions(length=-3, mass=1), name='picograms_per_cubic_foot', ascii_symbol='pg ft^-3', symbol='pgNone⁻³') +femtograms_per_cubic_foot = NamedUnit(3.5314666721488585e-17, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_foot', ascii_symbol='fg ft^-3', symbol='fgNone⁻³') +attograms_per_cubic_foot = NamedUnit(3.531466672148859e-20, Dimensions(length=-3, mass=1), name='attograms_per_cubic_foot', ascii_symbol='ag ft^-3', symbol='agNone⁻³') +atomic_mass_units_per_cubic_foot = NamedUnit(5.864137857317526e-26, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_foot', ascii_symbol='au ft^-3', symbol='NoneNone⁻³') +pounds_per_cubic_foot = NamedUnit(16.018463373960138, Dimensions(length=-3, mass=1), name='pounds_per_cubic_foot', ascii_symbol='lb ft^-3', symbol='NoneNone⁻³') +ounces_per_cubic_foot = NamedUnit(1.0011539608725086, Dimensions(length=-3, mass=1), name='ounces_per_cubic_foot', ascii_symbol='oz ft^-3', symbol='NoneNone⁻³') +grams_per_cubic_inch = NamedUnit(61.02374409473229, Dimensions(length=-3, mass=1), name='grams_per_cubic_inch', ascii_symbol='g in^-3', symbol='NoneNone⁻³') +exagrams_per_cubic_inch = NamedUnit(6.102374409473229e+19, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_inch', ascii_symbol='Eg in^-3', symbol='EgNone⁻³') +petagrams_per_cubic_inch = NamedUnit(6.102374409473229e+16, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_inch', ascii_symbol='Pg in^-3', symbol='PgNone⁻³') +teragrams_per_cubic_inch = NamedUnit(61023744094732.29, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_inch', ascii_symbol='Tg in^-3', symbol='TgNone⁻³') +gigagrams_per_cubic_inch = NamedUnit(61023744094.732285, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_inch', ascii_symbol='Gg in^-3', symbol='GgNone⁻³') +megagrams_per_cubic_inch = NamedUnit(61023744.094732285, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_inch', ascii_symbol='Mg in^-3', symbol='MgNone⁻³') +kilograms_per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_inch', ascii_symbol='kg in^-3', symbol='kgNone⁻³') +milligrams_per_cubic_inch = NamedUnit(0.06102374409473228, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_inch', ascii_symbol='mg in^-3', symbol='mgNone⁻³') +micrograms_per_cubic_inch = NamedUnit(6.102374409473229e-05, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_inch', ascii_symbol='ug in^-3', symbol='µgNone⁻³') +nanograms_per_cubic_inch = NamedUnit(6.10237440947323e-08, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_inch', ascii_symbol='ng in^-3', symbol='ngNone⁻³') +picograms_per_cubic_inch = NamedUnit(6.102374409473229e-11, Dimensions(length=-3, mass=1), name='picograms_per_cubic_inch', ascii_symbol='pg in^-3', symbol='pgNone⁻³') +femtograms_per_cubic_inch = NamedUnit(6.10237440947323e-14, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_inch', ascii_symbol='fg in^-3', symbol='fgNone⁻³') +attograms_per_cubic_inch = NamedUnit(6.10237440947323e-17, Dimensions(length=-3, mass=1), name='attograms_per_cubic_inch', ascii_symbol='ag in^-3', symbol='agNone⁻³') +atomic_mass_units_per_cubic_inch = NamedUnit(1.0133230217444687e-22, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_inch', ascii_symbol='au in^-3', symbol='NoneNone⁻³') +pounds_per_cubic_inch = NamedUnit(27679.904710203125, Dimensions(length=-3, mass=1), name='pounds_per_cubic_inch', ascii_symbol='lb in^-3', symbol='NoneNone⁻³') +ounces_per_cubic_inch = NamedUnit(1729.9940443876953, Dimensions(length=-3, mass=1), name='ounces_per_cubic_inch', ascii_symbol='oz in^-3', symbol='NoneNone⁻³') +moles_per_cubic_meter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_meter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_meter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_meter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_meter = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_meter = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_meter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolNone⁻³') +moles_per_cubic_exameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='NoneEm⁻³') +millimoles_per_cubic_exameter = NamedUnit(6.02214076e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') +micromoles_per_cubic_exameter = NamedUnit(6.02214076e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') +nanomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') +picomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') +femtomoles_per_cubic_exameter = NamedUnit(6.02214076e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') +attomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') +moles_per_cubic_petameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='NonePm⁻³') +millimoles_per_cubic_petameter = NamedUnit(6.0221407600000005e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') +micromoles_per_cubic_petameter = NamedUnit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') +nanomoles_per_cubic_petameter = NamedUnit(6.02214076e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') +picomoles_per_cubic_petameter = NamedUnit(6.0221407600000005e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') +femtomoles_per_cubic_petameter = NamedUnit(6.022140760000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') +attomoles_per_cubic_petameter = NamedUnit(6.022140760000001e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') +moles_per_cubic_terameter = NamedUnit(6.022140759999999e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='NoneTm⁻³') +millimoles_per_cubic_terameter = NamedUnit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') +micromoles_per_cubic_terameter = NamedUnit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') +nanomoles_per_cubic_terameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') +picomoles_per_cubic_terameter = NamedUnit(6.02214076e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') +femtomoles_per_cubic_terameter = NamedUnit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') +attomoles_per_cubic_terameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') +moles_per_cubic_gigameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='NoneGm⁻³') +millimoles_per_cubic_gigameter = NamedUnit(6.022140760000001e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') +micromoles_per_cubic_gigameter = NamedUnit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') +nanomoles_per_cubic_gigameter = NamedUnit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') +picomoles_per_cubic_gigameter = NamedUnit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') +femtomoles_per_cubic_gigameter = NamedUnit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') +attomoles_per_cubic_gigameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') +moles_per_cubic_megameter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='NoneMm⁻³') +millimoles_per_cubic_megameter = NamedUnit(602.214076, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') +micromoles_per_cubic_megameter = NamedUnit(0.602214076, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') +nanomoles_per_cubic_megameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') +picomoles_per_cubic_megameter = NamedUnit(6.02214076e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') +femtomoles_per_cubic_megameter = NamedUnit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') +attomoles_per_cubic_megameter = NamedUnit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') +moles_per_cubic_kilometer = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='Nonekm⁻³') +millimoles_per_cubic_kilometer = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') +micromoles_per_cubic_kilometer = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') +nanomoles_per_cubic_kilometer = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') +picomoles_per_cubic_kilometer = NamedUnit(602.214076, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') +femtomoles_per_cubic_kilometer = NamedUnit(0.602214076, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') +attomoles_per_cubic_kilometer = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') +moles_per_cubic_millimeter = NamedUnit(6.0221407599999995e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='Nonemm⁻³') +millimoles_per_cubic_millimeter = NamedUnit(6.02214076e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') +micromoles_per_cubic_millimeter = NamedUnit(6.0221407599999996e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') +nanomoles_per_cubic_millimeter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') +picomoles_per_cubic_millimeter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') +femtomoles_per_cubic_millimeter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') +attomoles_per_cubic_millimeter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') +moles_per_cubic_micrometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='Noneµm⁻³') +millimoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') +micromoles_per_cubic_micrometer = NamedUnit(6.0221407600000004e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') +nanomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') +picomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') +femtomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') +attomoles_per_cubic_micrometer = NamedUnit(6.0221407600000005e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') +moles_per_cubic_nanometer = NamedUnit(6.022140759999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='Nonenm⁻³') +millimoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') +micromoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') +nanomoles_per_cubic_nanometer = NamedUnit(6.022140759999998e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') +picomoles_per_cubic_nanometer = NamedUnit(6.0221407599999985e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') +femtomoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') +attomoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') +moles_per_cubic_picometer = NamedUnit(6.0221407600000005e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='Nonepm⁻³') +millimoles_per_cubic_picometer = NamedUnit(6.0221407600000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') +micromoles_per_cubic_picometer = NamedUnit(6.022140760000001e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') +nanomoles_per_cubic_picometer = NamedUnit(6.0221407600000005e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') +picomoles_per_cubic_picometer = NamedUnit(6.02214076e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') +femtomoles_per_cubic_picometer = NamedUnit(6.022140760000001e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') +attomoles_per_cubic_picometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') +moles_per_cubic_femtometer = NamedUnit(6.022140759999998e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='Nonefm⁻³') +millimoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') +micromoles_per_cubic_femtometer = NamedUnit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') +nanomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') +picomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') +femtomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') +attomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') +moles_per_cubic_attometer = NamedUnit(6.022140759999998e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='Noneam⁻³') +millimoles_per_cubic_attometer = NamedUnit(6.022140759999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') +micromoles_per_cubic_attometer = NamedUnit(6.022140759999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') +nanomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') +picomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') +femtomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') +attomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') +moles_per_cubic_decimeter = NamedUnit(6.022140759999998e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='Nonedm⁻³') +millimoles_per_cubic_decimeter = NamedUnit(6.0221407599999985e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') +micromoles_per_cubic_decimeter = NamedUnit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') +nanomoles_per_cubic_decimeter = NamedUnit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') +picomoles_per_cubic_decimeter = NamedUnit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') +femtomoles_per_cubic_decimeter = NamedUnit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') +attomoles_per_cubic_decimeter = NamedUnit(602214075.9999999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') +moles_per_cubic_centimeter = NamedUnit(6.022140759999999e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='Nonecm⁻³') +millimoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') +micromoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') +nanomoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') +picomoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') +femtomoles_per_cubic_centimeter = NamedUnit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') +attomoles_per_cubic_centimeter = NamedUnit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') +moles_per_cubic_angstrom = NamedUnit(6.022140759999999e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='NoneÅ⁻³') +millimoles_per_cubic_angstrom = NamedUnit(6.02214076e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') +micromoles_per_cubic_angstrom = NamedUnit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') +nanomoles_per_cubic_angstrom = NamedUnit(6.02214076e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') +picomoles_per_cubic_angstrom = NamedUnit(6.02214076e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') +femtomoles_per_cubic_angstrom = NamedUnit(6.022140759999999e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') +attomoles_per_cubic_angstrom = NamedUnit(6.02214076e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') +moles_per_cubic_mile = NamedUnit(144478840228220.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_mile', ascii_symbol='mol miles^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_mile = NamedUnit(144478840228.22003, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_mile', ascii_symbol='mmol miles^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_mile = NamedUnit(144478840.22822002, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_mile', ascii_symbol='umol miles^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_mile = NamedUnit(144478.84022822, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_mile', ascii_symbol='nmol miles^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_mile = NamedUnit(144.47884022822, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_mile', ascii_symbol='pmol miles^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_mile = NamedUnit(0.14447884022822002, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_mile', ascii_symbol='fmol miles^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_mile = NamedUnit(0.00014447884022822003, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_mile', ascii_symbol='amol miles^-3', symbol='amolNone⁻³') +moles_per_cubic_yard = NamedUnit(7.876662736640442e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_yard', ascii_symbol='mol yrd^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_yard = NamedUnit(7.876662736640442e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_yard', ascii_symbol='mmol yrd^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_yard = NamedUnit(7.876662736640442e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_yard', ascii_symbol='umol yrd^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_yard = NamedUnit(787666273664044.2, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_yard', ascii_symbol='nmol yrd^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_yard = NamedUnit(787666273664.0442, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_yard', ascii_symbol='pmol yrd^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_yard = NamedUnit(787666273.6640443, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_yard', ascii_symbol='fmol yrd^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_yard = NamedUnit(787666.2736640442, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_yard', ascii_symbol='amol yrd^-3', symbol='amolNone⁻³') +moles_per_cubic_foot = NamedUnit(2.1266989388929195e+25, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_foot', ascii_symbol='mol ft^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_foot = NamedUnit(2.1266989388929197e+22, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_foot', ascii_symbol='mmol ft^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_foot = NamedUnit(2.1266989388929196e+19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_foot', ascii_symbol='umol ft^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_foot = NamedUnit(2.1266989388929196e+16, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_foot', ascii_symbol='nmol ft^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_foot = NamedUnit(21266989388929.2, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_foot', ascii_symbol='pmol ft^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_foot = NamedUnit(21266989388.9292, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_foot', ascii_symbol='fmol ft^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_foot = NamedUnit(21266989.388929196, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_foot', ascii_symbol='amol ft^-3', symbol='amolNone⁻³') +moles_per_cubic_inch = NamedUnit(3.6749357664069658e+28, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_inch', ascii_symbol='mol in^-3', symbol='NoneNone⁻³') +millimoles_per_cubic_inch = NamedUnit(3.674935766406966e+25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_inch', ascii_symbol='mmol in^-3', symbol='mmolNone⁻³') +micromoles_per_cubic_inch = NamedUnit(3.674935766406966e+22, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_inch', ascii_symbol='umol in^-3', symbol='µmolNone⁻³') +nanomoles_per_cubic_inch = NamedUnit(3.674935766406966e+19, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_inch', ascii_symbol='nmol in^-3', symbol='nmolNone⁻³') +picomoles_per_cubic_inch = NamedUnit(3.674935766406966e+16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_inch', ascii_symbol='pmol in^-3', symbol='pmolNone⁻³') +femtomoles_per_cubic_inch = NamedUnit(36749357664069.664, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_inch', ascii_symbol='fmol in^-3', symbol='fmolNone⁻³') +attomoles_per_cubic_inch = NamedUnit(36749357664.069664, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_inch', ascii_symbol='amol in^-3', symbol='amolNone⁻³') # # Lookup table from symbols to units @@ -1673,6 +1917,13 @@ def __init__(self, name: str, units: list[Unit]): "fmol": femtomoles, "amol": attomoles, "kgForce": kg_force, + "miles": miles, + "yrd": yards, + "ft": feet, + "in": inches, + "lb": pounds, + "oz": ounces, + "psi": pound_force_per_square_inch, "yr": years, "year": years, "day": days, @@ -1707,6 +1958,10 @@ def __init__(self, name: str, units: list[Unit]): decimeters, centimeters, angstroms, + miles, + yards, + feet, + inches, ]) area = UnitGroup( @@ -1728,6 +1983,10 @@ def __init__(self, name: str, units: list[Unit]): square_decimeters, square_centimeters, square_angstroms, + square_miles, + square_yards, + square_feet, + square_inches, ]) volume = UnitGroup( @@ -1750,6 +2009,10 @@ def __init__(self, name: str, units: list[Unit]): cubic_decimeters, cubic_centimeters, cubic_angstroms, + cubic_miles, + cubic_yards, + cubic_feet, + cubic_inches, ]) inverse_length = UnitGroup( @@ -1771,6 +2034,10 @@ def __init__(self, name: str, units: list[Unit]): per_decimeter, per_centimeter, per_angstrom, + per_mile, + per_yard, + per_foot, + per_inch, ]) inverse_area = UnitGroup( @@ -1792,6 +2059,10 @@ def __init__(self, name: str, units: list[Unit]): per_square_decimeter, per_square_centimeter, per_square_angstrom, + per_square_mile, + per_square_yard, + per_square_foot, + per_square_inch, ]) inverse_volume = UnitGroup( @@ -1813,6 +2084,10 @@ def __init__(self, name: str, units: list[Unit]): per_cubic_decimeter, per_cubic_centimeter, per_cubic_angstrom, + per_cubic_mile, + per_cubic_yard, + per_cubic_foot, + per_cubic_inch, ]) time = UnitGroup( @@ -2029,6 +2304,50 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_hour, angstroms_per_day, angstroms_per_year, + miles_per_second, + miles_per_millisecond, + miles_per_microsecond, + miles_per_nanosecond, + miles_per_picosecond, + miles_per_femtosecond, + miles_per_attosecond, + miles_per_minute, + miles_per_hour, + miles_per_day, + miles_per_year, + yards_per_second, + yards_per_millisecond, + yards_per_microsecond, + yards_per_nanosecond, + yards_per_picosecond, + yards_per_femtosecond, + yards_per_attosecond, + yards_per_minute, + yards_per_hour, + yards_per_day, + yards_per_year, + feet_per_second, + feet_per_millisecond, + feet_per_microsecond, + feet_per_nanosecond, + feet_per_picosecond, + feet_per_femtosecond, + feet_per_attosecond, + feet_per_minute, + feet_per_hour, + feet_per_day, + feet_per_year, + inches_per_second, + inches_per_millisecond, + inches_per_microsecond, + inches_per_nanosecond, + inches_per_picosecond, + inches_per_femtosecond, + inches_per_attosecond, + inches_per_minute, + inches_per_hour, + inches_per_day, + inches_per_year, ]) acceleration = UnitGroup( @@ -2210,6 +2529,50 @@ def __init__(self, name: str, units: list[Unit]): angstroms_per_square_hour, angstroms_per_square_day, angstroms_per_square_year, + miles_per_square_second, + miles_per_square_millisecond, + miles_per_square_microsecond, + miles_per_square_nanosecond, + miles_per_square_picosecond, + miles_per_square_femtosecond, + miles_per_square_attosecond, + miles_per_square_minute, + miles_per_square_hour, + miles_per_square_day, + miles_per_square_year, + yards_per_square_second, + yards_per_square_millisecond, + yards_per_square_microsecond, + yards_per_square_nanosecond, + yards_per_square_picosecond, + yards_per_square_femtosecond, + yards_per_square_attosecond, + yards_per_square_minute, + yards_per_square_hour, + yards_per_square_day, + yards_per_square_year, + feet_per_square_second, + feet_per_square_millisecond, + feet_per_square_microsecond, + feet_per_square_nanosecond, + feet_per_square_picosecond, + feet_per_square_femtosecond, + feet_per_square_attosecond, + feet_per_square_minute, + feet_per_square_hour, + feet_per_square_day, + feet_per_square_year, + inches_per_square_second, + inches_per_square_millisecond, + inches_per_square_microsecond, + inches_per_square_nanosecond, + inches_per_square_picosecond, + inches_per_square_femtosecond, + inches_per_square_attosecond, + inches_per_square_minute, + inches_per_square_hour, + inches_per_square_day, + inches_per_square_year, ]) density = UnitGroup( @@ -2229,6 +2592,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_meter, attograms_per_cubic_meter, atomic_mass_units_per_cubic_meter, + pounds_per_cubic_meter, + ounces_per_cubic_meter, grams_per_cubic_exameter, exagrams_per_cubic_exameter, petagrams_per_cubic_exameter, @@ -2243,6 +2608,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_exameter, attograms_per_cubic_exameter, atomic_mass_units_per_cubic_exameter, + pounds_per_cubic_exameter, + ounces_per_cubic_exameter, grams_per_cubic_petameter, exagrams_per_cubic_petameter, petagrams_per_cubic_petameter, @@ -2257,6 +2624,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_petameter, attograms_per_cubic_petameter, atomic_mass_units_per_cubic_petameter, + pounds_per_cubic_petameter, + ounces_per_cubic_petameter, grams_per_cubic_terameter, exagrams_per_cubic_terameter, petagrams_per_cubic_terameter, @@ -2271,6 +2640,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_terameter, attograms_per_cubic_terameter, atomic_mass_units_per_cubic_terameter, + pounds_per_cubic_terameter, + ounces_per_cubic_terameter, grams_per_cubic_gigameter, exagrams_per_cubic_gigameter, petagrams_per_cubic_gigameter, @@ -2285,6 +2656,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_gigameter, attograms_per_cubic_gigameter, atomic_mass_units_per_cubic_gigameter, + pounds_per_cubic_gigameter, + ounces_per_cubic_gigameter, grams_per_cubic_megameter, exagrams_per_cubic_megameter, petagrams_per_cubic_megameter, @@ -2299,6 +2672,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_megameter, attograms_per_cubic_megameter, atomic_mass_units_per_cubic_megameter, + pounds_per_cubic_megameter, + ounces_per_cubic_megameter, grams_per_cubic_kilometer, exagrams_per_cubic_kilometer, petagrams_per_cubic_kilometer, @@ -2313,6 +2688,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_kilometer, attograms_per_cubic_kilometer, atomic_mass_units_per_cubic_kilometer, + pounds_per_cubic_kilometer, + ounces_per_cubic_kilometer, grams_per_cubic_millimeter, exagrams_per_cubic_millimeter, petagrams_per_cubic_millimeter, @@ -2327,6 +2704,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_millimeter, attograms_per_cubic_millimeter, atomic_mass_units_per_cubic_millimeter, + pounds_per_cubic_millimeter, + ounces_per_cubic_millimeter, grams_per_cubic_micrometer, exagrams_per_cubic_micrometer, petagrams_per_cubic_micrometer, @@ -2341,6 +2720,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_micrometer, attograms_per_cubic_micrometer, atomic_mass_units_per_cubic_micrometer, + pounds_per_cubic_micrometer, + ounces_per_cubic_micrometer, grams_per_cubic_nanometer, exagrams_per_cubic_nanometer, petagrams_per_cubic_nanometer, @@ -2355,6 +2736,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_nanometer, attograms_per_cubic_nanometer, atomic_mass_units_per_cubic_nanometer, + pounds_per_cubic_nanometer, + ounces_per_cubic_nanometer, grams_per_cubic_picometer, exagrams_per_cubic_picometer, petagrams_per_cubic_picometer, @@ -2369,6 +2752,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_picometer, attograms_per_cubic_picometer, atomic_mass_units_per_cubic_picometer, + pounds_per_cubic_picometer, + ounces_per_cubic_picometer, grams_per_cubic_femtometer, exagrams_per_cubic_femtometer, petagrams_per_cubic_femtometer, @@ -2383,6 +2768,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_femtometer, attograms_per_cubic_femtometer, atomic_mass_units_per_cubic_femtometer, + pounds_per_cubic_femtometer, + ounces_per_cubic_femtometer, grams_per_cubic_attometer, exagrams_per_cubic_attometer, petagrams_per_cubic_attometer, @@ -2397,6 +2784,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_attometer, attograms_per_cubic_attometer, atomic_mass_units_per_cubic_attometer, + pounds_per_cubic_attometer, + ounces_per_cubic_attometer, grams_per_cubic_decimeter, exagrams_per_cubic_decimeter, petagrams_per_cubic_decimeter, @@ -2411,6 +2800,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_decimeter, attograms_per_cubic_decimeter, atomic_mass_units_per_cubic_decimeter, + pounds_per_cubic_decimeter, + ounces_per_cubic_decimeter, grams_per_cubic_centimeter, exagrams_per_cubic_centimeter, petagrams_per_cubic_centimeter, @@ -2425,6 +2816,8 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_centimeter, attograms_per_cubic_centimeter, atomic_mass_units_per_cubic_centimeter, + pounds_per_cubic_centimeter, + ounces_per_cubic_centimeter, grams_per_cubic_angstrom, exagrams_per_cubic_angstrom, petagrams_per_cubic_angstrom, @@ -2439,6 +2832,72 @@ def __init__(self, name: str, units: list[Unit]): femtograms_per_cubic_angstrom, attograms_per_cubic_angstrom, atomic_mass_units_per_cubic_angstrom, + pounds_per_cubic_angstrom, + ounces_per_cubic_angstrom, + grams_per_cubic_mile, + exagrams_per_cubic_mile, + petagrams_per_cubic_mile, + teragrams_per_cubic_mile, + gigagrams_per_cubic_mile, + megagrams_per_cubic_mile, + kilograms_per_cubic_mile, + milligrams_per_cubic_mile, + micrograms_per_cubic_mile, + nanograms_per_cubic_mile, + picograms_per_cubic_mile, + femtograms_per_cubic_mile, + attograms_per_cubic_mile, + atomic_mass_units_per_cubic_mile, + pounds_per_cubic_mile, + ounces_per_cubic_mile, + grams_per_cubic_yard, + exagrams_per_cubic_yard, + petagrams_per_cubic_yard, + teragrams_per_cubic_yard, + gigagrams_per_cubic_yard, + megagrams_per_cubic_yard, + kilograms_per_cubic_yard, + milligrams_per_cubic_yard, + micrograms_per_cubic_yard, + nanograms_per_cubic_yard, + picograms_per_cubic_yard, + femtograms_per_cubic_yard, + attograms_per_cubic_yard, + atomic_mass_units_per_cubic_yard, + pounds_per_cubic_yard, + ounces_per_cubic_yard, + grams_per_cubic_foot, + exagrams_per_cubic_foot, + petagrams_per_cubic_foot, + teragrams_per_cubic_foot, + gigagrams_per_cubic_foot, + megagrams_per_cubic_foot, + kilograms_per_cubic_foot, + milligrams_per_cubic_foot, + micrograms_per_cubic_foot, + nanograms_per_cubic_foot, + picograms_per_cubic_foot, + femtograms_per_cubic_foot, + attograms_per_cubic_foot, + atomic_mass_units_per_cubic_foot, + pounds_per_cubic_foot, + ounces_per_cubic_foot, + grams_per_cubic_inch, + exagrams_per_cubic_inch, + petagrams_per_cubic_inch, + teragrams_per_cubic_inch, + gigagrams_per_cubic_inch, + megagrams_per_cubic_inch, + kilograms_per_cubic_inch, + milligrams_per_cubic_inch, + micrograms_per_cubic_inch, + nanograms_per_cubic_inch, + picograms_per_cubic_inch, + femtograms_per_cubic_inch, + attograms_per_cubic_inch, + atomic_mass_units_per_cubic_inch, + pounds_per_cubic_inch, + ounces_per_cubic_inch, ]) force = UnitGroup( @@ -2476,6 +2935,7 @@ def __init__(self, name: str, units: list[Unit]): picopascals, femtopascals, attopascals, + pound_force_per_square_inch, ]) energy = UnitGroup( @@ -2836,4 +3296,32 @@ def __init__(self, name: str, units: list[Unit]): picomoles_per_cubic_angstrom, femtomoles_per_cubic_angstrom, attomoles_per_cubic_angstrom, + moles_per_cubic_mile, + millimoles_per_cubic_mile, + micromoles_per_cubic_mile, + nanomoles_per_cubic_mile, + picomoles_per_cubic_mile, + femtomoles_per_cubic_mile, + attomoles_per_cubic_mile, + moles_per_cubic_yard, + millimoles_per_cubic_yard, + micromoles_per_cubic_yard, + nanomoles_per_cubic_yard, + picomoles_per_cubic_yard, + femtomoles_per_cubic_yard, + attomoles_per_cubic_yard, + moles_per_cubic_foot, + millimoles_per_cubic_foot, + micromoles_per_cubic_foot, + nanomoles_per_cubic_foot, + picomoles_per_cubic_foot, + femtomoles_per_cubic_foot, + attomoles_per_cubic_foot, + moles_per_cubic_inch, + millimoles_per_cubic_inch, + micromoles_per_cubic_inch, + nanomoles_per_cubic_inch, + picomoles_per_cubic_inch, + femtomoles_per_cubic_inch, + attomoles_per_cubic_inch, ]) From 1dc82d7561b4d387946fcdaa0c654d153ea02bc6 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 16:58:02 +0100 Subject: [PATCH 500/675] More units --- sasdata/quantities/_build_tables.py | 5 +++-- sasdata/quantities/accessors.py | 8 ++++++-- sasdata/quantities/quantities_tests.py | 5 +++-- sasdata/quantities/quantity.py | 2 +- sasdata/quantities/units.py | 13 ++++++++++--- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index ba0db8bb2..43367989e 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -75,8 +75,9 @@ ("ft", None, "foot", "feet", 0.3048, 1, 0, 0, 0, 0, 0, 0, []), ("in", None, "inch", "inches", 0.0254, 1, 0, 0, 0, 0, 0, 0, []), ("lb", None, "pound", "pounds", 0.45359237, 0, 0, 1, 0, 0, 0, 0, []), + ("lbf", None, "pound force", "pounds force", 4.448222, 1, -2, 1, 0, 0, 0, 0, []), ("oz", None, "ounce", "ounces", 0.45359237/16, 0, 0, 1, 0, 0, 0, 0, []), - ("psi", None, "pound force per square inch", "pound force per square inch", 6.894757e3, -1, -2, 1, 0, 0, 0, 0, []), + ("psi", None, "pound force per square inch", "pounds force per square inch", 4.448222/(0.0254**2), -1, -2, 1, 0, 0, 0, 0, []), ] aliases = { @@ -372,4 +373,4 @@ def format_name(name: str): fid.write("\nall_si = [\n") for name in si_unit_names: fid.write(f" {name},\n") - fid.write("]\n") + fid.write("]\n") \ No newline at end of file diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 5882a9795..945e6f3b5 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -3830,6 +3830,10 @@ def attonewtons(self) -> T: def kg_force(self) -> T: return self.quantity.in_units_of(units.kg_force) + @property + def pounds_force(self) -> T: + return self.quantity.in_units_of(units.pounds_force) + class PressureAccessor[T](Accessor[T]): @@ -3888,8 +3892,8 @@ def attopascals(self) -> T: return self.quantity.in_units_of(units.attopascals) @property - def pound_force_per_square_inch(self) -> T: - return self.quantity.in_units_of(units.pound_force_per_square_inch) + def pounds_force_per_square_inch(self) -> T: + return self.quantity.in_units_of(units.pounds_force_per_square_inch) diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index 28bab065d..3c66624a5 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -62,17 +62,18 @@ def test_mixed_quantity_add_sub(unit_1, unit_2): with pytest.raises(UnitError): Quantity(1, unit_1) + Quantity(1, unit_2) -def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float): +def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float, abs=1e-9): """ Helper function for testing units that are multiples of each other """ assert u1.equivalent(u2), "Units should be compatible for this test" - assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=1e-9) + assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) def test_american_units(): assert_unit_ratio(units.feet, units.inches, 12) assert_unit_ratio(units.yards, units.inches, 36) assert_unit_ratio(units.miles, units.inches, 63360) + assert_unit_ratio(units.pounds_force_per_square_inch, units.pounds_force / (units.inches**2), 1, abs=1e-5) @pytest.mark.parametrize("unit_1", si.all_si) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 0c98c6cce..2c07a3d95 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -57,7 +57,7 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: def __add__(self: Self, other: Self | ArrayLike) -> Self: if isinstance(other, Quantity): if self.units.equivalent(other.units): - return Quantity(self.value + (other.value * other.scale)/self.scale, self.units) + return Quantity(self.value + (other.value * other.units.scale)/self.units.scale, self.units) else: raise UnitError(f"Units do not have the same dimensionality: {self.units} vs {other.units}") diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index ba3d8ae18..80fcba319 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -279,6 +279,7 @@ def __repr__(self): return f"Unit[{self.scale}, {self.dimensions}]" class NamedUnit(Unit): + """ Units, but they have a name, and a symbol""" def __init__(self, si_scaling_factor: float, dimensions: Dimensions, @@ -291,6 +292,9 @@ def __init__(self, self.ascii_symbol = ascii_symbol self.symbol = symbol + def __repr__(self): + return self.name + # # Parsing plan: # Require unknown amounts of units to be explicitly positive or negative? @@ -614,8 +618,9 @@ def __init__(self, name: str, units: list[Unit]): feet = NamedUnit(0.3048, Dimensions(1, 0, 0, 0, 0, 0, 0),name='feet',ascii_symbol='ft',symbol='ft') inches = NamedUnit(0.0254, Dimensions(1, 0, 0, 0, 0, 0, 0),name='inches',ascii_symbol='in',symbol='in') pounds = NamedUnit(0.45359237, Dimensions(0, 0, 1, 0, 0, 0, 0),name='pounds',ascii_symbol='lb',symbol='lb') +pounds_force = NamedUnit(4.448222, Dimensions(1, -2, 1, 0, 0, 0, 0),name='pounds_force',ascii_symbol='lbf',symbol='lbf') ounces = NamedUnit(0.028349523125, Dimensions(0, 0, 1, 0, 0, 0, 0),name='ounces',ascii_symbol='oz',symbol='oz') -pound_force_per_square_inch = NamedUnit(6894.757, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pound_force_per_square_inch',ascii_symbol='psi',symbol='psi') +pounds_force_per_square_inch = NamedUnit(6894.757, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pounds_force_per_square_inch',ascii_symbol='psi',symbol='psi') square_meters = NamedUnit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = NamedUnit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = NamedUnit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -1922,8 +1927,9 @@ def __init__(self, name: str, units: list[Unit]): "ft": feet, "in": inches, "lb": pounds, + "lbf": pounds_force, "oz": ounces, - "psi": pound_force_per_square_inch, + "psi": pounds_force_per_square_inch, "yr": years, "year": years, "day": days, @@ -2917,6 +2923,7 @@ def __init__(self, name: str, units: list[Unit]): femtonewtons, attonewtons, kg_force, + pounds_force, ]) pressure = UnitGroup( @@ -2935,7 +2942,7 @@ def __init__(self, name: str, units: list[Unit]): picopascals, femtopascals, attopascals, - pound_force_per_square_inch, + pounds_force_per_square_inch, ]) energy = UnitGroup( From 696609835a561bff4d7bb432e8e539b8227616b8 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 7 Aug 2024 17:14:36 +0100 Subject: [PATCH 501/675] Notes --- sasdata/quantities/_build_tables.py | 7 ++++++- sasdata/quantities/units.py | 3 +-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 43367989e..bce6f1a73 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -80,6 +80,10 @@ ("psi", None, "pound force per square inch", "pounds force per square inch", 4.448222/(0.0254**2), -1, -2, 1, 0, 0, 0, 0, []), ] +# TODO: +# Add Hartree? Rydberg? Bohrs? +# Add CGS + aliases = { "y": ["yr", "year"], "d": ["day"], @@ -283,7 +287,8 @@ def format_name(name: str): fid.write("\n#\n# Lookup table from symbols to units\n#\n\n") fid.write("symbol_lookup = {\n") for k in symbol_lookup: - fid.write(f' "{k}": {symbol_lookup[k]},\n') + if k != "none": + fid.write(f' "{k}": {symbol_lookup[k]},\n') fid.write("}\n\n") # diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 80fcba319..3b5e6fdb4 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -620,7 +620,7 @@ def __init__(self, name: str, units: list[Unit]): pounds = NamedUnit(0.45359237, Dimensions(0, 0, 1, 0, 0, 0, 0),name='pounds',ascii_symbol='lb',symbol='lb') pounds_force = NamedUnit(4.448222, Dimensions(1, -2, 1, 0, 0, 0, 0),name='pounds_force',ascii_symbol='lbf',symbol='lbf') ounces = NamedUnit(0.028349523125, Dimensions(0, 0, 1, 0, 0, 0, 0),name='ounces',ascii_symbol='oz',symbol='oz') -pounds_force_per_square_inch = NamedUnit(6894.757, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pounds_force_per_square_inch',ascii_symbol='psi',symbol='psi') +pounds_force_per_square_inch = NamedUnit(6894.757889515779, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pounds_force_per_square_inch',ascii_symbol='psi',symbol='psi') square_meters = NamedUnit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = NamedUnit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = NamedUnit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -1896,7 +1896,6 @@ def __init__(self, name: str, units: list[Unit]): "deg": degrees, "rad": radians, "sr": stradians, - "none": none, "l": litres, "eV": electronvolts, "EeV": exaelectronvolts, From c01fbf81f70d0ea42bf86213902f7f1d21886546 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 8 Aug 2024 10:40:39 +0100 Subject: [PATCH 502/675] Notes --- sasdata/quantities/_build_tables.py | 1 + sasdata/quantities/_units_base.py | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index bce6f1a73..c2adc2afa 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -271,6 +271,7 @@ def format_name(name: str): unit_types[hash(dimensions)].append(name) + # TODO: Torque, Momentum, Entropy # # Add aliases to symbol lookup table diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 995c62ce7..8df62bcc6 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -195,7 +195,14 @@ def __repr__(self): return f"Unit[{self.scale}, {self.dimensions}]" class NamedUnit(Unit): - """ Units, but they have a name, and a symbol""" + """ Units, but they have a name, and a symbol + + :si_scaling_factor: Number of these units per SI equivalent + :param dimensions: Dimensions object representing the dimensionality of these units + :param name: Name of unit - string without unicode + :param ascii_symbol: Symbol for unit without unicode + :param symbol: Unicode symbol + """ def __init__(self, si_scaling_factor: float, dimensions: Dimensions, From 10eaeacc0e1853ec679525eaa5d5511bd23bfd43 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 8 Aug 2024 16:20:41 +0100 Subject: [PATCH 503/675] start of metadata structure --- sasdata/metadata.py | 368 +- sasdata/quantities/_accessor_base.py | 41 +- sasdata/quantities/_build_tables.py | 10 +- sasdata/quantities/accessors.py | 7551 +++++++++++++++++++++----- sasdata/quantities/units.py | 9 +- 5 files changed, 6625 insertions(+), 1354 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 28b94d42e..78faa107f 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -1,8 +1,63 @@ -from typing import TypeVar +import numpy as np +from numpy.typing import ArrayLike -from numpy._typing import ArrayLike +import sasdata.quantities.units as units +from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor +class Detector: + """ + Detector information + """ -from sasdata.quantities.quantity import Unit, Quantity + def __init__(self, target_object): + self.target_object = target_object + + # Name of the instrument [string] + self.name = StringAccessor(self.target_object, "detector.name") + + # Sample to detector distance [float] [mm] + self.distance = LengthAccessor[float](self.target_object, + "detector.distance", + "detector.distance.units", + default_unit=units.millimeters) + + # Offset of this detector position in X, Y, + # (and Z if necessary) [Vector] [mm] + self.offset = LengthAccessor[ArrayLike](self.target_object, + "detector.offset", + "detector.offset.units", + default_units=units.millimeters) + + self.orientation = AngleAccessor[ArrayLike](self.target_object, + "detector.orientation", + "detector.orientation.units", + default_units=units.degrees) + + self.beam_center = LengthAccessor[ArrayLike](self.target_object, + "detector.beam_center", + "detector.beam_center.units", + default_units=units.millimeters) + + # Pixel size in X, Y, (and Z if necessary) [Vector] [mm] + self.pixel_size = LengthAccessor[ArrayLike](self.target_object, + "detector.pixel_size", + "detector.pixel_size.units", + default_units=units.millimeters) + + # Slit length of the instrument for this detector.[float] [mm] + self.slit_length = LengthAccessor[float](self.target_object, + "detector.slit_length", + "detector.slit_length.units", + default_units=units.millimeters) + + def summary(self): + return (f"Detector:\n" + f" Name: {self.name.value}\n" + f" Distance: {self.distance.value}\n" + f" Offset: {self.offset.value}\n" + f" Orientation: {self.orientation.value}\n" + f" Beam center: {self.beam_center.value}\n" + f" Pixel size: {self.pixel_size.value}\n" + f" Slit length: {self.slit_length.value}\n") def __init__(self, target_object: AccessorTarget): @@ -44,80 +99,253 @@ def __init__(self, target_object: AccessorTarget): "slit_length.units", default_unit=units.millimeters) -class RawMetaData: - pass +class Aperture: + def __init__(self, target_object): + self.target_object = target_object -FieldDataType = TypeVar("FieldDataType") -OutputDataType = TypeVar("OutputDataType") + # Name + name = StringAccessor(self.target_object, "aperture.name") -class Accessor[FieldDataType, OutputDataType]: - def __init__(self, target_field: str): - self._target_field = target_field + # Type + type = StringAccessor(self.target_object, "aperture.type") - def _raw_values(self) -> FieldDataType: - raise NotImplementedError("not implemented in base class") + # Size name - TODO: What is the name of a size + size_name = StringAccessor(self.target_object, "aperture.size_name") - @property - def value(self) -> OutputDataType: - raise NotImplementedError("value not implemented in base class") + # Aperture size [Vector] # TODO: Wat!?! + size = QuantityAccessor(self.target_object, + "aperture.size", + "aperture.size", + default_unit=units.millimeters) + size = None + size_unit = 'mm' + # Aperture distance [float] + distance = None + distance_unit = 'mm' - -class QuantityAccessor(Accessor[ArrayLike, Quantity[ArrayLike]]): - def __init__(self, target_field: str, units_field: str | None = None): - super().__init__(target_field) - self._units_field = units_field - - def _units(self) -> Unit: - pass - - def _raw_values(self) -> ArrayLike: + def summary(self): 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 - - @property - def value(self) -> str: - return self._raw_values() - -# -# Quantity specific accessors, provides helper methods for quantities with known dimensionality -# - -class LengthAccessor(QuantityAccessor): - @property - def m(self): - return self.value.in_units_of("m") - - -class TimeAccessor(QuantityAccessor): - pass - - -class TemperatureAccessor(QuantityAccessor): - pass - - -class AbsoluteTemperatureAccessor(QuantityAccessor): - 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 +class Collimation: + """ + Class to hold collimation information + """ + # Name + name = None + # Length [float] [mm] + length = None + length_unit = 'mm' + # Aperture + aperture = None + + def __init__(self): + self.aperture = [] + + def __str__(self): + _str = "Collimation:\n" + _str += " Length: %s [%s]\n" % \ + (str(self.length), str(self.length_unit)) + for item in self.aperture: + _str += " Aperture size:%s [%s]\n" % \ + (str(item.size), str(item.size_unit)) + _str += " Aperture_dist:%s [%s]\n" % \ + (str(item.distance), str(item.distance_unit)) + return _str + + +class Source: + """ + Class to hold source information + """ + # Name + name = None + # Generic radiation type (Type and probe give more specific info) [string] + radiation = None + # Type and probe are only written to by the NXcanSAS reader + # Specific radiation type (Synchotron X-ray, Reactor neutron, etc) [string] + type = None + # Radiation probe (generic probe such as neutron, x-ray, muon, etc) [string] + probe = None + # Beam size name + beam_size_name = None + # Beam size [Vector] [mm] + beam_size = None + beam_size_unit = 'mm' + # Beam shape [string] + beam_shape = None + # Wavelength [float] [Angstrom] + wavelength = None + wavelength_unit = 'A' + # Minimum wavelength [float] [Angstrom] + wavelength_min = None + wavelength_min_unit = 'nm' + # Maximum wavelength [float] [Angstrom] + wavelength_max = None + wavelength_max_unit = 'nm' + # Wavelength spread [float] [Angstrom] + wavelength_spread = None + wavelength_spread_unit = 'percent' + + def __init__(self): + self.beam_size = None #Vector() + + def __str__(self): + _str = "Source:\n" + radiation = self.radiation + if self.radiation is None and self.type and self.probe: + radiation = self.type + " " + self.probe + _str += " Radiation: %s\n" % str(radiation) + _str += " Shape: %s\n" % str(self.beam_shape) + _str += " Wavelength: %s [%s]\n" % \ + (str(self.wavelength), str(self.wavelength_unit)) + _str += " Waveln_min: %s [%s]\n" % \ + (str(self.wavelength_min), str(self.wavelength_min_unit)) + _str += " Waveln_max: %s [%s]\n" % \ + (str(self.wavelength_max), str(self.wavelength_max_unit)) + _str += " Waveln_spread:%s [%s]\n" % \ + (str(self.wavelength_spread), str(self.wavelength_spread_unit)) + _str += " Beam_size: %s [%s]\n" % \ + (str(self.beam_size), str(self.beam_size_unit)) + return _str + + +""" +Definitions of radiation types +""" +NEUTRON = 'neutron' +XRAY = 'x-ray' +MUON = 'muon' +ELECTRON = 'electron' + + +class Sample: + """ + Class to hold the sample description + """ + # Short name for sample + name = '' + # ID + ID = '' + # Thickness [float] [mm] + thickness = None + thickness_unit = 'mm' + # Transmission [float] [fraction] + transmission = None + # Temperature [float] [No Default] + temperature = None + temperature_unit = None + # Position [Vector] [mm] + position = None + position_unit = 'mm' + # Orientation [Vector] [degrees] + orientation = None + orientation_unit = 'degree' + # Details + details = None + # SESANS zacceptance + zacceptance = (0,"") + yacceptance = (0,"") + + def __init__(self): + self.position = None # Vector() + self.orientation = None # Vector() + self.details = [] + + def __str__(self): + _str = "Sample:\n" + _str += " ID: %s\n" % str(self.ID) + _str += " Transmission: %s\n" % str(self.transmission) + _str += " Thickness: %s [%s]\n" % \ + (str(self.thickness), str(self.thickness_unit)) + _str += " Temperature: %s [%s]\n" % \ + (str(self.temperature), str(self.temperature_unit)) + _str += " Position: %s [%s]\n" % \ + (str(self.position), str(self.position_unit)) + _str += " Orientation: %s [%s]\n" % \ + (str(self.orientation), str(self.orientation_unit)) + + _str += " Details:\n" + for item in self.details: + _str += " %s\n" % item + + return _str + + +class Process: + """ + Class that holds information about the processes + performed on the data. + """ + name = '' + date = '' + description = '' + term = None + notes = None + + def __init__(self): + self.term = [] + self.notes = [] + + def is_empty(self): + """ + Return True if the object is empty + """ + return (len(self.name) == 0 and len(self.date) == 0 + and len(self.description) == 0 and len(self.term) == 0 + and len(self.notes) == 0) + + def single_line_desc(self): + """ + Return a single line string representing the process + """ + return "%s %s %s" % (self.name, self.date, self.description) + + def __str__(self): + _str = "Process:\n" + _str += " Name: %s\n" % self.name + _str += " Date: %s\n" % self.date + _str += " Description: %s\n" % self.description + for item in self.term: + _str += " Term: %s\n" % item + for item in self.notes: + _str += " Note: %s\n" % item + return _str + + +class TransmissionSpectrum(object): + """ + Class that holds information about transmission spectrum + for white beams and spallation sources. + """ + name = '' + timestamp = '' + # Wavelength (float) [A] + wavelength = None + wavelength_unit = 'A' + # Transmission (float) [unit less] + transmission = None + transmission_unit = '' + # Transmission Deviation (float) [unit less] + transmission_deviation = None + transmission_deviation_unit = '' + + def __init__(self): + self.wavelength = [] + self.transmission = [] + self.transmission_deviation = [] + + def __str__(self): + _str = "Transmission Spectrum:\n" + _str += " Name: \t{0}\n".format(self.name) + _str += " Timestamp: \t{0}\n".format(self.timestamp) + _str += " Wavelength unit: \t{0}\n".format(self.wavelength_unit) + _str += " Transmission unit:\t{0}\n".format(self.transmission_unit) + _str += " Trans. Dev. unit: \t{0}\n".format( + self.transmission_deviation_unit) + length_list = [len(self.wavelength), len(self.transmission), + len(self.transmission_deviation)] + _str += " Number of Pts: \t{0}\n".format(max(length_list)) + return _str diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index c887b7242..a1437d170 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -4,14 +4,45 @@ import sasdata.quantities.units as units -T = TypeVar("T") +DataType = TypeVar("DataType") +OutputType = TypeVar("OutputType") -class Accessor[T]: +class Accessor[DataType, OutputType]: """ Base class """ - def __init__(self, value_target: str, unit_target: str): - self._value_target = value_target + def __init__(self, target_object, value_target: str): + self.target_object = target_object + self.value_target = value_target + + @property + def value(self) -> OutputType | None: + pass + +class StringAccessor(Accessor[str, str]): + """ String based fields """ + @property + def value(self) -> str | None: + pass + +class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): + """ Base class for accessors that work with quantities that have units """ + def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): + super().__init__(target_object, value_target) self._unit_target = unit_target + self.default_unit = default_unit + + def _lookup_unit(self) -> units.Unit | None: + # TODO: Implement + return None + + def data_unit(self): + unit = self._lookup_unit + if unit is None: + return self.default_unit + else: + return unit + @property - def quantity(self) -> Quantity[T]: + def quantity(self) -> Quantity[DataType]: raise NotImplementedError("Not implemented yet") + diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index c2adc2afa..e5dc41b03 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -343,6 +343,8 @@ def format_name(name: str): fid.write("])\n") + + with open("accessors.py", 'w', encoding=encoding) as fid: @@ -357,14 +359,18 @@ def format_name(name: str): accessor_name = dimension_name.capitalize().replace("_", "") + "Accessor" fid.write(f"\n" - f"class {accessor_name}[T](Accessor[T]):\n" + f"class {accessor_name}[T](QuantityAccessor[T]):\n" f" dimension_name = '{dimension_name}'\n" f"\n") for unit_name in unit_types[hash(dimensions)]: fid.write(f" @property\n" f" def {unit_name}(self) -> T:\n" - f" return self.quantity.in_units_of(units.{unit_name})\n" + f" quantity = self.quantity\n" + f" if quantity is None:\n" + f" return None\n" + f" else:\n" + f" return quantity.in_units_of(units.{unit_name})\n" f"\n") fid.write("\n") diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 945e6f3b5..eaffa5e23 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -84,5127 +84,10126 @@ import sasdata.quantities.units as units -T = TypeVar("T") +DataType = TypeVar("DataType") +OutputType = TypeVar("OutputType") -class Accessor[T]: +class Accessor[DataType, OutputType]: """ Base class """ - def __init__(self, value_target: str, unit_target: str): - self._value_target = value_target + def __init__(self, target_object, value_target: str): + self.target_object = target_object + self.value_target = value_target + + @property + def value(self) -> OutputType | None: + pass + +class StringAccessor(Accessor[str, str]): + """ String based fields """ + @property + def value(self) -> str | None: + pass + +class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): + """ Base class for accessors that work with quantities that have units """ + def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): + super().__init__(target_object, value_target) self._unit_target = unit_target + self.default_unit = default_unit + + def _lookup_unit(self) -> units.Unit | None: + # TODO: Implement + return None + + def data_unit(self): + unit = self._lookup_unit + if unit is None: + return self.default_unit + else: + return unit + @property - def quantity(self) -> Quantity[T]: + def quantity(self) -> Quantity[DataType]: raise NotImplementedError("Not implemented yet") -class LengthAccessor[T](Accessor[T]): + +class LengthAccessor[T](QuantityAccessor[T]): dimension_name = 'length' @property def meters(self) -> T: - return self.quantity.in_units_of(units.meters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters) @property def exameters(self) -> T: - return self.quantity.in_units_of(units.exameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters) @property def petameters(self) -> T: - return self.quantity.in_units_of(units.petameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters) @property def terameters(self) -> T: - return self.quantity.in_units_of(units.terameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters) @property def gigameters(self) -> T: - return self.quantity.in_units_of(units.gigameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters) @property def megameters(self) -> T: - return self.quantity.in_units_of(units.megameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters) @property def kilometers(self) -> T: - return self.quantity.in_units_of(units.kilometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers) @property def millimeters(self) -> T: - return self.quantity.in_units_of(units.millimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters) @property def micrometers(self) -> T: - return self.quantity.in_units_of(units.micrometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers) @property def nanometers(self) -> T: - return self.quantity.in_units_of(units.nanometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers) @property def picometers(self) -> T: - return self.quantity.in_units_of(units.picometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers) @property def femtometers(self) -> T: - return self.quantity.in_units_of(units.femtometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers) @property def attometers(self) -> T: - return self.quantity.in_units_of(units.attometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers) @property def decimeters(self) -> T: - return self.quantity.in_units_of(units.decimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters) @property def centimeters(self) -> T: - return self.quantity.in_units_of(units.centimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters) @property def angstroms(self) -> T: - return self.quantity.in_units_of(units.angstroms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms) @property def miles(self) -> T: - return self.quantity.in_units_of(units.miles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles) @property def yards(self) -> T: - return self.quantity.in_units_of(units.yards) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards) @property def feet(self) -> T: - return self.quantity.in_units_of(units.feet) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet) @property def inches(self) -> T: - return self.quantity.in_units_of(units.inches) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches) -class AreaAccessor[T](Accessor[T]): +class AreaAccessor[T](QuantityAccessor[T]): dimension_name = 'area' @property def square_meters(self) -> T: - return self.quantity.in_units_of(units.square_meters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_meters) @property def square_exameters(self) -> T: - return self.quantity.in_units_of(units.square_exameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_exameters) @property def square_petameters(self) -> T: - return self.quantity.in_units_of(units.square_petameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_petameters) @property def square_terameters(self) -> T: - return self.quantity.in_units_of(units.square_terameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_terameters) @property def square_gigameters(self) -> T: - return self.quantity.in_units_of(units.square_gigameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_gigameters) @property def square_megameters(self) -> T: - return self.quantity.in_units_of(units.square_megameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_megameters) @property def square_kilometers(self) -> T: - return self.quantity.in_units_of(units.square_kilometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_kilometers) @property def square_millimeters(self) -> T: - return self.quantity.in_units_of(units.square_millimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_millimeters) @property def square_micrometers(self) -> T: - return self.quantity.in_units_of(units.square_micrometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_micrometers) @property def square_nanometers(self) -> T: - return self.quantity.in_units_of(units.square_nanometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_nanometers) @property def square_picometers(self) -> T: - return self.quantity.in_units_of(units.square_picometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_picometers) @property def square_femtometers(self) -> T: - return self.quantity.in_units_of(units.square_femtometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_femtometers) @property def square_attometers(self) -> T: - return self.quantity.in_units_of(units.square_attometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_attometers) @property def square_decimeters(self) -> T: - return self.quantity.in_units_of(units.square_decimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_decimeters) @property def square_centimeters(self) -> T: - return self.quantity.in_units_of(units.square_centimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_centimeters) @property def square_angstroms(self) -> T: - return self.quantity.in_units_of(units.square_angstroms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_angstroms) @property def square_miles(self) -> T: - return self.quantity.in_units_of(units.square_miles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_miles) @property def square_yards(self) -> T: - return self.quantity.in_units_of(units.square_yards) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_yards) @property def square_feet(self) -> T: - return self.quantity.in_units_of(units.square_feet) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_feet) @property def square_inches(self) -> T: - return self.quantity.in_units_of(units.square_inches) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.square_inches) -class VolumeAccessor[T](Accessor[T]): +class VolumeAccessor[T](QuantityAccessor[T]): dimension_name = 'volume' @property def litres(self) -> T: - return self.quantity.in_units_of(units.litres) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.litres) @property def cubic_meters(self) -> T: - return self.quantity.in_units_of(units.cubic_meters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_meters) @property def cubic_exameters(self) -> T: - return self.quantity.in_units_of(units.cubic_exameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_exameters) @property def cubic_petameters(self) -> T: - return self.quantity.in_units_of(units.cubic_petameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_petameters) @property def cubic_terameters(self) -> T: - return self.quantity.in_units_of(units.cubic_terameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_terameters) @property def cubic_gigameters(self) -> T: - return self.quantity.in_units_of(units.cubic_gigameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_gigameters) @property def cubic_megameters(self) -> T: - return self.quantity.in_units_of(units.cubic_megameters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_megameters) @property def cubic_kilometers(self) -> T: - return self.quantity.in_units_of(units.cubic_kilometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_kilometers) @property def cubic_millimeters(self) -> T: - return self.quantity.in_units_of(units.cubic_millimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_millimeters) @property def cubic_micrometers(self) -> T: - return self.quantity.in_units_of(units.cubic_micrometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_micrometers) @property def cubic_nanometers(self) -> T: - return self.quantity.in_units_of(units.cubic_nanometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_nanometers) @property def cubic_picometers(self) -> T: - return self.quantity.in_units_of(units.cubic_picometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_picometers) @property def cubic_femtometers(self) -> T: - return self.quantity.in_units_of(units.cubic_femtometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_femtometers) @property def cubic_attometers(self) -> T: - return self.quantity.in_units_of(units.cubic_attometers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_attometers) @property def cubic_decimeters(self) -> T: - return self.quantity.in_units_of(units.cubic_decimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_decimeters) @property def cubic_centimeters(self) -> T: - return self.quantity.in_units_of(units.cubic_centimeters) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_centimeters) @property def cubic_angstroms(self) -> T: - return self.quantity.in_units_of(units.cubic_angstroms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_angstroms) @property def cubic_miles(self) -> T: - return self.quantity.in_units_of(units.cubic_miles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_miles) @property def cubic_yards(self) -> T: - return self.quantity.in_units_of(units.cubic_yards) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_yards) @property def cubic_feet(self) -> T: - return self.quantity.in_units_of(units.cubic_feet) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_feet) @property def cubic_inches(self) -> T: - return self.quantity.in_units_of(units.cubic_inches) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.cubic_inches) -class InverselengthAccessor[T](Accessor[T]): +class InverselengthAccessor[T](QuantityAccessor[T]): dimension_name = 'inverse_length' @property def per_meter(self) -> T: - return self.quantity.in_units_of(units.per_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_meter) @property def per_exameter(self) -> T: - return self.quantity.in_units_of(units.per_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_exameter) @property def per_petameter(self) -> T: - return self.quantity.in_units_of(units.per_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_petameter) @property def per_terameter(self) -> T: - return self.quantity.in_units_of(units.per_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_terameter) @property def per_gigameter(self) -> T: - return self.quantity.in_units_of(units.per_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_gigameter) @property def per_megameter(self) -> T: - return self.quantity.in_units_of(units.per_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_megameter) @property def per_kilometer(self) -> T: - return self.quantity.in_units_of(units.per_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_kilometer) @property def per_millimeter(self) -> T: - return self.quantity.in_units_of(units.per_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_millimeter) @property def per_micrometer(self) -> T: - return self.quantity.in_units_of(units.per_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_micrometer) @property def per_nanometer(self) -> T: - return self.quantity.in_units_of(units.per_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_nanometer) @property def per_picometer(self) -> T: - return self.quantity.in_units_of(units.per_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_picometer) @property def per_femtometer(self) -> T: - return self.quantity.in_units_of(units.per_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_femtometer) @property def per_attometer(self) -> T: - return self.quantity.in_units_of(units.per_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_attometer) @property def per_decimeter(self) -> T: - return self.quantity.in_units_of(units.per_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_decimeter) @property def per_centimeter(self) -> T: - return self.quantity.in_units_of(units.per_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_centimeter) @property def per_angstrom(self) -> T: - return self.quantity.in_units_of(units.per_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_angstrom) @property def per_mile(self) -> T: - return self.quantity.in_units_of(units.per_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_mile) @property def per_yard(self) -> T: - return self.quantity.in_units_of(units.per_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_yard) @property def per_foot(self) -> T: - return self.quantity.in_units_of(units.per_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_foot) @property def per_inch(self) -> T: - return self.quantity.in_units_of(units.per_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_inch) -class InverseareaAccessor[T](Accessor[T]): +class InverseareaAccessor[T](QuantityAccessor[T]): dimension_name = 'inverse_area' @property def per_square_meter(self) -> T: - return self.quantity.in_units_of(units.per_square_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_meter) @property def per_square_exameter(self) -> T: - return self.quantity.in_units_of(units.per_square_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_exameter) @property def per_square_petameter(self) -> T: - return self.quantity.in_units_of(units.per_square_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_petameter) @property def per_square_terameter(self) -> T: - return self.quantity.in_units_of(units.per_square_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_terameter) @property def per_square_gigameter(self) -> T: - return self.quantity.in_units_of(units.per_square_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_gigameter) @property def per_square_megameter(self) -> T: - return self.quantity.in_units_of(units.per_square_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_megameter) @property def per_square_kilometer(self) -> T: - return self.quantity.in_units_of(units.per_square_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_kilometer) @property def per_square_millimeter(self) -> T: - return self.quantity.in_units_of(units.per_square_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_millimeter) @property def per_square_micrometer(self) -> T: - return self.quantity.in_units_of(units.per_square_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_micrometer) @property def per_square_nanometer(self) -> T: - return self.quantity.in_units_of(units.per_square_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_nanometer) @property def per_square_picometer(self) -> T: - return self.quantity.in_units_of(units.per_square_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_picometer) @property def per_square_femtometer(self) -> T: - return self.quantity.in_units_of(units.per_square_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_femtometer) @property def per_square_attometer(self) -> T: - return self.quantity.in_units_of(units.per_square_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_attometer) @property def per_square_decimeter(self) -> T: - return self.quantity.in_units_of(units.per_square_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_decimeter) @property def per_square_centimeter(self) -> T: - return self.quantity.in_units_of(units.per_square_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_centimeter) @property def per_square_angstrom(self) -> T: - return self.quantity.in_units_of(units.per_square_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_angstrom) @property def per_square_mile(self) -> T: - return self.quantity.in_units_of(units.per_square_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_mile) @property def per_square_yard(self) -> T: - return self.quantity.in_units_of(units.per_square_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_yard) @property def per_square_foot(self) -> T: - return self.quantity.in_units_of(units.per_square_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_foot) @property def per_square_inch(self) -> T: - return self.quantity.in_units_of(units.per_square_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_square_inch) -class InversevolumeAccessor[T](Accessor[T]): +class InversevolumeAccessor[T](QuantityAccessor[T]): dimension_name = 'inverse_volume' @property def per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_meter) @property def per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_exameter) @property def per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_petameter) @property def per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_terameter) @property def per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_gigameter) @property def per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_megameter) @property def per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_kilometer) @property def per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_millimeter) @property def per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_micrometer) @property def per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_nanometer) @property def per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_picometer) @property def per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_femtometer) @property def per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_attometer) @property def per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_decimeter) @property def per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_centimeter) @property def per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_angstrom) @property def per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_mile) @property def per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_yard) @property def per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_foot) @property def per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.per_cubic_inch) -class TimeAccessor[T](Accessor[T]): +class TimeAccessor[T](QuantityAccessor[T]): dimension_name = 'time' @property def seconds(self) -> T: - return self.quantity.in_units_of(units.seconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.seconds) @property def milliseconds(self) -> T: - return self.quantity.in_units_of(units.milliseconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milliseconds) @property def microseconds(self) -> T: - return self.quantity.in_units_of(units.microseconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microseconds) @property def nanoseconds(self) -> T: - return self.quantity.in_units_of(units.nanoseconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanoseconds) @property def picoseconds(self) -> T: - return self.quantity.in_units_of(units.picoseconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picoseconds) @property def femtoseconds(self) -> T: - return self.quantity.in_units_of(units.femtoseconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtoseconds) @property def attoseconds(self) -> T: - return self.quantity.in_units_of(units.attoseconds) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attoseconds) @property def minutes(self) -> T: - return self.quantity.in_units_of(units.minutes) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.minutes) @property def hours(self) -> T: - return self.quantity.in_units_of(units.hours) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.hours) @property def days(self) -> T: - return self.quantity.in_units_of(units.days) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.days) @property def years(self) -> T: - return self.quantity.in_units_of(units.years) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.years) -class RateAccessor[T](Accessor[T]): +class RateAccessor[T](QuantityAccessor[T]): dimension_name = 'rate' @property def hertz(self) -> T: - return self.quantity.in_units_of(units.hertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.hertz) @property def exahertz(self) -> T: - return self.quantity.in_units_of(units.exahertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exahertz) @property def petahertz(self) -> T: - return self.quantity.in_units_of(units.petahertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petahertz) @property def terahertz(self) -> T: - return self.quantity.in_units_of(units.terahertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terahertz) @property def gigahertz(self) -> T: - return self.quantity.in_units_of(units.gigahertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigahertz) @property def megahertz(self) -> T: - return self.quantity.in_units_of(units.megahertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megahertz) @property def kilohertz(self) -> T: - return self.quantity.in_units_of(units.kilohertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilohertz) @property def millihertz(self) -> T: - return self.quantity.in_units_of(units.millihertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millihertz) @property def microhertz(self) -> T: - return self.quantity.in_units_of(units.microhertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microhertz) @property def nanohertz(self) -> T: - return self.quantity.in_units_of(units.nanohertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanohertz) @property def picohertz(self) -> T: - return self.quantity.in_units_of(units.picohertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picohertz) @property def femtohertz(self) -> T: - return self.quantity.in_units_of(units.femtohertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtohertz) @property def attohertz(self) -> T: - return self.quantity.in_units_of(units.attohertz) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attohertz) -class SpeedAccessor[T](Accessor[T]): +class SpeedAccessor[T](QuantityAccessor[T]): dimension_name = 'speed' @property def meters_per_second(self) -> T: - return self.quantity.in_units_of(units.meters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_second) @property def meters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_millisecond) @property def meters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_microsecond) @property def meters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_nanosecond) @property def meters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_picosecond) @property def meters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_femtosecond) @property def meters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_attosecond) @property def meters_per_minute(self) -> T: - return self.quantity.in_units_of(units.meters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_minute) @property def meters_per_hour(self) -> T: - return self.quantity.in_units_of(units.meters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_hour) @property def meters_per_day(self) -> T: - return self.quantity.in_units_of(units.meters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_day) @property def meters_per_year(self) -> T: - return self.quantity.in_units_of(units.meters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_year) @property def exameters_per_second(self) -> T: - return self.quantity.in_units_of(units.exameters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_second) @property def exameters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_millisecond) @property def exameters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_microsecond) @property def exameters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_nanosecond) @property def exameters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_picosecond) @property def exameters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_femtosecond) @property def exameters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_attosecond) @property def exameters_per_minute(self) -> T: - return self.quantity.in_units_of(units.exameters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_minute) @property def exameters_per_hour(self) -> T: - return self.quantity.in_units_of(units.exameters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_hour) @property def exameters_per_day(self) -> T: - return self.quantity.in_units_of(units.exameters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_day) @property def exameters_per_year(self) -> T: - return self.quantity.in_units_of(units.exameters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_year) @property def petameters_per_second(self) -> T: - return self.quantity.in_units_of(units.petameters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_second) @property def petameters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_millisecond) @property def petameters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_microsecond) @property def petameters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_nanosecond) @property def petameters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_picosecond) @property def petameters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_femtosecond) @property def petameters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_attosecond) @property def petameters_per_minute(self) -> T: - return self.quantity.in_units_of(units.petameters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_minute) @property def petameters_per_hour(self) -> T: - return self.quantity.in_units_of(units.petameters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_hour) @property def petameters_per_day(self) -> T: - return self.quantity.in_units_of(units.petameters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_day) @property def petameters_per_year(self) -> T: - return self.quantity.in_units_of(units.petameters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_year) @property def terameters_per_second(self) -> T: - return self.quantity.in_units_of(units.terameters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_second) @property def terameters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_millisecond) @property def terameters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_microsecond) @property def terameters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_nanosecond) @property def terameters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_picosecond) @property def terameters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_femtosecond) @property def terameters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_attosecond) @property def terameters_per_minute(self) -> T: - return self.quantity.in_units_of(units.terameters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_minute) @property def terameters_per_hour(self) -> T: - return self.quantity.in_units_of(units.terameters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_hour) @property def terameters_per_day(self) -> T: - return self.quantity.in_units_of(units.terameters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_day) @property def terameters_per_year(self) -> T: - return self.quantity.in_units_of(units.terameters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_year) @property def gigameters_per_second(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_second) @property def gigameters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_millisecond) @property def gigameters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_microsecond) @property def gigameters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_nanosecond) @property def gigameters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_picosecond) @property def gigameters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_femtosecond) @property def gigameters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_attosecond) @property def gigameters_per_minute(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_minute) @property def gigameters_per_hour(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_hour) @property def gigameters_per_day(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_day) @property def gigameters_per_year(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_year) @property def megameters_per_second(self) -> T: - return self.quantity.in_units_of(units.megameters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_second) @property def megameters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_millisecond) @property def megameters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_microsecond) @property def megameters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_nanosecond) @property def megameters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_picosecond) @property def megameters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_femtosecond) @property def megameters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_attosecond) @property def megameters_per_minute(self) -> T: - return self.quantity.in_units_of(units.megameters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_minute) @property def megameters_per_hour(self) -> T: - return self.quantity.in_units_of(units.megameters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_hour) @property def megameters_per_day(self) -> T: - return self.quantity.in_units_of(units.megameters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_day) @property def megameters_per_year(self) -> T: - return self.quantity.in_units_of(units.megameters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_year) @property def kilometers_per_second(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_second) @property def kilometers_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_millisecond) @property def kilometers_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_microsecond) @property def kilometers_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_nanosecond) @property def kilometers_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_picosecond) @property def kilometers_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_femtosecond) @property def kilometers_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_attosecond) @property def kilometers_per_minute(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_minute) @property def kilometers_per_hour(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_hour) @property def kilometers_per_day(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_day) @property def kilometers_per_year(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_year) @property def millimeters_per_second(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_second) @property def millimeters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_millisecond) @property def millimeters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_microsecond) @property def millimeters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_nanosecond) @property def millimeters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_picosecond) @property def millimeters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_femtosecond) @property def millimeters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_attosecond) @property def millimeters_per_minute(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_minute) @property def millimeters_per_hour(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_hour) @property def millimeters_per_day(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_day) @property def millimeters_per_year(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_year) @property def micrometers_per_second(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_second) @property def micrometers_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_millisecond) @property def micrometers_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_microsecond) @property def micrometers_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_nanosecond) @property def micrometers_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_picosecond) @property def micrometers_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_femtosecond) @property def micrometers_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_attosecond) @property def micrometers_per_minute(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_minute) @property def micrometers_per_hour(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_hour) @property def micrometers_per_day(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_day) @property def micrometers_per_year(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_year) @property def nanometers_per_second(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_second) @property def nanometers_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_millisecond) @property def nanometers_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_microsecond) @property def nanometers_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_nanosecond) @property def nanometers_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_picosecond) @property def nanometers_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_femtosecond) @property def nanometers_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_attosecond) @property def nanometers_per_minute(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_minute) @property def nanometers_per_hour(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_hour) @property def nanometers_per_day(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_day) @property def nanometers_per_year(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_year) @property def picometers_per_second(self) -> T: - return self.quantity.in_units_of(units.picometers_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_second) @property def picometers_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_millisecond) @property def picometers_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_microsecond) @property def picometers_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_nanosecond) @property def picometers_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_picosecond) @property def picometers_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_femtosecond) @property def picometers_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_attosecond) @property def picometers_per_minute(self) -> T: - return self.quantity.in_units_of(units.picometers_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_minute) @property def picometers_per_hour(self) -> T: - return self.quantity.in_units_of(units.picometers_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_hour) @property def picometers_per_day(self) -> T: - return self.quantity.in_units_of(units.picometers_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_day) @property def picometers_per_year(self) -> T: - return self.quantity.in_units_of(units.picometers_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_year) @property def femtometers_per_second(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_second) @property def femtometers_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_millisecond) @property def femtometers_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_microsecond) @property def femtometers_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_nanosecond) @property def femtometers_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_picosecond) @property def femtometers_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_femtosecond) @property def femtometers_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_attosecond) @property def femtometers_per_minute(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_minute) @property def femtometers_per_hour(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_hour) @property def femtometers_per_day(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_day) @property def femtometers_per_year(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_year) @property def attometers_per_second(self) -> T: - return self.quantity.in_units_of(units.attometers_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_second) @property def attometers_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_millisecond) @property def attometers_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_microsecond) @property def attometers_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_nanosecond) @property def attometers_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_picosecond) @property def attometers_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_femtosecond) @property def attometers_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_attosecond) @property def attometers_per_minute(self) -> T: - return self.quantity.in_units_of(units.attometers_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_minute) @property def attometers_per_hour(self) -> T: - return self.quantity.in_units_of(units.attometers_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_hour) @property def attometers_per_day(self) -> T: - return self.quantity.in_units_of(units.attometers_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_day) @property def attometers_per_year(self) -> T: - return self.quantity.in_units_of(units.attometers_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_year) @property def decimeters_per_second(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_second) @property def decimeters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_millisecond) @property def decimeters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_microsecond) @property def decimeters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_nanosecond) @property def decimeters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_picosecond) @property def decimeters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_femtosecond) @property def decimeters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_attosecond) @property def decimeters_per_minute(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_minute) @property def decimeters_per_hour(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_hour) @property def decimeters_per_day(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_day) @property def decimeters_per_year(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_year) @property def centimeters_per_second(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_second) @property def centimeters_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_millisecond) @property def centimeters_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_microsecond) @property def centimeters_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_nanosecond) @property def centimeters_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_picosecond) @property def centimeters_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_femtosecond) @property def centimeters_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_attosecond) @property def centimeters_per_minute(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_minute) @property def centimeters_per_hour(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_hour) @property def centimeters_per_day(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_day) @property def centimeters_per_year(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_year) @property def angstroms_per_second(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_second) @property def angstroms_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_millisecond) @property def angstroms_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_microsecond) @property def angstroms_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_nanosecond) @property def angstroms_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_picosecond) @property def angstroms_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_femtosecond) @property def angstroms_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_attosecond) @property def angstroms_per_minute(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_minute) @property def angstroms_per_hour(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_hour) @property def angstroms_per_day(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_day) @property def angstroms_per_year(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_year) @property def miles_per_second(self) -> T: - return self.quantity.in_units_of(units.miles_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_second) @property def miles_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_millisecond) @property def miles_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_microsecond) @property def miles_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_nanosecond) @property def miles_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_picosecond) @property def miles_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_femtosecond) @property def miles_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_attosecond) @property def miles_per_minute(self) -> T: - return self.quantity.in_units_of(units.miles_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_minute) @property def miles_per_hour(self) -> T: - return self.quantity.in_units_of(units.miles_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_hour) @property def miles_per_day(self) -> T: - return self.quantity.in_units_of(units.miles_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_day) @property def miles_per_year(self) -> T: - return self.quantity.in_units_of(units.miles_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_year) @property def yards_per_second(self) -> T: - return self.quantity.in_units_of(units.yards_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_second) @property def yards_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_millisecond) @property def yards_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_microsecond) @property def yards_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_nanosecond) @property def yards_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_picosecond) @property def yards_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_femtosecond) @property def yards_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_attosecond) @property def yards_per_minute(self) -> T: - return self.quantity.in_units_of(units.yards_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_minute) @property def yards_per_hour(self) -> T: - return self.quantity.in_units_of(units.yards_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_hour) @property def yards_per_day(self) -> T: - return self.quantity.in_units_of(units.yards_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_day) @property def yards_per_year(self) -> T: - return self.quantity.in_units_of(units.yards_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_year) @property def feet_per_second(self) -> T: - return self.quantity.in_units_of(units.feet_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_second) @property def feet_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_millisecond) @property def feet_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_microsecond) @property def feet_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_nanosecond) @property def feet_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_picosecond) @property def feet_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_femtosecond) @property def feet_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_attosecond) @property def feet_per_minute(self) -> T: - return self.quantity.in_units_of(units.feet_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_minute) @property def feet_per_hour(self) -> T: - return self.quantity.in_units_of(units.feet_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_hour) @property def feet_per_day(self) -> T: - return self.quantity.in_units_of(units.feet_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_day) @property def feet_per_year(self) -> T: - return self.quantity.in_units_of(units.feet_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_year) @property def inches_per_second(self) -> T: - return self.quantity.in_units_of(units.inches_per_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_second) @property def inches_per_millisecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_millisecond) @property def inches_per_microsecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_microsecond) @property def inches_per_nanosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_nanosecond) @property def inches_per_picosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_picosecond) @property def inches_per_femtosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_femtosecond) @property def inches_per_attosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_attosecond) @property def inches_per_minute(self) -> T: - return self.quantity.in_units_of(units.inches_per_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_minute) @property def inches_per_hour(self) -> T: - return self.quantity.in_units_of(units.inches_per_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_hour) @property def inches_per_day(self) -> T: - return self.quantity.in_units_of(units.inches_per_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_day) @property def inches_per_year(self) -> T: - return self.quantity.in_units_of(units.inches_per_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_year) -class AccelerationAccessor[T](Accessor[T]): +class AccelerationAccessor[T](QuantityAccessor[T]): dimension_name = 'acceleration' @property def meters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_second) @property def meters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_millisecond) @property def meters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_microsecond) @property def meters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_nanosecond) @property def meters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_picosecond) @property def meters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_femtosecond) @property def meters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_attosecond) @property def meters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_minute) @property def meters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_hour) @property def meters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_day) @property def meters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.meters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meters_per_square_year) @property def exameters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_second) @property def exameters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_millisecond) @property def exameters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_microsecond) @property def exameters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_nanosecond) @property def exameters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_picosecond) @property def exameters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_femtosecond) @property def exameters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_attosecond) @property def exameters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_minute) @property def exameters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_hour) @property def exameters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_day) @property def exameters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.exameters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exameters_per_square_year) @property def petameters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_second) @property def petameters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_millisecond) @property def petameters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_microsecond) @property def petameters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_nanosecond) @property def petameters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_picosecond) @property def petameters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_femtosecond) @property def petameters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_attosecond) @property def petameters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_minute) @property def petameters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_hour) @property def petameters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_day) @property def petameters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.petameters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petameters_per_square_year) @property def terameters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_second) @property def terameters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_millisecond) @property def terameters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_microsecond) @property def terameters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_nanosecond) @property def terameters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_picosecond) @property def terameters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_femtosecond) @property def terameters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_attosecond) @property def terameters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_minute) @property def terameters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_hour) @property def terameters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_day) @property def terameters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.terameters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terameters_per_square_year) @property def gigameters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_second) @property def gigameters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_millisecond) @property def gigameters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_microsecond) @property def gigameters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_nanosecond) @property def gigameters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_picosecond) @property def gigameters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_femtosecond) @property def gigameters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_attosecond) @property def gigameters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_minute) @property def gigameters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_hour) @property def gigameters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_day) @property def gigameters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.gigameters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigameters_per_square_year) @property def megameters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_second) @property def megameters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_millisecond) @property def megameters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_microsecond) @property def megameters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_nanosecond) @property def megameters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_picosecond) @property def megameters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_femtosecond) @property def megameters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_attosecond) @property def megameters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_minute) @property def megameters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_hour) @property def megameters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_day) @property def megameters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.megameters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megameters_per_square_year) @property def kilometers_per_square_second(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_second) @property def kilometers_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_millisecond) @property def kilometers_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_microsecond) @property def kilometers_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_nanosecond) @property def kilometers_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_picosecond) @property def kilometers_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_femtosecond) @property def kilometers_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_attosecond) @property def kilometers_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_minute) @property def kilometers_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_hour) @property def kilometers_per_square_day(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_day) @property def kilometers_per_square_year(self) -> T: - return self.quantity.in_units_of(units.kilometers_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilometers_per_square_year) @property def millimeters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_second) @property def millimeters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_millisecond) @property def millimeters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_microsecond) @property def millimeters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_nanosecond) @property def millimeters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_picosecond) @property def millimeters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_femtosecond) @property def millimeters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_attosecond) @property def millimeters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_minute) @property def millimeters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_hour) @property def millimeters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_day) @property def millimeters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.millimeters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimeters_per_square_year) @property def micrometers_per_square_second(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_second) @property def micrometers_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_millisecond) @property def micrometers_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_microsecond) @property def micrometers_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_nanosecond) @property def micrometers_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_picosecond) @property def micrometers_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_femtosecond) @property def micrometers_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_attosecond) @property def micrometers_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_minute) @property def micrometers_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_hour) @property def micrometers_per_square_day(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_day) @property def micrometers_per_square_year(self) -> T: - return self.quantity.in_units_of(units.micrometers_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrometers_per_square_year) @property def nanometers_per_square_second(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_second) @property def nanometers_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_millisecond) @property def nanometers_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_microsecond) @property def nanometers_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_nanosecond) @property def nanometers_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_picosecond) @property def nanometers_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_femtosecond) @property def nanometers_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_attosecond) @property def nanometers_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_minute) @property def nanometers_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_hour) @property def nanometers_per_square_day(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_day) @property def nanometers_per_square_year(self) -> T: - return self.quantity.in_units_of(units.nanometers_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanometers_per_square_year) @property def picometers_per_square_second(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_second) @property def picometers_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_millisecond) @property def picometers_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_microsecond) @property def picometers_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_nanosecond) @property def picometers_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_picosecond) @property def picometers_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_femtosecond) @property def picometers_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_attosecond) @property def picometers_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_minute) @property def picometers_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_hour) @property def picometers_per_square_day(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_day) @property def picometers_per_square_year(self) -> T: - return self.quantity.in_units_of(units.picometers_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picometers_per_square_year) @property def femtometers_per_square_second(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_second) @property def femtometers_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_millisecond) @property def femtometers_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_microsecond) @property def femtometers_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_nanosecond) @property def femtometers_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_picosecond) @property def femtometers_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_femtosecond) @property def femtometers_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_attosecond) @property def femtometers_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_minute) @property def femtometers_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_hour) @property def femtometers_per_square_day(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_day) @property def femtometers_per_square_year(self) -> T: - return self.quantity.in_units_of(units.femtometers_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtometers_per_square_year) @property def attometers_per_square_second(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_second) @property def attometers_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_millisecond) @property def attometers_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_microsecond) @property def attometers_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_nanosecond) @property def attometers_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_picosecond) @property def attometers_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_femtosecond) @property def attometers_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_attosecond) @property def attometers_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_minute) @property def attometers_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_hour) @property def attometers_per_square_day(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_day) @property def attometers_per_square_year(self) -> T: - return self.quantity.in_units_of(units.attometers_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attometers_per_square_year) @property def decimeters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_second) @property def decimeters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_millisecond) @property def decimeters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_microsecond) @property def decimeters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_nanosecond) @property def decimeters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_picosecond) @property def decimeters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_femtosecond) @property def decimeters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_attosecond) @property def decimeters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_minute) @property def decimeters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_hour) @property def decimeters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_day) @property def decimeters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.decimeters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.decimeters_per_square_year) @property def centimeters_per_square_second(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_second) @property def centimeters_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_millisecond) @property def centimeters_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_microsecond) @property def centimeters_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_nanosecond) @property def centimeters_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_picosecond) @property def centimeters_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_femtosecond) @property def centimeters_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_attosecond) @property def centimeters_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_minute) @property def centimeters_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_hour) @property def centimeters_per_square_day(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_day) @property def centimeters_per_square_year(self) -> T: - return self.quantity.in_units_of(units.centimeters_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.centimeters_per_square_year) @property def angstroms_per_square_second(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_second) @property def angstroms_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_millisecond) @property def angstroms_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_microsecond) @property def angstroms_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_nanosecond) @property def angstroms_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_picosecond) @property def angstroms_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_femtosecond) @property def angstroms_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_attosecond) @property def angstroms_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_minute) @property def angstroms_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_hour) @property def angstroms_per_square_day(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_day) @property def angstroms_per_square_year(self) -> T: - return self.quantity.in_units_of(units.angstroms_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.angstroms_per_square_year) @property def miles_per_square_second(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_second) @property def miles_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_millisecond) @property def miles_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_microsecond) @property def miles_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_nanosecond) @property def miles_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_picosecond) @property def miles_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_femtosecond) @property def miles_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_attosecond) @property def miles_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_minute) @property def miles_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_hour) @property def miles_per_square_day(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_day) @property def miles_per_square_year(self) -> T: - return self.quantity.in_units_of(units.miles_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.miles_per_square_year) @property def yards_per_square_second(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_second) @property def yards_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_millisecond) @property def yards_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_microsecond) @property def yards_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_nanosecond) @property def yards_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_picosecond) @property def yards_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_femtosecond) @property def yards_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_attosecond) @property def yards_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_minute) @property def yards_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_hour) @property def yards_per_square_day(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_day) @property def yards_per_square_year(self) -> T: - return self.quantity.in_units_of(units.yards_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.yards_per_square_year) @property def feet_per_square_second(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_second) @property def feet_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_millisecond) @property def feet_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_microsecond) @property def feet_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_nanosecond) @property def feet_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_picosecond) @property def feet_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_femtosecond) @property def feet_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_attosecond) @property def feet_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_minute) @property def feet_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_hour) @property def feet_per_square_day(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_day) @property def feet_per_square_year(self) -> T: - return self.quantity.in_units_of(units.feet_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.feet_per_square_year) @property def inches_per_square_second(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_second) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_second) @property def inches_per_square_millisecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_millisecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_millisecond) @property def inches_per_square_microsecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_microsecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_microsecond) @property def inches_per_square_nanosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_nanosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_nanosecond) @property def inches_per_square_picosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_picosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_picosecond) @property def inches_per_square_femtosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_femtosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_femtosecond) @property def inches_per_square_attosecond(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_attosecond) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_attosecond) @property def inches_per_square_minute(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_minute) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_minute) @property def inches_per_square_hour(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_hour) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_hour) @property def inches_per_square_day(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_day) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_day) @property def inches_per_square_year(self) -> T: - return self.quantity.in_units_of(units.inches_per_square_year) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.inches_per_square_year) -class DensityAccessor[T](Accessor[T]): +class DensityAccessor[T](QuantityAccessor[T]): dimension_name = 'density' @property def grams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_meter) @property def exagrams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_meter) @property def petagrams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_meter) @property def teragrams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_meter) @property def gigagrams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_meter) @property def megagrams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_meter) @property def kilograms_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_meter) @property def milligrams_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_meter) @property def micrograms_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_meter) @property def nanograms_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_meter) @property def picograms_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_meter) @property def femtograms_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_meter) @property def attograms_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_meter) @property def atomic_mass_units_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_meter) @property def pounds_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_meter) @property def ounces_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_meter) @property def grams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_exameter) @property def exagrams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_exameter) @property def petagrams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_exameter) @property def teragrams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_exameter) @property def gigagrams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_exameter) @property def megagrams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_exameter) @property def kilograms_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_exameter) @property def milligrams_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_exameter) @property def micrograms_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_exameter) @property def nanograms_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_exameter) @property def picograms_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_exameter) @property def femtograms_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_exameter) @property def attograms_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_exameter) @property def atomic_mass_units_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_exameter) @property def pounds_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_exameter) @property def ounces_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_exameter) @property def grams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_petameter) @property def exagrams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_petameter) @property def petagrams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_petameter) @property def teragrams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_petameter) @property def gigagrams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_petameter) @property def megagrams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_petameter) @property def kilograms_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_petameter) @property def milligrams_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_petameter) @property def micrograms_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_petameter) @property def nanograms_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_petameter) @property def picograms_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_petameter) @property def femtograms_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_petameter) @property def attograms_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_petameter) @property def atomic_mass_units_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_petameter) @property def pounds_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_petameter) @property def ounces_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_petameter) @property def grams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_terameter) @property def exagrams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_terameter) @property def petagrams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_terameter) @property def teragrams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_terameter) @property def gigagrams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_terameter) @property def megagrams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_terameter) @property def kilograms_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_terameter) @property def milligrams_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_terameter) @property def micrograms_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_terameter) @property def nanograms_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_terameter) @property def picograms_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_terameter) @property def femtograms_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_terameter) @property def attograms_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_terameter) @property def atomic_mass_units_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_terameter) @property def pounds_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_terameter) @property def ounces_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_terameter) @property def grams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_gigameter) @property def exagrams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_gigameter) @property def petagrams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_gigameter) @property def teragrams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_gigameter) @property def gigagrams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_gigameter) @property def megagrams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_gigameter) @property def kilograms_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_gigameter) @property def milligrams_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_gigameter) @property def micrograms_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_gigameter) @property def nanograms_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_gigameter) @property def picograms_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_gigameter) @property def femtograms_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_gigameter) @property def attograms_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_gigameter) @property def atomic_mass_units_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_gigameter) @property def pounds_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_gigameter) @property def ounces_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_gigameter) @property def grams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_megameter) @property def exagrams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_megameter) @property def petagrams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_megameter) @property def teragrams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_megameter) @property def gigagrams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_megameter) @property def megagrams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_megameter) @property def kilograms_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_megameter) @property def milligrams_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_megameter) @property def micrograms_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_megameter) @property def nanograms_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_megameter) @property def picograms_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_megameter) @property def femtograms_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_megameter) @property def attograms_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_megameter) @property def atomic_mass_units_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_megameter) @property def pounds_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_megameter) @property def ounces_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_megameter) @property def grams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_kilometer) @property def exagrams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_kilometer) @property def petagrams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_kilometer) @property def teragrams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_kilometer) @property def gigagrams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_kilometer) @property def megagrams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_kilometer) @property def kilograms_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_kilometer) @property def milligrams_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_kilometer) @property def micrograms_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_kilometer) @property def nanograms_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_kilometer) @property def picograms_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_kilometer) @property def femtograms_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_kilometer) @property def attograms_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_kilometer) @property def atomic_mass_units_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_kilometer) @property def pounds_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_kilometer) @property def ounces_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_kilometer) @property def grams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_millimeter) @property def exagrams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_millimeter) @property def petagrams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_millimeter) @property def teragrams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_millimeter) @property def gigagrams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_millimeter) @property def megagrams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_millimeter) @property def kilograms_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_millimeter) @property def milligrams_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_millimeter) @property def micrograms_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_millimeter) @property def nanograms_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_millimeter) @property def picograms_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_millimeter) @property def femtograms_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_millimeter) @property def attograms_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_millimeter) @property def atomic_mass_units_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_millimeter) @property def pounds_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_millimeter) @property def ounces_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_millimeter) @property def grams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_micrometer) @property def exagrams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_micrometer) @property def petagrams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_micrometer) @property def teragrams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_micrometer) @property def gigagrams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_micrometer) @property def megagrams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_micrometer) @property def kilograms_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_micrometer) @property def milligrams_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_micrometer) @property def micrograms_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_micrometer) @property def nanograms_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_micrometer) @property def picograms_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_micrometer) @property def femtograms_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_micrometer) @property def attograms_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_micrometer) @property def atomic_mass_units_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_micrometer) @property def pounds_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_micrometer) @property def ounces_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_micrometer) @property def grams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_nanometer) @property def exagrams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_nanometer) @property def petagrams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_nanometer) @property def teragrams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_nanometer) @property def gigagrams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_nanometer) @property def megagrams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_nanometer) @property def kilograms_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_nanometer) @property def milligrams_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_nanometer) @property def micrograms_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_nanometer) @property def nanograms_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_nanometer) @property def picograms_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_nanometer) @property def femtograms_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_nanometer) @property def attograms_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_nanometer) @property def atomic_mass_units_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_nanometer) @property def pounds_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_nanometer) @property def ounces_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_nanometer) @property def grams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_picometer) @property def exagrams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_picometer) @property def petagrams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_picometer) @property def teragrams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_picometer) @property def gigagrams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_picometer) @property def megagrams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_picometer) @property def kilograms_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_picometer) @property def milligrams_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_picometer) @property def micrograms_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_picometer) @property def nanograms_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_picometer) @property def picograms_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_picometer) @property def femtograms_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_picometer) @property def attograms_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_picometer) @property def atomic_mass_units_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_picometer) @property def pounds_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_picometer) @property def ounces_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_picometer) @property def grams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_femtometer) @property def exagrams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_femtometer) @property def petagrams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_femtometer) @property def teragrams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_femtometer) @property def gigagrams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_femtometer) @property def megagrams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_femtometer) @property def kilograms_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_femtometer) @property def milligrams_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_femtometer) @property def micrograms_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_femtometer) @property def nanograms_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_femtometer) @property def picograms_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_femtometer) @property def femtograms_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_femtometer) @property def attograms_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_femtometer) @property def atomic_mass_units_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_femtometer) @property def pounds_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_femtometer) @property def ounces_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_femtometer) @property def grams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_attometer) @property def exagrams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_attometer) @property def petagrams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_attometer) @property def teragrams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_attometer) @property def gigagrams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_attometer) @property def megagrams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_attometer) @property def kilograms_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_attometer) @property def milligrams_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_attometer) @property def micrograms_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_attometer) @property def nanograms_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_attometer) @property def picograms_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_attometer) @property def femtograms_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_attometer) @property def attograms_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_attometer) @property def atomic_mass_units_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_attometer) @property def pounds_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_attometer) @property def ounces_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_attometer) @property def grams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_decimeter) @property def exagrams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_decimeter) @property def petagrams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_decimeter) @property def teragrams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_decimeter) @property def gigagrams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_decimeter) @property def megagrams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_decimeter) @property def kilograms_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_decimeter) @property def milligrams_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_decimeter) @property def micrograms_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_decimeter) @property def nanograms_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_decimeter) @property def picograms_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_decimeter) @property def femtograms_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_decimeter) @property def attograms_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_decimeter) @property def atomic_mass_units_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_decimeter) @property def pounds_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_decimeter) @property def ounces_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_decimeter) @property def grams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_centimeter) @property def exagrams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_centimeter) @property def petagrams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_centimeter) @property def teragrams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_centimeter) @property def gigagrams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_centimeter) @property def megagrams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_centimeter) @property def kilograms_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_centimeter) @property def milligrams_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_centimeter) @property def micrograms_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_centimeter) @property def nanograms_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_centimeter) @property def picograms_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_centimeter) @property def femtograms_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_centimeter) @property def attograms_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_centimeter) @property def atomic_mass_units_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_centimeter) @property def pounds_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_centimeter) @property def ounces_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_centimeter) @property def grams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_angstrom) @property def exagrams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_angstrom) @property def petagrams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_angstrom) @property def teragrams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_angstrom) @property def gigagrams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_angstrom) @property def megagrams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_angstrom) @property def kilograms_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_angstrom) @property def milligrams_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_angstrom) @property def micrograms_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_angstrom) @property def nanograms_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_angstrom) @property def picograms_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_angstrom) @property def femtograms_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_angstrom) @property def attograms_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_angstrom) @property def atomic_mass_units_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_angstrom) @property def pounds_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_angstrom) @property def ounces_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_angstrom) @property def grams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_mile) @property def exagrams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_mile) @property def petagrams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_mile) @property def teragrams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_mile) @property def gigagrams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_mile) @property def megagrams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_mile) @property def kilograms_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_mile) @property def milligrams_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_mile) @property def micrograms_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_mile) @property def nanograms_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_mile) @property def picograms_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_mile) @property def femtograms_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_mile) @property def attograms_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_mile) @property def atomic_mass_units_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_mile) @property def pounds_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_mile) @property def ounces_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_mile) @property def grams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_yard) @property def exagrams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_yard) @property def petagrams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_yard) @property def teragrams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_yard) @property def gigagrams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_yard) @property def megagrams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_yard) @property def kilograms_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_yard) @property def milligrams_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_yard) @property def micrograms_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_yard) @property def nanograms_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_yard) @property def picograms_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_yard) @property def femtograms_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_yard) @property def attograms_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_yard) @property def atomic_mass_units_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_yard) @property def pounds_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_yard) @property def ounces_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_yard) @property def grams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_foot) @property def exagrams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_foot) @property def petagrams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_foot) @property def teragrams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_foot) @property def gigagrams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_foot) @property def megagrams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_foot) @property def kilograms_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_foot) @property def milligrams_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_foot) @property def micrograms_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_foot) @property def nanograms_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_foot) @property def picograms_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_foot) @property def femtograms_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_foot) @property def attograms_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_foot) @property def atomic_mass_units_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_foot) @property def pounds_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_foot) @property def ounces_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_foot) @property def grams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.grams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.grams_per_cubic_inch) @property def exagrams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.exagrams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exagrams_per_cubic_inch) @property def petagrams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.petagrams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petagrams_per_cubic_inch) @property def teragrams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.teragrams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teragrams_per_cubic_inch) @property def gigagrams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.gigagrams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigagrams_per_cubic_inch) @property def megagrams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.megagrams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megagrams_per_cubic_inch) @property def kilograms_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.kilograms_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilograms_per_cubic_inch) @property def milligrams_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.milligrams_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milligrams_per_cubic_inch) @property def micrograms_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.micrograms_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micrograms_per_cubic_inch) @property def nanograms_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.nanograms_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanograms_per_cubic_inch) @property def picograms_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.picograms_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picograms_per_cubic_inch) @property def femtograms_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.femtograms_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtograms_per_cubic_inch) @property def attograms_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.attograms_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attograms_per_cubic_inch) @property def atomic_mass_units_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.atomic_mass_units_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.atomic_mass_units_per_cubic_inch) @property def pounds_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.pounds_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_per_cubic_inch) @property def ounces_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.ounces_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ounces_per_cubic_inch) -class ForceAccessor[T](Accessor[T]): +class ForceAccessor[T](QuantityAccessor[T]): dimension_name = 'force' @property def newtons(self) -> T: - return self.quantity.in_units_of(units.newtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.newtons) @property def exanewtons(self) -> T: - return self.quantity.in_units_of(units.exanewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exanewtons) @property def petanewtons(self) -> T: - return self.quantity.in_units_of(units.petanewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petanewtons) @property def teranewtons(self) -> T: - return self.quantity.in_units_of(units.teranewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teranewtons) @property def giganewtons(self) -> T: - return self.quantity.in_units_of(units.giganewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.giganewtons) @property def meganewtons(self) -> T: - return self.quantity.in_units_of(units.meganewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.meganewtons) @property def kilonewtons(self) -> T: - return self.quantity.in_units_of(units.kilonewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilonewtons) @property def millinewtons(self) -> T: - return self.quantity.in_units_of(units.millinewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millinewtons) @property def micronewtons(self) -> T: - return self.quantity.in_units_of(units.micronewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micronewtons) @property def nanonewtons(self) -> T: - return self.quantity.in_units_of(units.nanonewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanonewtons) @property def piconewtons(self) -> T: - return self.quantity.in_units_of(units.piconewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.piconewtons) @property def femtonewtons(self) -> T: - return self.quantity.in_units_of(units.femtonewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtonewtons) @property def attonewtons(self) -> T: - return self.quantity.in_units_of(units.attonewtons) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attonewtons) @property def kg_force(self) -> T: - return self.quantity.in_units_of(units.kg_force) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kg_force) @property def pounds_force(self) -> T: - return self.quantity.in_units_of(units.pounds_force) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_force) -class PressureAccessor[T](Accessor[T]): +class PressureAccessor[T](QuantityAccessor[T]): dimension_name = 'pressure' @property def pascals(self) -> T: - return self.quantity.in_units_of(units.pascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pascals) @property def exapascals(self) -> T: - return self.quantity.in_units_of(units.exapascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exapascals) @property def petapascals(self) -> T: - return self.quantity.in_units_of(units.petapascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petapascals) @property def terapascals(self) -> T: - return self.quantity.in_units_of(units.terapascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terapascals) @property def gigapascals(self) -> T: - return self.quantity.in_units_of(units.gigapascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigapascals) @property def megapascals(self) -> T: - return self.quantity.in_units_of(units.megapascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megapascals) @property def kilopascals(self) -> T: - return self.quantity.in_units_of(units.kilopascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilopascals) @property def millipascals(self) -> T: - return self.quantity.in_units_of(units.millipascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millipascals) @property def micropascals(self) -> T: - return self.quantity.in_units_of(units.micropascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micropascals) @property def nanopascals(self) -> T: - return self.quantity.in_units_of(units.nanopascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanopascals) @property def picopascals(self) -> T: - return self.quantity.in_units_of(units.picopascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picopascals) @property def femtopascals(self) -> T: - return self.quantity.in_units_of(units.femtopascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtopascals) @property def attopascals(self) -> T: - return self.quantity.in_units_of(units.attopascals) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attopascals) @property def pounds_force_per_square_inch(self) -> T: - return self.quantity.in_units_of(units.pounds_force_per_square_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.pounds_force_per_square_inch) -class EnergyAccessor[T](Accessor[T]): +class EnergyAccessor[T](QuantityAccessor[T]): dimension_name = 'energy' @property def joules(self) -> T: - return self.quantity.in_units_of(units.joules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.joules) @property def exajoules(self) -> T: - return self.quantity.in_units_of(units.exajoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exajoules) @property def petajoules(self) -> T: - return self.quantity.in_units_of(units.petajoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petajoules) @property def terajoules(self) -> T: - return self.quantity.in_units_of(units.terajoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terajoules) @property def gigajoules(self) -> T: - return self.quantity.in_units_of(units.gigajoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigajoules) @property def megajoules(self) -> T: - return self.quantity.in_units_of(units.megajoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megajoules) @property def kilojoules(self) -> T: - return self.quantity.in_units_of(units.kilojoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilojoules) @property def millijoules(self) -> T: - return self.quantity.in_units_of(units.millijoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millijoules) @property def microjoules(self) -> T: - return self.quantity.in_units_of(units.microjoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microjoules) @property def nanojoules(self) -> T: - return self.quantity.in_units_of(units.nanojoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanojoules) @property def picojoules(self) -> T: - return self.quantity.in_units_of(units.picojoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picojoules) @property def femtojoules(self) -> T: - return self.quantity.in_units_of(units.femtojoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtojoules) @property def attojoules(self) -> T: - return self.quantity.in_units_of(units.attojoules) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attojoules) @property def electronvolts(self) -> T: - return self.quantity.in_units_of(units.electronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.electronvolts) @property def exaelectronvolts(self) -> T: - return self.quantity.in_units_of(units.exaelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exaelectronvolts) @property def petaelectronvolts(self) -> T: - return self.quantity.in_units_of(units.petaelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petaelectronvolts) @property def teraelectronvolts(self) -> T: - return self.quantity.in_units_of(units.teraelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teraelectronvolts) @property def gigaelectronvolts(self) -> T: - return self.quantity.in_units_of(units.gigaelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigaelectronvolts) @property def megaelectronvolts(self) -> T: - return self.quantity.in_units_of(units.megaelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megaelectronvolts) @property def kiloelectronvolts(self) -> T: - return self.quantity.in_units_of(units.kiloelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kiloelectronvolts) @property def millielectronvolts(self) -> T: - return self.quantity.in_units_of(units.millielectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millielectronvolts) @property def microelectronvolts(self) -> T: - return self.quantity.in_units_of(units.microelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microelectronvolts) @property def nanoelectronvolts(self) -> T: - return self.quantity.in_units_of(units.nanoelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanoelectronvolts) @property def picoelectronvolts(self) -> T: - return self.quantity.in_units_of(units.picoelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picoelectronvolts) @property def femtoelectronvolts(self) -> T: - return self.quantity.in_units_of(units.femtoelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtoelectronvolts) @property def attoelectronvolts(self) -> T: - return self.quantity.in_units_of(units.attoelectronvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attoelectronvolts) -class PowerAccessor[T](Accessor[T]): +class PowerAccessor[T](QuantityAccessor[T]): dimension_name = 'power' @property def watts(self) -> T: - return self.quantity.in_units_of(units.watts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.watts) @property def exawatts(self) -> T: - return self.quantity.in_units_of(units.exawatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exawatts) @property def petawatts(self) -> T: - return self.quantity.in_units_of(units.petawatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petawatts) @property def terawatts(self) -> T: - return self.quantity.in_units_of(units.terawatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terawatts) @property def gigawatts(self) -> T: - return self.quantity.in_units_of(units.gigawatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigawatts) @property def megawatts(self) -> T: - return self.quantity.in_units_of(units.megawatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megawatts) @property def kilowatts(self) -> T: - return self.quantity.in_units_of(units.kilowatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilowatts) @property def milliwatts(self) -> T: - return self.quantity.in_units_of(units.milliwatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milliwatts) @property def microwatts(self) -> T: - return self.quantity.in_units_of(units.microwatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microwatts) @property def nanowatts(self) -> T: - return self.quantity.in_units_of(units.nanowatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanowatts) @property def picowatts(self) -> T: - return self.quantity.in_units_of(units.picowatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picowatts) @property def femtowatts(self) -> T: - return self.quantity.in_units_of(units.femtowatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtowatts) @property def attowatts(self) -> T: - return self.quantity.in_units_of(units.attowatts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attowatts) -class ChargeAccessor[T](Accessor[T]): +class ChargeAccessor[T](QuantityAccessor[T]): dimension_name = 'charge' @property def coulombs(self) -> T: - return self.quantity.in_units_of(units.coulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.coulombs) @property def exacoulombs(self) -> T: - return self.quantity.in_units_of(units.exacoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exacoulombs) @property def petacoulombs(self) -> T: - return self.quantity.in_units_of(units.petacoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petacoulombs) @property def teracoulombs(self) -> T: - return self.quantity.in_units_of(units.teracoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teracoulombs) @property def gigacoulombs(self) -> T: - return self.quantity.in_units_of(units.gigacoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigacoulombs) @property def megacoulombs(self) -> T: - return self.quantity.in_units_of(units.megacoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megacoulombs) @property def kilocoulombs(self) -> T: - return self.quantity.in_units_of(units.kilocoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilocoulombs) @property def millicoulombs(self) -> T: - return self.quantity.in_units_of(units.millicoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millicoulombs) @property def microcoulombs(self) -> T: - return self.quantity.in_units_of(units.microcoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microcoulombs) @property def nanocoulombs(self) -> T: - return self.quantity.in_units_of(units.nanocoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanocoulombs) @property def picocoulombs(self) -> T: - return self.quantity.in_units_of(units.picocoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picocoulombs) @property def femtocoulombs(self) -> T: - return self.quantity.in_units_of(units.femtocoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtocoulombs) @property def attocoulombs(self) -> T: - return self.quantity.in_units_of(units.attocoulombs) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attocoulombs) -class PotentialAccessor[T](Accessor[T]): +class PotentialAccessor[T](QuantityAccessor[T]): dimension_name = 'potential' @property def volts(self) -> T: - return self.quantity.in_units_of(units.volts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.volts) @property def exavolts(self) -> T: - return self.quantity.in_units_of(units.exavolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exavolts) @property def petavolts(self) -> T: - return self.quantity.in_units_of(units.petavolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petavolts) @property def teravolts(self) -> T: - return self.quantity.in_units_of(units.teravolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teravolts) @property def gigavolts(self) -> T: - return self.quantity.in_units_of(units.gigavolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigavolts) @property def megavolts(self) -> T: - return self.quantity.in_units_of(units.megavolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megavolts) @property def kilovolts(self) -> T: - return self.quantity.in_units_of(units.kilovolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilovolts) @property def millivolts(self) -> T: - return self.quantity.in_units_of(units.millivolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millivolts) @property def microvolts(self) -> T: - return self.quantity.in_units_of(units.microvolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microvolts) @property def nanovolts(self) -> T: - return self.quantity.in_units_of(units.nanovolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanovolts) @property def picovolts(self) -> T: - return self.quantity.in_units_of(units.picovolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picovolts) @property def femtovolts(self) -> T: - return self.quantity.in_units_of(units.femtovolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtovolts) @property def attovolts(self) -> T: - return self.quantity.in_units_of(units.attovolts) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attovolts) -class ResistanceAccessor[T](Accessor[T]): +class ResistanceAccessor[T](QuantityAccessor[T]): dimension_name = 'resistance' @property def ohms(self) -> T: - return self.quantity.in_units_of(units.ohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.ohms) @property def exaohms(self) -> T: - return self.quantity.in_units_of(units.exaohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exaohms) @property def petaohms(self) -> T: - return self.quantity.in_units_of(units.petaohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petaohms) @property def teraohms(self) -> T: - return self.quantity.in_units_of(units.teraohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teraohms) @property def gigaohms(self) -> T: - return self.quantity.in_units_of(units.gigaohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigaohms) @property def megaohms(self) -> T: - return self.quantity.in_units_of(units.megaohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megaohms) @property def kiloohms(self) -> T: - return self.quantity.in_units_of(units.kiloohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kiloohms) @property def milliohms(self) -> T: - return self.quantity.in_units_of(units.milliohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milliohms) @property def microohms(self) -> T: - return self.quantity.in_units_of(units.microohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microohms) @property def nanoohms(self) -> T: - return self.quantity.in_units_of(units.nanoohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanoohms) @property def picoohms(self) -> T: - return self.quantity.in_units_of(units.picoohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picoohms) @property def femtoohms(self) -> T: - return self.quantity.in_units_of(units.femtoohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtoohms) @property def attoohms(self) -> T: - return self.quantity.in_units_of(units.attoohms) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attoohms) -class CapacitanceAccessor[T](Accessor[T]): +class CapacitanceAccessor[T](QuantityAccessor[T]): dimension_name = 'capacitance' @property def farads(self) -> T: - return self.quantity.in_units_of(units.farads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.farads) @property def exafarads(self) -> T: - return self.quantity.in_units_of(units.exafarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exafarads) @property def petafarads(self) -> T: - return self.quantity.in_units_of(units.petafarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petafarads) @property def terafarads(self) -> T: - return self.quantity.in_units_of(units.terafarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terafarads) @property def gigafarads(self) -> T: - return self.quantity.in_units_of(units.gigafarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigafarads) @property def megafarads(self) -> T: - return self.quantity.in_units_of(units.megafarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megafarads) @property def kilofarads(self) -> T: - return self.quantity.in_units_of(units.kilofarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilofarads) @property def millifarads(self) -> T: - return self.quantity.in_units_of(units.millifarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millifarads) @property def microfarads(self) -> T: - return self.quantity.in_units_of(units.microfarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microfarads) @property def nanofarads(self) -> T: - return self.quantity.in_units_of(units.nanofarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanofarads) @property def picofarads(self) -> T: - return self.quantity.in_units_of(units.picofarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picofarads) @property def femtofarads(self) -> T: - return self.quantity.in_units_of(units.femtofarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtofarads) @property def attofarads(self) -> T: - return self.quantity.in_units_of(units.attofarads) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attofarads) -class ConductanceAccessor[T](Accessor[T]): +class ConductanceAccessor[T](QuantityAccessor[T]): dimension_name = 'conductance' @property def siemens(self) -> T: - return self.quantity.in_units_of(units.siemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.siemens) @property def exasiemens(self) -> T: - return self.quantity.in_units_of(units.exasiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exasiemens) @property def petasiemens(self) -> T: - return self.quantity.in_units_of(units.petasiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petasiemens) @property def terasiemens(self) -> T: - return self.quantity.in_units_of(units.terasiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terasiemens) @property def gigasiemens(self) -> T: - return self.quantity.in_units_of(units.gigasiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigasiemens) @property def megasiemens(self) -> T: - return self.quantity.in_units_of(units.megasiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megasiemens) @property def kilosiemens(self) -> T: - return self.quantity.in_units_of(units.kilosiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilosiemens) @property def millisiemens(self) -> T: - return self.quantity.in_units_of(units.millisiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millisiemens) @property def microsiemens(self) -> T: - return self.quantity.in_units_of(units.microsiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microsiemens) @property def nanosiemens(self) -> T: - return self.quantity.in_units_of(units.nanosiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanosiemens) @property def picosiemens(self) -> T: - return self.quantity.in_units_of(units.picosiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picosiemens) @property def femtosiemens(self) -> T: - return self.quantity.in_units_of(units.femtosiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtosiemens) @property def attosiemens(self) -> T: - return self.quantity.in_units_of(units.attosiemens) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attosiemens) -class MagneticfluxAccessor[T](Accessor[T]): +class MagneticfluxAccessor[T](QuantityAccessor[T]): dimension_name = 'magnetic_flux' @property def webers(self) -> T: - return self.quantity.in_units_of(units.webers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.webers) @property def exawebers(self) -> T: - return self.quantity.in_units_of(units.exawebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exawebers) @property def petawebers(self) -> T: - return self.quantity.in_units_of(units.petawebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petawebers) @property def terawebers(self) -> T: - return self.quantity.in_units_of(units.terawebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terawebers) @property def gigawebers(self) -> T: - return self.quantity.in_units_of(units.gigawebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigawebers) @property def megawebers(self) -> T: - return self.quantity.in_units_of(units.megawebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megawebers) @property def kilowebers(self) -> T: - return self.quantity.in_units_of(units.kilowebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilowebers) @property def milliwebers(self) -> T: - return self.quantity.in_units_of(units.milliwebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.milliwebers) @property def microwebers(self) -> T: - return self.quantity.in_units_of(units.microwebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microwebers) @property def nanowebers(self) -> T: - return self.quantity.in_units_of(units.nanowebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanowebers) @property def picowebers(self) -> T: - return self.quantity.in_units_of(units.picowebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picowebers) @property def femtowebers(self) -> T: - return self.quantity.in_units_of(units.femtowebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtowebers) @property def attowebers(self) -> T: - return self.quantity.in_units_of(units.attowebers) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attowebers) -class MagneticfluxdensityAccessor[T](Accessor[T]): +class MagneticfluxdensityAccessor[T](QuantityAccessor[T]): dimension_name = 'magnetic_flux_density' @property def tesla(self) -> T: - return self.quantity.in_units_of(units.tesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.tesla) @property def exatesla(self) -> T: - return self.quantity.in_units_of(units.exatesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exatesla) @property def petatesla(self) -> T: - return self.quantity.in_units_of(units.petatesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petatesla) @property def teratesla(self) -> T: - return self.quantity.in_units_of(units.teratesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.teratesla) @property def gigatesla(self) -> T: - return self.quantity.in_units_of(units.gigatesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigatesla) @property def megatesla(self) -> T: - return self.quantity.in_units_of(units.megatesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megatesla) @property def kilotesla(self) -> T: - return self.quantity.in_units_of(units.kilotesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilotesla) @property def millitesla(self) -> T: - return self.quantity.in_units_of(units.millitesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millitesla) @property def microtesla(self) -> T: - return self.quantity.in_units_of(units.microtesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microtesla) @property def nanotesla(self) -> T: - return self.quantity.in_units_of(units.nanotesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanotesla) @property def picotesla(self) -> T: - return self.quantity.in_units_of(units.picotesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picotesla) @property def femtotesla(self) -> T: - return self.quantity.in_units_of(units.femtotesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtotesla) @property def attotesla(self) -> T: - return self.quantity.in_units_of(units.attotesla) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attotesla) -class InductanceAccessor[T](Accessor[T]): +class InductanceAccessor[T](QuantityAccessor[T]): dimension_name = 'inductance' @property def henry(self) -> T: - return self.quantity.in_units_of(units.henry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.henry) @property def exahenry(self) -> T: - return self.quantity.in_units_of(units.exahenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exahenry) @property def petahenry(self) -> T: - return self.quantity.in_units_of(units.petahenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petahenry) @property def terahenry(self) -> T: - return self.quantity.in_units_of(units.terahenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terahenry) @property def gigahenry(self) -> T: - return self.quantity.in_units_of(units.gigahenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigahenry) @property def megahenry(self) -> T: - return self.quantity.in_units_of(units.megahenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megahenry) @property def kilohenry(self) -> T: - return self.quantity.in_units_of(units.kilohenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilohenry) @property def millihenry(self) -> T: - return self.quantity.in_units_of(units.millihenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millihenry) @property def microhenry(self) -> T: - return self.quantity.in_units_of(units.microhenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microhenry) @property def nanohenry(self) -> T: - return self.quantity.in_units_of(units.nanohenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanohenry) @property def picohenry(self) -> T: - return self.quantity.in_units_of(units.picohenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picohenry) @property def femtohenry(self) -> T: - return self.quantity.in_units_of(units.femtohenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtohenry) @property def attohenry(self) -> T: - return self.quantity.in_units_of(units.attohenry) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attohenry) -class TemperatureAccessor[T](Accessor[T]): +class TemperatureAccessor[T](QuantityAccessor[T]): dimension_name = 'temperature' @property def kelvin(self) -> T: - return self.quantity.in_units_of(units.kelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kelvin) @property def exakelvin(self) -> T: - return self.quantity.in_units_of(units.exakelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.exakelvin) @property def petakelvin(self) -> T: - return self.quantity.in_units_of(units.petakelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.petakelvin) @property def terakelvin(self) -> T: - return self.quantity.in_units_of(units.terakelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.terakelvin) @property def gigakelvin(self) -> T: - return self.quantity.in_units_of(units.gigakelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.gigakelvin) @property def megakelvin(self) -> T: - return self.quantity.in_units_of(units.megakelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.megakelvin) @property def kilokelvin(self) -> T: - return self.quantity.in_units_of(units.kilokelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.kilokelvin) @property def millikelvin(self) -> T: - return self.quantity.in_units_of(units.millikelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millikelvin) @property def microkelvin(self) -> T: - return self.quantity.in_units_of(units.microkelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.microkelvin) @property def nanokelvin(self) -> T: - return self.quantity.in_units_of(units.nanokelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanokelvin) @property def picokelvin(self) -> T: - return self.quantity.in_units_of(units.picokelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picokelvin) @property def femtokelvin(self) -> T: - return self.quantity.in_units_of(units.femtokelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtokelvin) @property def attokelvin(self) -> T: - return self.quantity.in_units_of(units.attokelvin) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attokelvin) @property def degrees_celsius(self) -> T: - return self.quantity.in_units_of(units.degrees_celsius) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.degrees_celsius) -class DimensionlessAccessor[T](Accessor[T]): +class DimensionlessAccessor[T](QuantityAccessor[T]): dimension_name = 'dimensionless' @property def none(self) -> T: - return self.quantity.in_units_of(units.none) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.none) -class AngleAccessor[T](Accessor[T]): +class AngleAccessor[T](QuantityAccessor[T]): dimension_name = 'angle' @property def degrees(self) -> T: - return self.quantity.in_units_of(units.degrees) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.degrees) @property def radians(self) -> T: - return self.quantity.in_units_of(units.radians) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.radians) -class SolidangleAccessor[T](Accessor[T]): +class SolidangleAccessor[T](QuantityAccessor[T]): dimension_name = 'solid_angle' @property def stradians(self) -> T: - return self.quantity.in_units_of(units.stradians) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.stradians) -class AmountAccessor[T](Accessor[T]): +class AmountAccessor[T](QuantityAccessor[T]): dimension_name = 'amount' @property def moles(self) -> T: - return self.quantity.in_units_of(units.moles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles) @property def millimoles(self) -> T: - return self.quantity.in_units_of(units.millimoles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles) @property def micromoles(self) -> T: - return self.quantity.in_units_of(units.micromoles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles) @property def nanomoles(self) -> T: - return self.quantity.in_units_of(units.nanomoles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles) @property def picomoles(self) -> T: - return self.quantity.in_units_of(units.picomoles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles) @property def femtomoles(self) -> T: - return self.quantity.in_units_of(units.femtomoles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles) @property def attomoles(self) -> T: - return self.quantity.in_units_of(units.attomoles) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles) -class ConcentrationAccessor[T](Accessor[T]): +class ConcentrationAccessor[T](QuantityAccessor[T]): dimension_name = 'concentration' @property def moles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_meter) @property def millimoles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_meter) @property def micromoles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_meter) @property def nanomoles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_meter) @property def picomoles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_meter) @property def femtomoles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_meter) @property def attomoles_per_cubic_meter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_meter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_meter) @property def moles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_exameter) @property def millimoles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_exameter) @property def micromoles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_exameter) @property def nanomoles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_exameter) @property def picomoles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_exameter) @property def femtomoles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_exameter) @property def attomoles_per_cubic_exameter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_exameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_exameter) @property def moles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_petameter) @property def millimoles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_petameter) @property def micromoles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_petameter) @property def nanomoles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_petameter) @property def picomoles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_petameter) @property def femtomoles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_petameter) @property def attomoles_per_cubic_petameter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_petameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_petameter) @property def moles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_terameter) @property def millimoles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_terameter) @property def micromoles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_terameter) @property def nanomoles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_terameter) @property def picomoles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_terameter) @property def femtomoles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_terameter) @property def attomoles_per_cubic_terameter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_terameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_terameter) @property def moles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_gigameter) @property def millimoles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_gigameter) @property def micromoles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_gigameter) @property def nanomoles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_gigameter) @property def picomoles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_gigameter) @property def femtomoles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_gigameter) @property def attomoles_per_cubic_gigameter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_gigameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_gigameter) @property def moles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_megameter) @property def millimoles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_megameter) @property def micromoles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_megameter) @property def nanomoles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_megameter) @property def picomoles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_megameter) @property def femtomoles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_megameter) @property def attomoles_per_cubic_megameter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_megameter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_megameter) @property def moles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_kilometer) @property def millimoles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_kilometer) @property def micromoles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_kilometer) @property def nanomoles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_kilometer) @property def picomoles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_kilometer) @property def femtomoles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_kilometer) @property def attomoles_per_cubic_kilometer(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_kilometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_kilometer) @property def moles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_millimeter) @property def millimoles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_millimeter) @property def micromoles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_millimeter) @property def nanomoles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_millimeter) @property def picomoles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_millimeter) @property def femtomoles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_millimeter) @property def attomoles_per_cubic_millimeter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_millimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_millimeter) @property def moles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_micrometer) @property def millimoles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_micrometer) @property def micromoles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_micrometer) @property def nanomoles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_micrometer) @property def picomoles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_micrometer) @property def femtomoles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_micrometer) @property def attomoles_per_cubic_micrometer(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_micrometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_micrometer) @property def moles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_nanometer) @property def millimoles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_nanometer) @property def micromoles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_nanometer) @property def nanomoles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_nanometer) @property def picomoles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_nanometer) @property def femtomoles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_nanometer) @property def attomoles_per_cubic_nanometer(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_nanometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_nanometer) @property def moles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_picometer) @property def millimoles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_picometer) @property def micromoles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_picometer) @property def nanomoles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_picometer) @property def picomoles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_picometer) @property def femtomoles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_picometer) @property def attomoles_per_cubic_picometer(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_picometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_picometer) @property def moles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_femtometer) @property def millimoles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_femtometer) @property def micromoles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_femtometer) @property def nanomoles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_femtometer) @property def picomoles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_femtometer) @property def femtomoles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_femtometer) @property def attomoles_per_cubic_femtometer(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_femtometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_femtometer) @property def moles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_attometer) @property def millimoles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_attometer) @property def micromoles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_attometer) @property def nanomoles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_attometer) @property def picomoles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_attometer) @property def femtomoles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_attometer) @property def attomoles_per_cubic_attometer(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_attometer) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_attometer) @property def moles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_decimeter) @property def millimoles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_decimeter) @property def micromoles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_decimeter) @property def nanomoles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_decimeter) @property def picomoles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_decimeter) @property def femtomoles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_decimeter) @property def attomoles_per_cubic_decimeter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_decimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_decimeter) @property def moles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_centimeter) @property def millimoles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_centimeter) @property def micromoles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_centimeter) @property def nanomoles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_centimeter) @property def picomoles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_centimeter) @property def femtomoles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_centimeter) @property def attomoles_per_cubic_centimeter(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_centimeter) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_centimeter) @property def moles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_angstrom) @property def millimoles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_angstrom) @property def micromoles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_angstrom) @property def nanomoles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_angstrom) @property def picomoles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_angstrom) @property def femtomoles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_angstrom) @property def attomoles_per_cubic_angstrom(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_angstrom) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_angstrom) @property def moles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_mile) @property def millimoles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_mile) @property def micromoles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_mile) @property def nanomoles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_mile) @property def picomoles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_mile) @property def femtomoles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_mile) @property def attomoles_per_cubic_mile(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_mile) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_mile) @property def moles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_yard) @property def millimoles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_yard) @property def micromoles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_yard) @property def nanomoles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_yard) @property def picomoles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_yard) @property def femtomoles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_yard) @property def attomoles_per_cubic_yard(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_yard) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_yard) @property def moles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_foot) @property def millimoles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_foot) @property def micromoles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_foot) @property def nanomoles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_foot) @property def picomoles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_foot) @property def femtomoles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_foot) @property def attomoles_per_cubic_foot(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_foot) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_foot) @property def moles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.moles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.moles_per_cubic_inch) @property def millimoles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.millimoles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.millimoles_per_cubic_inch) @property def micromoles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.micromoles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.micromoles_per_cubic_inch) @property def nanomoles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.nanomoles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.nanomoles_per_cubic_inch) @property def picomoles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.picomoles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.picomoles_per_cubic_inch) @property def femtomoles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.femtomoles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.femtomoles_per_cubic_inch) @property def attomoles_per_cubic_inch(self) -> T: - return self.quantity.in_units_of(units.attomoles_per_cubic_inch) + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.attomoles_per_cubic_inch) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 3b5e6fdb4..b3a88c50d 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -279,7 +279,14 @@ def __repr__(self): return f"Unit[{self.scale}, {self.dimensions}]" class NamedUnit(Unit): - """ Units, but they have a name, and a symbol""" + """ Units, but they have a name, and a symbol + + :si_scaling_factor: Number of these units per SI equivalent + :param dimensions: Dimensions object representing the dimensionality of these units + :param name: Name of unit - string without unicode + :param ascii_symbol: Symbol for unit without unicode + :param symbol: Unicode symbol + """ def __init__(self, si_scaling_factor: float, dimensions: Dimensions, From 9645fbde9db36c361f9e723d6a9e7f4760c95546 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 13 Aug 2024 10:27:58 +0100 Subject: [PATCH 504/675] Metadata work, and unit groups --- sasdata/metadata.py | 50 +++++++++++----------- sasdata/quantities/_build_tables.py | 12 ++++++ sasdata/quantities/units.py | 66 +++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 24 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 78faa107f..695502ed2 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -105,54 +105,56 @@ def __init__(self, target_object): self.target_object = target_object # Name - name = StringAccessor(self.target_object, "aperture.name") + self.name = StringAccessor(self.target_object, "aperture.name") # Type - type = StringAccessor(self.target_object, "aperture.type") + self.type = StringAccessor(self.target_object, "aperture.type") # Size name - TODO: What is the name of a size - size_name = StringAccessor(self.target_object, "aperture.size_name") + self.size_name = StringAccessor(self.target_object, "aperture.size_name") # Aperture size [Vector] # TODO: Wat!?! - size = QuantityAccessor(self.target_object, - "aperture.size", + self.size = QuantityAccessor(self.target_object, "aperture.size", + "aperture.size.units", default_unit=units.millimeters) - size = None - size_unit = 'mm' # Aperture distance [float] - distance = None - distance_unit = 'mm' + self.distance = QuantityAccessor(self.target_object, + "apature.distance", + "apature.distance.units", + default_unit=units.millimeters) + def summary(self): - pass + return (f"Aperture:" + f" Name: {self.name.value}" + f" Aperture size: {self.value}\n") + _str += " Aperture_dist:%s [%s]\n" % \ + (str(item.distance), str(item.distance_unit)) + class Collimation: """ Class to hold collimation information """ - # Name - name = None - # Length [float] [mm] - length = None - length_unit = 'mm' - # Aperture - aperture = None - def __init__(self): - self.aperture = [] + def __init__(self, target_object): + + # Name + name = None + # Length [float] [mm] + length = None + length_unit = 'mm' + # Aperture + aperture = None + def __str__(self): _str = "Collimation:\n" _str += " Length: %s [%s]\n" % \ (str(self.length), str(self.length_unit)) for item in self.aperture: - _str += " Aperture size:%s [%s]\n" % \ - (str(item.size), str(item.size_unit)) - _str += " Aperture_dist:%s [%s]\n" % \ - (str(item.distance), str(item.distance_unit)) - return _str class Source: diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index e5dc41b03..b82946d13 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -344,6 +344,18 @@ def format_name(name: str): fid.write("])\n") + # List of dimensions + fid.write("\n\n") + fid.write("unit_group_names = [\n") + for dimension_name, _ in dimension_names: + fid.write(f" '{dimension_name}',\n") + fid.write("]\n\n") + + fid.write("unit_groups = {\n") + for dimension_name, _ in dimension_names: + fid.write(f" '{dimension_name}': {dimension_name},\n") + fid.write("}\n\n") + with open("accessors.py", 'w', encoding=encoding) as fid: diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index b3a88c50d..c992777cf 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -3338,3 +3338,69 @@ def __init__(self, name: str, units: list[Unit]): femtomoles_per_cubic_inch, attomoles_per_cubic_inch, ]) + + +unit_group_names = [ + 'length', + 'area', + 'volume', + 'inverse_length', + 'inverse_area', + 'inverse_volume', + 'time', + 'rate', + 'speed', + 'acceleration', + 'density', + 'force', + 'pressure', + 'energy', + 'power', + 'charge', + 'potential', + 'resistance', + 'capacitance', + 'conductance', + 'magnetic_flux', + 'magnetic_flux_density', + 'inductance', + 'temperature', + 'dimensionless', + 'angle', + 'solid_angle', + 'amount', + 'concentration', +] + +unit_groups = { + 'length': length, + 'area': area, + 'volume': volume, + 'inverse_length': inverse_length, + 'inverse_area': inverse_area, + 'inverse_volume': inverse_volume, + 'time': time, + 'rate': rate, + 'speed': speed, + 'acceleration': acceleration, + 'density': density, + 'force': force, + 'pressure': pressure, + 'energy': energy, + 'power': power, + 'charge': charge, + 'potential': potential, + 'resistance': resistance, + 'capacitance': capacitance, + 'conductance': conductance, + 'magnetic_flux': magnetic_flux, + 'magnetic_flux_density': magnetic_flux_density, + 'inductance': inductance, + 'temperature': temperature, + 'dimensionless': dimensionless, + 'angle': angle, + 'solid_angle': solid_angle, + 'amount': amount, + 'concentration': concentration, +} + From 3fac420597fad9d448fe3caa355d24bec8a3778e Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 13 Aug 2024 10:39:32 +0100 Subject: [PATCH 505/675] More metadata stuff --- sasdata/metadata.py | 49 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 695502ed2..ad2fc1ad4 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -9,42 +9,41 @@ class Detector: """ def __init__(self, target_object): - self.target_object = target_object # Name of the instrument [string] - self.name = StringAccessor(self.target_object, "detector.name") + self.name = StringAccessor(target_object, "detector.name") # Sample to detector distance [float] [mm] - self.distance = LengthAccessor[float](self.target_object, + self.distance = LengthAccessor[float](target_object, "detector.distance", "detector.distance.units", default_unit=units.millimeters) # Offset of this detector position in X, Y, # (and Z if necessary) [Vector] [mm] - self.offset = LengthAccessor[ArrayLike](self.target_object, + self.offset = LengthAccessor[ArrayLike](target_object, "detector.offset", "detector.offset.units", default_units=units.millimeters) - self.orientation = AngleAccessor[ArrayLike](self.target_object, + self.orientation = AngleAccessor[ArrayLike](target_object, "detector.orientation", "detector.orientation.units", default_units=units.degrees) - self.beam_center = LengthAccessor[ArrayLike](self.target_object, + self.beam_center = LengthAccessor[ArrayLike](target_object, "detector.beam_center", "detector.beam_center.units", default_units=units.millimeters) # Pixel size in X, Y, (and Z if necessary) [Vector] [mm] - self.pixel_size = LengthAccessor[ArrayLike](self.target_object, + self.pixel_size = LengthAccessor[ArrayLike](target_object, "detector.pixel_size", "detector.pixel_size.units", default_units=units.millimeters) # Slit length of the instrument for this detector.[float] [mm] - self.slit_length = LengthAccessor[float](self.target_object, + self.slit_length = LengthAccessor[float](target_object, "detector.slit_length", "detector.slit_length.units", default_units=units.millimeters) @@ -102,37 +101,34 @@ def __init__(self, target_object: AccessorTarget): class Aperture: def __init__(self, target_object): - self.target_object = target_object # Name - self.name = StringAccessor(self.target_object, "aperture.name") + self.name = StringAccessor(target_object, "aperture.name") # Type - self.type = StringAccessor(self.target_object, "aperture.type") + self.type = StringAccessor(target_object, "aperture.type") # Size name - TODO: What is the name of a size - self.size_name = StringAccessor(self.target_object, "aperture.size_name") + self.size_name = StringAccessor(target_object, "aperture.size_name") # Aperture size [Vector] # TODO: Wat!?! - self.size = QuantityAccessor(self.target_object, + self.size = QuantityAccessor[ArrayLike](target_object, "aperture.size", "aperture.size.units", default_unit=units.millimeters) # Aperture distance [float] - self.distance = QuantityAccessor(self.target_object, + self.distance = QuantityAccessor[float](self.target_object, "apature.distance", "apature.distance.units", default_unit=units.millimeters) def summary(self): - return (f"Aperture:" - f" Name: {self.name.value}" - f" Aperture size: {self.value}\n") - _str += " Aperture_dist:%s [%s]\n" % \ - (str(item.distance), str(item.distance_unit)) - + return (f"Aperture:\n" + f" Name: {self.name.value}\n" + f" Aperture size: {self.size.value}\n" + f" Aperture distance: {self.distance.value}") class Collimation: """ @@ -142,13 +138,16 @@ class Collimation: def __init__(self, target_object): # Name - name = None + self.name = StringAccessor(target_object, "collimation.name") # Length [float] [mm] - length = None - length_unit = 'mm' - # Aperture - aperture = None + self.length = QuantityAccessor[float](target_object, + "collimation.length", + "collimation.length.units", + default_units=units.millimeters) + + # Todo - how do we handle this + self.collimator = Collimation(target_object) def __str__(self): _str = "Collimation:\n" From ee6d8a96cd99d08873321bb4d8220103a7e7dbb1 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 13 Aug 2024 10:40:46 +0100 Subject: [PATCH 506/675] Named units in unit groups --- sasdata/quantities/_units_base.py | 2 +- sasdata/quantities/units.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 8df62bcc6..52d43b0ef 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -264,6 +264,6 @@ class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): class UnitGroup: """ A group of units that all have the same dimensionality """ - def __init__(self, name: str, units: list[Unit]): + def __init__(self, name: str, units: list[NamedUnit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index c992777cf..479bd5fec 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -348,7 +348,7 @@ class GreedyAbsDimensionUnitFormatProcessor(UnitFormatProcessor): class UnitGroup: """ A group of units that all have the same dimensionality """ - def __init__(self, name: str, units: list[Unit]): + def __init__(self, name: str, units: list[NamedUnit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) From d276e9979325d95090d70c282136b31ac0369fe0 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 14 Aug 2024 10:59:38 +0100 Subject: [PATCH 507/675] More metadata, added absolute temperature stuff --- sasdata/metadata.py | 245 ++++++++++----------- sasdata/quantities/_accessor_base.py | 35 ++- sasdata/quantities/_build_tables.py | 15 +- sasdata/quantities/_units_base.py | 5 + sasdata/quantities/absolute_temperature.py | 2 +- sasdata/quantities/accessors.py | 43 +++- sasdata/quantities/quantity.py | 4 + sasdata/quantities/units.py | 11 +- 8 files changed, 206 insertions(+), 154 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index ad2fc1ad4..c01c8ba40 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -2,7 +2,10 @@ from numpy.typing import ArrayLike import sasdata.quantities.units as units -from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor +from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor, \ + DimensionlessAccessor, FloatAccessor, TemperatureAccessor + + class Detector: """ Detector information @@ -24,29 +27,29 @@ def __init__(self, target_object): self.offset = LengthAccessor[ArrayLike](target_object, "detector.offset", "detector.offset.units", - default_units=units.millimeters) + default_unit=units.millimeters) self.orientation = AngleAccessor[ArrayLike](target_object, "detector.orientation", "detector.orientation.units", - default_units=units.degrees) + default_unit=units.degrees) self.beam_center = LengthAccessor[ArrayLike](target_object, "detector.beam_center", "detector.beam_center.units", - default_units=units.millimeters) + default_unit=units.millimeters) # Pixel size in X, Y, (and Z if necessary) [Vector] [mm] self.pixel_size = LengthAccessor[ArrayLike](target_object, "detector.pixel_size", "detector.pixel_size.units", - default_units=units.millimeters) + default_unit=units.millimeters) # Slit length of the instrument for this detector.[float] [mm] self.slit_length = LengthAccessor[float](target_object, "detector.slit_length", "detector.slit_length.units", - default_units=units.millimeters) + default_unit=units.millimeters) def summary(self): return (f"Detector:\n" @@ -58,46 +61,6 @@ def summary(self): f" Pixel size: {self.pixel_size.value}\n" f" Slit length: {self.slit_length.value}\n") - def __init__(self, target_object: AccessorTarget): - - # Name of the instrument [string] - self.name = StringAccessor(target_object, "name") - - # Sample to detector distance [float] [mm] - self.distance = LengthAccessor[float](target_object, - "distance", - "distance.units", - default_unit=units.millimeters) - - # Offset of this detector position in X, Y, - # (and Z if necessary) [Vector] [mm] - self.offset = LengthAccessor[ArrayLike](target_object, - "offset", - "offset.units", - default_unit=units.millimeters) - - self.orientation = AngleAccessor[ArrayLike](target_object, - "orientation", - "orientation.units", - default_unit=units.degrees) - - self.beam_center = LengthAccessor[ArrayLike](target_object, - "beam_center", - "beam_center.units", - default_unit=units.millimeters) - - # Pixel size in X, Y, (and Z if necessary) [Vector] [mm] - self.pixel_size = LengthAccessor[ArrayLike](target_object, - "pixel_size", - "pixel_size.units", - default_unit=units.millimeters) - - # Slit length of the instrument for this detector.[float] [mm] - self.slit_length = LengthAccessor[float](target_object, - "slit_length", - "slit_length.units", - default_unit=units.millimeters) - class Aperture: def __init__(self, target_object): @@ -118,7 +81,7 @@ def __init__(self, target_object): default_unit=units.millimeters) # Aperture distance [float] - self.distance = QuantityAccessor[float](self.target_object, + self.distance = LengthAccessor[float](target_object, "apature.distance", "apature.distance.units", default_unit=units.millimeters) @@ -140,76 +103,97 @@ def __init__(self, target_object): # Name self.name = StringAccessor(target_object, "collimation.name") # Length [float] [mm] - self.length = QuantityAccessor[float](target_object, + self.length = LengthAccessor[float](target_object, "collimation.length", "collimation.length.units", - default_units=units.millimeters) + default_unit=units.millimeters) # Todo - how do we handle this self.collimator = Collimation(target_object) - def __str__(self): - _str = "Collimation:\n" - _str += " Length: %s [%s]\n" % \ - (str(self.length), str(self.length_unit)) - for item in self.aperture: + def summary(self): + + #TODO collimation stuff + return ( + f"Collimation:\n" + f" Length: {self.length.value}\n") + class Source: """ Class to hold source information """ - # Name - name = None - # Generic radiation type (Type and probe give more specific info) [string] - radiation = None - # Type and probe are only written to by the NXcanSAS reader - # Specific radiation type (Synchotron X-ray, Reactor neutron, etc) [string] - type = None - # Radiation probe (generic probe such as neutron, x-ray, muon, etc) [string] - probe = None - # Beam size name - beam_size_name = None - # Beam size [Vector] [mm] - beam_size = None - beam_size_unit = 'mm' - # Beam shape [string] - beam_shape = None - # Wavelength [float] [Angstrom] - wavelength = None - wavelength_unit = 'A' - # Minimum wavelength [float] [Angstrom] - wavelength_min = None - wavelength_min_unit = 'nm' - # Maximum wavelength [float] [Angstrom] - wavelength_max = None - wavelength_max_unit = 'nm' - # Wavelength spread [float] [Angstrom] - wavelength_spread = None - wavelength_spread_unit = 'percent' - def __init__(self): - self.beam_size = None #Vector() + def __init__(self, target_object): + # Name + self.name = StringAccessor(target_object, "source.name") + + # Generic radiation type (Type and probe give more specific info) [string] + self.radiation = StringAccessor(target_object, "source.radiation") + + # Type and probe are only written to by the NXcanSAS reader + # Specific radiation type (Synchotron X-ray, Reactor neutron, etc) [string] + self.type = StringAccessor(target_object, "source.type") + + # Radiation probe (generic probe such as neutron, x-ray, muon, etc) [string] + self.probe_particle = StringAccessor(target_object, "source.probe") + + # Beam size name + self.beam_size_name = StringAccessor(target_object, "source.beam_size_name") + + # Beam size [Vector] [mm] + self.beam_size = LengthAccessor[ArrayLike](target_object, + "source.beam_size", + "source.beam_size.units", + default_unit=units.millimeters) + + # Beam shape [string] + self.beam_shape = StringAccessor(target_object, "source.beam_shape") + + # Wavelength [float] [Angstrom] + self.wavelength = LengthAccessor[float](target_object, + "source.wavelength", + "source.wavelength.units", + default_unit=units.angstroms) + + # Minimum wavelength [float] [Angstrom] + self.wavelength_min = LengthAccessor[float](target_object, + "source.wavelength_min", + "source.wavelength_min.units", + default_unit=units.angstroms) + + # Maximum wavelength [float] [Angstrom] + self.wavelength_max = LengthAccessor[float](target_object, + "source.wavelength_min", + "source.wavelength_max.units", + default_unit=units.angstroms) + + # Wavelength spread [float] [Angstrom] + # Quantity because it might have other units, such as percent + self.wavelength_spread = QuantityAccessor[float](target_object, + "source.wavelength_spread", + "source.wavelength_spread.units", + default_unit=units.angstroms) + + + def summary(self): + + if self.radiation.value is None and self.type.value and self.probe_particle.value: + radiation = f"{self.type.value} {self.probe_particle.value}" + else: + radiation = f"{self.radiation.value}" + + return (f"Source:\n" + f" Radiation: {radiation}\n" + f" Shape: {self.beam_shape.value}\n" + f" Wavelength: {self.wavelength.value}\n" + f" Min. Wavelength: {self.wavelength_min.value}\n" + f" Max. Wavelength: {self.wavelength_max.value}\n" + f" Wavelength Spread: {self.wavelength_spread.value}\n" + f" Beam Size: {self.beam_size}\n") - def __str__(self): - _str = "Source:\n" - radiation = self.radiation - if self.radiation is None and self.type and self.probe: - radiation = self.type + " " + self.probe - _str += " Radiation: %s\n" % str(radiation) - _str += " Shape: %s\n" % str(self.beam_shape) - _str += " Wavelength: %s [%s]\n" % \ - (str(self.wavelength), str(self.wavelength_unit)) - _str += " Waveln_min: %s [%s]\n" % \ - (str(self.wavelength_min), str(self.wavelength_min_unit)) - _str += " Waveln_max: %s [%s]\n" % \ - (str(self.wavelength_max), str(self.wavelength_max_unit)) - _str += " Waveln_spread:%s [%s]\n" % \ - (str(self.wavelength_spread), str(self.wavelength_spread_unit)) - _str += " Beam_size: %s [%s]\n" % \ - (str(self.beam_size), str(self.beam_size_unit)) - return _str """ @@ -225,29 +209,40 @@ class Sample: """ Class to hold the sample description """ - # Short name for sample - name = '' - # ID - ID = '' - # Thickness [float] [mm] - thickness = None - thickness_unit = 'mm' - # Transmission [float] [fraction] - transmission = None - # Temperature [float] [No Default] - temperature = None - temperature_unit = None - # Position [Vector] [mm] - position = None - position_unit = 'mm' - # Orientation [Vector] [degrees] - orientation = None - orientation_unit = 'degree' - # Details - details = None - # SESANS zacceptance - zacceptance = (0,"") - yacceptance = (0,"") + def __init__(self, target_object): + + # Short name for sample + self.name = StringAccessor(target_object, "sample.name") + # ID + + self.sample_id = StringAccessor(target_object, "sample.id") + + # Thickness [float] [mm] + self.thickness = LengthAccessor(target_object, + "sample.thickness", + "sample.thickness.units", + default_unit=units.millimeters) + + # Transmission [float] [fraction] + self.transmission = FloatAccessor(target_object,"sample.transmission") + + # Temperature [float] [No Default] + self.temperature = TemperatureAccessor(target_object, + "sample.temperature", + "sample.temperature.unit") + temperature = None + temperature_unit = None + # Position [Vector] [mm] + position = None + position_unit = 'mm' + # Orientation [Vector] [degrees] + orientation = None + orientation_unit = 'degree' + # Details + details = None + # SESANS zacceptance + zacceptance = (0,"") + yacceptance = (0,"") def __init__(self): self.position = None # Vector() diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index a1437d170..42d14fc10 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -1,7 +1,8 @@ -from typing import TypeVar +from typing import TypeVar, Sequence from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units +from sasdata.quantities.units import Dimensions, Unit DataType = TypeVar("DataType") @@ -23,6 +24,15 @@ class StringAccessor(Accessor[str, str]): def value(self) -> str | None: pass +class FloatAccessor(Accessor[float, float]): + """ Float based fields """ + @property + def value(self) -> float | None: + pass + + + + class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): """ Base class for accessors that work with quantities that have units """ def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): @@ -30,19 +40,22 @@ def __init__(self, target_object, value_target: str, unit_target: str, default_u self._unit_target = unit_target self.default_unit = default_unit - def _lookup_unit(self) -> units.Unit | None: - # TODO: Implement - return None + def _numerical_part(self) -> DataType | None: + """ Numerical part of the data """ + + def _unit_part(self) -> str | None: + """ String form of units for the data """ - def data_unit(self): - unit = self._lookup_unit - if unit is None: + @property + def unit(self) -> Unit: + if self._unit_part() is None: return self.default_unit else: - return unit - + return Unit.parse(self._unit_part()) @property - def quantity(self) -> Quantity[DataType]: - raise NotImplementedError("Not implemented yet") + def value(self) -> Quantity[DataType] | None: + if self._unit_part() is not None and self._numerical_part() is not None: + return Quantity(self._numerical_part(), self.unit) + diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index b82946d13..303ccfee7 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -54,7 +54,7 @@ ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), ] -non_si_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ +non_si_dimensioned_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, 0, 0, []), ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, 0, 0, []), ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, 0, 0, []), @@ -63,7 +63,6 @@ ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, 0, 1, []), ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, 0, 1, []), ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, 0, 2, []), - ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), ("l", None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), ("eV", None, "electronvolt", "electronvolts", 1.602176634e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), ("au", None, "atomic mass unit", "atomic mass units", 1.660538921e-27, 0, 0, 1, 0, 0, 0, 0, []), @@ -80,6 +79,13 @@ ("psi", None, "pound force per square inch", "pounds force per square inch", 4.448222/(0.0254**2), -1, -2, 1, 0, 0, 0, 0, []), ] +non_si_dimensionless_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ + ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), + ("percent", "%", "percent", "percent", 100, 0, 0, 0, 0, 0, 0, 0, []) +] + +non_si_units = non_si_dimensioned_units + non_si_dimensionless_units + # TODO: # Add Hartree? Rydberg? Bohrs? # Add CGS @@ -89,7 +95,8 @@ "d": ["day"], "h": ["hr", "hour"], "Ang": ["A", "Å"], - "au": ["a.u.", "amu"] + "au": ["a.u.", "amu"], + "percent": ["%"] } @@ -325,7 +332,7 @@ def format_name(name: str): ("angle", Dimensions(angle_hint=1)), ("solid_angle", Dimensions(angle_hint=2)), ("amount", Dimensions(moles_hint=1)), - ("concentration", Dimensions(length=-3, moles_hint=1)) + ("concentration", Dimensions(length=-3, moles_hint=1)), ] fid.write("\n#\n# Units by type\n#\n\n") diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 52d43b0ef..a7d9504c0 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -194,6 +194,10 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): def __repr__(self): return f"Unit[{self.scale}, {self.dimensions}]" + @staticmethod + def parse(unit_string: str) -> "Unit": + pass + class NamedUnit(Unit): """ Units, but they have a name, and a symbol @@ -267,3 +271,4 @@ class UnitGroup: def __init__(self, name: str, units: list[NamedUnit]): self.name = name self.units = sorted(units, key=lambda unit: unit.scale) + diff --git a/sasdata/quantities/absolute_temperature.py b/sasdata/quantities/absolute_temperature.py index ecfd0e6d9..95c8982fb 100644 --- a/sasdata/quantities/absolute_temperature.py +++ b/sasdata/quantities/absolute_temperature.py @@ -1,6 +1,6 @@ from typing import TypeVar -from sasdata.quantities.quantity import Quantity +from quantities.quantity import Quantity from sasdata.quantities.accessors import TemperatureAccessor diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index eaffa5e23..45f801084 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -78,10 +78,11 @@ """ -from typing import TypeVar +from typing import TypeVar, Sequence from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units +from sasdata.quantities.units import Dimensions, Unit DataType = TypeVar("DataType") @@ -103,6 +104,15 @@ class StringAccessor(Accessor[str, str]): def value(self) -> str | None: pass +class FloatAccessor(Accessor[float, float]): + """ Float based fields """ + @property + def value(self) -> float | None: + pass + + + + class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): """ Base class for accessors that work with quantities that have units """ def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): @@ -110,21 +120,22 @@ def __init__(self, target_object, value_target: str, unit_target: str, default_u self._unit_target = unit_target self.default_unit = default_unit - def _lookup_unit(self) -> units.Unit | None: - # TODO: Implement - return None + def _numerical_part(self) -> DataType | None: + pass + + def _unit_part(self) -> str | None: + pass - def data_unit(self): - unit = self._lookup_unit - if unit is None: + def unit(self) -> Unit: + if self._unit_part() is None: return self.default_unit else: - return unit - - + return Unit.parse(self._unit_part()) @property - def quantity(self) -> Quantity[DataType]: - raise NotImplementedError("Not implemented yet") + def value(self) -> Quantity[DataType] | None: + if self._unit_part() is not None and self._numerical_part() is not None: + return Quantity(self.unit()) + class LengthAccessor[T](QuantityAccessor[T]): @@ -8986,6 +8997,14 @@ def none(self) -> T: else: return quantity.in_units_of(units.none) + @property + def percent(self) -> T: + quantity = self.quantity + if quantity is None: + return None + else: + return quantity.in_units_of(units.percent) + class AngleAccessor[T](QuantityAccessor[T]): diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 2c07a3d95..d02f643c4 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -77,3 +77,7 @@ def __rsub__(self: Self, other: Self | ArrayLike) -> Self: def __pow__(self: Self, other: int): return Quantity(self.value**other, self.units**other) + + def parse(self, number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): + pass + diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 479bd5fec..e25dd59e6 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -278,6 +278,10 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): def __repr__(self): return f"Unit[{self.scale}, {self.dimensions}]" + @staticmethod + def parse(unit_string: str) -> "Unit": + pass + class NamedUnit(Unit): """ Units, but they have a name, and a symbol @@ -353,6 +357,7 @@ def __init__(self, name: str, units: list[NamedUnit]): self.units = sorted(units, key=lambda unit: unit.scale) + # # Specific units # @@ -595,7 +600,6 @@ def __init__(self, name: str, units: list[NamedUnit]): degrees = NamedUnit(57.29577951308232, Dimensions(0, 0, 0, 0, 0, 0, 1),name='degrees',ascii_symbol='deg',symbol='deg') radians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 1),name='radians',ascii_symbol='rad',symbol='rad') stradians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') -none = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') litres = NamedUnit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') electronvolts = NamedUnit(1.602176634e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') exaelectronvolts = NamedUnit(0.1602176634, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') @@ -628,6 +632,8 @@ def __init__(self, name: str, units: list[NamedUnit]): pounds_force = NamedUnit(4.448222, Dimensions(1, -2, 1, 0, 0, 0, 0),name='pounds_force',ascii_symbol='lbf',symbol='lbf') ounces = NamedUnit(0.028349523125, Dimensions(0, 0, 1, 0, 0, 0, 0),name='ounces',ascii_symbol='oz',symbol='oz') pounds_force_per_square_inch = NamedUnit(6894.757889515779, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pounds_force_per_square_inch',ascii_symbol='psi',symbol='psi') +none = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') +percent = NamedUnit(100, Dimensions(0, 0, 0, 0, 0, 0, 0),name='percent',ascii_symbol='percent',symbol='%') square_meters = NamedUnit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = NamedUnit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = NamedUnit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -1936,6 +1942,8 @@ def __init__(self, name: str, units: list[NamedUnit]): "lbf": pounds_force, "oz": ounces, "psi": pounds_force_per_square_inch, + "percent": percent, + "%": percent, "yr": years, "year": years, "day": days, @@ -3167,6 +3175,7 @@ def __init__(self, name: str, units: list[NamedUnit]): name = 'dimensionless', units = [ none, + percent, ]) angle = UnitGroup( From 9599d68fbe38e2eff404c85deeeeb10b2a73a7eb Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 14 Aug 2024 13:23:16 +0100 Subject: [PATCH 508/675] Metadata objects complete for now --- sasdata/metadata.py | 171 +++++++++++++--------------- sasdata/quantities/_build_tables.py | 3 +- 2 files changed, 81 insertions(+), 93 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index c01c8ba40..c48997b10 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -2,6 +2,7 @@ from numpy.typing import ArrayLike import sasdata.quantities.units as units +from quantities.absolute_temperature import AbsoluteTemperatureAccessor from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor, \ DimensionlessAccessor, FloatAccessor, TemperatureAccessor @@ -177,8 +178,7 @@ def __init__(self, target_object): "source.wavelength_spread.units", default_unit=units.angstroms) - - def summary(self): + def summary(self) -> str: if self.radiation.value is None and self.type.value and self.probe_particle.value: radiation = f"{self.type.value} {self.probe_particle.value}" @@ -227,46 +227,44 @@ def __init__(self, target_object): self.transmission = FloatAccessor(target_object,"sample.transmission") # Temperature [float] [No Default] - self.temperature = TemperatureAccessor(target_object, - "sample.temperature", - "sample.temperature.unit") - temperature = None - temperature_unit = None + self.temperature = AbsoluteTemperatureAccessor(target_object, + "sample.temperature", + "sample.temperature.unit", + default_unit=units.kelvin) # Position [Vector] [mm] - position = None - position_unit = 'mm' + self.position = LengthAccessor[ArrayLike](target_object, + "sample.position", + "sample.position.unit", + default_unit=units.millimeters) + # Orientation [Vector] [degrees] - orientation = None - orientation_unit = 'degree' + self.orientation = AngleAccessor[ArrayLike](target_object, + "sample.orientation", + "sample.orientation.unit", + default_unit=units.degrees) + # Details - details = None + self.details = StringAccessor(target_object, "sample.details") + + # SESANS zacceptance zacceptance = (0,"") yacceptance = (0,"") - def __init__(self): - self.position = None # Vector() - self.orientation = None # Vector() - self.details = [] - - def __str__(self): - _str = "Sample:\n" - _str += " ID: %s\n" % str(self.ID) - _str += " Transmission: %s\n" % str(self.transmission) - _str += " Thickness: %s [%s]\n" % \ - (str(self.thickness), str(self.thickness_unit)) - _str += " Temperature: %s [%s]\n" % \ - (str(self.temperature), str(self.temperature_unit)) - _str += " Position: %s [%s]\n" % \ - (str(self.position), str(self.position_unit)) - _str += " Orientation: %s [%s]\n" % \ - (str(self.orientation), str(self.orientation_unit)) - - _str += " Details:\n" - for item in self.details: - _str += " %s\n" % item - - return _str + def summary(self) -> str: + return (f"Sample:\n" + f" ID: {self.sample_id.value}\n" + f" Transmission: {self.transmission.value}\n" + f" Thickness: {self.thickness.value}\n" + f" Temperature: {self.temperature.value}\n" + f" Position: {self.position.value}\n" + f" Orientation: {self.orientation.value}\n") + # + # _str += " Details:\n" + # for item in self.details: + # _str += " %s\n" % item + # + # return _str class Process: @@ -274,74 +272,63 @@ class Process: Class that holds information about the processes performed on the data. """ - name = '' - date = '' - description = '' - term = None - notes = None + def __init__(self, target_object): + self.name = StringAccessor(target_object, "process.name") + self.date = StringAccessor(target_object, "process.date") + self.description = StringAccessor(target_object, "process.description") - def __init__(self): - self.term = [] - self.notes = [] + #TODO: It seems like these might be lists of strings, this should be checked - def is_empty(self): - """ - Return True if the object is empty - """ - return (len(self.name) == 0 and len(self.date) == 0 - and len(self.description) == 0 and len(self.term) == 0 - and len(self.notes) == 0) + self.term = StringAccessor(target_object, "process.term") + self.notes = StringAccessor(target_object, "process.notes") def single_line_desc(self): """ Return a single line string representing the process """ - return "%s %s %s" % (self.name, self.date, self.description) + return f"{self.name.value} {self.date.value} {self.description.value}" def __str__(self): - _str = "Process:\n" - _str += " Name: %s\n" % self.name - _str += " Date: %s\n" % self.date - _str += " Description: %s\n" % self.description - for item in self.term: - _str += " Term: %s\n" % item - for item in self.notes: - _str += " Note: %s\n" % item - return _str - - -class TransmissionSpectrum(object): + return (f"Process:\n" + f" Name: {self.name.value}\n" + f" Date: {self.date.value}\n" + f" Description: {self.description.value}\n" + f" Term: {self.term.value}\n" + f" Notes: {self.notes.value}" + ) + +class TransmissionSpectrum: """ Class that holds information about transmission spectrum for white beams and spallation sources. """ - name = '' - timestamp = '' - # Wavelength (float) [A] - wavelength = None - wavelength_unit = 'A' - # Transmission (float) [unit less] - transmission = None - transmission_unit = '' - # Transmission Deviation (float) [unit less] - transmission_deviation = None - transmission_deviation_unit = '' - - def __init__(self): - self.wavelength = [] - self.transmission = [] - self.transmission_deviation = [] - - def __str__(self): - _str = "Transmission Spectrum:\n" - _str += " Name: \t{0}\n".format(self.name) - _str += " Timestamp: \t{0}\n".format(self.timestamp) - _str += " Wavelength unit: \t{0}\n".format(self.wavelength_unit) - _str += " Transmission unit:\t{0}\n".format(self.transmission_unit) - _str += " Trans. Dev. unit: \t{0}\n".format( - self.transmission_deviation_unit) - length_list = [len(self.wavelength), len(self.transmission), - len(self.transmission_deviation)] - _str += " Number of Pts: \t{0}\n".format(max(length_list)) - return _str + def __init__(self, target_object): + # TODO: Needs to be multiple cases + self.name = StringAccessor(target_object, "transmission.") + self.timestamp = StringAccessor(target_object, "transmission.timestamp") + + # Wavelength (float) [A] + self.wavelength = LengthAccessor[ArrayLike](target_object, + "transmission.wavelength", + "transmission.wavelength.units") + + # Transmission (float) [unit less] + self.transmission = DimensionlessAccessor[ArrayLike](target_object, + "transmission.transmission", + "transmission.units", + default_unit=units.none) + + # Transmission Deviation (float) [unit less] + self.transmission_deviation = DimensionlessAccessor[ArrayLike](target_object, + "transmission.transmission_deviation", + "transmission.transmission_deviation.units", + default_units=units.none) + + + def summary(self) -> str: + return (f"Transmission Spectrum:\n" + f" Name: {self.name.value}\n" + f" Timestamp: {self.timestamp.value}\n" + f" Wavelengths: {self.wavelength.value}\n" + f" Transmission: {self.transmission.value}\n") diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 303ccfee7..d17dae120 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -96,7 +96,8 @@ "h": ["hr", "hour"], "Ang": ["A", "Å"], "au": ["a.u.", "amu"], - "percent": ["%"] + "percent": ["%"], + "deg": ["degr"], } From 1c0c0eb4449e74d743beeb0b222afce05f8347cc Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 14 Aug 2024 13:29:22 +0100 Subject: [PATCH 509/675] Percent test and fix --- sasdata/quantities/_build_tables.py | 2 +- sasdata/quantities/accessors.py | 8 +++++--- sasdata/quantities/quantities_tests.py | 2 ++ sasdata/quantities/units.py | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index d17dae120..490f9fad4 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -81,7 +81,7 @@ non_si_dimensionless_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), - ("percent", "%", "percent", "percent", 100, 0, 0, 0, 0, 0, 0, 0, []) + ("percent", "%", "percent", "percent", 0.01, 0, 0, 0, 0, 0, 0, 0, []) ] non_si_units = non_si_dimensioned_units + non_si_dimensionless_units diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 45f801084..92ef82bd1 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -121,20 +121,22 @@ def __init__(self, target_object, value_target: str, unit_target: str, default_u self.default_unit = default_unit def _numerical_part(self) -> DataType | None: - pass + """ Numerical part of the data """ def _unit_part(self) -> str | None: - pass + """ String form of units for the data """ + @property def unit(self) -> Unit: if self._unit_part() is None: return self.default_unit else: return Unit.parse(self._unit_part()) + @property def value(self) -> Quantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self.unit()) + return Quantity(self._numerical_part(), self.unit) diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index 3c66624a5..c6cab77e3 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -75,6 +75,8 @@ def test_american_units(): assert_unit_ratio(units.miles, units.inches, 63360) assert_unit_ratio(units.pounds_force_per_square_inch, units.pounds_force / (units.inches**2), 1, abs=1e-5) +def test_percent(): + assert Quantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) @pytest.mark.parametrize("unit_1", si.all_si) @pytest.mark.parametrize("unit_2", si.all_si) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index e25dd59e6..2da7b4537 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -633,7 +633,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ounces = NamedUnit(0.028349523125, Dimensions(0, 0, 1, 0, 0, 0, 0),name='ounces',ascii_symbol='oz',symbol='oz') pounds_force_per_square_inch = NamedUnit(6894.757889515779, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pounds_force_per_square_inch',ascii_symbol='psi',symbol='psi') none = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') -percent = NamedUnit(100, Dimensions(0, 0, 0, 0, 0, 0, 0),name='percent',ascii_symbol='percent',symbol='%') +percent = NamedUnit(0.01, Dimensions(0, 0, 0, 0, 0, 0, 0),name='percent',ascii_symbol='percent',symbol='%') square_meters = NamedUnit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = NamedUnit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = NamedUnit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') @@ -1951,6 +1951,7 @@ def __init__(self, name: str, units: list[NamedUnit]): "hour": hours, "a.u.": atomic_mass_units, "amu": atomic_mass_units, + "degr": degrees, } From 29bed57d7399db2e3ba707ca3ca29e81c8303066 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 21 Aug 2024 10:38:51 +0100 Subject: [PATCH 510/675] Work towards new data object --- sasdata/data.py | 91 ++------------- sasdata/metadata.py | 4 +- sasdata/model_requirements.py | 7 +- sasdata/quantities/quantity.py | 6 + sasdata/raw_form.py | 63 ++++++++++ sasdata/temp_hdf5_reader.py | 206 +++++---------------------------- 6 files changed, 116 insertions(+), 261 deletions(-) create mode 100644 sasdata/raw_form.py diff --git a/sasdata/data.py b/sasdata/data.py index 2c3dfad37..2c6e07b0c 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,6 +1,6 @@ from dataclasses import dataclass -from quantities.quantity import Quantity, NamedQuantity -from sasdata.metadata import MetaData +from sasdata.quantities.quantity import Quantity, NamedQuantity +from sasdata.metadata import Metadata import numpy as np @@ -19,84 +19,11 @@ def __init__(self, name: str, self._raw_metadata = raw_metadata self._verbose = verbose - self.metadata = Metadata(AccessorTarget(raw_metadata, verbose=verbose), instrument) +@dataclass +class DataSet: + abscissae: list[NamedQuantity[np.ndarray]] + ordinate: NamedQuantity[np.ndarray] + other: list[NamedQuantity[np.ndarray]] - # Components that need to be organised after creation - self.mask = None # TODO: fill out - self.model_requirements = None # TODO: fill out - - #TODO: This seems oriented around 1D I vs Q data. What about 2D data? - @property - def ordinate() -> Quantity: - raise NotImplementedError() - - @property - def abscissae(self) -> Quantity: - if self.dataset_type == one_dim: - return self._data_contents['Q'] - elif self.dataset_type == two_dim: - # Type hinting is a bit lacking. Assume each part of the zip is a scalar value. - data_contents = zip(self._data_contents['Qx'].value, self._data_contents['Qy'].value) - # Use this value to extract units etc. Assume they will be the same for Qy. - reference_data_content = self._data_contents['Qx'] - # TODO: If this is a derived quantity then we are going to lose that - # information. - # - # TODO: Won't work when there's errors involved. On reflection, we - # probably want to avoid creating a new Quantity but at the moment I - # can't see a way around it. - return Quantity(data_contents, reference_data_content.units) - return None - - def __getitem__(self, item: str): - return self._data_contents[item] - - def summary(self, indent = " ", include_raw=False): - s = f"{self.name}\n" - - for data in self._data_contents: - s += f"{indent}{data}\n" - - s += f"Metadata:\n" - s += "\n" - s += self.metadata.summary() - - if include_raw: - s += key_tree(self._raw_metadata) - - return s - - @staticmethod - def deserialise(data: str) -> "SasData": - json_data = json.loads(data) - return SasData.deserialise_json(json_data) - - @staticmethod - def deserialise_json(json_data: dict) -> "SasData": - name = json_data["name"] - data_contents = {} - dataset_type = json_data["dataset_type"] # TODO: update when DatasetType is more finalized - metadata = json_data["metadata"].deserialise_json() - for quantity in json_data["data_contents"]: - data_contents[quantity["label"]] = Quantity.deserialise_json(quantity) - return SasData(name, data_contents, dataset_type, metadata) - - def serialise(self) -> str: - return json.dumps(self._serialise_json()) - - # TODO: fix serializers eventually - def _serialise_json(self) -> dict[str, Any]: - data = [] - for d in self._data_contents: - quantity = self._data_contents[d] - quantity["label"] = d - data.append(quantity) - return { - "name": self.name, - "data_contents": data, - "dataset_type": None, # TODO: update when DatasetType is more finalized - "verbose": self._verbose, - "metadata": self.metadata.serialise_json(), - "mask": {}, - "model_requirements": {} - } + metadata: Metadata + model_requirements: ModellingRequirements diff --git a/sasdata/metadata.py b/sasdata/metadata.py index c48997b10..1ba8718c0 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -303,7 +303,7 @@ class TransmissionSpectrum: for white beams and spallation sources. """ def __init__(self, target_object): - # TODO: Needs to be multiple cases + # TODO: Needs to be multiple instances self.name = StringAccessor(target_object, "transmission.") self.timestamp = StringAccessor(target_object, "transmission.timestamp") @@ -332,3 +332,5 @@ def summary(self) -> str: f" Wavelengths: {self.wavelength.value}\n" f" Transmission: {self.transmission.value}\n") +class Metadata: + pass \ No newline at end of file diff --git a/sasdata/model_requirements.py b/sasdata/model_requirements.py index a35759bd2..5d68ad1b4 100644 --- a/sasdata/model_requirements.py +++ b/sasdata/model_requirements.py @@ -2,6 +2,7 @@ import numpy as np +from sasdata.metadata import Metadata from transforms.operation import Operation @@ -11,12 +12,12 @@ class ModellingRequirements: dimensionality: int operation: Operation - - def from_qi_transformation(self, data: np.ndarray) -> np.ndaarray: + def from_qi_transformation(self, data: np.ndarray, metadata: Metadata) -> np.ndarray: + """ Transformation for going from qi to this data""" pass def guess_requirements(abscissae, ordinate) -> ModellingRequirements: - """ Use names of axes and units to guess what kind of processing needs to be done """ + """ Use names of axes and units to guess what kind of processing needs to be done """ \ No newline at end of file diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index d02f643c4..cf350365d 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -81,3 +81,9 @@ def __pow__(self: Self, other: int): def parse(self, number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass + +class NamedQuantity[QuantityType](Quantity[QuantityType]): + def __init__(self, value: QuantityType, units: Unit, name: str): + super().__init__(value, units) + self.name = name + diff --git a/sasdata/raw_form.py b/sasdata/raw_form.py new file mode 100644 index 000000000..37736d37a --- /dev/null +++ b/sasdata/raw_form.py @@ -0,0 +1,63 @@ +from typing import TypeVar, Any, Self +from dataclasses import dataclass + +from quantities.quantity import NamedQuantity + +DataType = TypeVar("DataType") + +def shorten_string(string): + lines = string.split("\n") + if len(lines) <= 1: + return string + else: + return lines[0][:30] + " ... " + lines[-1][-30:] + +@dataclass +class Dataset[DataType]: + name: str + data: DataType + attributes: dict[str, Self] + + def summary(self, indent_amount: int = 0, indent: str = " ") -> str: + + s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" + s += f"{indent*(indent_amount+1)}{shorten_string(str(self.data))}\n" + for key in self.attributes: + value = self.attributes[key] + if isinstance(value, (Group, Dataset)): + value_string = value.summary(indent_amount+1, indent) + else: + value_string = f"{indent * indent_amount}{self.data}\n" + + s += value_string + + + return s + +@dataclass +class Group: + name: str + children: dict[str, Self | Dataset] + + + def summary(self, indent_amount: int=0, indent=" "): + s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" + for key in self.children: + s += self.children[key].summary(indent_amount+1, indent) + + return s + +@dataclass +class RawData: + name: str + data_contents: list[NamedQuantity] + raw_metadata: dict[str, Dataset | Group] + + def __repr__(self): + indent = " " + + s = f"{self.name}\n" + for key in self.raw_metadata: + s += self.raw_metadata[key].summary(1, indent) + + return s \ No newline at end of file diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index a8f275b74..b7194b008 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -11,180 +11,50 @@ from h5py._hl.group import Group as HDF5Group -from sasdata.data import SasData -from sasdata.data_backing import Dataset as SASDataDataset, Group as SASDataGroup -from sasdata.metadata import Instrument, Collimation, Aperture, Source -from sasdata.quantities.accessors import AccessorTarget +from sasdata.data import DataSet +from sasdata.raw_form import RawData +from sasdata.raw_form import Dataset as SASDataDataset, Group as SASDataGroup -from sasdata.quantities.quantity import NamedQuantity -from sasdata.quantities import units -from sasdata.quantities.unit_parser import parse - -# test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" -# test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" -# test_file = "./example_data/2d_data/BAM_2D.h5" -test_file = "./example_data/2d_data/14250_2D_NoDetInfo_NXcanSAS_v3.h5" -# test_file = "./example_data/2d_data/33837rear_2D_1.75_16.5_NXcanSAS_v3.h5" +test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" +test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" logger = logging.getLogger(__name__) +def hdf5_attr(entry): + return entry def recurse_hdf5(hdf5_entry): if isinstance(hdf5_entry, HDF5Dataset): - attributes = {name: hdf5_entry.attrs[name] for name in hdf5_entry.attrs} - if isinstance(hdf5_entry.dtype, np.dtypes.BytesDType): data = hdf5_entry[()][0].decode("utf-8") - - return SASDataDataset[str]( - name=hdf5_entry.name, data=data, attributes=attributes - ) - else: data = np.array(hdf5_entry, dtype=hdf5_entry.dtype) - return SASDataDataset[np.ndarray]( - name=hdf5_entry.name, data=data, attributes=attributes - ) + attributes = {name: hdf5_attr(hdf5_entry.attrs[name]) for name in hdf5_entry.attrs} + + return SASDataDataset( + name=hdf5_entry.name, + data=data, + attributes=attributes) elif isinstance(hdf5_entry, HDF5Group): return SASDataGroup( name=hdf5_entry.name, - children={key: recurse_hdf5(hdf5_entry[key]) for key in hdf5_entry.keys()}, - ) - - else: - raise TypeError( - f"Unknown type found during HDF5 parsing: {type(hdf5_entry)} ({hdf5_entry})" - ) - - -GET_UNITS_FROM_ELSEWHERE = units.meters + children={key: recurse_hdf5(hdf5_entry[key]) for key in hdf5_entry.keys()}) - -def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: - """In the context of NeXus files, load a group of data entries that are organised together - match up the units and errors with their values""" - # Gather together data with its error terms - - uncertainty_map = {} - uncertainties = set() - entries = {} - - for name in node.children: - child = node.children[name] - - if "units" in child.attributes: - units = parse(child.attributes["units"]) - else: - units = GET_UNITS_FROM_ELSEWHERE - - quantity = NamedQuantity( - name=name_prefix + child.name, value=child.data, units=units - ) - - # Turns out people can't be trusted to use the same keys here - if "uncertainty" in child.attributes or "uncertainties" in child.attributes: - try: - uncertainty_name = child.attributes["uncertainty"] - except: - uncertainty_name = child.attributes["uncertainties"] - uncertainty_map[name] = uncertainty_name - uncertainties.add(uncertainty_name) - - entries[name] = quantity - - output = [] - - for name, entry in entries.items(): - if name not in uncertainties: - if name in uncertainty_map: - uncertainty = entries[uncertainty_map[name]] - new_entry = entry.with_standard_error(uncertainty) - output.append(new_entry) - else: - output.append(entry) - - return output - - -def parse_apertures(node) -> list[Aperture]: - result = [] - aps = [a for a in node if "aperture" in a] - for ap in aps: - distance = None - size = None - if "distance" in node[ap]: - distance = node[ap]["distance"] - if "size" in node[ap]: - x = y = z = None - if "x" in node[ap]: - x = node[ap]["size"]["x"] - if "y" in node[ap]: - y = node[ap]["size"]["y"] - if "z" in node[ap]: - z = node[ap]["size"]["z"] - if x is not None or y is not None or z is not None: - size = (x, y, z) - result.append(Aperture(distance=distance, size=size, size_name=size_name, name=name, apType=apType)) - return result - - -def parse_source(node) -> Source: - beam_shape = None - beam_size = None - wavelength = None - wavelength_min = None - wavelength_max = None - wavelength_spread = None - if "beam_shape" in node: - beam_shape = node["beam_shape"] - if "wavelength" in node: - wavelength = node["wavelength"] - if "wavelength_min" in node: - wavelength = node["wavelength_min"] - if "wavelength_max" in node: - wavelength = node["wavelength_max"] - if "wavelength_spread" in node: - wavelength = node["wavelength_spread"] - return Source( - radiation=node["radiation"].asstr()[0], - beam_shape=beam_shape, - beam_size=beam_size, - wavelength=wavelength, - wavelength_min=wavelength_min, - wavelength_max=wavelength_max, - wavelength_spread=wavelength_spread, - ) - - -def parse_collimation(node) -> Collimation: - if "length" in node: - length = node["length"] else: - length = None - return Collimation(length=length, apertures=parse_apertures(node)) + raise TypeError(f"Unknown type found during HDF5 parsing: {type(hdf5_entry)} ({hdf5_entry})") +def load_data(filename) -> list[RawData]: + with h5py.File(filename, 'r') as f: -def parse_instrument(raw, node) -> Instrument: - collimations = [ - parse_collimation(node[x]) - for x in node - if "collimation" in x - ] - return Instrument( - collimations= [parse_collimation(node[x]) for x in node if "collimation" in x], - detector=[parse_detector(node[d]) for d in node if "detector" in d], - source=parse_source(node["sassource"]), - ) + loaded_data: list[RawData] = [] + for root_key in f.keys(): -def load_data(filename) -> list[SasData]: - with h5py.File(filename, "r") as f: - loaded_data: list[SasData] = [] + print(root_key) - for root_key in f.keys(): entry = f[root_key] data_contents = [] @@ -192,44 +62,30 @@ def load_data(filename) -> list[SasData]: entry_keys = [key for key in entry.keys()] - if "sasdata" not in entry_keys and "data" not in entry_keys: - logger.warning("No sasdata or data key") + if "sasdata" not in entry_keys: + logger.warning("") for key in entry_keys: component = entry[key] - lower_key = key.lower() - if lower_key == "sasdata" or lower_key == "data": - datum = recurse_hdf5(component) - # TODO: Use named identifier - data_contents = connected_data(datum, "FILE_ID_HERE") + if key.lower() == "sasdata": + print("found sasdata, skipping for now") else: raw_metadata[key] = recurse_hdf5(component) - instrument = opt_parse(f["sasentry01"], "sasinstrument", parse_instrument) - sample = opt_parse(f["sasentry01"], "sassample", parse_sample) - process = [parse_process(f["sasentry01"][p]) for p in f["sasentry01"] if "sasprocess" in p] - title = opt_parse(f["sasentry01"], "title", parse_string) - run = [parse_string(f["sasentry01"][r]) for r in f["sasentry01"] if "run" in r] - definition = opt_parse(f["sasentry01"], "definition", parse_string) - - metadata = Metadata(process=process, instrument=instrument, sample=sample, title=title, run=run, definition=definition) loaded_data.append( - SasData( + RawData( name=root_key, data_contents=data_contents, - raw_metadata=SASDataGroup("root", raw_metadata), - instrument=instrument, - verbose=False, - ) - ) + raw_metadata=raw_metadata)) return loaded_data -if __name__ == "__main__": - data = load_data(test_file) - for dataset in data: - print(dataset.summary(include_raw=False)) + +data = load_data(test_file) + +for dataset in data: + print(dataset) From 6b6c97abe2f22d30f6ea7068b86a0b0f91a4973c Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 28 Aug 2024 15:54:50 +0100 Subject: [PATCH 511/675] Basic reading --- sasdata/raw_form.py | 3 +-- sasdata/temp_hdf5_reader.py | 15 ++++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sasdata/raw_form.py b/sasdata/raw_form.py index 37736d37a..a58c09ce5 100644 --- a/sasdata/raw_form.py +++ b/sasdata/raw_form.py @@ -27,11 +27,10 @@ def summary(self, indent_amount: int = 0, indent: str = " ") -> str: if isinstance(value, (Group, Dataset)): value_string = value.summary(indent_amount+1, indent) else: - value_string = f"{indent * indent_amount}{self.data}\n" + value_string = f"{indent * (indent_amount+1)}{key}: {shorten_string(repr(value))}\n" s += value_string - return s @dataclass diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index b7194b008..e72e6a11c 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -53,8 +53,6 @@ def load_data(filename) -> list[RawData]: for root_key in f.keys(): - print(root_key) - entry = f[root_key] data_contents = [] @@ -63,15 +61,18 @@ def load_data(filename) -> list[RawData]: entry_keys = [key for key in entry.keys()] if "sasdata" not in entry_keys: - logger.warning("") + logger.warning("No sasdata key") for key in entry_keys: component = entry[key] - if key.lower() == "sasdata": - print("found sasdata, skipping for now") + # if key.lower() == "sasdata": + # datum = recurse_hdf5(component) + # data_contents.append(datum) + # + # else: + # raw_metadata[key] = recurse_hdf5(component) + raw_metadata[key] = recurse_hdf5(component) - else: - raw_metadata[key] = recurse_hdf5(component) loaded_data.append( From 3e4fd6b0bad86f649715cf0117fede45c20de361 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 29 Aug 2024 14:57:53 +0100 Subject: [PATCH 512/675] Work towards structuring inputs with uncertainties --- sasdata/quantities/quantity.py | 12 ++++++++++++ sasdata/temp_hdf5_reader.py | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index cf350365d..e4f99a063 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -87,3 +87,15 @@ def __init__(self, value: QuantityType, units: Unit, name: str): super().__init__(value, units) self.name = name + + +class UncertainQuantity[QuantityType](Quantity[QuantityType]): + def __init__(self, value: QuantityType, units: Unit, uncertainty: Quantity[QuantityType]): + super().__init__(value, units) + self.uncertainty = uncertainty + +class UncertainNamedQuantity[QuantityType](Quantity[QuantityType]): + def __init__(self, value: QuantityType, units: Unit, uncertainty: Quantity[QuantityType], name: str): + super().__init__(value, units) + self.uncertainty = uncertainty + self.name = name diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index e72e6a11c..51bd86695 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -11,12 +11,11 @@ from h5py._hl.group import Group as HDF5Group -from sasdata.data import DataSet from sasdata.raw_form import RawData from sasdata.raw_form import Dataset as SASDataDataset, Group as SASDataGroup test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" -test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" +# test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" logger = logging.getLogger(__name__) @@ -28,6 +27,7 @@ def recurse_hdf5(hdf5_entry): if isinstance(hdf5_entry.dtype, np.dtypes.BytesDType): data = hdf5_entry[()][0].decode("utf-8") + else: data = np.array(hdf5_entry, dtype=hdf5_entry.dtype) From 499af4938fd1c02bf72b4d09d30a8c36ec75a5a6 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 9 Sep 2024 14:35:08 +0100 Subject: [PATCH 513/675] Work on uncertainty propagation --- sasdata/data.py | 2 +- sasdata/quantities/_accessor_base.py | 8 +- sasdata/quantities/absolute_temperature.py | 6 +- sasdata/quantities/accessors.py | 8 +- sasdata/quantities/operations.py | 712 +++++++++++++++++++++ sasdata/quantities/operations_examples.py | 2 +- sasdata/quantities/operations_test.py | 2 +- sasdata/quantities/quantities_tests.py | 54 +- sasdata/quantities/quantity.py | 66 +- sasdata/transforms/operation.py | 4 +- 10 files changed, 797 insertions(+), 67 deletions(-) create mode 100644 sasdata/quantities/operations.py diff --git a/sasdata/data.py b/sasdata/data.py index 2c6e07b0c..096bdb401 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from sasdata.quantities.quantity import Quantity, NamedQuantity +from sasdata.quantities.quantity import BaseQuantity, NamedQuantity from sasdata.metadata import Metadata import numpy as np diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 42d14fc10..4ec7bf8cb 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -1,6 +1,6 @@ from typing import TypeVar, Sequence -from sasdata.quantities.quantity import Quantity +from sasdata.quantities.quantity import BaseQuantity import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit @@ -33,7 +33,7 @@ def value(self) -> float | None: -class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): +class QuantityAccessor[DataType](Accessor[DataType, BaseQuantity[DataType]]): """ Base class for accessors that work with quantities that have units """ def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): super().__init__(target_object, value_target) @@ -54,8 +54,8 @@ def unit(self) -> Unit: return Unit.parse(self._unit_part()) @property - def value(self) -> Quantity[DataType] | None: + def value(self) -> BaseQuantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self._numerical_part(), self.unit) + return BaseQuantity(self._numerical_part(), self.unit) diff --git a/sasdata/quantities/absolute_temperature.py b/sasdata/quantities/absolute_temperature.py index 95c8982fb..7bcccce4e 100644 --- a/sasdata/quantities/absolute_temperature.py +++ b/sasdata/quantities/absolute_temperature.py @@ -1,6 +1,6 @@ from typing import TypeVar -from quantities.quantity import Quantity +from quantities.quantity import BaseQuantity from sasdata.quantities.accessors import TemperatureAccessor @@ -8,8 +8,8 @@ class AbsoluteTemperatureAccessor(TemperatureAccessor[DataType]): """ Parsing for absolute temperatures """ @property - def value(self) -> Quantity[DataType] | None: + def value(self) -> BaseQuantity[DataType] | None: if self._numerical_part() is None: return None else: - return Quantity.parse(self._numerical_part(), self._unit_part(), absolute_temperature=True) + return BaseQuantity.parse(self._numerical_part(), self._unit_part(), absolute_temperature=True) diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 92ef82bd1..caaeb0022 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -80,7 +80,7 @@ from typing import TypeVar, Sequence -from sasdata.quantities.quantity import Quantity +from sasdata.quantities.quantity import BaseQuantity import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit @@ -113,7 +113,7 @@ def value(self) -> float | None: -class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): +class QuantityAccessor[DataType](Accessor[DataType, BaseQuantity[DataType]]): """ Base class for accessors that work with quantities that have units """ def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): super().__init__(target_object, value_target) @@ -134,9 +134,9 @@ def unit(self) -> Unit: return Unit.parse(self._unit_part()) @property - def value(self) -> Quantity[DataType] | None: + def value(self) -> BaseQuantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: - return Quantity(self._numerical_part(), self.unit) + return BaseQuantity(self._numerical_part(), self.unit) diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py new file mode 100644 index 000000000..60590333d --- /dev/null +++ b/sasdata/quantities/operations.py @@ -0,0 +1,712 @@ +from typing import Any, TypeVar, Union + +import json + +from sasdata.quantities.quantity import BaseQuantity + +T = TypeVar("T") + +def hash_and_name(hash_or_name: int | str): + """ Infer the name of a variable from a hash, or the hash from the name + + Note: hash_and_name(hash_and_name(number)[1]) is not the identity + however: hash_and_name(hash_and_name(number)) is + """ + + if isinstance(hash_or_name, str): + hash_value = hash(hash_or_name) + name = hash_or_name + + return hash_value, name + + elif isinstance(hash_or_name, int): + hash_value = hash_or_name + name = f"#{hash_or_name}" + + return hash_value, name + + elif isinstance(hash_or_name, tuple): + return hash_or_name + + else: + raise TypeError("Variable name_or_hash_value must be either str or int") + + +class Operation: + + serialisation_name = "unknown" + def summary(self, indent_amount: int = 0, indent: str=" "): + """ Summary of the operation tree""" + + s = f"{indent_amount*indent}{self._summary_open()}(\n" + + for chunk in self._summary_components(): + s += chunk.summary(indent_amount+1, indent) + "\n" + + s += f"{indent_amount*indent})" + + return s + def _summary_open(self): + """ First line of summary """ + + def _summary_components(self) -> list["Operation"]: + return [] + def evaluate(self, variables: dict[int, T]) -> T: + + """ Evaluate this operation """ + + def _derivative(self, hash_value: int) -> "Operation": + """ Get the derivative of this operation """ + + def _clean(self): + """ Clean up this operation - i.e. remove silly things like 1*x """ + return self + + def derivative(self, variable: Union[str, int, "Variable"], simplify=True): + if isinstance(variable, Variable): + hash_value = variable.hash_value + else: + hash_value, _ = hash_and_name(variable) + + derivative = self._derivative(hash_value) + + if not simplify: + return derivative + + derivative_string = derivative.serialise() + + print("---------------") + print("Base") + print("---------------") + print(derivative.summary()) + + # Inefficient way of doing repeated simplification, but it will work + for i in range(100): # set max iterations + + derivative = derivative._clean() + + print("-------------------") + print("Iteration", i+1) + print("-------------------") + print(derivative.summary()) + print("-------------------") + + new_derivative_string = derivative.serialise() + + if derivative_string == new_derivative_string: + break + + derivative_string = new_derivative_string + + return derivative + + @staticmethod + def deserialise(data: str) -> "Operation": + json_data = json.loads(data) + return Operation.deserialise_json(json_data) + + @staticmethod + def deserialise_json(json_data: dict) -> "Operation": + + operation = json_data["operation"] + parameters = json_data["parameters"] + cls = _serialisation_lookup[operation] + + try: + return cls._deserialise(parameters) + + except NotImplementedError: + raise NotImplementedError(f"No method to deserialise {operation} with {parameters} (cls={cls})") + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + raise NotImplementedError(f"Deserialise not implemented for this class") + + def serialise(self) -> str: + return json.dumps(self._serialise_json()) + + def _serialise_json(self) -> dict[str, Any]: + return {"operation": self.serialisation_name, + "parameters": self._serialise_parameters()} + + def _serialise_parameters(self) -> dict[str, Any]: + raise NotImplementedError("_serialise_parameters not implemented") + + def __eq__(self, other: "Operation"): + return NotImplemented + +class ConstantBase(Operation): + pass + +class AdditiveIdentity(ConstantBase): + + serialisation_name = "zero" + def evaluate(self, variables: dict[int, T]) -> T: + return 0 + + def _derivative(self, hash_value: int) -> Operation: + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return AdditiveIdentity() + + def _serialise_parameters(self) -> dict[str, Any]: + return {} + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}0 [Add.Id.]" + + def __eq__(self, other): + if isinstance(other, AdditiveIdentity): + return True + elif isinstance(other, Constant): + if other.value == 0: + return True + + return False + + + +class MultiplicativeIdentity(ConstantBase): + + serialisation_name = "one" + + def evaluate(self, variables: dict[int, T]) -> T: + return 1 + + def _derivative(self, hash_value: int): + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return MultiplicativeIdentity() + + + def _serialise_parameters(self) -> dict[str, Any]: + return {} + + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}1 [Mul.Id.]" + + def __eq__(self, other): + if isinstance(other, MultiplicativeIdentity): + return True + elif isinstance(other, Constant): + if other.value == 1: + return True + + return False + + +class Constant(ConstantBase): + + serialisation_name = "constant" + def __init__(self, value): + self.value = value + + def summary(self, indent_amount: int = 0, indent: str=" "): + pass + + def evaluate(self, variables: dict[int, T]) -> T: + return self.value + + def _derivative(self, hash_value: int): + return AdditiveIdentity() + + def _clean(self): + + if self.value == 0: + return AdditiveIdentity() + + elif self.value == 1: + return MultiplicativeIdentity() + + else: + return self + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + value = parameters["value"] + return Constant(value) + + + def _serialise_parameters(self) -> dict[str, Any]: + return {"value": self.value} + + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}{self.value}" + + def __eq__(self, other): + if isinstance(other, AdditiveIdentity): + return self.value == 0 + + elif isinstance(other, MultiplicativeIdentity): + return self.value == 1 + + elif isinstance(other, Constant): + if other.value == self.value: + return True + + return False + + +class Variable(Operation): + + serialisation_name = "variable" + def __init__(self, name_or_hash_value: int | str | tuple[int, str]): + self.hash_value, self.name = hash_and_name(name_or_hash_value) + + def evaluate(self, variables: dict[int, T]) -> T: + try: + return variables[self.hash_value] + except KeyError: + raise ValueError(f"Variable dictionary didn't have an entry for {self.name} (hash={self.hash_value})") + + def _derivative(self, hash_value: int) -> Operation: + if hash_value == self.hash_value: + return MultiplicativeIdentity() + else: + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + hash_value = parameters["hash_value"] + name = parameters["name"] + + return Variable((hash_value, name)) + + def _serialise_parameters(self) -> dict[str, Any]: + return {"hash_value": self.hash_value, + "name": self.name} + + def summary(self, indent_amount: int = 0, indent: str=" "): + return f"{indent_amount*indent}{self.name}" + + def __eq__(self, other): + if isinstance(other, Variable): + return self.hash_value == other.hash_value + + return False + +class UnaryOperation(Operation): + + def __init__(self, a: Operation): + self.a = a + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": self.a._serialise_json()} + + def _summary_components(self) -> list["Operation"]: + return [self.a] + + + + +class Neg(UnaryOperation): + + serialisation_name = "neg" + def evaluate(self, variables: dict[int, T]) -> T: + return -self.a.evaluate(variables) + + def _derivative(self, hash_value: int): + return Neg(self.a._derivative(hash_value)) + + def _clean(self): + + clean_a = self.a._clean() + + if isinstance(clean_a, Neg): + # Removes double negations + return clean_a.a + + elif isinstance(clean_a, Constant): + return Constant(-clean_a.value)._clean() + + else: + return Neg(clean_a) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Neg(Operation.deserialise_json(parameters["a"])) + + + def _summary_open(self): + return "Neg" + + def __eq__(self, other): + if isinstance(other, Neg): + return other.a == self.a + + +class Inv(UnaryOperation): + + serialisation_name = "reciprocal" + + def evaluate(self, variables: dict[int, T]) -> T: + return 1/self.a.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Neg(Div(self.a._derivative(hash_value), Mul(self.a, self.a))) + + def _clean(self): + clean_a = self.a._clean() + + if isinstance(clean_a, Inv): + # Removes double negations + return clean_a.a + + elif isinstance(clean_a, Neg): + # cannonicalise 1/-a to -(1/a) + # over multiple iterations this should have the effect of ordering and gathering Neg and Inv + return Neg(Inv(clean_a.a)) + + elif isinstance(clean_a, Constant): + return Constant(1/clean_a.value)._clean() + + else: + return Inv(clean_a) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Inv(Operation.deserialise_json(parameters["a"])) + + def _summary_open(self): + return "Inv" + + + def __eq__(self, other): + if isinstance(other, Inv): + return other.a == self.a + +class BinaryOperation(Operation): + def __init__(self, a: Operation, b: Operation): + self.a = a + self.b = b + + def _clean(self): + return self._clean_ab(self.a._clean(), self.b._clean()) + + def _clean_ab(self, a, b): + raise NotImplementedError("_clean_ab not implemented") + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": self.a._serialise_json(), + "b": self.b._serialise_json()} + + @staticmethod + def _deserialise_ab(parameters) -> tuple[Operation, Operation]: + return (Operation.deserialise_json(parameters["a"]), + Operation.deserialise_json(parameters["b"])) + + + def _summary_components(self) -> list["Operation"]: + return [self.a, self.b] + + def _self_cls(self) -> type: + """ Own class""" + def __eq__(self, other): + if isinstance(other, self._self_cls()): + return other.a == self.a and self.b == other.b + +class Add(BinaryOperation): + + serialisation_name = "add" + + def _self_cls(self) -> type: + return Add + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) + self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add(self.a._derivative(hash_value), self.b._derivative(hash_value)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity): + # Convert 0 + b to b + return b + + elif isinstance(b, AdditiveIdentity): + # Convert a + 0 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"+"b" to "a+b" + return Constant(a.evaluate({}) + b.evaluate({}))._clean() + + elif isinstance(a, Neg): + if isinstance(b, Neg): + # Convert (-a)+(-b) to -(a+b) + return Neg(Add(a.a, b.a)) + else: + # Convert (-a) + b to b-a + return Sub(b, a.a) + + elif isinstance(b, Neg): + # Convert a+(-b) to a-b + return Sub(a, b.a) + + elif a == b: + return Mul(Constant(2), a) + + else: + return Add(a, b) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Add(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Add" + +class Sub(BinaryOperation): + + serialisation_name = "sub" + + + def _self_cls(self) -> type: + return Sub + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) - self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Sub(self.a._derivative(hash_value), self.b._derivative(hash_value)) + + def _clean_ab(self, a, b): + if isinstance(a, AdditiveIdentity): + # Convert 0 - b to -b + return Neg(b) + + elif isinstance(b, AdditiveIdentity): + # Convert a - 0 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant pair "a" - "b" to "a-b" + return Constant(a.evaluate({}) - b.evaluate({}))._clean() + + elif isinstance(a, Neg): + if isinstance(b, Neg): + # Convert (-a)-(-b) to b-a + return Sub(b.a, a.a) + else: + # Convert (-a)-b to -(a+b) + return Neg(Add(a.a, b)) + + elif isinstance(b, Neg): + # Convert a-(-b) to a+b + return Add(a, b.a) + + elif a == b: + return AdditiveIdentity() + + else: + return Sub(a, b) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Sub(*BinaryOperation._deserialise_ab(parameters)) + + + def _summary_open(self): + return "Sub" + +class Mul(BinaryOperation): + + serialisation_name = "mul" + + + def _self_cls(self) -> type: + return Mul + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) * self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add(Mul(self.a, self.b._derivative(hash_value)), Mul(self.a._derivative(hash_value), self.b)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): + # Convert 0*b or a*0 to 0 + return AdditiveIdentity() + + elif isinstance(a, MultiplicativeIdentity): + # Convert 1*b to b + return b + + elif isinstance(b, MultiplicativeIdentity): + # Convert a*1 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"*"b" to "a*b" + return Constant(a.evaluate({}) * b.evaluate({}))._clean() + + elif isinstance(a, Inv) and isinstance(b, Inv): + return Inv(Mul(a.a, b.a)) + + elif isinstance(a, Inv) and not isinstance(b, Inv): + return Div(b, a.a) + + elif not isinstance(a, Inv) and isinstance(b, Inv): + return Div(a, b.a) + + elif isinstance(a, Neg): + return Neg(Mul(a.a, b)) + + elif isinstance(b, Neg): + return Neg(Mul(a, b.a)) + + elif a == b: + return Pow(a, 2) + + elif isinstance(a, Pow) and a.a == b: + return Pow(b, a.power + 1) + + elif isinstance(b, Pow) and b.a == a: + return Pow(a, b.power + 1) + + elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: + return Pow(a.a, a.power + b.power) + + else: + return Mul(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Mul(*BinaryOperation._deserialise_ab(parameters)) + + + def _summary_open(self): + return "Mul" + +class Div(BinaryOperation): + + serialisation_name = "div" + + + def _self_cls(self) -> type: + return Div + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) / self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Sub(Div(self.a.derivative(hash_value), self.b), + Div(Mul(self.a, self.b.derivative(hash_value)), Mul(self.b, self.b))) + + def _clean_ab(self, a, b): + if isinstance(a, AdditiveIdentity): + # Convert 0/b to 0 + return AdditiveIdentity() + + elif isinstance(a, MultiplicativeIdentity): + # Convert 1/b to inverse of b + return Inv(b) + + elif isinstance(b, MultiplicativeIdentity): + # Convert a/1 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constants "a"/"b" to "a/b" + return Constant(self.a.evaluate({}) / self.b.evaluate({}))._clean() + + + elif isinstance(a, Inv) and isinstance(b, Inv): + return Div(b.a, a.a) + + elif isinstance(a, Inv) and not isinstance(b, Inv): + return Inv(Mul(a.a, b)) + + elif not isinstance(a, Inv) and isinstance(b, Inv): + return Mul(a, b.a) + + elif a == b: + return MultiplicativeIdentity() + + elif isinstance(a, Pow) and a.a == b: + return Pow(b, a.power - 1) + + elif isinstance(b, Pow) and b.a == a: + return Pow(a, 1 - b.power) + + elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: + return Pow(a.a, a.power - b.power) + + else: + return Div(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Div(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Div" + +class Pow(Operation): + + serialisation_name = "pow" + + def __init__(self, a: Operation, power: float): + self.a = a + self.power = power + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) ** self.power + + def _derivative(self, hash_value: int) -> Operation: + if self.power == 0: + return AdditiveIdentity() + + elif self.power == 1: + return self.a._derivative(hash_value) + + else: + return Mul(Constant(self.power), Mul(Pow(self.a, self.power-1), self.a._derivative(hash_value))) + + def _clean(self) -> Operation: + a = self.a._clean() + + if self.power == 1: + return a + + elif self.power == 0: + return MultiplicativeIdentity() + + elif self.power == -1: + return Inv(a) + + else: + return Pow(a, self.power) + + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": Operation._serialise_json(self.a), + "power": self.power} + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Pow(Operation.deserialise_json(parameters["a"]), parameters["power"]) + + def summary(self, indent_amount: int=0, indent=" "): + return (f"{indent_amount*indent}Pow\n" + + self.a.summary(indent_amount+1, indent) + "\n" + + f"{(indent_amount+1)*indent}{self.power}\n" + + f"{indent_amount*indent})") + + def __eq__(self, other): + if isinstance(other, Pow): + return self.a == other.a and self.power == other.power + +_serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, + Variable, + Neg, Inv, + Add, Sub, Mul, Div, Pow] + +_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} \ No newline at end of file diff --git a/sasdata/quantities/operations_examples.py b/sasdata/quantities/operations_examples.py index e2e25666f..194980e83 100644 --- a/sasdata/quantities/operations_examples.py +++ b/sasdata/quantities/operations_examples.py @@ -1,4 +1,4 @@ -from sasdata.quantities.quantity import Variable, Mul +from quantities.operations import Variable, Mul x = Variable("x") y = Variable("y") diff --git a/sasdata/quantities/operations_test.py b/sasdata/quantities/operations_test.py index 6767e32a0..0899eee7f 100644 --- a/sasdata/quantities/operations_test.py +++ b/sasdata/quantities/operations_test.py @@ -1,6 +1,6 @@ import pytest -from sasdata.quantities.quantity import Operation, \ +from sasdata.quantities.operations import Operation, \ Neg, Inv, \ Add, Sub, Mul, Div, Pow, \ Variable, Constant, AdditiveIdentity, MultiplicativeIdentity diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index c6cab77e3..27d516cc9 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -1,72 +1,72 @@ import numpy as np -from sasdata.quantities.quantity import Quantity, UnitError +from sasdata.quantities.quantity import BaseQuantity, UnitError import sasdata.quantities.units as units import sasdata.quantities.si as si import pytest def test_in_units_of_calculation(): """ Just a couple of unit conversions """ - assert Quantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 - assert Quantity(10, units.minutes).in_units_of(units.seconds) == 600 - assert Quantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000/9.81, abs=1) - assert Quantity(0, units.meters).in_units_of(units.exameters) == 0 + assert BaseQuantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 + assert BaseQuantity(10, units.minutes).in_units_of(units.seconds) == 600 + assert BaseQuantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000 / 9.81, abs=1) + assert BaseQuantity(0, units.meters).in_units_of(units.exameters) == 0 def test_unit_compounding_pow(): """ Test units compound correctly when __pow__ is used""" - assert (Quantity(1, units.millimeters)**2).in_units_of(units.square_meters) == 1e-6 - assert (Quantity(1, units.minutes)**3).in_units_of(units.seconds**3) == 60**3 + assert (BaseQuantity(1, units.millimeters) ** 2).in_units_of(units.square_meters) == 1e-6 + assert (BaseQuantity(1, units.minutes) ** 3).in_units_of(units.seconds ** 3) == 60 ** 3 def test_unit_compounding_mul(): """ Test units compound correctly when __mul__ is used""" - assert (Quantity(4, units.minutes) * Quantity(0.25, units.hertz)).in_units_of(units.none) == 60 - assert (Quantity(250, units.volts) * Quantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 + assert (BaseQuantity(4, units.minutes) * BaseQuantity(0.25, units.hertz)).in_units_of(units.none) == 60 + assert (BaseQuantity(250, units.volts) * BaseQuantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 def test_unit_compounding_div(): """ Test units compound correctly when __truediv__ is used""" - assert (Quantity(10, units.kilometers) / Quantity(2, units.minutes) + assert (BaseQuantity(10, units.kilometers) / BaseQuantity(2, units.minutes) ).in_units_of(units.meters_per_second) == pytest.approx(250/3, abs=1e-6) - assert (Quantity(1, units.nanowebers) / (Quantity(1, units.millimeters)**2)).in_units_of(units.millitesla) == 1 + assert (BaseQuantity(1, units.nanowebers) / (BaseQuantity(1, units.millimeters) ** 2)).in_units_of(units.millitesla) == 1 def test_value_mul(): """ Test value part of quantities multiply correctly""" - assert (Quantity(1j, units.seconds) * Quantity(1j, units.watts)).in_units_of(units.joules) == -1 + assert (BaseQuantity(1j, units.seconds) * BaseQuantity(1j, units.watts)).in_units_of(units.joules) == -1 def test_scalar_mul(): - assert (Quantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 - assert (10 * Quantity(1, units.seconds)).in_units_of(units.seconds) == 10 - assert (1000 * Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 + assert (BaseQuantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 + assert (10 * BaseQuantity(1, units.seconds)).in_units_of(units.seconds) == 10 + assert (1000 * BaseQuantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 def test_scalar_div(): - assert (Quantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 - assert (10 / Quantity(1, units.seconds)).in_units_of(units.hertz) == 10 - assert (0.001 / Quantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 + assert (BaseQuantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 + assert (10 / BaseQuantity(1, units.seconds)).in_units_of(units.hertz) == 10 + assert (0.001 / BaseQuantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 def test_good_add_sub(): """ Test that adding and subtracting units works """ - assert (Quantity(1, units.seconds) + Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 - assert (Quantity(1, units.seconds) - Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 + assert (BaseQuantity(1, units.seconds) + BaseQuantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 + assert (BaseQuantity(1, units.seconds) - BaseQuantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 - assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == 13 + assert (BaseQuantity(1, units.inches) + BaseQuantity(1, units.feet)).in_units_of(units.inches) == 13 @pytest.mark.parametrize("unit_1", si.all_si) @pytest.mark.parametrize("unit_2", si.all_si) def test_mixed_quantity_add_sub(unit_1, unit_2): if unit_1.equivalent(unit_2): - assert (Quantity(0, unit_1) + Quantity(0, unit_2)).in_units_of(unit_1) == 0 + assert (BaseQuantity(0, unit_1) + BaseQuantity(0, unit_2)).in_units_of(unit_1) == 0 else: with pytest.raises(UnitError): - Quantity(1, unit_1) + Quantity(1, unit_2) + BaseQuantity(1, unit_1) + BaseQuantity(1, unit_2) def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float, abs=1e-9): """ Helper function for testing units that are multiples of each other """ assert u1.equivalent(u2), "Units should be compatible for this test" - assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) + assert (BaseQuantity(1, u1) / BaseQuantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) def test_american_units(): @@ -76,7 +76,7 @@ def test_american_units(): assert_unit_ratio(units.pounds_force_per_square_inch, units.pounds_force / (units.inches**2), 1, abs=1e-5) def test_percent(): - assert Quantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) + assert BaseQuantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) @pytest.mark.parametrize("unit_1", si.all_si) @pytest.mark.parametrize("unit_2", si.all_si) @@ -84,9 +84,9 @@ def test_conversion_errors(unit_1, unit_2): """ Test conversion errors are thrown when units are not compatible """ if unit_1 == unit_2: - assert Quantity(1, unit_1).in_units_of(unit_2) == 1 + assert BaseQuantity(1, unit_1).in_units_of(unit_2) == 1 else: with pytest.raises(UnitError): - Quantity(1, units.seconds).in_units_of(units.meters) + BaseQuantity(1, units.seconds).in_units_of(units.meters) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index e4f99a063..30cbea289 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -5,8 +5,11 @@ from numpy._typing import ArrayLike +from quantities.operations import Operation, Variable from sasdata.quantities.units import Unit +import hashlib + class UnitError(Exception): """Errors caused by unit specification not being correct""" @@ -14,7 +17,7 @@ class UnitError(Exception): QuantityType = TypeVar("QuantityType") -class Quantity[QuantityType]: +class BaseQuantity[QuantityType]: def __init__(self, value: QuantityType, units: Unit): self.value = value self.units = units @@ -26,38 +29,37 @@ def in_units_of(self, units: Unit) -> QuantityType: raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") def __mul__(self: Self, other: ArrayLike | Self ) -> Self: - if isinstance(other, Quantity): - return Quantity(self.value * other.value, self.units * other.units) + if isinstance(other, BaseQuantity): + return BaseQuantity(self.value * other.value, self.units * other.units) else: - return Quantity(self.value * other, self.units) + return BaseQuantity(self.value * other, self.units) def __rmul__(self: Self, other: ArrayLike | Self): - if isinstance(other, Quantity): - return Quantity(other.value * self.value, other.units * self.units) + if isinstance(other, BaseQuantity): + return BaseQuantity(other.value * self.value, other.units * self.units) else: - return Quantity(other * self.value, self.units) - + return BaseQuantity(other * self.value, self.units) def __truediv__(self: Self, other: float | Self) -> Self: - if isinstance(other, Quantity): - return Quantity(self.value / other.value, self.units / other.units) + if isinstance(other, BaseQuantity): + return BaseQuantity(self.value / other.value, self.units / other.units) else: - return Quantity(self.value / other, self.units) + return BaseQuantity(self.value / other, self.units) def __rtruediv__(self: Self, other: float | Self) -> Self: - if isinstance(other, Quantity): - return Quantity(self.value / other.value, self.units / other.units) + if isinstance(other, BaseQuantity): + return BaseQuantity(self.value / other.value, self.units / other.units) else: - return Quantity(self.value / other, self.units) + return BaseQuantity(self.value / other, self.units) def __add__(self: Self, other: Self | ArrayLike) -> Self: - if isinstance(other, Quantity): + if isinstance(other, BaseQuantity): if self.units.equivalent(other.units): - return Quantity(self.value + (other.value * other.units.scale)/self.units.scale, self.units) + return BaseQuantity(self.value + (other.value * other.units.scale) / self.units.scale, self.units) else: raise UnitError(f"Units do not have the same dimensionality: {self.units} vs {other.units}") @@ -67,7 +69,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: # Don't need __radd__ because only quantity/quantity operations should be allowed def __neg__(self): - return Quantity(-self.value, self.units) + return BaseQuantity(-self.value, self.units) def __sub__(self: Self, other: Self | ArrayLike) -> Self: return self + (-other) @@ -76,26 +78,42 @@ def __rsub__(self: Self, other: Self | ArrayLike) -> Self: return (-self) + other def __pow__(self: Self, other: int): - return Quantity(self.value**other, self.units**other) + return BaseQuantity(self.value ** other, self.units ** other) - def parse(self, number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): + @staticmethod + def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass -class NamedQuantity[QuantityType](Quantity[QuantityType]): +class Quantity[QuantityType](BaseQuantity[QuantityType]): + def with_uncertainty(self, uncertainty: BaseQuantity[QuantityType]): + return UncertainQuantity(self.value, self.units, uncertainty=uncertainty) + + +class NamedQuantity[QuantityType](BaseQuantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, name: str): super().__init__(value, units) self.name = name + def with_uncertainty(self, uncertainty: BaseQuantity[QuantityType]): + return UncertainNamedQuantity(self.value, self.units, uncertainty=uncertainty, name=self.name) -class UncertainQuantity[QuantityType](Quantity[QuantityType]): - def __init__(self, value: QuantityType, units: Unit, uncertainty: Quantity[QuantityType]): +class UncertainBaseQuantity[QuantityType](BaseQuantity[QuantityType]): + pass + +class UncertainQuantity[QuantityType](BaseQuantity[QuantityType]): + def __init__(self, value: QuantityType, units: Unit, uncertainty: BaseQuantity[QuantityType]): super().__init__(value, units) self.uncertainty = uncertainty -class UncertainNamedQuantity[QuantityType](Quantity[QuantityType]): - def __init__(self, value: QuantityType, units: Unit, uncertainty: Quantity[QuantityType], name: str): + hash_value = hashlib.md5(value, uncertainty) + + +class UncertainNamedQuantity[QuantityType](BaseQuantity[QuantityType]): + def __init__(self, value: QuantityType, units: Unit, uncertainty: BaseQuantity[QuantityType], name: str): super().__init__(value, units) self.uncertainty = uncertainty self.name = name + + self.history = Variable(self.name) \ No newline at end of file diff --git a/sasdata/transforms/operation.py b/sasdata/transforms/operation.py index c06bb379a..65ef6e604 100644 --- a/sasdata/transforms/operation.py +++ b/sasdata/transforms/operation.py @@ -1,5 +1,5 @@ import numpy as np -from sasdata.quantities.quantity import Quantity +from sasdata.quantities.quantity import BaseQuantity class Operation: """ Sketch of what model post-processing classes might look like """ @@ -11,7 +11,7 @@ class Operation: def name(self) -> str: raise NotImplementedError("No name for transform") - def evaluate(self) -> Quantity[np.ndarray]: + def evaluate(self) -> BaseQuantity[np.ndarray]: pass def __call__(self, *children, **named_children): From dd9615eacac40b032f1281cc769526376e09bcb3 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 09:07:23 +0100 Subject: [PATCH 514/675] Added some code to enable test driven development. --- sasdata/quantities/unit_parser.py | 181 +----------------------------- 1 file changed, 4 insertions(+), 177 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 082e6e340..97e1d0847 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,179 +1,6 @@ -from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup, unit_groups, UnitGroup -from re import findall, fullmatch +from sasdata.quantities.units import NamedUnit -# TODO: This shouldn't be in this file but I don't want to edit Lucas' code before he is finished. -all_units_groups = [group.units for group in unit_groups.values()] -unit_groups_by_dimension_hash = {hash(group.units[0].dimensions): group for group in unit_groups.values()} -all_units: list[NamedUnit] = [] -for group in all_units_groups: - all_units.extend(group) - -def split_unit_str(unit_str: str) -> list[str]: - """Separate the letters from the numbers in unit_str""" - return findall(r'[A-Za-zΩ%Å]+|[-\d]+|/', unit_str) - -def validate_unit_str(unit_str: str) -> bool: - """Validate whether unit_str is valid. This doesn't mean that the unit specified in unit_str exists but rather it - only consists of letters, and numbers as a unit string should.""" - return not fullmatch(r'[A-Za-zΩ%Å^1-9\-\+/\ \.]+', unit_str) is None - -def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: - """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit - cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str. - - The shortest_unit parameter specifies how to resolve ambiguities. If it is true, then it will parse the longest unit - available. Otherwise, it will stop parsing as soon as it has found any unit. - - If unit_group is set, it will only try to parse units within that group. This is useful for resolving ambiguities. - """ - current_unit = '' - string_pos = 0 - if unit_group is None: - lookup_dict = symbol_lookup - else: - lookup_dict = dict([(name, unit) for name, unit in symbol_lookup.items() if unit in unit_group.units]) - for next_char in unit_str: - potential_unit_str = current_unit + next_char - potential_symbols = [symbol for symbol in lookup_dict.keys() if symbol.startswith(potential_unit_str)] - if len(potential_symbols) == 0: - break - string_pos += 1 - current_unit = potential_unit_str - if not longest_unit and current_unit in lookup_dict.keys(): - break - if current_unit == '': - return None, unit_str - remaining_str = unit_str[string_pos::] - return lookup_dict[current_unit], remaining_str - -def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longest_unit: bool = True) -> list[Unit]: - """Recursively parse units from unit_str until no more characters are present.""" - if current_units is None: - current_units = [] - if unit_str == '': - return current_units - parsed_unit, remaining_str = parse_single_unit(unit_str, longest_unit=longest_unit) - if parsed_unit is not None: - current_units += [parsed_unit] - return parse_unit_strs(remaining_str, current_units, longest_unit) - else: - raise ValueError(f'Could not interpret {remaining_str}') - -# Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there -# are two functions. - -def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: - """Split unit_str into a stack of parsed units.""" - unit_stack: list[Unit] = [] - split_str = split_unit_str(unit_str) - inverse_next_unit = False - for token in split_str: - try: - if token == '/': - inverse_next_unit = True - continue - power = int(token) - to_modify = unit_stack[-1] - modified = to_modify ** power - # modified = unit_power(to_modify, power) - unit_stack[-1] = modified - except ValueError: - new_units = parse_unit_strs(token, None, longest_unit) - if inverse_next_unit: - # TODO: Assume the power is going to be -1. This might not be true. - power = -1 - new_units[0] = new_units[0] ** power - # new_units[0] = unit_power(new_units[0], power) - unit_stack += new_units - # This error will happen if it tries to read a modifier but there are no units on the stack. We will just have - # to ignore it. Strings being parsed shouldn't really have it anyway (e.g. -1m). - except IndexError: - pass - return unit_stack - -def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: - """Parse unit_str into a unit.""" - try: - if not validate_unit_str(unit_str): - raise ValueError('unit_str contains forbidden characters.') - parsed_unit = Unit(1, Dimensions()) - unit_stack = parse_unit_stack(unit_str, longest_unit) - for unit in unit_stack: - # parsed_unit = combine_units(parsed_unit, unit) - parsed_unit *= unit - return parsed_unit - except KeyError: - raise ValueError('Unit string contains an unrecognised pattern.') - -def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: - """Tries to use the given unit group to resolve ambiguities. Parse a unit twice with different options, and returns - whatever conforms to the unit group.""" - longest_parsed_unit = parse_unit(unit_str, True) - shortest_parsed_unit = parse_unit(unit_str, False) - if longest_parsed_unit in from_group.units: - return longest_parsed_unit - elif shortest_parsed_unit in from_group.units: - return shortest_parsed_unit - else: - return None - -def parse_named_unit(unit_string: str, rtol: float=1e-14) -> NamedUnit: - """Parses unit into a named unit. Parses unit into a Unit if it is not already, and then finds an equivaelent named - unit. Please note that this might not be the expected unit from the string itself. E.g. 'kgm/2' will become - newtons. - - :param unit_string: string describing the units, e.g. km/s - :param rtol: relative tolerance for matching scale factors - """ - unit = parse_unit(unit_string) - named_unit = find_named_unit(unit) - if named_unit is None: - raise ValueError(f"We don't have a for this unit: '{unit}'") - else: - return named_unit - -def find_named_unit(unit: Unit, rtol: float=1e-14) -> NamedUnit | None: - """ Find a named unit matching the one provided """ - dimension_hash = hash(unit.dimensions) - if dimension_hash in unit_groups_by_dimension_hash: - unit_group = unit_groups_by_dimension_hash[hash(unit.dimensions)] - - for named_unit in unit_group.units: - if abs(named_unit.scale - unit.scale) < rtol*named_unit.scale: - return named_unit - - return None - - -def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUnit: - """Parses unit_str into a named unit. The named unit found must be part of from_group. If two units are found, the - unit that is present in from_group is returned. This is useful in cases of ambiguities.""" - parsed_unit = parse_unit_from_group(unit_str, from_group) - if parsed_unit is None: - raise ValueError('That unit cannot be parsed from the specified group.') - return find_named_unit(parsed_unit) - -def parse(string: str, - name_lookup: bool = True, - longest_unit: bool = True, - lookup_rtol: float = 1e-14): - - unit = parse_unit(string, longest_unit=longest_unit) - if name_lookup: - named = find_named_unit(unit, rtol=lookup_rtol) - if named is not None: - return named - - return unit - - -if __name__ == "__main__": - to_parse = input('Enter a unit to parse: ') - try: - generic_unit = parse_unit(to_parse) - print(f'Generic Unit: {generic_unit}') - named_unit = find_named_unit(generic_unit) - print(f'Named Unit: {named_unit}') - except ValueError: - print('There is no named unit available.') +def parse_unit(unit_str: str) -> NamedUnit: + # TODO: Not implemented. This is just to enable testing. + return NamedUnit() From a19f53174d1f940d266f270b061108e6780635de Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 09:08:24 +0100 Subject: [PATCH 515/675] Some minor changes to stop my editor from crying. Can probably revert back later. --- sasdata/quantities/operations.py | 4 ++-- sasdata/quantities/operations_examples.py | 4 ++-- sasdata/quantities/quantity.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py index 60590333d..dcbdcf242 100644 --- a/sasdata/quantities/operations.py +++ b/sasdata/quantities/operations.py @@ -2,7 +2,7 @@ import json -from sasdata.quantities.quantity import BaseQuantity +# from sasdata.quantities.quantity import BaseQuantity T = TypeVar("T") @@ -709,4 +709,4 @@ def __eq__(self, other): Neg, Inv, Add, Sub, Mul, Div, Pow] -_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} \ No newline at end of file +_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} diff --git a/sasdata/quantities/operations_examples.py b/sasdata/quantities/operations_examples.py index 194980e83..6c484eb36 100644 --- a/sasdata/quantities/operations_examples.py +++ b/sasdata/quantities/operations_examples.py @@ -1,4 +1,4 @@ -from quantities.operations import Variable, Mul +from sasdata.quantities.operations import Variable, Mul x = Variable("x") y = Variable("y") @@ -8,4 +8,4 @@ dfdx = f.derivative(x).derivative(y).derivative(z) -print(dfdx.summary()) +print(dfdx.summary()) \ No newline at end of file diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 30cbea289..2071495e2 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -5,7 +5,7 @@ from numpy._typing import ArrayLike -from quantities.operations import Operation, Variable +from sasdata.quantities.operations import Operation, Variable from sasdata.quantities.units import Unit import hashlib From 727b30e9c33d3096365833487d27087f03aa2a87 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 09:23:26 +0100 Subject: [PATCH 516/675] Pass in the dimensions so this code is correct. --- sasdata/quantities/unit_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 97e1d0847..8b9e187f0 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,6 +1,6 @@ -from sasdata.quantities.units import NamedUnit +from sasdata.quantities.units import Dimensions, NamedUnit def parse_unit(unit_str: str) -> NamedUnit: # TODO: Not implemented. This is just to enable testing. - return NamedUnit() + return NamedUnit(1, Dimensions()) From 2b06236a106e5bf1a840b779eb22c5a383767ce4 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 09:51:08 +0100 Subject: [PATCH 517/675] Wrote some tests ahead. Enables some test driven development. --- sasdata/quantities/unit_parser_test.py | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 sasdata/quantities/unit_parser_test.py diff --git a/sasdata/quantities/unit_parser_test.py b/sasdata/quantities/unit_parser_test.py new file mode 100644 index 000000000..679727f33 --- /dev/null +++ b/sasdata/quantities/unit_parser_test.py @@ -0,0 +1,32 @@ +from sasdata.quantities.unit_parser import parse_unit +from sasdata.quantities.units_tests import EqualUnits +from sasdata.quantities.units import Dimensions, Unit, meters, meters_per_second, per_angstrom, \ + kilometers_per_square_hour + +# Lets start with the straight forward ones first, and get progressivel more complex as the list goes on. +tests = [ + EqualUnits('Metres', + meters, + parse_unit('m')), + EqualUnits('Metres per second', + meters_per_second, + parse_unit('ms-1')), + EqualUnits('Inverse Test', + per_angstrom, + parse_unit('1/A'), + parse_unit('A-1')), + # This test is primarily to ensure that the 'mm' doesn't get interpreted as two separate metres. + EqualUnits('Milimetres * Centimetres', + # TODO: Not sure if this calculation is right. + Unit(0.001 * 0.01, Dimensions(length=2)), + parse_unit('mmcm')), + EqualUnits("Acceleration", + kilometers_per_square_hour, + parse_unit('kmh-2'), + parse_unit('km/h2') + ) +] + +for test in tests: + print(test.test_name) + test.run_test() From e7e78304fe0657f6d096c3bb9c880068092bcae0 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 09:53:54 +0100 Subject: [PATCH 518/675] Parse using a slant as well. --- sasdata/quantities/unit_parser_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser_test.py b/sasdata/quantities/unit_parser_test.py index 679727f33..383c079c0 100644 --- a/sasdata/quantities/unit_parser_test.py +++ b/sasdata/quantities/unit_parser_test.py @@ -10,7 +10,8 @@ parse_unit('m')), EqualUnits('Metres per second', meters_per_second, - parse_unit('ms-1')), + parse_unit('ms-1'), + parse_unit('m/s')), EqualUnits('Inverse Test', per_angstrom, parse_unit('1/A'), From ad8addaea533d319e5f88bacadcecead14c89711 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 15:49:17 +0100 Subject: [PATCH 519/675] Found a regex for splitting up the string. --- sasdata/quantities/unit_parser.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 8b9e187f0..a571d0838 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,5 +1,8 @@ from sasdata.quantities.units import Dimensions, NamedUnit +from re import findall +def split_unit_str(unit_str: str) -> list[str]: + return findall(r'[A-Za-z]+|[-\d]+', unit_str) def parse_unit(unit_str: str) -> NamedUnit: # TODO: Not implemented. This is just to enable testing. From 7f9124d452c9aa0d9a4c21f94abd5aafe4e85e96 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 16:14:06 +0100 Subject: [PATCH 520/675] Implemented the parse_single_unit function. --- sasdata/quantities/unit_parser.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index a571d0838..fbdde41b3 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,9 +1,27 @@ -from sasdata.quantities.units import Dimensions, NamedUnit +from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup from re import findall def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+', unit_str) +def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: + """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit + cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str""" + current_unit = '' + string_pos = 0 + for char in unit_str: + potential_unit_str = current_unit + char + potential_symbol = symbol_lookup.get(potential_unit_str, None) + if potential_symbol is None: + break + string_pos += 1 + current_unit= potential_unit_str + if current_unit == '': + return (None, unit_str) + remaining_str = unit_str[string_pos::] + return (symbol_lookup[current_unit], remaining_str) + + def parse_unit(unit_str: str) -> NamedUnit: # TODO: Not implemented. This is just to enable testing. return NamedUnit(1, Dimensions()) From 489a326af82be62540f153e684b1953bd06d9a51 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 11 Sep 2024 16:17:30 +0100 Subject: [PATCH 521/675] Use two functions for parsing. --- sasdata/quantities/unit_parser.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index fbdde41b3..f23987623 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -21,7 +21,13 @@ def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: remaining_str = unit_str[string_pos::] return (symbol_lookup[current_unit], remaining_str) +# Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there +# are two functions. -def parse_unit(unit_str: str) -> NamedUnit: +def parse_unit(unit_str: str) -> Unit: # TODO: Not implemented. This is just to enable testing. + return Unit(1, Dimensions()) + +def parse_named_unit(unit_str: str) -> NamedUnit: + # TODO: Not implemented. return NamedUnit(1, Dimensions()) From 524483fbd9949ad7f53ce12e85642b1c25c75b48 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 08:26:54 +0100 Subject: [PATCH 522/675] Use list comprehension to get potential symbols. --- sasdata/quantities/unit_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index f23987623..c53af08ac 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -11,8 +11,8 @@ def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: string_pos = 0 for char in unit_str: potential_unit_str = current_unit + char - potential_symbol = symbol_lookup.get(potential_unit_str, None) - if potential_symbol is None: + potential_symbols = [symbol for symbol in symbol_lookup.keys() if symbol.startswith(potential_unit_str)] + if len(potential_symbols) == 0: break string_pos += 1 current_unit= potential_unit_str From 92127204f4b409f61575b47760dbda0be7cec8ac Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 10:10:44 +0100 Subject: [PATCH 523/675] parse unit strs function. --- sasdata/quantities/unit_parser.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index c53af08ac..7241e11da 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -21,6 +21,17 @@ def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: remaining_str = unit_str[string_pos::] return (symbol_lookup[current_unit], remaining_str) +def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None) -> list[Unit]: + if current_units is None: + current_units = [] + if unit_str == '': + return current_units + parsed_unit, remaining_str = parse_single_unit(unit_str) + if not parsed_unit is None: + current_units += [parsed_unit] + return parse_unit_strs(remaining_str, current_units) + + # Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there # are two functions. From 62350f49cc3eb8f23913d29db285552a62ba5bd7 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 10:36:55 +0100 Subject: [PATCH 524/675] Created a function to pass in a stack of units. --- sasdata/quantities/unit_parser.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 7241e11da..7dba3dde3 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -35,6 +35,34 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None) -> lis # Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there # are two functions. +def parse_unit_stack(unit_str: str) -> list[Unit]: + # TODO: This doesn't work for 1/ (or any fraction) yet. + unit_stack: list[Unit] = [] + split_str = split_unit_str(unit_str) + for token in split_str: + try: + dimension_modifier = int(token) + to_modify = unit_stack[-1] + # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. + to_modify.dimensions = Dimensions( + length=to_modify.dimensions.length * dimension_modifier, + time=to_modify.dimensions.time * dimension_modifier, + mass=to_modify.dimensions.mass * dimension_modifier, + current=to_modify.dimensions.current * dimension_modifier, + temperature=to_modify.dimensions.temperature * dimension_modifier, + moles_hint=to_modify.dimensions.moles_hint * dimension_modifier, + angle_hint=to_modify.dimensions.angle_hint * dimension_modifier + ) + + except ValueError: + new_units = parse_unit_strs(token) + unit_stack += new_units + # This error will happen if it tries to read a modifier but there are no units on the stack. We will just have + # to ignore it. Strings being parsed shouldn't really have it anyway (e.g. -1m). + except IndexError: + pass + return unit_stack + def parse_unit(unit_str: str) -> Unit: # TODO: Not implemented. This is just to enable testing. return Unit(1, Dimensions()) From de6e4f53a1a10832b2928e7aae6f2a23ebb3a76b Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 11:16:42 +0100 Subject: [PATCH 525/675] Multiply dimensions function. --- sasdata/quantities/unit_parser.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 7dba3dde3..b4801fc7d 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,6 +1,19 @@ from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup from re import findall +# TODO: This should probably be part of the Dimensions class but I don't want to change Lucas's code without asking him +# when he gets back. +def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> Dimensions: + return Dimensions( + length=dimensions_1.length * dimensions_2.length, + time=dimensions_1.time * dimensions_2.time, + mass=dimensions_1.mass * dimensions_2.mass, + current=dimensions_1.current * dimensions_2.current, + temperature=dimensions_1.temperature * dimensions_2.temperature, + moles_hint=dimensions_1.moles_hint * dimensions_2.moles_hint, + angle_hint=dimensions_1.angle_hint * dimensions_2.angle_hint + ) + def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+', unit_str) From e42baf3c1b3d7e44485343ddef159deda3164f44 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 11:20:26 +0100 Subject: [PATCH 526/675] Use the new multiply function. --- sasdata/quantities/unit_parser.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index b4801fc7d..600f73068 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -57,16 +57,8 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: dimension_modifier = int(token) to_modify = unit_stack[-1] # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. - to_modify.dimensions = Dimensions( - length=to_modify.dimensions.length * dimension_modifier, - time=to_modify.dimensions.time * dimension_modifier, - mass=to_modify.dimensions.mass * dimension_modifier, - current=to_modify.dimensions.current * dimension_modifier, - temperature=to_modify.dimensions.temperature * dimension_modifier, - moles_hint=to_modify.dimensions.moles_hint * dimension_modifier, - angle_hint=to_modify.dimensions.angle_hint * dimension_modifier - ) - + multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) + to_modify.dimensions = multiply_dimensions(to_modify.dimensions, multiplier) except ValueError: new_units = parse_unit_strs(token) unit_stack += new_units From 69c21243e3ad352b69aeba5edd4e6ea84119e0b5 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 11:22:51 +0100 Subject: [PATCH 527/675] Nvm I'm blind; there already was a multiply method. --- sasdata/quantities/unit_parser.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 600f73068..c6aeb2bea 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,19 +1,6 @@ from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup from re import findall -# TODO: This should probably be part of the Dimensions class but I don't want to change Lucas's code without asking him -# when he gets back. -def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> Dimensions: - return Dimensions( - length=dimensions_1.length * dimensions_2.length, - time=dimensions_1.time * dimensions_2.time, - mass=dimensions_1.mass * dimensions_2.mass, - current=dimensions_1.current * dimensions_2.current, - temperature=dimensions_1.temperature * dimensions_2.temperature, - moles_hint=dimensions_1.moles_hint * dimensions_2.moles_hint, - angle_hint=dimensions_1.angle_hint * dimensions_2.angle_hint - ) - def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+', unit_str) @@ -58,7 +45,7 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: to_modify = unit_stack[-1] # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) - to_modify.dimensions = multiply_dimensions(to_modify.dimensions, multiplier) + to_modify.dimensions *= multiplier except ValueError: new_units = parse_unit_strs(token) unit_stack += new_units From d42eb27902d1dada2339216539fd3ad19e401050 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 11:28:05 +0100 Subject: [PATCH 528/675] Parse in a whole unit. --- sasdata/quantities/unit_parser.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index c6aeb2bea..88a9d97ca 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -57,7 +57,11 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: def parse_unit(unit_str: str) -> Unit: # TODO: Not implemented. This is just to enable testing. - return Unit(1, Dimensions()) + parsed_unit = Unit(1, Dimensions()) + unit_stack = parse_unit_stack(unit_str) + for unit in unit_stack: + parsed_unit *= unit + return parsed_unit def parse_named_unit(unit_str: str) -> NamedUnit: # TODO: Not implemented. From ffb47b1849fd14fc878bcd26f1acf922a747876b Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 12 Sep 2024 11:39:36 +0100 Subject: [PATCH 529/675] I still need this multply for parse_unit_stack. Since the implementation in Lucas' code is just added the dimensions together which isn't what I'm looking for here. --- sasdata/quantities/unit_parser.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 88a9d97ca..5493ef2cf 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,6 +1,17 @@ from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup from re import findall +def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> Dimensions: + return Dimensions( + length=dimensions_1.length * dimensions_2.length, + time=dimensions_1.time * dimensions_2.time, + mass=dimensions_1.mass * dimensions_2.mass, + current=dimensions_1.current * dimensions_2.current, + temperature=dimensions_1.temperature * dimensions_2.temperature, + moles_hint=dimensions_1.moles_hint * dimensions_2.moles_hint, + angle_hint=dimensions_1.angle_hint * dimensions_2.angle_hint + ) + def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+', unit_str) @@ -45,7 +56,7 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: to_modify = unit_stack[-1] # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) - to_modify.dimensions *= multiplier + to_modify.dimensions = multiply_dimensions(to_modify.dimensions, multiplier) except ValueError: new_units = parse_unit_strs(token) unit_stack += new_units From b83fe9d304ce242968934eb1407c293cce40e025 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 16 Sep 2024 14:55:58 +0100 Subject: [PATCH 530/675] System for combining units. Very dodgy. --- sasdata/quantities/unit_parser.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 5493ef2cf..daf17bc37 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -12,6 +12,27 @@ def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> D angle_hint=dimensions_1.angle_hint * dimensions_2.angle_hint ) +def sum_dimensions(dimensions: Dimensions): + return sum([ + dimensions.length, + dimensions.time, + dimensions.mass, + dimensions.current, + dimensions.temperature, + dimensions.moles_hint, + dimensions.angle_hint + ]) + +def combine_units(unit_1: Unit, unit_2: Unit): + if unit_1.dimensions.is_dimensionless or unit_2.dimensions.is_dimensionless: + unit_1_scale = unit_1.scale + unit_2_scale = unit_2.scale + else: + unit_1_scale = unit_1.scale ** sum_dimensions(unit_1.dimensions) + unit_2_scale = unit_2.scale ** sum_dimensions(unit_2.dimensions) + return Unit(unit_1_scale * unit_2_scale, unit_1.dimensions * unit_2.dimensions) + + def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+', unit_str) @@ -71,9 +92,12 @@ def parse_unit(unit_str: str) -> Unit: parsed_unit = Unit(1, Dimensions()) unit_stack = parse_unit_stack(unit_str) for unit in unit_stack: - parsed_unit *= unit + parsed_unit = combine_units(parsed_unit, unit) return parsed_unit def parse_named_unit(unit_str: str) -> NamedUnit: # TODO: Not implemented. return NamedUnit(1, Dimensions()) + +if __name__ == "__main__": + print(parse_unit('kmh-1')) From 02306d717f00665d4389fcbd69cdce8389311d4f Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 16 Sep 2024 15:03:03 +0100 Subject: [PATCH 531/675] Removed not implemented comment. --- sasdata/quantities/unit_parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index daf17bc37..4ccb5afb0 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -88,7 +88,6 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: return unit_stack def parse_unit(unit_str: str) -> Unit: - # TODO: Not implemented. This is just to enable testing. parsed_unit = Unit(1, Dimensions()) unit_stack = parse_unit_stack(unit_str) for unit in unit_stack: From 2dc8d87535b06b41f85251e768b8638610420051 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 16 Sep 2024 15:57:06 +0100 Subject: [PATCH 532/675] Parse in a named unit. --- sasdata/quantities/unit_parser.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 4ccb5afb0..e99efa6e9 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,6 +1,13 @@ -from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup +from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup, unit_groups from re import findall +# TODO: This shouldn't be in this file but I don't want to edit Lucas' code before he is finished. + +all_units_groups = [group.units for group in unit_groups.values()] +all_units: list[NamedUnit] = [] +for group in all_units_groups: + all_units.extend(group) + def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> Dimensions: return Dimensions( length=dimensions_1.length * dimensions_2.length, @@ -95,8 +102,12 @@ def parse_unit(unit_str: str) -> Unit: return parsed_unit def parse_named_unit(unit_str: str) -> NamedUnit: - # TODO: Not implemented. - return NamedUnit(1, Dimensions()) + # TODO: Not actually sure if this includes all units. + generic_unit = parse_unit(unit_str) + for named_unit in all_units: + if named_unit == generic_unit: + return named_unit + raise ValueError('A named unit does not exist for this unit.') if __name__ == "__main__": - print(parse_unit('kmh-1')) + print(parse_named_unit('kmh-1')) From 43439cdd1cc926fa38e7c44ce6a605d0fa13edbe Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 16 Sep 2024 16:23:06 +0100 Subject: [PATCH 533/675] Avoid mutating state. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index e99efa6e9..462ba8da0 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -84,7 +84,7 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: to_modify = unit_stack[-1] # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) - to_modify.dimensions = multiply_dimensions(to_modify.dimensions, multiplier) + to_modify = Unit(to_modify.scale, multiply_dimensions(to_modify.dimensions, multiplier)) except ValueError: new_units = parse_unit_strs(token) unit_stack += new_units From cf7b65bc57c1d20ebcca1b8204b52b50359f4903 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 08:25:18 +0100 Subject: [PATCH 534/675] Replace the unit on the stack. --- sasdata/quantities/unit_parser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 462ba8da0..70a452640 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -85,6 +85,7 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) to_modify = Unit(to_modify.scale, multiply_dimensions(to_modify.dimensions, multiplier)) + unit_stack[-1] = to_modify except ValueError: new_units = parse_unit_strs(token) unit_stack += new_units From 18a5a946cdd4be8655042a42901e59211143e862 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 09:46:25 +0100 Subject: [PATCH 535/675] Fixed the logic around combining units. --- sasdata/quantities/unit_parser.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 70a452640..47d70a006 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -31,14 +31,7 @@ def sum_dimensions(dimensions: Dimensions): ]) def combine_units(unit_1: Unit, unit_2: Unit): - if unit_1.dimensions.is_dimensionless or unit_2.dimensions.is_dimensionless: - unit_1_scale = unit_1.scale - unit_2_scale = unit_2.scale - else: - unit_1_scale = unit_1.scale ** sum_dimensions(unit_1.dimensions) - unit_2_scale = unit_2.scale ** sum_dimensions(unit_2.dimensions) - return Unit(unit_1_scale * unit_2_scale, unit_1.dimensions * unit_2.dimensions) - + return Unit(unit_1.scale * unit_2.scale, unit_1.dimensions * unit_2.dimensions) def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+', unit_str) @@ -84,7 +77,7 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: to_modify = unit_stack[-1] # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) - to_modify = Unit(to_modify.scale, multiply_dimensions(to_modify.dimensions, multiplier)) + to_modify = Unit(to_modify.scale ** dimension_modifier, multiply_dimensions(to_modify.dimensions, multiplier)) unit_stack[-1] = to_modify except ValueError: new_units = parse_unit_strs(token) From b5f5c7f65b0b91dd1dc9b0f5f725fefecbe35a90 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 09:54:33 +0100 Subject: [PATCH 536/675] Parse_name_unit can take in an already parsed unit. --- sasdata/quantities/unit_parser.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 47d70a006..776a8f0d6 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -95,9 +95,12 @@ def parse_unit(unit_str: str) -> Unit: parsed_unit = combine_units(parsed_unit, unit) return parsed_unit -def parse_named_unit(unit_str: str) -> NamedUnit: +def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: # TODO: Not actually sure if this includes all units. - generic_unit = parse_unit(unit_str) + if parsed_unit is None: + generic_unit = parse_unit(unit_str) + else: + generic_unit = parsed_unit for named_unit in all_units: if named_unit == generic_unit: return named_unit From 2c827fcf3a8afb781df7a0d1bc48124243d15c3f Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 10:05:22 +0100 Subject: [PATCH 537/675] Added a todo comment. --- sasdata/quantities/unit_parser.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 776a8f0d6..48010e50f 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -95,6 +95,8 @@ def parse_unit(unit_str: str) -> Unit: parsed_unit = combine_units(parsed_unit, unit) return parsed_unit +# TODO: Just noticed that, if a parsed unit is already provided, then the unit_str is redundant. Could solve this +# through function overloading but I don't know if I can do this based on the types of parameters alone. def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: # TODO: Not actually sure if this includes all units. if parsed_unit is None: From cc0477a47915c124b299ac7a5c28b09b8b558eb2 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 10:05:40 +0100 Subject: [PATCH 538/675] Take a unit from the command line. --- sasdata/quantities/unit_parser.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 48010e50f..c34aa5f78 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -109,4 +109,11 @@ def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: raise ValueError('A named unit does not exist for this unit.') if __name__ == "__main__": - print(parse_named_unit('kmh-1')) + to_parse = input('Enter a unit to parse:') + generic_unit = parse_unit(to_parse) + print(f'Generic Unit: {generic_unit}') + try: + named_unit = parse_named_unit(to_parse, generic_unit) + print(f'Named Unit: {named_unit}') + except ValueError: + print('There is no named unit available.') From a4eb3d346c54e836e1cae1b75466122f425ba991 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 10:06:18 +0100 Subject: [PATCH 539/675] Added whitespace on input. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index c34aa5f78..a81c874f3 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -109,7 +109,7 @@ def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: raise ValueError('A named unit does not exist for this unit.') if __name__ == "__main__": - to_parse = input('Enter a unit to parse:') + to_parse = input('Enter a unit to parse: ') generic_unit = parse_unit(to_parse) print(f'Generic Unit: {generic_unit}') try: From b1b19d1bb9dffedd0242b43280a176c722630fe3 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 10:08:04 +0100 Subject: [PATCH 540/675] Fixed typo. --- sasdata/quantities/unit_parser_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser_test.py b/sasdata/quantities/unit_parser_test.py index 383c079c0..64aa3cc47 100644 --- a/sasdata/quantities/unit_parser_test.py +++ b/sasdata/quantities/unit_parser_test.py @@ -3,7 +3,7 @@ from sasdata.quantities.units import Dimensions, Unit, meters, meters_per_second, per_angstrom, \ kilometers_per_square_hour -# Lets start with the straight forward ones first, and get progressivel more complex as the list goes on. +# Lets start with the straight forward ones first, and get progressively more complex as the list goes on. tests = [ EqualUnits('Metres', meters, From e1ad3757c0cc5cb2e8eb0b57d6ad2e4ff20e688c Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 10:53:19 +0100 Subject: [PATCH 541/675] Only multiply scale by 1, or -1. --- sasdata/quantities/unit_parser.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index a81c874f3..6d4473e26 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -76,8 +76,9 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: dimension_modifier = int(token) to_modify = unit_stack[-1] # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. - multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) - to_modify = Unit(to_modify.scale ** dimension_modifier, multiply_dimensions(to_modify.dimensions, multiplier)) + dimension_multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) + scale_multiplier = 1 if dimension_modifier > 0 else -1 + to_modify = Unit(to_modify.scale ** scale_multiplier, multiply_dimensions(to_modify.dimensions, dimension_multiplier)) unit_stack[-1] = to_modify except ValueError: new_units = parse_unit_strs(token) From 5817d5a7bc186bfa9e664067bd0ad0e1181719f6 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 11:04:46 +0100 Subject: [PATCH 542/675] Look for slashes in the string. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 6d4473e26..f13103488 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -34,7 +34,7 @@ def combine_units(unit_1: Unit, unit_2: Unit): return Unit(unit_1.scale * unit_2.scale, unit_1.dimensions * unit_2.dimensions) def split_unit_str(unit_str: str) -> list[str]: - return findall(r'[A-Za-z]+|[-\d]+', unit_str) + return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit From 945087996396c2417e5cc92fa20de2163ed3385e Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 17 Sep 2024 11:22:23 +0100 Subject: [PATCH 543/675] Got fraction units working as well :) --- sasdata/quantities/unit_parser.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index f13103488..238450c29 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -63,6 +63,12 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None) -> lis current_units += [parsed_unit] return parse_unit_strs(remaining_str, current_units) +def unit_power(to_modify: Unit, power: int): + # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. + dimension_multiplier = Dimensions(power, power, power, power, power, power, power) + scale_multiplier = 1 if power > 0 else -1 + return Unit(to_modify.scale ** scale_multiplier, multiply_dimensions(to_modify.dimensions, dimension_multiplier)) + # Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there # are two functions. @@ -71,17 +77,22 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: # TODO: This doesn't work for 1/ (or any fraction) yet. unit_stack: list[Unit] = [] split_str = split_unit_str(unit_str) + inverse_next_unit = False for token in split_str: try: - dimension_modifier = int(token) + if token == '/': + inverse_next_unit = True + continue + power = int(token) to_modify = unit_stack[-1] - # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. - dimension_multiplier = Dimensions(dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier, dimension_modifier) - scale_multiplier = 1 if dimension_modifier > 0 else -1 - to_modify = Unit(to_modify.scale ** scale_multiplier, multiply_dimensions(to_modify.dimensions, dimension_multiplier)) - unit_stack[-1] = to_modify + modified = unit_power(to_modify, power) + unit_stack[-1] = modified except ValueError: new_units = parse_unit_strs(token) + if inverse_next_unit: + # TODO: Assume the power is going to be -1. This might not be true. + power = -1 + new_units[0] = unit_power(new_units[0], power) unit_stack += new_units # This error will happen if it tries to read a modifier but there are no units on the stack. We will just have # to ignore it. Strings being parsed shouldn't really have it anyway (e.g. -1m). From 60b95879ce23f56d54e7453d2d1880c4afd7cd02 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 18 Sep 2024 15:44:55 +0100 Subject: [PATCH 544/675] Configure how ambiguities are dealt with. --- sasdata/quantities/unit_parser.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 238450c29..cf6a272b1 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -36,9 +36,14 @@ def combine_units(unit_1: Unit, unit_2: Unit): def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) -def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: +def parse_single_unit(unit_str: str, longest_unit: bool = True) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit - cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str""" + cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str. + + The shortest_unit parameter specifies how to resolve ambiguities. If it is true, then it will parse the longest unit + available. Otherwise, it will stop parsing as soon as it has found any unit. + + """ current_unit = '' string_pos = 0 for char in unit_str: @@ -48,6 +53,8 @@ def parse_single_unit(unit_str: str) -> tuple[Unit | None, str]: break string_pos += 1 current_unit= potential_unit_str + if not longest_unit: + break if current_unit == '': return (None, unit_str) remaining_str = unit_str[string_pos::] From 1a252567c8a1d9b46b189f37a60dcd7b5112f292 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 18 Sep 2024 15:49:47 +0100 Subject: [PATCH 545/675] Only break if we have found a symbol. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index cf6a272b1..37d2580b0 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -53,7 +53,7 @@ def parse_single_unit(unit_str: str, longest_unit: bool = True) -> tuple[Unit | break string_pos += 1 current_unit= potential_unit_str - if not longest_unit: + if not longest_unit and current_unit in symbol_lookup.keys(): break if current_unit == '': return (None, unit_str) From a8f2622ee3aee0bb6115f746df61a319d8e84789 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 18 Sep 2024 16:07:55 +0100 Subject: [PATCH 546/675] Take in longest unit across the whole file. --- sasdata/quantities/unit_parser.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 37d2580b0..b65ccd856 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -60,15 +60,15 @@ def parse_single_unit(unit_str: str, longest_unit: bool = True) -> tuple[Unit | remaining_str = unit_str[string_pos::] return (symbol_lookup[current_unit], remaining_str) -def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None) -> list[Unit]: +def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longest_unit: bool = True) -> list[Unit]: if current_units is None: current_units = [] if unit_str == '': return current_units - parsed_unit, remaining_str = parse_single_unit(unit_str) + parsed_unit, remaining_str = parse_single_unit(unit_str, longest_unit) if not parsed_unit is None: current_units += [parsed_unit] - return parse_unit_strs(remaining_str, current_units) + return parse_unit_strs(remaining_str, current_units, longest_unit) def unit_power(to_modify: Unit, power: int): # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. @@ -80,7 +80,7 @@ def unit_power(to_modify: Unit, power: int): # Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there # are two functions. -def parse_unit_stack(unit_str: str) -> list[Unit]: +def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: # TODO: This doesn't work for 1/ (or any fraction) yet. unit_stack: list[Unit] = [] split_str = split_unit_str(unit_str) @@ -95,7 +95,7 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: modified = unit_power(to_modify, power) unit_stack[-1] = modified except ValueError: - new_units = parse_unit_strs(token) + new_units = parse_unit_strs(token, None, longest_unit) if inverse_next_unit: # TODO: Assume the power is going to be -1. This might not be true. power = -1 @@ -107,9 +107,9 @@ def parse_unit_stack(unit_str: str) -> list[Unit]: pass return unit_stack -def parse_unit(unit_str: str) -> Unit: +def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: parsed_unit = Unit(1, Dimensions()) - unit_stack = parse_unit_stack(unit_str) + unit_stack = parse_unit_stack(unit_str, longest_unit) for unit in unit_stack: parsed_unit = combine_units(parsed_unit, unit) return parsed_unit From 0c0a8fb34e45f59d4c762c92951e30c4fbfa3f33 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 10:29:45 +0100 Subject: [PATCH 547/675] Take in a unit group in parse_singe_unit. --- sasdata/quantities/unit_parser.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index b65ccd856..2f51bb044 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,4 +1,4 @@ -from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup, unit_groups +from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup, unit_groups, UnitGroup from re import findall # TODO: This shouldn't be in this file but I don't want to edit Lucas' code before he is finished. @@ -36,7 +36,7 @@ def combine_units(unit_1: Unit, unit_2: Unit): def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) -def parse_single_unit(unit_str: str, longest_unit: bool = True) -> tuple[Unit | None, str]: +def parse_single_unit(unit_str: str, longest_unit: bool = True, unit_group: UnitGroup | None = None) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str. @@ -46,19 +46,23 @@ def parse_single_unit(unit_str: str, longest_unit: bool = True) -> tuple[Unit | """ current_unit = '' string_pos = 0 + if unit_group is None: + lookup_dict = symbol_lookup + else: + lookup_dict = dict([name_unit for name_unit in symbol_lookup.items() if name_unit[1] in unit_group.units]) for char in unit_str: potential_unit_str = current_unit + char - potential_symbols = [symbol for symbol in symbol_lookup.keys() if symbol.startswith(potential_unit_str)] + potential_symbols = [symbol for symbol in lookup_dict.keys() if symbol.startswith(potential_unit_str)] if len(potential_symbols) == 0: break string_pos += 1 current_unit= potential_unit_str - if not longest_unit and current_unit in symbol_lookup.keys(): + if not longest_unit and current_unit in lookup_dict.keys(): break if current_unit == '': return (None, unit_str) remaining_str = unit_str[string_pos::] - return (symbol_lookup[current_unit], remaining_str) + return (lookup_dict[current_unit], remaining_str) def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longest_unit: bool = True) -> list[Unit]: if current_units is None: From 106aa717e5b14291628fdf4aa1cda4f8fd8e640a Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 10:51:13 +0100 Subject: [PATCH 548/675] Parse a unit from a specific group. --- sasdata/quantities/unit_parser.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 2f51bb044..019130567 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -118,6 +118,18 @@ def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: parsed_unit = combine_units(parsed_unit, unit) return parsed_unit +def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: + """Tries to use the given unit group to resolve ambiguities. Parse a unit twice with different options, and returns + whatever conforms to the unit group.""" + longest_parsed_unit = parse_unit(unit_str, True) + shortest_parsed_unit = parse_unit(unit_str, False) + if longest_parsed_unit in from_group.units: + return longest_parsed_unit + elif shortest_parsed_unit in from_group.units: + return shortest_parsed_unit + else: + return None + # TODO: Just noticed that, if a parsed unit is already provided, then the unit_str is redundant. Could solve this # through function overloading but I don't know if I can do this based on the types of parameters alone. def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: From e75fb0752aa9baac39feefe616e8fbf90120b834 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 11:18:52 +0100 Subject: [PATCH 549/675] Equivalent function for from group. --- sasdata/quantities/unit_parser.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 019130567..7a01ce487 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -143,6 +143,12 @@ def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: return named_unit raise ValueError('A named unit does not exist for this unit.') +def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUnit: + parsed_unit = parse_unit_from_group(unit_str, from_group) + if parsed_unit == None: + raise ValueError('That unit cannot be parsed from the specified group.') + return parse_named_unit('', parsed_unit) + if __name__ == "__main__": to_parse = input('Enter a unit to parse: ') generic_unit = parse_unit(to_parse) From c5c6d8a141a5ea04cfedae8c16c414f4483d826b Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 11:28:42 +0100 Subject: [PATCH 550/675] Is none not equal to none. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 7a01ce487..90eee7154 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -145,7 +145,7 @@ def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUnit: parsed_unit = parse_unit_from_group(unit_str, from_group) - if parsed_unit == None: + if parsed_unit is None: raise ValueError('That unit cannot be parsed from the specified group.') return parse_named_unit('', parsed_unit) From 350e736f3cd4bbf2ce1f5d5036bcf5549d5e8791 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 11:57:17 +0100 Subject: [PATCH 551/675] Removed old TODO comment. --- sasdata/quantities/unit_parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 90eee7154..96052ffae 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -85,7 +85,6 @@ def unit_power(to_modify: Unit, power: int): # are two functions. def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: - # TODO: This doesn't work for 1/ (or any fraction) yet. unit_stack: list[Unit] = [] split_str = split_unit_str(unit_str) inverse_next_unit = False From 8952fe45c91640d7ff414179984d553d41923696 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 15:02:33 +0100 Subject: [PATCH 552/675] Catch key errors. These will happen when we try to parse a unit that doesn't exist. --- sasdata/quantities/unit_parser.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 96052ffae..b60f95cf4 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -111,11 +111,14 @@ def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: return unit_stack def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: - parsed_unit = Unit(1, Dimensions()) - unit_stack = parse_unit_stack(unit_str, longest_unit) - for unit in unit_stack: - parsed_unit = combine_units(parsed_unit, unit) - return parsed_unit + try: + parsed_unit = Unit(1, Dimensions()) + unit_stack = parse_unit_stack(unit_str, longest_unit) + for unit in unit_stack: + parsed_unit = combine_units(parsed_unit, unit) + return parsed_unit + except KeyError: + raise ValueError('Unit string contains an unrecognised pattern.') def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: """Tries to use the given unit group to resolve ambiguities. Parse a unit twice with different options, and returns From 729825dc4620218bc3c3ebbf0784001121ed14e3 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 15:05:13 +0100 Subject: [PATCH 553/675] Expand the try block. --- sasdata/quantities/unit_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index b60f95cf4..bb243e148 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -153,9 +153,9 @@ def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUn if __name__ == "__main__": to_parse = input('Enter a unit to parse: ') - generic_unit = parse_unit(to_parse) - print(f'Generic Unit: {generic_unit}') try: + generic_unit = parse_unit(to_parse) + print(f'Generic Unit: {generic_unit}') named_unit = parse_named_unit(to_parse, generic_unit) print(f'Named Unit: {named_unit}') except ValueError: From 3b44869650f5221e0d48c3bf7ac67d7a0bbb8bb9 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Thu, 19 Sep 2024 15:45:18 +0100 Subject: [PATCH 554/675] Removed an old todo comment. --- sasdata/quantities/unit_parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index bb243e148..66a580cbe 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -135,7 +135,6 @@ def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: # TODO: Just noticed that, if a parsed unit is already provided, then the unit_str is redundant. Could solve this # through function overloading but I don't know if I can do this based on the types of parameters alone. def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: - # TODO: Not actually sure if this includes all units. if parsed_unit is None: generic_unit = parse_unit(unit_str) else: From 7fd7eeda2e4e269e2da17c2cbb1c9190b46e40ec Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 13:45:19 +0100 Subject: [PATCH 555/675] New unit test in pytest. --- test/utest_unit_parser.py | 75 ++++++++------------------------------- 1 file changed, 14 insertions(+), 61 deletions(-) diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index afce93f13..0f3e19184 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -1,61 +1,14 @@ -from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group, parse_unit -from sasdata.quantities import units -from sasdata.quantities.units import Unit - -import pytest - -named_units_for_testing = [ - ('m', units.meters), - ('A-1', units.per_angstrom), - ('1/A', units.per_angstrom), - ('kmh-2', units.kilometers_per_square_hour), - ('km/h2', units.kilometers_per_square_hour), - ('kgm/s2', units.newtons), - ('m m', units.square_meters), - ('mm', units.millimeters), - ('A^-1', units.per_angstrom), - ('V/Amps', units.ohms), - ('Ω', units.ohms), - ('Å', units.angstroms), - ('%', units.percent) -] - -unnamed_units_for_testing = [ - ('m13', units.meters**13), - ('kW/sr', units.kilowatts/units.stradians) -] - - -@pytest.mark.parametrize("string, expected_units", named_units_for_testing) -def test_name_parse(string: str, expected_units: Unit): - """ Test basic parsing""" - assert parse_named_unit(string) == expected_units - -@pytest.mark.parametrize("string, expected_units", named_units_for_testing + unnamed_units_for_testing) -def test_equivalent(string: str, expected_units: Unit): - """ Check dimensions of parsed units""" - assert parse_unit(string).equivalent(expected_units) - - -@pytest.mark.parametrize("string, expected_units", named_units_for_testing + unnamed_units_for_testing) -def test_scale_same(string: str, expected_units: Unit): - """ Test basic parsing""" - assert parse_unit(string).scale == pytest.approx(expected_units.scale, rel=1e-14) - - -def test_parse_from_group(): - """ Test group based disambiguation""" - parsed_metres_per_second = parse_named_unit_from_group('ms-1', units.speed) - assert parsed_metres_per_second == units.meters_per_second - - -def test_parse_errors(): - # Fails because the unit is not in that specific group. - with pytest.raises(ValueError, match='That unit cannot be parsed from the specified group.'): - parse_named_unit_from_group('km', units.speed) - # Fails because part of the unit matches but there is an unknown unit '@' - with pytest.raises(ValueError, match='unit_str contains forbidden characters.'): - parse_unit('km@-1') - # Fails because 'da' is not a unit. - with pytest.raises(ValueError, match='Unit string contains an unrecognised pattern.'): - parse_unit('mmda2') +from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group +from sasdata.quantities.units import meters, speed, meters_per_second, per_angstrom, kilometers_per_square_hour + + +def test_parse(): + parsed_metres = parse_named_unit('m') + assert parsed_metres == meters + # Have to specify a group because this is ambigious with inverse of milliseconds. + parsed_metres_per_second = parse_named_unit_from_group('ms-1', speed) + assert parsed_metres_per_second == meters_per_second + parsed_inverse_angstroms = parse_named_unit('A-1') + assert parsed_inverse_angstroms == per_angstrom + parsed_kilometers_per_square_hour = parse_named_unit('kmh-2') + assert parsed_kilometers_per_square_hour == kilometers_per_square_hour From dae132c00d5c12dfb99765644f05ca92ae10d178 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 15:26:22 +0100 Subject: [PATCH 556/675] Raise an exception if the unit can't be parsed. --- sasdata/quantities/unit_parser.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 66a580cbe..df47aa40b 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -72,7 +72,9 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longes parsed_unit, remaining_str = parse_single_unit(unit_str, longest_unit) if not parsed_unit is None: current_units += [parsed_unit] - return parse_unit_strs(remaining_str, current_units, longest_unit) + return parse_unit_strs(remaining_str, current_units, longest_unit) + else: + raise ValueError(f'Could not interpret {remaining_str}') def unit_power(to_modify: Unit, power: int): # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. From 64a877f689e2bf8ca3332c500efeb08777cfe6a5 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 15:46:28 +0100 Subject: [PATCH 557/675] Added some unit tests that should error. --- test/utest_unit_parser.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index 0f3e19184..8fcfc0b76 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -1,5 +1,6 @@ -from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group +from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group, parse_unit from sasdata.quantities.units import meters, speed, meters_per_second, per_angstrom, kilometers_per_square_hour +from pytest import raises def test_parse(): @@ -12,3 +13,11 @@ def test_parse(): assert parsed_inverse_angstroms == per_angstrom parsed_kilometers_per_square_hour = parse_named_unit('kmh-2') assert parsed_kilometers_per_square_hour == kilometers_per_square_hour + +def test_parse_errors(): + # Fails because the unit is not in that specific group. + with raises(ValueError, match='That unit cannot be parsed from the specified group.'): + parse_named_unit_from_group('km', speed) + # Fails because part of the unit matches but there is an unknown unit '@' + with raises(ValueError, match='Unit string contains an unrecognised pattern.'): + parse_unit('km@-1') From 9f4c2ba17d481152387821bf3dde3f66afdb6d76 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 16:11:15 +0100 Subject: [PATCH 558/675] Created a regex validator for the unit str. --- sasdata/quantities/unit_parser.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index df47aa40b..b461cfbd1 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,5 +1,5 @@ from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup, unit_groups, UnitGroup -from re import findall +from re import findall, fullmatch # TODO: This shouldn't be in this file but I don't want to edit Lucas' code before he is finished. @@ -36,6 +36,9 @@ def combine_units(unit_1: Unit, unit_2: Unit): def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) +def validate_unit_str(unit_str: str) -> bool: + return not fullmatch(r'[A-Za-z1-9\-\+]+', unit_str) is None + def parse_single_unit(unit_str: str, longest_unit: bool = True, unit_group: UnitGroup | None = None) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str. From 837c285d4696e4ec08abeb46e478cceb7eea33b1 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 16:14:44 +0100 Subject: [PATCH 559/675] Throw an exception if the validation fails. --- sasdata/quantities/unit_parser.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index b461cfbd1..52b5451ec 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -117,6 +117,8 @@ def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: try: + if not validate_unit_str(unit_str): + raise ValueError('unit_str contains forbidden characters.') parsed_unit = Unit(1, Dimensions()) unit_stack = parse_unit_stack(unit_str, longest_unit) for unit in unit_stack: From ece9561b5d465771cce7e9dc00785718961cad92 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 16:19:25 +0100 Subject: [PATCH 560/675] Update unit test to reflect new error. --- test/utest_unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index 8fcfc0b76..4372a4dfa 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -19,5 +19,5 @@ def test_parse_errors(): with raises(ValueError, match='That unit cannot be parsed from the specified group.'): parse_named_unit_from_group('km', speed) # Fails because part of the unit matches but there is an unknown unit '@' - with raises(ValueError, match='Unit string contains an unrecognised pattern.'): + with raises(ValueError, match='unit_str contains forbidden characters.'): parse_unit('km@-1') From ab185635f8d814a03b059e794b5a039728ebbdc0 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Sep 2024 16:22:17 +0100 Subject: [PATCH 561/675] Unit test for what I was originally testing for. --- test/utest_unit_parser.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index 4372a4dfa..bf477ab48 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -21,3 +21,6 @@ def test_parse_errors(): # Fails because part of the unit matches but there is an unknown unit '@' with raises(ValueError, match='unit_str contains forbidden characters.'): parse_unit('km@-1') + # Fails because 'da' is not a unit. + with raises(ValueError, match='Unit string contains an unrecognised pattern.'): + parse_unit('mmda2') From 218bf098d90ea0073696458e247d33fac2eba7ba Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 12:05:34 +0100 Subject: [PATCH 562/675] Added more tests for slants. --- test/utest_unit_parser.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index bf477ab48..4227c8bfa 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -11,8 +11,12 @@ def test_parse(): assert parsed_metres_per_second == meters_per_second parsed_inverse_angstroms = parse_named_unit('A-1') assert parsed_inverse_angstroms == per_angstrom + parsed_inverse_angstroms_slant = parse_named_unit('1/A') + assert parsed_inverse_angstroms_slant == per_angstrom parsed_kilometers_per_square_hour = parse_named_unit('kmh-2') assert parsed_kilometers_per_square_hour == kilometers_per_square_hour + parsed_kilometers_per_square_hour_slant = parse_named_unit('km/h2') + assert parsed_kilometers_per_square_hour_slant == kilometers_per_square_hour def test_parse_errors(): # Fails because the unit is not in that specific group. From 368b9e533004cb3fb4abbf64dd920d2d816784e1 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:02:41 +0100 Subject: [PATCH 563/675] Slants should be valid unit strings as well. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 52b5451ec..1a2b51737 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -37,7 +37,7 @@ def split_unit_str(unit_str: str) -> list[str]: return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) def validate_unit_str(unit_str: str) -> bool: - return not fullmatch(r'[A-Za-z1-9\-\+]+', unit_str) is None + return not fullmatch(r'[A-Za-z1-9\-\+/]+', unit_str) is None def parse_single_unit(unit_str: str, longest_unit: bool = True, unit_group: UnitGroup | None = None) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit From 0d2c5d9ae25364a89d826b2e21644839057a310f Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:07:06 +0100 Subject: [PATCH 564/675] Parse in newton as its defined value. --- test/utest_unit_parser.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index 4227c8bfa..d5fc0d5d1 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -1,5 +1,5 @@ from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group, parse_unit -from sasdata.quantities.units import meters, speed, meters_per_second, per_angstrom, kilometers_per_square_hour +from sasdata.quantities.units import meters, speed, meters_per_second, per_angstrom, kilometers_per_square_hour, newtons from pytest import raises @@ -17,6 +17,8 @@ def test_parse(): assert parsed_kilometers_per_square_hour == kilometers_per_square_hour parsed_kilometers_per_square_hour_slant = parse_named_unit('km/h2') assert parsed_kilometers_per_square_hour_slant == kilometers_per_square_hour + parsed_newton = parse_named_unit('kgm/s2') + assert parsed_newton == newtons def test_parse_errors(): # Fails because the unit is not in that specific group. From 5f8b174fce69a06734c7fd161aff8cd0640158e0 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:13:43 +0100 Subject: [PATCH 565/675] Remove the old testing file. --- sasdata/quantities/unit_parser_test.py | 33 -------------------------- 1 file changed, 33 deletions(-) delete mode 100644 sasdata/quantities/unit_parser_test.py diff --git a/sasdata/quantities/unit_parser_test.py b/sasdata/quantities/unit_parser_test.py deleted file mode 100644 index 64aa3cc47..000000000 --- a/sasdata/quantities/unit_parser_test.py +++ /dev/null @@ -1,33 +0,0 @@ -from sasdata.quantities.unit_parser import parse_unit -from sasdata.quantities.units_tests import EqualUnits -from sasdata.quantities.units import Dimensions, Unit, meters, meters_per_second, per_angstrom, \ - kilometers_per_square_hour - -# Lets start with the straight forward ones first, and get progressively more complex as the list goes on. -tests = [ - EqualUnits('Metres', - meters, - parse_unit('m')), - EqualUnits('Metres per second', - meters_per_second, - parse_unit('ms-1'), - parse_unit('m/s')), - EqualUnits('Inverse Test', - per_angstrom, - parse_unit('1/A'), - parse_unit('A-1')), - # This test is primarily to ensure that the 'mm' doesn't get interpreted as two separate metres. - EqualUnits('Milimetres * Centimetres', - # TODO: Not sure if this calculation is right. - Unit(0.001 * 0.01, Dimensions(length=2)), - parse_unit('mmcm')), - EqualUnits("Acceleration", - kilometers_per_square_hour, - parse_unit('kmh-2'), - parse_unit('km/h2') - ) -] - -for test in tests: - print(test.test_name) - test.run_test() From a582638e3b1d2e8b07aa0e333bb3f9a1996d9373 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:21:51 +0100 Subject: [PATCH 566/675] This function isn't being used. --- sasdata/quantities/unit_parser.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 1a2b51737..5541360f8 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -19,17 +19,6 @@ def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> D angle_hint=dimensions_1.angle_hint * dimensions_2.angle_hint ) -def sum_dimensions(dimensions: Dimensions): - return sum([ - dimensions.length, - dimensions.time, - dimensions.mass, - dimensions.current, - dimensions.temperature, - dimensions.moles_hint, - dimensions.angle_hint - ]) - def combine_units(unit_1: Unit, unit_2: Unit): return Unit(unit_1.scale * unit_2.scale, unit_1.dimensions * unit_2.dimensions) From e51d38c36e083336284de9cdf8fef99213d58c83 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:26:12 +0100 Subject: [PATCH 567/675] Added to the doc string about unit groups. --- sasdata/quantities/unit_parser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 5541360f8..528fe1317 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -35,6 +35,7 @@ def parse_single_unit(unit_str: str, longest_unit: bool = True, unit_group: Unit The shortest_unit parameter specifies how to resolve ambiguities. If it is true, then it will parse the longest unit available. Otherwise, it will stop parsing as soon as it has found any unit. + If unit_group is set, it will only try to parse units within that group. This is useful for resolving ambiguities. """ current_unit = '' string_pos = 0 From d8009b191ccf49d14c4f7b1804b1d6f2e8312d65 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:27:12 +0100 Subject: [PATCH 568/675] Moved the unit group to first. This is probably better as I think the caller is more likely to use longest_unit's default value when unit_group is set. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 528fe1317..2b05ce834 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -28,7 +28,7 @@ def split_unit_str(unit_str: str) -> list[str]: def validate_unit_str(unit_str: str) -> bool: return not fullmatch(r'[A-Za-z1-9\-\+/]+', unit_str) is None -def parse_single_unit(unit_str: str, longest_unit: bool = True, unit_group: UnitGroup | None = None) -> tuple[Unit | None, str]: +def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit cannot be parsed, the unit will be None, and the remaining string will be the entire unit_str. From 76c92ef406c8fcbfbe1619e946734b4457eed252 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:43:37 +0100 Subject: [PATCH 569/675] Small rename. --- sasdata/quantities/unit_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 2b05ce834..27015aabd 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -43,8 +43,8 @@ def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longes lookup_dict = symbol_lookup else: lookup_dict = dict([name_unit for name_unit in symbol_lookup.items() if name_unit[1] in unit_group.units]) - for char in unit_str: - potential_unit_str = current_unit + char + for next_char in unit_str: + potential_unit_str = current_unit + next_char potential_symbols = [symbol for symbol in lookup_dict.keys() if symbol.startswith(potential_unit_str)] if len(potential_symbols) == 0: break From baa63cdbf45da33bdd4479578e8db0bdc44e833d Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:50:00 +0100 Subject: [PATCH 570/675] Use destructuring to make this a bit cleaner. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 27015aabd..5ca0effbe 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -42,7 +42,7 @@ def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longes if unit_group is None: lookup_dict = symbol_lookup else: - lookup_dict = dict([name_unit for name_unit in symbol_lookup.items() if name_unit[1] in unit_group.units]) + lookup_dict = dict([(name, unit) for name, unit in symbol_lookup.items() if unit in unit_group.units]) for next_char in unit_str: potential_unit_str = current_unit + next_char potential_symbols = [symbol for symbol in lookup_dict.keys() if symbol.startswith(potential_unit_str)] From 91e3b86c4b284aa6645d49508eeb2f79f15a8f7a Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 14:51:55 +0100 Subject: [PATCH 571/675] Fixed function call. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 5ca0effbe..7db651ff2 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -62,7 +62,7 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longes current_units = [] if unit_str == '': return current_units - parsed_unit, remaining_str = parse_single_unit(unit_str, longest_unit) + parsed_unit, remaining_str = parse_single_unit(unit_str, longest_unit=longest_unit) if not parsed_unit is None: current_units += [parsed_unit] return parse_unit_strs(remaining_str, current_units, longest_unit) From 4196cc6b084ff4ef58c6663f68a691e17d10b25b Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 23 Sep 2024 15:40:56 +0100 Subject: [PATCH 572/675] Added some docstrings. --- sasdata/quantities/unit_parser.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 7db651ff2..90f86633b 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -9,6 +9,7 @@ all_units.extend(group) def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> Dimensions: + """Multiply each dimension in dimensions_1 with the same dimension in dimensions_2""" return Dimensions( length=dimensions_1.length * dimensions_2.length, time=dimensions_1.time * dimensions_2.time, @@ -20,12 +21,16 @@ def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> D ) def combine_units(unit_1: Unit, unit_2: Unit): + """Combine unit_1, and unit_2 into one unit.""" return Unit(unit_1.scale * unit_2.scale, unit_1.dimensions * unit_2.dimensions) def split_unit_str(unit_str: str) -> list[str]: + """Separate the letters from the numbers in unit_str""" return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) def validate_unit_str(unit_str: str) -> bool: + """Validate whether unit_str is valid. This doesn't mean that the unit specified in unit_str exists but rather it + only consists of letters, and numbers as a unit string should.""" return not fullmatch(r'[A-Za-z1-9\-\+/]+', unit_str) is None def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: @@ -58,6 +63,7 @@ def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longes return (lookup_dict[current_unit], remaining_str) def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longest_unit: bool = True) -> list[Unit]: + """Recursively parse units from unit_str until no more characters are present.""" if current_units is None: current_units = [] if unit_str == '': @@ -70,6 +76,7 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longes raise ValueError(f'Could not interpret {remaining_str}') def unit_power(to_modify: Unit, power: int): + """Raise to_modify to power""" # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. dimension_multiplier = Dimensions(power, power, power, power, power, power, power) scale_multiplier = 1 if power > 0 else -1 @@ -80,6 +87,7 @@ def unit_power(to_modify: Unit, power: int): # are two functions. def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: + """Split unit_str into a stack of parsed units.""" unit_stack: list[Unit] = [] split_str = split_unit_str(unit_str) inverse_next_unit = False @@ -106,6 +114,7 @@ def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: return unit_stack def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: + """Parse unit_str into a unit.""" try: if not validate_unit_str(unit_str): raise ValueError('unit_str contains forbidden characters.') From 1c4b2ef5911d2f45ae46268ec34ee567e702947e Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 25 Sep 2024 08:18:42 +0100 Subject: [PATCH 573/675] Refactored parse named unit so it just takes one arg. --- sasdata/quantities/unit_parser.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 90f86633b..351036b3c 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -140,11 +140,13 @@ def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: # TODO: Just noticed that, if a parsed unit is already provided, then the unit_str is redundant. Could solve this # through function overloading but I don't know if I can do this based on the types of parameters alone. -def parse_named_unit(unit_str: str, parsed_unit: Unit|None=None) -> NamedUnit: - if parsed_unit is None: - generic_unit = parse_unit(unit_str) +def parse_named_unit(unit: str | Unit) -> NamedUnit: + if isinstance(unit, str): + generic_unit = parse_unit(unit) + elif isinstance(unit, Unit): + generic_unit = unit else: - generic_unit = parsed_unit + raise ValueError('Unit must be a string, or Unit') for named_unit in all_units: if named_unit == generic_unit: return named_unit @@ -154,14 +156,14 @@ def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUn parsed_unit = parse_unit_from_group(unit_str, from_group) if parsed_unit is None: raise ValueError('That unit cannot be parsed from the specified group.') - return parse_named_unit('', parsed_unit) + return parse_named_unit(parsed_unit) if __name__ == "__main__": to_parse = input('Enter a unit to parse: ') try: generic_unit = parse_unit(to_parse) print(f'Generic Unit: {generic_unit}') - named_unit = parse_named_unit(to_parse, generic_unit) + named_unit = parse_named_unit(generic_unit) print(f'Named Unit: {named_unit}') except ValueError: print('There is no named unit available.') From 1b5e73bd0d174c7d9833b461dbe2ea1312909105 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 25 Sep 2024 08:20:58 +0100 Subject: [PATCH 574/675] Removed old todo comment. --- sasdata/quantities/unit_parser.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 351036b3c..ffe0333ae 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -138,8 +138,6 @@ def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: else: return None -# TODO: Just noticed that, if a parsed unit is already provided, then the unit_str is redundant. Could solve this -# through function overloading but I don't know if I can do this based on the types of parameters alone. def parse_named_unit(unit: str | Unit) -> NamedUnit: if isinstance(unit, str): generic_unit = parse_unit(unit) From bd7687adda9edda00c20d922ac49a3f71d5bc9db Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 25 Sep 2024 08:25:56 +0100 Subject: [PATCH 575/675] Added some docstrings. --- sasdata/quantities/unit_parser.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index ffe0333ae..a7c557a0d 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -139,6 +139,9 @@ def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: return None def parse_named_unit(unit: str | Unit) -> NamedUnit: + """Parses unit into a named unit. Parses unit into a Unit if it is not already, and then finds an equivaelent named + unit. Please note that this might not be the expected unit from the string itself. E.g. 'kgm/2' will become + newtons.""" if isinstance(unit, str): generic_unit = parse_unit(unit) elif isinstance(unit, Unit): @@ -151,6 +154,8 @@ def parse_named_unit(unit: str | Unit) -> NamedUnit: raise ValueError('A named unit does not exist for this unit.') def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUnit: + """Parses unit_str into a named unit. The named unit found must be part of from_group. If two units are found, the + unit that is present in from_group is returned. This is useful in cases of ambiguities.""" parsed_unit = parse_unit_from_group(unit_str, from_group) if parsed_unit is None: raise ValueError('That unit cannot be parsed from the specified group.') From 3ecbad9243812590501fc83461d1c74d7f9941d5 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 25 Sep 2024 08:31:18 +0100 Subject: [PATCH 576/675] Stop linter from moaning. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index a7c557a0d..97de492f6 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -69,7 +69,7 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longes if unit_str == '': return current_units parsed_unit, remaining_str = parse_single_unit(unit_str, longest_unit=longest_unit) - if not parsed_unit is None: + if parsed_unit is not None: current_units += [parsed_unit] return parse_unit_strs(remaining_str, current_units, longest_unit) else: From 550619f1da362bbf2e1128ff62f7802f713e6b90 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 30 Sep 2024 13:49:34 +0100 Subject: [PATCH 577/675] Accept spaces in the unit str. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 97de492f6..e670ed4df 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -31,7 +31,7 @@ def split_unit_str(unit_str: str) -> list[str]: def validate_unit_str(unit_str: str) -> bool: """Validate whether unit_str is valid. This doesn't mean that the unit specified in unit_str exists but rather it only consists of letters, and numbers as a unit string should.""" - return not fullmatch(r'[A-Za-z1-9\-\+/]+', unit_str) is None + return not fullmatch(r'[A-Za-z1-9\-\+/\ ]+', unit_str) is None def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit From d19824b9a16cbb0657bc37a412dcfb0c74ec3ca2 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 30 Sep 2024 14:40:59 +0100 Subject: [PATCH 578/675] Split by dots as well. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index e670ed4df..599abd5d2 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -31,7 +31,7 @@ def split_unit_str(unit_str: str) -> list[str]: def validate_unit_str(unit_str: str) -> bool: """Validate whether unit_str is valid. This doesn't mean that the unit specified in unit_str exists but rather it only consists of letters, and numbers as a unit string should.""" - return not fullmatch(r'[A-Za-z1-9\-\+/\ ]+', unit_str) is None + return not fullmatch(r'[A-Za-z1-9\-\+/\ \.]+', unit_str) is None def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit From aad07a815aaf1142803079c6e7e2f43cdb6b8b65 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 23 Sep 2024 16:17:37 +0100 Subject: [PATCH 579/675] Work on adding uncertainties, adding non-integer powers --- sasdata/data.py | 2 +- sasdata/quantities/_accessor_base.py | 8 +- sasdata/quantities/_build_tables.py | 1 + sasdata/quantities/_units_base.py | 50 ++++-- sasdata/quantities/absolute_temperature.py | 6 +- sasdata/quantities/accessors.py | 8 +- sasdata/quantities/notes.rst | 16 +- sasdata/quantities/operations.py | 2 +- sasdata/quantities/quantities_tests.py | 54 +++---- sasdata/quantities/quantity.py | 180 +++++++++++++++------ sasdata/quantities/units.py | 4 + sasdata/transforms/operation.py | 4 +- 12 files changed, 223 insertions(+), 112 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 096bdb401..2c6e07b0c 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from sasdata.quantities.quantity import BaseQuantity, NamedQuantity +from sasdata.quantities.quantity import Quantity, NamedQuantity from sasdata.metadata import Metadata import numpy as np diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 4ec7bf8cb..42d14fc10 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -1,6 +1,6 @@ from typing import TypeVar, Sequence -from sasdata.quantities.quantity import BaseQuantity +from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit @@ -33,7 +33,7 @@ def value(self) -> float | None: -class QuantityAccessor[DataType](Accessor[DataType, BaseQuantity[DataType]]): +class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): """ Base class for accessors that work with quantities that have units """ def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): super().__init__(target_object, value_target) @@ -54,8 +54,8 @@ def unit(self) -> Unit: return Unit.parse(self._unit_part()) @property - def value(self) -> BaseQuantity[DataType] | None: + def value(self) -> Quantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: - return BaseQuantity(self._numerical_part(), self.unit) + return Quantity(self._numerical_part(), self.unit) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 490f9fad4..3484c0f66 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -98,6 +98,7 @@ "au": ["a.u.", "amu"], "percent": ["%"], "deg": ["degr"], + "none": ["Counts", "counts", "cnts", "Cnts"] } diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index a7d9504c0..8f62f1f36 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -1,10 +1,14 @@ from dataclasses import dataclass from typing import Sequence, Self, TypeVar +from fractions import Fraction import numpy as np from sasdata.quantities.unicode_superscript import int_as_unicode_superscript +class DimensionError(Exception): + pass + class Dimensions: """ @@ -65,19 +69,46 @@ def __truediv__(self: Self, other: Self): self.moles_hint - other.moles_hint, self.angle_hint - other.angle_hint) - def __pow__(self, power: int): + def __pow__(self, power: int | float): - if not isinstance(power, int): + if not isinstance(power, (int, float)): return NotImplemented + frac = Fraction(power) + denominator = frac.denominator + numerator = frac.numerator + + # Throw errors if dimension is not a multiple of the denominator + + if self.length % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with length dimensionality {self.length}") + + if self.time % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with time dimensionality {self.time}") + + if self.mass % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with mass dimensionality {self.mass}") + + if self.current % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with current dimensionality {self.current}") + + if self.temperature % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with temperature dimensionality {self.temperature}") + + if self.moles_hint % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with moles hint dimensionality of {self.moles_hint}") + + if self.angle_hint % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with angle hint dimensionality of {self.angle_hint}") + return Dimensions( - self.length * power, - self.time * power, - self.mass * power, - self.current * power, - self.temperature * power, - self.moles_hint * power, - self.angle_hint * power) + (self.length * numerator) // denominator, + (self.time * numerator) // denominator, + (self.mass * numerator) // denominator, + (self.current * numerator) // denominator, + (self.temperature * numerator) // denominator, + (self.moles_hint * numerator) // denominator, + (self.angle_hint * numerator) // denominator) def __eq__(self: Self, other: Self): if isinstance(other, Dimensions): @@ -177,6 +208,7 @@ def __pow__(self, power: int): return Unit(self.scale**power, self.dimensions**power) + def equivalent(self: Self, other: "Unit"): return self.dimensions == other.dimensions diff --git a/sasdata/quantities/absolute_temperature.py b/sasdata/quantities/absolute_temperature.py index 7bcccce4e..95c8982fb 100644 --- a/sasdata/quantities/absolute_temperature.py +++ b/sasdata/quantities/absolute_temperature.py @@ -1,6 +1,6 @@ from typing import TypeVar -from quantities.quantity import BaseQuantity +from quantities.quantity import Quantity from sasdata.quantities.accessors import TemperatureAccessor @@ -8,8 +8,8 @@ class AbsoluteTemperatureAccessor(TemperatureAccessor[DataType]): """ Parsing for absolute temperatures """ @property - def value(self) -> BaseQuantity[DataType] | None: + def value(self) -> Quantity[DataType] | None: if self._numerical_part() is None: return None else: - return BaseQuantity.parse(self._numerical_part(), self._unit_part(), absolute_temperature=True) + return Quantity.parse(self._numerical_part(), self._unit_part(), absolute_temperature=True) diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index caaeb0022..92ef82bd1 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -80,7 +80,7 @@ from typing import TypeVar, Sequence -from sasdata.quantities.quantity import BaseQuantity +from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit @@ -113,7 +113,7 @@ def value(self) -> float | None: -class QuantityAccessor[DataType](Accessor[DataType, BaseQuantity[DataType]]): +class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): """ Base class for accessors that work with quantities that have units """ def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): super().__init__(target_object, value_target) @@ -134,9 +134,9 @@ def unit(self) -> Unit: return Unit.parse(self._unit_part()) @property - def value(self) -> BaseQuantity[DataType] | None: + def value(self) -> Quantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: - return BaseQuantity(self._numerical_part(), self.unit) + return Quantity(self._numerical_part(), self.unit) diff --git a/sasdata/quantities/notes.rst b/sasdata/quantities/notes.rst index 9c4f47c43..cc34a41e3 100644 --- a/sasdata/quantities/notes.rst +++ b/sasdata/quantities/notes.rst @@ -1,17 +1,3 @@ -Mutability ----------- - -DataSets: Immutable -Quantities: Immutable -Units: Hard coded - -Quantity methods ----------------- - -in_* methods return numbers/arrays in a given unit system -to_* converts to different units - - Identifying of Quantities -------------------- @@ -20,4 +6,4 @@ Either we give them names, in which case we risk collisions, or we use hashes, w have issues with things not being identified correctly. The decision here is to use hashes of the data, not names, because it would be too easy to -give different things the same name. \ No newline at end of file +give different things the same name. diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py index dcbdcf242..1ca284b27 100644 --- a/sasdata/quantities/operations.py +++ b/sasdata/quantities/operations.py @@ -2,7 +2,7 @@ import json -# from sasdata.quantities.quantity import BaseQuantity +from sasdata.quantities.quantity import Quantity T = TypeVar("T") diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index 27d516cc9..7c9181583 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -1,72 +1,72 @@ import numpy as np -from sasdata.quantities.quantity import BaseQuantity, UnitError +from sasdata.quantities.quantity import Quantity, UnitError import sasdata.quantities.units as units import sasdata.quantities.si as si import pytest def test_in_units_of_calculation(): """ Just a couple of unit conversions """ - assert BaseQuantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 - assert BaseQuantity(10, units.minutes).in_units_of(units.seconds) == 600 - assert BaseQuantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000 / 9.81, abs=1) - assert BaseQuantity(0, units.meters).in_units_of(units.exameters) == 0 + assert Quantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 + assert Quantity(10, units.minutes).in_units_of(units.seconds) == 600 + assert Quantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000 / 9.81, abs=1) + assert Quantity(0, units.meters).in_units_of(units.exameters) == 0 def test_unit_compounding_pow(): """ Test units compound correctly when __pow__ is used""" - assert (BaseQuantity(1, units.millimeters) ** 2).in_units_of(units.square_meters) == 1e-6 - assert (BaseQuantity(1, units.minutes) ** 3).in_units_of(units.seconds ** 3) == 60 ** 3 + assert (Quantity(1, units.millimeters) ** 2).in_units_of(units.square_meters) == 1e-6 + assert (Quantity(1, units.minutes) ** 3).in_units_of(units.seconds ** 3) == 60 ** 3 def test_unit_compounding_mul(): """ Test units compound correctly when __mul__ is used""" - assert (BaseQuantity(4, units.minutes) * BaseQuantity(0.25, units.hertz)).in_units_of(units.none) == 60 - assert (BaseQuantity(250, units.volts) * BaseQuantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 + assert (Quantity(4, units.minutes) * Quantity(0.25, units.hertz)).in_units_of(units.none) == 60 + assert (Quantity(250, units.volts) * Quantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 def test_unit_compounding_div(): """ Test units compound correctly when __truediv__ is used""" - assert (BaseQuantity(10, units.kilometers) / BaseQuantity(2, units.minutes) + assert (Quantity(10, units.kilometers) / Quantity(2, units.minutes) ).in_units_of(units.meters_per_second) == pytest.approx(250/3, abs=1e-6) - assert (BaseQuantity(1, units.nanowebers) / (BaseQuantity(1, units.millimeters) ** 2)).in_units_of(units.millitesla) == 1 + assert (Quantity(1, units.nanowebers) / (Quantity(1, units.millimeters) ** 2)).in_units_of(units.millitesla) == 1 def test_value_mul(): """ Test value part of quantities multiply correctly""" - assert (BaseQuantity(1j, units.seconds) * BaseQuantity(1j, units.watts)).in_units_of(units.joules) == -1 + assert (Quantity(1j, units.seconds) * Quantity(1j, units.watts)).in_units_of(units.joules) == -1 def test_scalar_mul(): - assert (BaseQuantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 - assert (10 * BaseQuantity(1, units.seconds)).in_units_of(units.seconds) == 10 - assert (1000 * BaseQuantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 + assert (Quantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 + assert (10 * Quantity(1, units.seconds)).in_units_of(units.seconds) == 10 + assert (1000 * Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 def test_scalar_div(): - assert (BaseQuantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 - assert (10 / BaseQuantity(1, units.seconds)).in_units_of(units.hertz) == 10 - assert (0.001 / BaseQuantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 + assert (Quantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 + assert (10 / Quantity(1, units.seconds)).in_units_of(units.hertz) == 10 + assert (0.001 / Quantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 def test_good_add_sub(): """ Test that adding and subtracting units works """ - assert (BaseQuantity(1, units.seconds) + BaseQuantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 - assert (BaseQuantity(1, units.seconds) - BaseQuantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 + assert (Quantity(1, units.seconds) + Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 + assert (Quantity(1, units.seconds) - Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 - assert (BaseQuantity(1, units.inches) + BaseQuantity(1, units.feet)).in_units_of(units.inches) == 13 + assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == 13 @pytest.mark.parametrize("unit_1", si.all_si) @pytest.mark.parametrize("unit_2", si.all_si) def test_mixed_quantity_add_sub(unit_1, unit_2): if unit_1.equivalent(unit_2): - assert (BaseQuantity(0, unit_1) + BaseQuantity(0, unit_2)).in_units_of(unit_1) == 0 + assert (Quantity(0, unit_1) + Quantity(0, unit_2)).in_units_of(unit_1) == 0 else: with pytest.raises(UnitError): - BaseQuantity(1, unit_1) + BaseQuantity(1, unit_2) + Quantity(1, unit_1) + Quantity(1, unit_2) def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float, abs=1e-9): """ Helper function for testing units that are multiples of each other """ assert u1.equivalent(u2), "Units should be compatible for this test" - assert (BaseQuantity(1, u1) / BaseQuantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) + assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) def test_american_units(): @@ -76,7 +76,7 @@ def test_american_units(): assert_unit_ratio(units.pounds_force_per_square_inch, units.pounds_force / (units.inches**2), 1, abs=1e-5) def test_percent(): - assert BaseQuantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) + assert Quantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) @pytest.mark.parametrize("unit_1", si.all_si) @pytest.mark.parametrize("unit_2", si.all_si) @@ -84,9 +84,9 @@ def test_conversion_errors(unit_1, unit_2): """ Test conversion errors are thrown when units are not compatible """ if unit_1 == unit_2: - assert BaseQuantity(1, unit_1).in_units_of(unit_2) == 1 + assert Quantity(1, unit_1).in_units_of(unit_2) == 1 else: with pytest.raises(UnitError): - BaseQuantity(1, units.seconds).in_units_of(units.meters) + Quantity(1, units.seconds).in_units_of(units.meters) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 2071495e2..52a0c640f 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -3,6 +3,7 @@ from typing import Collection, Sequence, TypeVar, Generic, Self from dataclasses import dataclass +import numpy as np from numpy._typing import ArrayLike from sasdata.quantities.operations import Operation, Variable @@ -14,52 +15,154 @@ class UnitError(Exception): """Errors caused by unit specification not being correct""" +def hash_numpy_data(*data: np.ndarray): + + md5_hash = hashlib.md5() + + for datum in data: + data_bytes = datum.tobytes() + md5_hash.update(data_bytes) + + # Hash function returns a hex string, we want an int + return int(md5_hash.hexdigest(), 16) + QuantityType = TypeVar("QuantityType") -class BaseQuantity[QuantityType]: - def __init__(self, value: QuantityType, units: Unit): + +class QuantityHistory: + def __init__(self, operation_tree: Operation, references: dict[int, "Quantity"]): + self.operation_tree = operation_tree + self.references = references + + def jacobian(self) -> list[Operation]: + """ Derivative of this quantity's operation history with respect to each of the references """ + + # Use the hash value to specify the variable of differentiation + return [self.operation_tree.derivative(hash_value) for hash_value in self.references] + + def standard_error_propagate(self, covariances: dict[tuple[int, int]: "Quantity"] = {}): + """ Do standard error propagation to calculate the uncertainties associated with this quantity + + @param: covariances, off diagonal entries for the covariance matrix + """ + + jacobian = self.jacobian() + + # Evaluate the jacobian + evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] # should we use quantities here? + + output = 0 + + for hash_value in self.references: + output += evaluated_jacobian * (self.references[hash_value].variance * evaluated_jacobian) + + for (cov1, cov2) in covariances: + pass + + + + @staticmethod + def variable(quantity: "Quantity"): + """ Create a history that starts with the provided data """ + return QuantityHistory(Variable(quantity.hash_value), {quantity.hash_value: quantity}) + + @staticmethod + def _apply_operation(operation: type[Operation], *histories: "QuantityHistory") -> "QuantityHistory": + """ Apply an operation to the history + + This is slightly unsafe as it is possible to attempt to apply an n-ary operation to a number of trees other + than n, but it is relatively concise. Because it is concise we'll go with this for now and see if it causes + any problems down the line. It is a private static method to discourage misuse. + + """ + + # Copy references over, even though it overrides on collision, + # this should behave because only data based variables should be represented. + # Should not be a problem any more than losing histories + references = {} + for history in histories: + references.update(history.references) + + return QuantityHistory( + operation(*[history.operation_tree for history in histories]), + references) + + + +class Quantity[QuantityType]: + + + def __init__(self, + value: QuantityType, + units: Unit, + variance: QuantityType | None = None): + self.value = value + """ Numerical value of this data, in the specified units""" + self.units = units + """ Units of this data """ + + self.hash_value = -1 + """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ + + self._variance = variance + """ Contains the variance if it is data driven, else it is """ + + if variance is None: + self.hash_value = hash_numpy_data(value) + else: + self.hash_value = hash_numpy_data(value, variance.value) + + self.history = QuantityHistory.variable(self) + + @property + def variance(self) -> "Quantity": + pass + + def standard_deviation(self) -> "Quantity": + return self.variance ** (1/2) def in_units_of(self, units: Unit) -> QuantityType: + """ Get this quantity in other units """ if self.units.equivalent(units): return (self.units.scale / units.scale) * self.value else: raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") def __mul__(self: Self, other: ArrayLike | Self ) -> Self: - if isinstance(other, BaseQuantity): - return BaseQuantity(self.value * other.value, self.units * other.units) + if isinstance(other, Quantity): + return Quantity(self.value * other.value, self.units * other.units) else: - return BaseQuantity(self.value * other, self.units) + return Quantity(self.value * other, self.units) def __rmul__(self: Self, other: ArrayLike | Self): - if isinstance(other, BaseQuantity): - return BaseQuantity(other.value * self.value, other.units * self.units) + if isinstance(other, Quantity): + return Quantity(other.value * self.value, other.units * self.units) else: - return BaseQuantity(other * self.value, self.units) + return Quantity(other * self.value, self.units) def __truediv__(self: Self, other: float | Self) -> Self: - if isinstance(other, BaseQuantity): - return BaseQuantity(self.value / other.value, self.units / other.units) + if isinstance(other, Quantity): + return Quantity(self.value / other.value, self.units / other.units) else: - return BaseQuantity(self.value / other, self.units) + return Quantity(self.value / other, self.units) def __rtruediv__(self: Self, other: float | Self) -> Self: - if isinstance(other, BaseQuantity): - return BaseQuantity(self.value / other.value, self.units / other.units) + if isinstance(other, Quantity): + return Quantity(self.value / other.value, self.units / other.units) else: - return BaseQuantity(self.value / other, self.units) + return Quantity(self.value / other, self.units) def __add__(self: Self, other: Self | ArrayLike) -> Self: - if isinstance(other, BaseQuantity): + if isinstance(other, Quantity): if self.units.equivalent(other.units): - return BaseQuantity(self.value + (other.value * other.units.scale) / self.units.scale, self.units) + return Quantity(self.value + (other.value * other.units.scale) / self.units.scale, self.units) else: raise UnitError(f"Units do not have the same dimensionality: {self.units} vs {other.units}") @@ -69,7 +172,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: # Don't need __radd__ because only quantity/quantity operations should be allowed def __neg__(self): - return BaseQuantity(-self.value, self.units) + return Quantity(-self.value, self.units) def __sub__(self: Self, other: Self | ArrayLike) -> Self: return self + (-other) @@ -78,42 +181,27 @@ def __rsub__(self: Self, other: Self | ArrayLike) -> Self: return (-self) + other def __pow__(self: Self, other: int): - return BaseQuantity(self.value ** other, self.units ** other) + return Quantity(self.value ** other, self.units ** other) @staticmethod def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass -class Quantity[QuantityType](BaseQuantity[QuantityType]): - def with_uncertainty(self, uncertainty: BaseQuantity[QuantityType]): - return UncertainQuantity(self.value, self.units, uncertainty=uncertainty) +class NamedQuantity[QuantityType](Quantity[QuantityType]): + def __init__(self, + value: QuantityType, + units: Unit, + name: str, + variance: QuantityType | None = None): - -class NamedQuantity[QuantityType](BaseQuantity[QuantityType]): - def __init__(self, value: QuantityType, units: Unit, name: str): - super().__init__(value, units) + super().__init__(value, units, variance=variance) self.name = name - def with_uncertainty(self, uncertainty: BaseQuantity[QuantityType]): - return UncertainNamedQuantity(self.value, self.units, uncertainty=uncertainty, name=self.name) - - -class UncertainBaseQuantity[QuantityType](BaseQuantity[QuantityType]): - pass - -class UncertainQuantity[QuantityType](BaseQuantity[QuantityType]): - def __init__(self, value: QuantityType, units: Unit, uncertainty: BaseQuantity[QuantityType]): - super().__init__(value, units) - self.uncertainty = uncertainty - - hash_value = hashlib.md5(value, uncertainty) - - -class UncertainNamedQuantity[QuantityType](BaseQuantity[QuantityType]): - def __init__(self, value: QuantityType, units: Unit, uncertainty: BaseQuantity[QuantityType], name: str): - super().__init__(value, units) - self.uncertainty = uncertainty - self.name = name +class DerivedQuantity[QuantityType](Quantity[QuantityType]): + def __init__(self, value, units, variance, history): - self.history = Variable(self.name) \ No newline at end of file + self._variance_cache = None + @property + def variance(self): + pass \ No newline at end of file diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 2da7b4537..da19d67d5 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -1952,6 +1952,10 @@ def __init__(self, name: str, units: list[NamedUnit]): "a.u.": atomic_mass_units, "amu": atomic_mass_units, "degr": degrees, + "Counts": none, + "counts": none, + "cnts": none, + "Cnts": none, } diff --git a/sasdata/transforms/operation.py b/sasdata/transforms/operation.py index 65ef6e604..c06bb379a 100644 --- a/sasdata/transforms/operation.py +++ b/sasdata/transforms/operation.py @@ -1,5 +1,5 @@ import numpy as np -from sasdata.quantities.quantity import BaseQuantity +from sasdata.quantities.quantity import Quantity class Operation: """ Sketch of what model post-processing classes might look like """ @@ -11,7 +11,7 @@ class Operation: def name(self) -> str: raise NotImplementedError("No name for transform") - def evaluate(self) -> BaseQuantity[np.ndarray]: + def evaluate(self) -> Quantity[np.ndarray]: pass def __call__(self, *children, **named_children): From 86bd4048d9ea6308f968f89bdad49b48a5ca4b27 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 23 Sep 2024 16:47:39 +0100 Subject: [PATCH 580/675] Integer unit powers now work --- sasdata/quantities/_units_base.py | 6 +-- sasdata/quantities/operations.py | 2 - sasdata/quantities/quantities_tests.py | 26 +++++++++++++ sasdata/quantities/quantity.py | 8 ++-- sasdata/quantities/units.py | 54 ++++++++++++++++++++------ 5 files changed, 76 insertions(+), 20 deletions(-) diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 8f62f1f36..cd610f9c3 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -74,7 +74,7 @@ def __pow__(self, power: int | float): if not isinstance(power, (int, float)): return NotImplemented - frac = Fraction(power) + frac = Fraction(power).limit_denominator(500) # Probably way bigger than needed, 10 would probably be fine denominator = frac.denominator numerator = frac.numerator @@ -202,8 +202,8 @@ def __rtruediv__(self: Self, other: "Unit"): else: return NotImplemented - def __pow__(self, power: int): - if not isinstance(power, int): + def __pow__(self, power: int | float): + if not isinstance(power, int | float): return NotImplemented return Unit(self.scale**power, self.dimensions**power) diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py index 1ca284b27..d6ee50356 100644 --- a/sasdata/quantities/operations.py +++ b/sasdata/quantities/operations.py @@ -2,8 +2,6 @@ import json -from sasdata.quantities.quantity import Quantity - T = TypeVar("T") def hash_and_name(hash_or_name: int | str): diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index 7c9181583..5eb2ac36b 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -51,6 +51,32 @@ def test_good_add_sub(): assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == 13 +@pytest.mark.parametrize("unit_in, power, unit_out", [ + (units.meters**2, 1/2, units.meters), + (units.meters**3, 1/3, units.meters), + (units.meters**3, 2/3, units.meters**2), + (units.meters**3, -5/3, units.meters**-5), + (units.none, 1/10, units.none), + (units.none, 19/17, units.none), + (units.none, np.pi, units.none) +]) +def test_good_non_integer_unit_powers(unit_in, power, unit_out): + """ Check that we can do various square and cube root stuff if we need to, + If dimensionless, we should be able to do arbitrary powers + """ + assert unit_in**power == unit_out + +@pytest.mark.parametrize("unit, power", [ + (units.meters, 1/2), + (units.milliohms, 1/3), + (units.meters, 3/2), + (units.meters**2, 2/3) +]) +def test_bad_non_integer_unit_powers(unit, power): + """ Check that we get an error if we try and do something silly with powers""" + with pytest.raises(units.DimensionError): + x = unit**power + @pytest.mark.parametrize("unit_1", si.all_si) @pytest.mark.parametrize("unit_2", si.all_si) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 52a0c640f..99965787f 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -15,12 +15,12 @@ class UnitError(Exception): """Errors caused by unit specification not being correct""" -def hash_numpy_data(*data: np.ndarray): +def hash_data_via_numpy(*data: ArrayLike): md5_hash = hashlib.md5() for datum in data: - data_bytes = datum.tobytes() + data_bytes = np.array(datum).tobytes() md5_hash.update(data_bytes) # Hash function returns a hex string, we want an int @@ -111,9 +111,9 @@ def __init__(self, """ Contains the variance if it is data driven, else it is """ if variance is None: - self.hash_value = hash_numpy_data(value) + self.hash_value = hash_data_via_numpy(value) else: - self.hash_value = hash_numpy_data(value, variance.value) + self.hash_value = hash_data_via_numpy(value, variance.value) self.history = QuantityHistory.variable(self) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index da19d67d5..33edc9354 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -84,11 +84,15 @@ from dataclasses import dataclass from typing import Sequence, Self, TypeVar +from fractions import Fraction import numpy as np from sasdata.quantities.unicode_superscript import int_as_unicode_superscript +class DimensionError(Exception): + pass + class Dimensions: """ @@ -149,19 +153,46 @@ def __truediv__(self: Self, other: Self): self.moles_hint - other.moles_hint, self.angle_hint - other.angle_hint) - def __pow__(self, power: int): + def __pow__(self, power: int | float): - if not isinstance(power, int): + if not isinstance(power, (int, float)): return NotImplemented + frac = Fraction(power).limit_denominator(500) # Probably way bigger than needed, 10 would probably be fine + denominator = frac.denominator + numerator = frac.numerator + + # Throw errors if dimension is not a multiple of the denominator + + if self.length % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with length dimensionality {self.length}") + + if self.time % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with time dimensionality {self.time}") + + if self.mass % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with mass dimensionality {self.mass}") + + if self.current % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with current dimensionality {self.current}") + + if self.temperature % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with temperature dimensionality {self.temperature}") + + if self.moles_hint % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with moles hint dimensionality of {self.moles_hint}") + + if self.angle_hint % denominator != 0: + raise DimensionError(f"Cannot apply power of {frac} to unit with angle hint dimensionality of {self.angle_hint}") + return Dimensions( - self.length * power, - self.time * power, - self.mass * power, - self.current * power, - self.temperature * power, - self.moles_hint * power, - self.angle_hint * power) + (self.length * numerator) // denominator, + (self.time * numerator) // denominator, + (self.mass * numerator) // denominator, + (self.current * numerator) // denominator, + (self.temperature * numerator) // denominator, + (self.moles_hint * numerator) // denominator, + (self.angle_hint * numerator) // denominator) def __eq__(self: Self, other: Self): if isinstance(other, Dimensions): @@ -255,12 +286,13 @@ def __rtruediv__(self: Self, other: "Unit"): else: return NotImplemented - def __pow__(self, power: int): - if not isinstance(power, int): + def __pow__(self, power: int | float): + if not isinstance(power, int | float): return NotImplemented return Unit(self.scale**power, self.dimensions**power) + def equivalent(self: Self, other: "Unit"): return self.dimensions == other.dimensions From c88b808598a273beef475c12b97331d9e28f9ca0 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 25 Sep 2024 15:26:25 +0100 Subject: [PATCH 581/675] Quantities now have histories, and variance could work, needs testing though --- sasdata/quantities/notes.rst | 10 ++- sasdata/quantities/quantity.py | 129 ++++++++++++++++++++++++++------- 2 files changed, 112 insertions(+), 27 deletions(-) diff --git a/sasdata/quantities/notes.rst b/sasdata/quantities/notes.rst index cc34a41e3..a9a2b58ea 100644 --- a/sasdata/quantities/notes.rst +++ b/sasdata/quantities/notes.rst @@ -1,3 +1,11 @@ +Mutability +---------- + +DataSets: Immutable +Quantities: Immutable +Units: Hard coded + + Identifying of Quantities -------------------- @@ -6,4 +14,4 @@ Either we give them names, in which case we risk collisions, or we use hashes, w have issues with things not being identified correctly. The decision here is to use hashes of the data, not names, because it would be too easy to -give different things the same name. +give different things the same name. \ No newline at end of file diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 99965787f..9f6bc585a 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -6,7 +6,8 @@ import numpy as np from numpy._typing import ArrayLike -from sasdata.quantities.operations import Operation, Variable +from quantities.operations import Operation, Variable +from quantities import operations from sasdata.quantities.units import Unit import hashlib @@ -47,19 +48,25 @@ def standard_error_propagate(self, covariances: dict[tuple[int, int]: "Quantity" @param: covariances, off diagonal entries for the covariance matrix """ + if covariances: + raise NotImplementedError("User specified covariances not currently implemented") + jacobian = self.jacobian() # Evaluate the jacobian - evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] # should we use quantities here? - - output = 0 + # TODO: should we use quantities here, does that work automatically? + evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] - for hash_value in self.references: - output += evaluated_jacobian * (self.references[hash_value].variance * evaluated_jacobian) + hash_values = [key for key in self.references] + output = None - for (cov1, cov2) in covariances: - pass + for hash_value, jac_component in zip(hash_values, evaluated_jacobian): + if output is None: + output = jac_component * (self.references[hash_value].variance * jac_component) + else: + output += jac_component * (self.references[hash_value].variance * jac_component) + return output @staticmethod @@ -68,7 +75,7 @@ def variable(quantity: "Quantity"): return QuantityHistory(Variable(quantity.hash_value), {quantity.hash_value: quantity}) @staticmethod - def _apply_operation(operation: type[Operation], *histories: "QuantityHistory") -> "QuantityHistory": + def apply_operation(operation: type[Operation], *histories: "QuantityHistory") -> "QuantityHistory": """ Apply an operation to the history This is slightly unsafe as it is possible to attempt to apply an n-ary operation to a number of trees other @@ -119,10 +126,14 @@ def __init__(self, @property def variance(self) -> "Quantity": - pass + """ Get the variance of this object""" + if self._variance is None: + return Quantity(np.zeros_like(self.value), self.units**2) + else: + return Quantity(self._variance, self.units**2) def standard_deviation(self) -> "Quantity": - return self.variance ** (1/2) + return self.variance ** 0.5 def in_units_of(self, units: Unit) -> QuantityType: """ Get this quantity in other units """ @@ -133,36 +144,86 @@ def in_units_of(self, units: Unit) -> QuantityType: def __mul__(self: Self, other: ArrayLike | Self ) -> Self: if isinstance(other, Quantity): - return Quantity(self.value * other.value, self.units * other.units) + return DerivedQuantity( + self.value * other.value, + self.units * other.units, + history=QuantityHistory.apply_operation(operations.Mul, self.history, other.history)) else: - return Quantity(self.value * other, self.units) + return DerivedQuantity(self.value * other, self.units, + QuantityHistory( + operations.Mul( + self.history.operation_tree, + operations.Constant(other)), + self.history.references)) def __rmul__(self: Self, other: ArrayLike | Self): if isinstance(other, Quantity): - return Quantity(other.value * self.value, other.units * self.units) + return DerivedQuantity( + other.value * self.value, + other.units * self.units, + history=QuantityHistory.apply_operation( + operations.Mul, + other.history, + self.history)) else: - return Quantity(other * self.value, self.units) + return DerivedQuantity(other * self.value, self.units, + QuantityHistory( + operations.Mul( + operations.Constant(other), + self.history.operation_tree), + self.history.references)) def __truediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): - return Quantity(self.value / other.value, self.units / other.units) + return DerivedQuantity( + self.value / other.value, + self.units / other.units, + history=QuantityHistory.apply_operation( + operations.Div, + self.history, + other.history)) else: - return Quantity(self.value / other, self.units) + return DerivedQuantity(self.value / other, self.units, + QuantityHistory( + operations.Div( + operations.Constant(other), + self.history.operation_tree), + self.history.references)) def __rtruediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): - return Quantity(self.value / other.value, self.units / other.units) + return DerivedQuantity( + other.value / self.value, + other.units / self.units, + history=QuantityHistory.apply_operation( + operations.Div, + other.history, + self.history + )) else: - return Quantity(self.value / other, self.units) + return DerivedQuantity( + other / self.value, + self.units ** -1, + QuantityHistory( + operations.Div( + operations.Constant(other), + self.history.operation_tree), + self.history.references)) def __add__(self: Self, other: Self | ArrayLike) -> Self: if isinstance(other, Quantity): if self.units.equivalent(other.units): - return Quantity(self.value + (other.value * other.units.scale) / self.units.scale, self.units) + return DerivedQuantity( + self.value + (other.value * other.units.scale) / self.units.scale, + self.units, + QuantityHistory.apply_operation( + operations.Add, + self.history, + other.history)) else: raise UnitError(f"Units do not have the same dimensionality: {self.units} vs {other.units}") @@ -172,7 +233,11 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: # Don't need __radd__ because only quantity/quantity operations should be allowed def __neg__(self): - return Quantity(-self.value, self.units) + return DerivedQuantity(-self.value, self.units, + QuantityHistory.apply_operation( + operations.Neg, + self.history + )) def __sub__(self: Self, other: Self | ArrayLike) -> Self: return self + (-other) @@ -180,8 +245,14 @@ def __sub__(self: Self, other: Self | ArrayLike) -> Self: def __rsub__(self: Self, other: Self | ArrayLike) -> Self: return (-self) + other - def __pow__(self: Self, other: int): - return Quantity(self.value ** other, self.units ** other) + def __pow__(self: Self, other: int | float): + return DerivedQuantity(self.value ** other, + self.units ** other, + QuantityHistory( + operations.Pow( + self.history.operation_tree, + other), + self.history.references)) @staticmethod def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): @@ -199,9 +270,15 @@ def __init__(self, self.name = name class DerivedQuantity[QuantityType](Quantity[QuantityType]): - def __init__(self, value, units, variance, history): + def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): + super().__init__(value, units, variance=None) + self.history = history self._variance_cache = None + @property - def variance(self): - pass \ No newline at end of file + def variance(self) -> Quantity: + if self._variance_cache is None: + self._variance_cache = self.history.standard_error_propagate() + + return self._variance_cache \ No newline at end of file From 63b8c410b334162291a84032a3741560287fcfd7 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 26 Sep 2024 10:21:33 +0100 Subject: [PATCH 582/675] Quantities ready for testing --- sasdata/quantities/quantity_examples.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sasdata/quantities/quantity_examples.py b/sasdata/quantities/quantity_examples.py index 8c505e59e..91b49c84a 100644 --- a/sasdata/quantities/quantity_examples.py +++ b/sasdata/quantities/quantity_examples.py @@ -1,8 +1,9 @@ -from sasdata.quantities.quantity import Quantity, NamedQuantity +from sasdata.quantities.quantity import Quantity from sasdata.quantities import units -x = NamedQuantity("x", 1, units.meters, standard_error=1) -y = NamedQuantity("y", 1, units.decimeters, standard_error=1) +x = Quantity(1, units.meters, variance=1) +y = Quantity(1, units.meters, variance=1) -print(x+y) -print((x+y).to_units_of(units.centimeters)) \ No newline at end of file +z = x+y + +print(z) From 2caadc77feaea9bfc5f1b7e393d85d0f24bb66ef Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 27 Sep 2024 13:26:26 +0100 Subject: [PATCH 583/675] Quantity combining seems to work --- sasdata/quantities/_units_base.py | 37 +++++++++- sasdata/quantities/notes.rst | 6 ++ sasdata/quantities/operations.py | 20 ++--- sasdata/quantities/quantity.py | 98 +++++++++++++++++++++++-- sasdata/quantities/quantity_examples.py | 11 ++- sasdata/quantities/units.py | 37 +++++++++- sasdata/raw_form.py | 2 + 7 files changed, 187 insertions(+), 24 deletions(-) diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index cd610f9c3..1ca8ba5f5 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -171,6 +171,36 @@ def __repr__(self): return s + def si_repr(self): + s = "" + for name, size in [ + ("kg", self.mass), + ("m", self.length), + ("s", self.time), + ("A", self.current), + ("K", self.temperature), + ("mol", self.moles_hint)]: + + if size == 0: + pass + elif size == 1: + s += f"{name}" + else: + s += f"{name}{int_as_unicode_superscript(size)}" + + match self.angle_hint: + case 0: + pass + case 2: + s += "sr" + case -2: + s += "sr" + int_as_unicode_superscript(-1) + case _: + s += "rad" + int_as_unicode_superscript(self.angle_hint) + + return s + + class Unit: def __init__(self, si_scaling_factor: float, @@ -224,7 +254,12 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): pass def __repr__(self): - return f"Unit[{self.scale}, {self.dimensions}]" + if self.scale == 1: + # We're in SI + return self.dimensions.si_repr() + + else: + return f"Unit[{self.scale}, {self.dimensions}]" @staticmethod def parse(unit_string: str) -> "Unit": diff --git a/sasdata/quantities/notes.rst b/sasdata/quantities/notes.rst index a9a2b58ea..9c4f47c43 100644 --- a/sasdata/quantities/notes.rst +++ b/sasdata/quantities/notes.rst @@ -5,6 +5,12 @@ DataSets: Immutable Quantities: Immutable Units: Hard coded +Quantity methods +---------------- + +in_* methods return numbers/arrays in a given unit system +to_* converts to different units + Identifying of Quantities -------------------- diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py index d6ee50356..724e55d0e 100644 --- a/sasdata/quantities/operations.py +++ b/sasdata/quantities/operations.py @@ -73,21 +73,21 @@ def derivative(self, variable: Union[str, int, "Variable"], simplify=True): derivative_string = derivative.serialise() - print("---------------") - print("Base") - print("---------------") - print(derivative.summary()) + # print("---------------") + # print("Base") + # print("---------------") + # print(derivative.summary()) # Inefficient way of doing repeated simplification, but it will work for i in range(100): # set max iterations derivative = derivative._clean() - - print("-------------------") - print("Iteration", i+1) - print("-------------------") - print(derivative.summary()) - print("-------------------") + # + # print("-------------------") + # print("Iteration", i+1) + # print("-------------------") + # print(derivative.summary()) + # print("-------------------") new_derivative_string = derivative.serialise() diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 9f6bc585a..7ce728f00 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -7,7 +7,7 @@ from numpy._typing import ArrayLike from quantities.operations import Operation, Variable -from quantities import operations +from quantities import operations, units from sasdata.quantities.units import Unit import hashlib @@ -95,6 +95,12 @@ def apply_operation(operation: type[Operation], *histories: "QuantityHistory") - operation(*[history.operation_tree for history in histories]), references) + def has_variance(self): + for key in self.references: + if self.references[key].has_variance: + return True + + return False class Quantity[QuantityType]: @@ -103,7 +109,8 @@ class Quantity[QuantityType]: def __init__(self, value: QuantityType, units: Unit, - variance: QuantityType | None = None): + variance: QuantityType | None = None, + hash_seed = ""): self.value = value """ Numerical value of this data, in the specified units""" @@ -118,12 +125,16 @@ def __init__(self, """ Contains the variance if it is data driven, else it is """ if variance is None: - self.hash_value = hash_data_via_numpy(value) + self.hash_value = hash_data_via_numpy(hash_seed, value) else: - self.hash_value = hash_data_via_numpy(value, variance.value) + self.hash_value = hash_data_via_numpy(hash_seed, value, variance) self.history = QuantityHistory.variable(self) + @property + def has_variance(self): + return self._variance is not None + @property def variance(self) -> "Quantity": """ Get the variance of this object""" @@ -142,6 +153,35 @@ def in_units_of(self, units: Unit) -> QuantityType: else: raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") + def variance_in_units_of(self, units: Unit) -> QuantityType: + """ Get the variance of quantity in other units """ + variance = self.variance + if variance.units.equivalent(units): + return (variance.units.scale / units.scale) * variance + else: + raise UnitError(f"Target units ({units}) not compatible with existing units ({variance.units}).") + + def in_si(self): + si_units = self.units.si_equivalent() + return self.in_units_of(si_units) + + def in_units_of_with_standard_error(self, units): + variance = self.variance + units_squared = units**2 + + if variance.units.equivalent(units_squared): + scale_factor = self.units.scale / units.scale + + return scale_factor*self.value, scale_factor * np.sqrt(self.variance.in_units_of(units_squared)) + else: + raise UnitError(f"Target units ({units}) not compatible with existing units ({variance.units}).") + + def in_si_with_standard_error(self): + if self.has_variance: + return self.in_units_of_with_standard_error(self.units.si_equivalent()) + else: + return self.in_si(), None + def __mul__(self: Self, other: ArrayLike | Self ) -> Self: if isinstance(other, Quantity): return DerivedQuantity( @@ -254,6 +294,44 @@ def __pow__(self: Self, other: int | float): other), self.history.references)) + @staticmethod + def _array_repr_format(arr: np.ndarray): + """ Format the array """ + order = len(arr.shape) + reshaped = arr.reshape(-1) + if len(reshaped) > 4: + numbers = ",".join([f"{n}" for n in reshaped]) + else: + numbers = f"{reshaped[0]}, {reshaped[1]} ... {reshaped[-2]}, {reshaped[-1]}" + + return "["*order + numbers + "]"*order + + def __repr__(self): + + if isinstance(self.units, units.NamedUnit): + + value = self.value + error = np.sqrt(self.standard_deviation().value) + unit_string = self.units.symbol + + else: + value, error = self.in_si_with_standard_error() + unit_string = self.units.dimensions.si_repr() + + if isinstance(self.value, np.ndarray): + # Get the array in short form + numeric_string = self._array_repr_format(value) + + if self.has_variance: + numeric_string += " ± " + self._array_repr_format(error) + + else: + numeric_string = f"{value}" + if self.has_variance: + numeric_string += f" ± {error}" + + return numeric_string + " " + unit_string + @staticmethod def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass @@ -261,20 +339,28 @@ def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: Fa class NamedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, + name: str, value: QuantityType, units: Unit, - name: str, variance: QuantityType | None = None): - super().__init__(value, units, variance=variance) + super().__init__(value, units, variance=variance, hash_seed=name) self.name = name + def __repr__(self): + return f"[{self.name}] " + super().__repr__() + class DerivedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): super().__init__(value, units, variance=None) self.history = history self._variance_cache = None + self._has_variance = history.has_variance() + + @property + def has_variance(self): + return self._has_variance @property def variance(self) -> Quantity: diff --git a/sasdata/quantities/quantity_examples.py b/sasdata/quantities/quantity_examples.py index 91b49c84a..9ccb77bbd 100644 --- a/sasdata/quantities/quantity_examples.py +++ b/sasdata/quantities/quantity_examples.py @@ -1,9 +1,8 @@ -from sasdata.quantities.quantity import Quantity +from sasdata.quantities.quantity import Quantity, NamedQuantity from sasdata.quantities import units -x = Quantity(1, units.meters, variance=1) -y = Quantity(1, units.meters, variance=1) +x = NamedQuantity("x", 1, units.meters, variance=1) +y = NamedQuantity("y", 1, units.meters, variance=1) -z = x+y - -print(z) +print(x+y) +print(x+x) \ No newline at end of file diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 33edc9354..ebe738331 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -255,6 +255,36 @@ def __repr__(self): return s + def si_repr(self): + s = "" + for name, size in [ + ("kg", self.mass), + ("m", self.length), + ("s", self.time), + ("A", self.current), + ("K", self.temperature), + ("mol", self.moles_hint)]: + + if size == 0: + pass + elif size == 1: + s += f"{name}" + else: + s += f"{name}{int_as_unicode_superscript(size)}" + + match self.angle_hint: + case 0: + pass + case 2: + s += "sr" + case -2: + s += "sr" + int_as_unicode_superscript(-1) + case _: + s += "rad" + int_as_unicode_superscript(self.angle_hint) + + return s + + class Unit: def __init__(self, si_scaling_factor: float, @@ -308,7 +338,12 @@ def _format_unit(self, format_process: list["UnitFormatProcessor"]): pass def __repr__(self): - return f"Unit[{self.scale}, {self.dimensions}]" + if self.scale == 1: + # We're in SI + return self.dimensions.si_repr() + + else: + return f"Unit[{self.scale}, {self.dimensions}]" @staticmethod def parse(unit_string: str) -> "Unit": diff --git a/sasdata/raw_form.py b/sasdata/raw_form.py index a58c09ce5..9519dead8 100644 --- a/sasdata/raw_form.py +++ b/sasdata/raw_form.py @@ -5,6 +5,8 @@ DataType = TypeVar("DataType") +""" Sasdata metadata tree """ + def shorten_string(string): lines = string.split("\n") if len(lines) <= 1: From a90a92dd2e9fe8814b71e825ad1c67192d474bd3 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 27 Sep 2024 16:46:49 +0100 Subject: [PATCH 584/675] Fixed error in helper function --- sasdata/quantities/quantities_tests.py | 8 +- sasdata/quantities/quantity.py | 44 ++++--- sasdata/quantities/quantity_error_tests.py | 134 --------------------- sasdata/quantities/quantity_examples.py | 7 +- 4 files changed, 39 insertions(+), 154 deletions(-) diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py index 5eb2ac36b..8ab0a41ce 100644 --- a/sasdata/quantities/quantities_tests.py +++ b/sasdata/quantities/quantities_tests.py @@ -17,6 +17,12 @@ def test_unit_compounding_pow(): assert (Quantity(1, units.millimeters) ** 2).in_units_of(units.square_meters) == 1e-6 assert (Quantity(1, units.minutes) ** 3).in_units_of(units.seconds ** 3) == 60 ** 3 +def test_pow_scaling(): + q2 = Quantity(1000, units.millimeters)**2 + assert q2.units.scale == 1e-6 + assert q2.value == 1e6 + + def test_unit_compounding_mul(): """ Test units compound correctly when __mul__ is used""" assert (Quantity(4, units.minutes) * Quantity(0.25, units.hertz)).in_units_of(units.none) == 60 @@ -49,7 +55,7 @@ def test_good_add_sub(): assert (Quantity(1, units.seconds) + Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 assert (Quantity(1, units.seconds) - Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 - assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == 13 + assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == pytest.approx(13, abs=1e-8) @pytest.mark.parametrize("unit_in, power, unit_out", [ (units.meters**2, 1/2, units.meters), diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 7ce728f00..9295d4972 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -36,30 +36,42 @@ def __init__(self, operation_tree: Operation, references: dict[int, "Quantity"]) self.operation_tree = operation_tree self.references = references + self.reference_key_list = [key for key in self.references] + self.si_reference_values = {key: self.references[key].in_si() for key in self.references} + def jacobian(self) -> list[Operation]: """ Derivative of this quantity's operation history with respect to each of the references """ # Use the hash value to specify the variable of differentiation - return [self.operation_tree.derivative(hash_value) for hash_value in self.references] + return [self.operation_tree.derivative(key) for key in self.reference_key_list] - def standard_error_propagate(self, covariances: dict[tuple[int, int]: "Quantity"] = {}): + def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, int]: "Quantity"] = {}): """ Do standard error propagation to calculate the uncertainties associated with this quantity - @param: covariances, off diagonal entries for the covariance matrix + :param quantity_units: units in which the output should be calculated + :param covariances: off diagonal entries for the covariance matrix """ if covariances: raise NotImplementedError("User specified covariances not currently implemented") jacobian = self.jacobian() + # jacobian_units = [quantity_units / self.references[key].units for key in self.reference_key_list] + # + # # Evaluate the jacobian + # # TODO: should we use quantities here, does that work automatically? + # evaluated_jacobian = [Quantity( + # value=entry.evaluate(self.si_reference_values), + # units=unit.si_equivalent()) + # for entry, unit in zip(jacobian, jacobian_units)] - # Evaluate the jacobian - # TODO: should we use quantities here, does that work automatically? evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] hash_values = [key for key in self.references] output = None + print(evaluated_jacobian) + for hash_value, jac_component in zip(hash_values, evaluated_jacobian): if output is None: output = jac_component * (self.references[hash_value].variance * jac_component) @@ -109,7 +121,7 @@ class Quantity[QuantityType]: def __init__(self, value: QuantityType, units: Unit, - variance: QuantityType | None = None, + standard_error: QuantityType | None = None, hash_seed = ""): self.value = value @@ -121,13 +133,14 @@ def __init__(self, self.hash_value = -1 """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ - self._variance = variance """ Contains the variance if it is data driven, else it is """ - if variance is None: + if standard_error is None: + self._variance = None self.hash_value = hash_data_via_numpy(hash_seed, value) else: - self.hash_value = hash_data_via_numpy(hash_seed, value, variance) + self._variance = standard_error ** 2 + self.hash_value = hash_data_via_numpy(hash_seed, value, standard_error) self.history = QuantityHistory.variable(self) @@ -170,9 +183,8 @@ def in_units_of_with_standard_error(self, units): units_squared = units**2 if variance.units.equivalent(units_squared): - scale_factor = self.units.scale / units.scale - return scale_factor*self.value, scale_factor * np.sqrt(self.variance.in_units_of(units_squared)) + return self.in_units_of(units), np.sqrt(self.variance.in_units_of(units_squared)) else: raise UnitError(f"Target units ({units}) not compatible with existing units ({variance.units}).") @@ -311,7 +323,7 @@ def __repr__(self): if isinstance(self.units, units.NamedUnit): value = self.value - error = np.sqrt(self.standard_deviation().value) + error = self.standard_deviation().value unit_string = self.units.symbol else: @@ -342,9 +354,9 @@ def __init__(self, name: str, value: QuantityType, units: Unit, - variance: QuantityType | None = None): + standard_error: QuantityType | None = None): - super().__init__(value, units, variance=variance, hash_seed=name) + super().__init__(value, units, standard_error=standard_error, hash_seed=name) self.name = name def __repr__(self): @@ -352,7 +364,7 @@ def __repr__(self): class DerivedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): - super().__init__(value, units, variance=None) + super().__init__(value, units, standard_error=None) self.history = history self._variance_cache = None @@ -365,6 +377,6 @@ def has_variance(self): @property def variance(self) -> Quantity: if self._variance_cache is None: - self._variance_cache = self.history.standard_error_propagate() + self._variance_cache = self.history.variance_propagate(self.units) return self._variance_cache \ No newline at end of file diff --git a/sasdata/quantities/quantity_error_tests.py b/sasdata/quantities/quantity_error_tests.py index e8e9378fb..9aae713f2 100644 --- a/sasdata/quantities/quantity_error_tests.py +++ b/sasdata/quantities/quantity_error_tests.py @@ -18,137 +18,3 @@ def test_addition_propagation(x_err, y_err, x_units, y_units): _, err = (x + y).in_si_with_standard_error() assert err == pytest.approx(expected_err, abs=1e-8) - -@pytest.mark.parametrize("x_val, y_val, x_units, y_units", - [(1, 1, units.meters, units.meters), - (1, 1, units.centimeters, units.centimeters), - (2, 2, units.meters, units.meters), - (1, 2, units.centimeters, units.centimeters), - (1, 2, units.meters, units.millimeters), - (3, 4, units.milliseconds, units.microseconds), - (0, 1, units.meters, units.meters)]) -def test_asymmetry_propagation(x_val, y_val, x_units, y_units): - - x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(x_val)) - y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(y_val)) - - x_si, x_err = x.in_si_with_standard_error() - y_si, y_err = y.in_si_with_standard_error() - - numerator = x-y - denominator = x+y - a = numerator/denominator - - # Check numerator and denominator - expected_error = np.sqrt(x_err ** 2 + y_err ** 2) - - value, error = numerator.in_si_with_standard_error() - assert error == pytest.approx(expected_error, rel=1e-6) - - value, error = denominator.in_si_with_standard_error() - assert error == pytest.approx(expected_error, rel=1e-6) - - # check whole thing - value, error = a.in_si_with_standard_error() - expected_error = (2 / (x_si + y_si)**2) * np.sqrt((x_err*y_si)**2 + (y_err*x_si)**2) - assert error == pytest.approx(expected_error, rel=1e-6) - -@pytest.mark.parametrize("x_val, y_val, x_units, y_units", - [(1, 1, units.meters, units.meters), - (1, 1, units.centimeters, units.centimeters), - (2, 2, units.meters, units.meters), - (1, 2, units.centimeters, units.centimeters), - (1, 2, units.meters, units.millimeters), - (3, 4, units.milliseconds, units.microseconds), - (0, 1, units.meters, units.meters)]) -def test_power_propagation(x_val, y_val, x_units, y_units): - - x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(x_val)) - y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(y_val)) - - x_si, x_err = x.in_si_with_standard_error() - y_si, y_err = y.in_si_with_standard_error() - - x_var = x_err ** 2 - y_var = y_err ** 2 - - z = (x*y)**3 - - # check whole thing - value, error = z.in_si_with_standard_error() - expected_variance = 9*((x_si*y_si)**4)*(x_var*y_si*y_si + x_si*x_si*y_var) - assert error == pytest.approx(np.sqrt(expected_variance), rel=1e-6) - -@pytest.mark.parametrize("k", [0.1, 0.5, 1, 2, 10]) -@pytest.mark.parametrize("x_val, y_val, x_units, y_units", - [(1, 1, units.meters, units.meters), - (1, 1, units.centimeters, units.centimeters), - (2, 2, units.meters, units.meters), - (1, 2, units.centimeters, units.centimeters), - (1, 2, units.meters, units.millimeters), - (3, 4, units.milliseconds, units.microseconds), - (0, 1, units.meters, units.meters), - (0, 0, units.meters, units.meters)]) -def test_complex_power_propagation(x_val, y_val, x_units, y_units, k): - - x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(k*x_val)) - y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(k*y_val)) - - x_si, x_err = x.in_si_with_standard_error() - y_si, y_err = y.in_si_with_standard_error() - - x_var = x_err ** 2 - y_var = y_err ** 2 - - z = (x+y)**3 + x**3 + y**3 - - value, error = z.in_si_with_standard_error() - expected_variance = \ - 9*x_var*(x_si**2 + (x_si+y_si)**2)**2 + \ - 9*y_var*(y_si**2 + (x_si+y_si)**2)**2 - - assert error == pytest.approx(np.sqrt(expected_variance), rel=1e-6) - -@pytest.mark.parametrize("k_x", [0.1, 0.5, 1, 2, 10]) -@pytest.mark.parametrize("k_y", [0.1, 0.5, 1, 2, 10]) -@pytest.mark.parametrize("x_val, y_val, x_units, y_units", - [(1, 1, units.meters, units.meters), - (1, 1, units.centimeters, units.centimeters), - (2, 2, units.meters, units.meters), - (1, 2, units.centimeters, units.centimeters), - (1, 2, units.meters, units.millimeters), - (3, 4, units.milliseconds, units.microseconds), - (0, 1, units.meters, units.meters), - (0, 0, units.meters, units.meters)]) -def test_complex_propagation(x_val, y_val, x_units, y_units, k_x, k_y): - - x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(k_x*x_val)) - y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(k_y*y_val)) - - cx = NamedQuantity("cx", 1.7, x_units) - cy = NamedQuantity("cy", 1.2, y_units) - c0 = 4*NamedQuantity("c0", value=7, units=units.none) - - cx_si = cx.in_si() - cy_si = cy.in_si() - - c0_si = c0.in_si() - - x_si, x_err = x.in_si_with_standard_error() - y_si, y_err = y.in_si_with_standard_error() - - x_var = x_err ** 2 - y_var = y_err ** 2 - - z = (((x-cx)**4 + (y-cy)**4)**(1/4)) + c0*(-x-y) - - value, error = z.in_si_with_standard_error() - - denom_factor = ((x_si - cx_si)**4 + (y_si - cy_si)**4)**(-3/4) - x_num = (cx_si - x_si)**3 - y_num = (cy_si - y_si)**3 - - expected_variance = x_var*(c0_si + x_num*denom_factor)**2 + y_var*(c0_si + y_num*denom_factor)**2 - - assert error == pytest.approx(np.sqrt(expected_variance), rel=1e-8) - diff --git a/sasdata/quantities/quantity_examples.py b/sasdata/quantities/quantity_examples.py index 9ccb77bbd..89ebb6a5c 100644 --- a/sasdata/quantities/quantity_examples.py +++ b/sasdata/quantities/quantity_examples.py @@ -1,8 +1,9 @@ from sasdata.quantities.quantity import Quantity, NamedQuantity from sasdata.quantities import units -x = NamedQuantity("x", 1, units.meters, variance=1) -y = NamedQuantity("y", 1, units.meters, variance=1) +x = NamedQuantity("x", 1, units.meters, standard_error=1) +y = NamedQuantity("y", 1, units.decimeters, standard_error=1) print(x+y) -print(x+x) \ No newline at end of file +print(x+x) +print(y+y) \ No newline at end of file From 40b720960ccf3c876dcc322b5d4541a622a5aaad Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 27 Sep 2024 17:09:04 +0100 Subject: [PATCH 585/675] Fixed error formatting bug --- sasdata/quantities/quantity.py | 35 +++++++++++++++++++++---- sasdata/quantities/quantity_examples.py | 3 +-- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 9295d4972..517b5cbfb 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -8,7 +8,7 @@ from quantities.operations import Operation, Variable from quantities import operations, units -from sasdata.quantities.units import Unit +from sasdata.quantities.units import Unit, NamedUnit import hashlib @@ -70,8 +70,6 @@ def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, hash_values = [key for key in self.references] output = None - print(evaluated_jacobian) - for hash_value, jac_component in zip(hash_values, evaluated_jacobian): if output is None: output = jac_component * (self.references[hash_value].variance * jac_component) @@ -130,6 +128,9 @@ def __init__(self, self.units = units """ Units of this data """ + self._hash_seed = hash_seed + """ Retain this for copying operations""" + self.hash_value = -1 """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ @@ -166,6 +167,13 @@ def in_units_of(self, units: Unit) -> QuantityType: else: raise UnitError(f"Target units ({units}) not compatible with existing units ({self.units}).") + def to_units_of(self, new_units: Unit) -> "Quantity[QuantityType]": + new_value, new_error = self.in_units_of_with_standard_error(new_units) + return Quantity(value=new_value, + units=new_units, + standard_error=new_error, + hash_seed=self._hash_seed) + def variance_in_units_of(self, units: Unit) -> QuantityType: """ Get the variance of quantity in other units """ variance = self.variance @@ -320,10 +328,10 @@ def _array_repr_format(arr: np.ndarray): def __repr__(self): - if isinstance(self.units, units.NamedUnit): + if isinstance(self.units, NamedUnit): value = self.value - error = self.standard_deviation().value + error = self.standard_deviation().in_units_of(self.units) unit_string = self.units.symbol else: @@ -362,6 +370,15 @@ def __init__(self, def __repr__(self): return f"[{self.name}] " + super().__repr__() + + def to_units_of(self, new_units: Unit) -> "NamedQuantity[QuantityType]": + new_value, new_error = self.in_units_of_with_standard_error(new_units) + return NamedQuantity(value=new_value, + units=new_units, + standard_error=new_error, + name=self.name) + + class DerivedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): super().__init__(value, units, standard_error=None) @@ -370,6 +387,14 @@ def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): self._variance_cache = None self._has_variance = history.has_variance() + + def to_units_of(self, new_units: Unit) -> "Quantity[QuantityType]": + # TODO: Lots of tests needed for this + return DerivedQuantity( + value=self.in_units_of(new_units), + units=new_units, + history=self.history) + @property def has_variance(self): return self._has_variance diff --git a/sasdata/quantities/quantity_examples.py b/sasdata/quantities/quantity_examples.py index 89ebb6a5c..8c505e59e 100644 --- a/sasdata/quantities/quantity_examples.py +++ b/sasdata/quantities/quantity_examples.py @@ -5,5 +5,4 @@ y = NamedQuantity("y", 1, units.decimeters, standard_error=1) print(x+y) -print(x+x) -print(y+y) \ No newline at end of file +print((x+y).to_units_of(units.centimeters)) \ No newline at end of file From 7edc5104118f9fb04cb370e46991670b7c713041 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 30 Sep 2024 11:50:15 +0100 Subject: [PATCH 586/675] Tests for error propagation --- sasdata/quantities/quantity_error_tests.py | 133 +++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/sasdata/quantities/quantity_error_tests.py b/sasdata/quantities/quantity_error_tests.py index 9aae713f2..f03ef59c1 100644 --- a/sasdata/quantities/quantity_error_tests.py +++ b/sasdata/quantities/quantity_error_tests.py @@ -18,3 +18,136 @@ def test_addition_propagation(x_err, y_err, x_units, y_units): _, err = (x + y).in_si_with_standard_error() assert err == pytest.approx(expected_err, abs=1e-8) +@pytest.mark.parametrize("x_val, y_val, x_units, y_units", + [(1, 1, units.meters, units.meters), + (1, 1, units.centimeters, units.centimeters), + (2, 2, units.meters, units.meters), + (1, 2, units.centimeters, units.centimeters), + (1, 2, units.meters, units.millimeters), + (3, 4, units.milliseconds, units.microseconds), + (0, 1, units.meters, units.meters)]) +def test_asymmetry_propagation(x_val, y_val, x_units, y_units): + + x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(x_val)) + y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(y_val)) + + x_si, x_err = x.in_si_with_standard_error() + y_si, y_err = y.in_si_with_standard_error() + + numerator = x-y + denominator = x+y + a = numerator/denominator + + # Check numerator and denominator + expected_error = np.sqrt(x_err ** 2 + y_err ** 2) + + value, error = numerator.in_si_with_standard_error() + assert error == pytest.approx(expected_error, rel=1e-6) + + value, error = denominator.in_si_with_standard_error() + assert error == pytest.approx(expected_error, rel=1e-6) + + # check whole thing + value, error = a.in_si_with_standard_error() + expected_error = (2 / (x_si + y_si)**2) * np.sqrt((x_err*y_si)**2 + (y_err*x_si)**2) + assert error == pytest.approx(expected_error, rel=1e-6) + +@pytest.mark.parametrize("x_val, y_val, x_units, y_units", + [(1, 1, units.meters, units.meters), + (1, 1, units.centimeters, units.centimeters), + (2, 2, units.meters, units.meters), + (1, 2, units.centimeters, units.centimeters), + (1, 2, units.meters, units.millimeters), + (3, 4, units.milliseconds, units.microseconds), + (0, 1, units.meters, units.meters)]) +def test_power_propagation(x_val, y_val, x_units, y_units): + + x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(x_val)) + y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(y_val)) + + x_si, x_err = x.in_si_with_standard_error() + y_si, y_err = y.in_si_with_standard_error() + + x_var = x_err ** 2 + y_var = y_err ** 2 + + z = (x*y)**3 + + # check whole thing + value, error = z.in_si_with_standard_error() + expected_variance = 9*((x_si*y_si)**4)*(x_var*y_si*y_si + x_si*x_si*y_var) + assert error == pytest.approx(np.sqrt(expected_variance), rel=1e-6) + +@pytest.mark.parametrize("k", [0.1, 0.5, 1, 2, 10]) +@pytest.mark.parametrize("x_val, y_val, x_units, y_units", + [(1, 1, units.meters, units.meters), + (1, 1, units.centimeters, units.centimeters), + (2, 2, units.meters, units.meters), + (1, 2, units.centimeters, units.centimeters), + (1, 2, units.meters, units.millimeters), + (3, 4, units.milliseconds, units.microseconds), + (0, 1, units.meters, units.meters), + (0, 0, units.meters, units.meters)]) +def test_complex_power_propagation(x_val, y_val, x_units, y_units, k): + + x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(k*x_val)) + y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(k*y_val)) + + x_si, x_err = x.in_si_with_standard_error() + y_si, y_err = y.in_si_with_standard_error() + + x_var = x_err ** 2 + y_var = y_err ** 2 + + z = (x+y)**3 + x**3 + y**3 + + value, error = z.in_si_with_standard_error() + expected_variance = \ + 9*x_var*(x_si**2 + (x_si+y_si)**2)**2 + \ + 9*y_var*(y_si**2 + (x_si+y_si)**2)**2 + + assert error == pytest.approx(np.sqrt(expected_variance), rel=1e-6) + +@pytest.mark.parametrize("k_x", [0.1, 0.5, 1, 2, 10]) +@pytest.mark.parametrize("k_y", [0.1, 0.5, 1, 2, 10]) +@pytest.mark.parametrize("x_val, y_val, x_units, y_units", + [(1, 1, units.meters, units.meters), + (1, 1, units.centimeters, units.centimeters), + (2, 2, units.meters, units.meters), + (1, 2, units.centimeters, units.centimeters), + (1, 2, units.meters, units.millimeters), + (3, 4, units.milliseconds, units.microseconds), + (0, 1, units.meters, units.meters), + (0, 0, units.meters, units.meters)]) +def test_complex_propagation(x_val, y_val, x_units, y_units, k_x, k_y): + + x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(k_x*x_val)) + y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(k_y*y_val)) + + cx = NamedQuantity("cx", 1.7, x_units) + cy = NamedQuantity("cy", 1.2, y_units) + c0 = 4*NamedQuantity("c0", value=7, units=units.none) + + cx_si = cx.in_si() + cy_si = cy.in_si() + + c0_si = c0.in_si() + + x_si, x_err = x.in_si_with_standard_error() + y_si, y_err = y.in_si_with_standard_error() + + x_var = x_err ** 2 + y_var = y_err ** 2 + + z = (((x-cx)**4 + (y-cy)**4)**(1/4)) + c0*(-x-y) + + value, error = z.in_si_with_standard_error() + + denom_factor = ((x_si - cx_si)**4 + (y_si - cy_si)**4)**(-3/4) + x_num = (cx_si - x_si)**3 + y_num = (cy_si - y_si)**3 + + expected_variance = x_var*(c0_si + x_num*denom_factor)**2 + y_var*(c0_si + y_num*denom_factor)**2 + + assert error == pytest.approx(np.sqrt(expected_variance), rel=1e-8) + From 6a34c4af5668e9de61a095d32806b3a793c1ceae Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 30 Sep 2024 12:21:08 +0100 Subject: [PATCH 587/675] More aliases for units --- sasdata/quantities/_build_tables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 3484c0f66..c672d1e4e 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -97,7 +97,7 @@ "Ang": ["A", "Å"], "au": ["a.u.", "amu"], "percent": ["%"], - "deg": ["degr"], + "deg": ["degr", "Deg", "degrees", "Degrees"], "none": ["Counts", "counts", "cnts", "Cnts"] } From fd5a6d5c89c371551d1771f7cc3b13a6c445ff35 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 30 Sep 2024 13:29:25 +0100 Subject: [PATCH 588/675] Made file for target object for metadata --- sasdata/target_data_object.py | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 sasdata/target_data_object.py diff --git a/sasdata/target_data_object.py b/sasdata/target_data_object.py new file mode 100644 index 000000000..7b868ef76 --- /dev/null +++ b/sasdata/target_data_object.py @@ -0,0 +1,3 @@ +class TargetData: + def __init__(self): + self.reference_string \ No newline at end of file From b8424c93a3bb955ba9ad0eecedee8e370617afcf Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 30 Sep 2024 17:01:52 +0100 Subject: [PATCH 589/675] Main data reading for HDF5 prototype --- sasdata/quantities/quantity.py | 28 +++++++++--- sasdata/raw_form.py | 13 +++--- sasdata/target_data_object.py | 2 +- sasdata/temp_hdf5_reader.py | 81 +++++++++++++++++++++++++++------- 4 files changed, 96 insertions(+), 28 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 517b5cbfb..7572ea9e8 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -319,10 +319,15 @@ def _array_repr_format(arr: np.ndarray): """ Format the array """ order = len(arr.shape) reshaped = arr.reshape(-1) - if len(reshaped) > 4: + if len(reshaped) <= 2: numbers = ",".join([f"{n}" for n in reshaped]) else: - numbers = f"{reshaped[0]}, {reshaped[1]} ... {reshaped[-2]}, {reshaped[-1]}" + numbers = f"{reshaped[0]} ... {reshaped[-1]}" + + # if len(reshaped) <= 4: + # numbers = ",".join([f"{n}" for n in reshaped]) + # else: + # numbers = f"{reshaped[0]}, {reshaped[1]} ... {reshaped[-2]}, {reshaped[-1]}" return "["*order + numbers + "]"*order @@ -370,13 +375,24 @@ def __init__(self, def __repr__(self): return f"[{self.name}] " + super().__repr__() - def to_units_of(self, new_units: Unit) -> "NamedQuantity[QuantityType]": new_value, new_error = self.in_units_of_with_standard_error(new_units) return NamedQuantity(value=new_value, - units=new_units, - standard_error=new_error, - name=self.name) + units=new_units, + standard_error=new_error, + name=self.name) + + def with_standard_error(self, standard_error: Quantity): + if standard_error.units.equivalent(self.units): + return NamedQuantity( + value=self.value, + units=self.units, + standard_error=standard_error.in_units_of(self.units), + name=self.name) + + else: + raise UnitError(f"Standard error units ({standard_error.units}) " + f"are not compatible with value units ({self.units})") class DerivedQuantity[QuantityType](Quantity[QuantityType]): diff --git a/sasdata/raw_form.py b/sasdata/raw_form.py index 9519dead8..e1883381d 100644 --- a/sasdata/raw_form.py +++ b/sasdata/raw_form.py @@ -18,7 +18,7 @@ def shorten_string(string): class Dataset[DataType]: name: str data: DataType - attributes: dict[str, Self] + attributes: dict[str, Self | str] def summary(self, indent_amount: int = 0, indent: str = " ") -> str: @@ -54,11 +54,14 @@ class RawData: data_contents: list[NamedQuantity] raw_metadata: dict[str, Dataset | Group] - def __repr__(self): - indent = " " - + def summary(self, indent = " "): s = f"{self.name}\n" + + for data in self.data_contents: + s += f"{indent}{data}\n" + + s += f"{indent}Metadata:\n" for key in self.raw_metadata: - s += self.raw_metadata[key].summary(1, indent) + s += self.raw_metadata[key].summary(2, indent) return s \ No newline at end of file diff --git a/sasdata/target_data_object.py b/sasdata/target_data_object.py index 7b868ef76..d88581752 100644 --- a/sasdata/target_data_object.py +++ b/sasdata/target_data_object.py @@ -1,3 +1,3 @@ class TargetData: def __init__(self): - self.reference_string \ No newline at end of file + self.reference_string = \ No newline at end of file diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 51bd86695..4b746855f 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -14,29 +14,35 @@ from sasdata.raw_form import RawData from sasdata.raw_form import Dataset as SASDataDataset, Group as SASDataGroup +from quantities.quantity import NamedQuantity +from quantities import units + test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" # test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" logger = logging.getLogger(__name__) -def hdf5_attr(entry): - return entry def recurse_hdf5(hdf5_entry): if isinstance(hdf5_entry, HDF5Dataset): + attributes = {name: hdf5_entry.attrs[name] for name in hdf5_entry.attrs} + if isinstance(hdf5_entry.dtype, np.dtypes.BytesDType): data = hdf5_entry[()][0].decode("utf-8") + return SASDataDataset[str]( + name=hdf5_entry.name, + data=data, + attributes=attributes) + else: data = np.array(hdf5_entry, dtype=hdf5_entry.dtype) - attributes = {name: hdf5_attr(hdf5_entry.attrs[name]) for name in hdf5_entry.attrs} - - return SASDataDataset( - name=hdf5_entry.name, - data=data, - attributes=attributes) + return SASDataDataset[np.ndarray]( + name=hdf5_entry.name, + data=data, + attributes=attributes) elif isinstance(hdf5_entry, HDF5Group): return SASDataGroup( @@ -46,6 +52,50 @@ def recurse_hdf5(hdf5_entry): else: raise TypeError(f"Unknown type found during HDF5 parsing: {type(hdf5_entry)} ({hdf5_entry})") +def parse_units_placeholder(string: str) -> units.Unit: + #TODO: Remove when not needed + return units.meters + +def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: + """ In the context of NeXus files, load a group of data entries that are organised together + match up the units and errors with their values""" + # Gather together data with its error terms + + uncertainty_map = {} + uncertainties = set() + entries = {} + + for name in node.children: + + child = node.children[name] + # TODO: Actual unit parser here + units = parse_units_placeholder(child.attributes["units"]) + + quantity = NamedQuantity(name=name_prefix+child.name, + value=child.data, + units=units) + + if "uncertainty" in child.attributes: + uncertainty_name = child.attributes["uncertainty"] + uncertainty_map[name] = uncertainty_name + uncertainties.add(uncertainty_name) + + entries[name] = quantity + + output = [] + + for name, entry in entries.items(): + if name not in uncertainties: + if name in uncertainty_map: + uncertainty = entries[uncertainty_map[name]] + new_entry = entry.with_standard_error(uncertainty) + output.append(new_entry) + else: + output.append(entry) + + return output + + def load_data(filename) -> list[RawData]: with h5py.File(filename, 'r') as f: @@ -65,14 +115,13 @@ def load_data(filename) -> list[RawData]: for key in entry_keys: component = entry[key] - # if key.lower() == "sasdata": - # datum = recurse_hdf5(component) - # data_contents.append(datum) - # - # else: - # raw_metadata[key] = recurse_hdf5(component) - raw_metadata[key] = recurse_hdf5(component) + if key.lower() == "sasdata": + datum = recurse_hdf5(component) + # TODO: Use named identifier + data_contents = connected_data(datum, "FILE_ID_HERE") + else: + raw_metadata[key] = recurse_hdf5(component) loaded_data.append( @@ -89,4 +138,4 @@ def load_data(filename) -> list[RawData]: data = load_data(test_file) for dataset in data: - print(dataset) + print(dataset.summary()) \ No newline at end of file From 2dabda2ffe9e6de651a699ba3cf0d9e6cb36aaa2 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 1 Oct 2024 15:15:12 +0100 Subject: [PATCH 590/675] integrating the units stuff --- sasdata/quantities/_build_tables.py | 21 +- sasdata/quantities/_units_base.py | 22 +- sasdata/quantities/unit_parser.py | 73 ++--- sasdata/quantities/units.py | 427 ++++++++++++++-------------- test/utest_unit_parser.py | 77 +++-- 5 files changed, 326 insertions(+), 294 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index c672d1e4e..f39628acb 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -90,7 +90,13 @@ # Add Hartree? Rydberg? Bohrs? # Add CGS -aliases = { +# Two stages of aliases, to make sure units don't get lost + +aliases_1 = { + "A": ["Amps", "amps"] +} + +aliases_2 = { "y": ["yr", "year"], "d": ["day"], "h": ["hr", "hour"], @@ -102,6 +108,7 @@ } + all_units = base_si_units + derived_si_units + non_si_units encoding = "utf-8" @@ -237,7 +244,7 @@ def format_name(name: str): f"ascii_symbol='{length_symbol}/{time_symbol}', " f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") - fid.write(f"{accel_name} = NamedUnit({length_scale / time_scale}, " + fid.write(f"{accel_name} = NamedUnit({length_scale / time_scale**2}, " f"Dimensions(length=1, time=-2), " f"name='{accel_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}^2', " @@ -286,10 +293,12 @@ def format_name(name: str): # Add aliases to symbol lookup table # - for base_name in aliases: - alias_list = aliases[base_name] - for alias in alias_list: - symbol_lookup[alias] = symbol_lookup[base_name] + # Apply the alias transforms sequentially + for aliases in [aliases_1, aliases_2]: + for base_name in aliases: + alias_list = aliases[base_name] + for alias in alias_list: + symbol_lookup[alias] = symbol_lookup[base_name] # # Write out the symbol lookup table diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 1ca8ba5f5..5a990ea27 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -152,7 +152,7 @@ def __hash__(self): 17**abs(self.moles_hint) * 19**abs(self.angle_hint) def __repr__(self): - s = "" + tokens = [] for name, size in [ ("length", self.length), ("time", self.time), @@ -165,14 +165,14 @@ def __repr__(self): if size == 0: pass elif size == 1: - s += f"{name}" + tokens.append(f"{name}") else: - s += f"{name}{int_as_unicode_superscript(size)}" + tokens.append(f"{name}{int_as_unicode_superscript(size)}") - return s + return ' '.join(tokens) def si_repr(self): - s = "" + tokens = [] for name, size in [ ("kg", self.mass), ("m", self.length), @@ -184,21 +184,21 @@ def si_repr(self): if size == 0: pass elif size == 1: - s += f"{name}" + tokens.append(f"{name}") else: - s += f"{name}{int_as_unicode_superscript(size)}" + tokens.append(f"{name}{int_as_unicode_superscript(size)}") match self.angle_hint: case 0: pass case 2: - s += "sr" + tokens.append("sr") case -2: - s += "sr" + int_as_unicode_superscript(-1) + tokens.append("sr" + int_as_unicode_superscript(-1)) case _: - s += "rad" + int_as_unicode_superscript(self.angle_hint) + tokens.append("rad" + int_as_unicode_superscript(self.angle_hint)) - return s + return ''.join(tokens) class Unit: diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 599abd5d2..cd3c10d71 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -4,26 +4,11 @@ # TODO: This shouldn't be in this file but I don't want to edit Lucas' code before he is finished. all_units_groups = [group.units for group in unit_groups.values()] +unit_groups_by_dimension_hash = {hash(group.units[0].dimensions): group for group in unit_groups.values()} all_units: list[NamedUnit] = [] for group in all_units_groups: all_units.extend(group) -def multiply_dimensions(dimensions_1: Dimensions, dimensions_2: Dimensions) -> Dimensions: - """Multiply each dimension in dimensions_1 with the same dimension in dimensions_2""" - return Dimensions( - length=dimensions_1.length * dimensions_2.length, - time=dimensions_1.time * dimensions_2.time, - mass=dimensions_1.mass * dimensions_2.mass, - current=dimensions_1.current * dimensions_2.current, - temperature=dimensions_1.temperature * dimensions_2.temperature, - moles_hint=dimensions_1.moles_hint * dimensions_2.moles_hint, - angle_hint=dimensions_1.angle_hint * dimensions_2.angle_hint - ) - -def combine_units(unit_1: Unit, unit_2: Unit): - """Combine unit_1, and unit_2 into one unit.""" - return Unit(unit_1.scale * unit_2.scale, unit_1.dimensions * unit_2.dimensions) - def split_unit_str(unit_str: str) -> list[str]: """Separate the letters from the numbers in unit_str""" return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) @@ -54,13 +39,13 @@ def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longes if len(potential_symbols) == 0: break string_pos += 1 - current_unit= potential_unit_str + current_unit = potential_unit_str if not longest_unit and current_unit in lookup_dict.keys(): break if current_unit == '': - return (None, unit_str) + return None, unit_str remaining_str = unit_str[string_pos::] - return (lookup_dict[current_unit], remaining_str) + return lookup_dict[current_unit], remaining_str def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longest_unit: bool = True) -> list[Unit]: """Recursively parse units from unit_str until no more characters are present.""" @@ -75,14 +60,6 @@ def parse_unit_strs(unit_str: str, current_units: list[Unit] | None=None, longes else: raise ValueError(f'Could not interpret {remaining_str}') -def unit_power(to_modify: Unit, power: int): - """Raise to_modify to power""" - # FIXME: This is horrible but I'm not sure how to fix this without changing the Dimension class itself. - dimension_multiplier = Dimensions(power, power, power, power, power, power, power) - scale_multiplier = 1 if power > 0 else -1 - return Unit(to_modify.scale ** scale_multiplier, multiply_dimensions(to_modify.dimensions, dimension_multiplier)) - - # Its probably useful to work out the unit first, and then later work out if a named unit exists for it. Hence why there # are two functions. @@ -98,14 +75,16 @@ def parse_unit_stack(unit_str: str, longest_unit: bool = True) -> list[Unit]: continue power = int(token) to_modify = unit_stack[-1] - modified = unit_power(to_modify, power) + modified = to_modify ** power + # modified = unit_power(to_modify, power) unit_stack[-1] = modified except ValueError: new_units = parse_unit_strs(token, None, longest_unit) if inverse_next_unit: # TODO: Assume the power is going to be -1. This might not be true. power = -1 - new_units[0] = unit_power(new_units[0], power) + new_units[0] = new_units[0] ** power + # new_units[0] = unit_power(new_units[0], power) unit_stack += new_units # This error will happen if it tries to read a modifier but there are no units on the stack. We will just have # to ignore it. Strings being parsed shouldn't really have it anyway (e.g. -1m). @@ -121,7 +100,8 @@ def parse_unit(unit_str: str, longest_unit: bool = True) -> Unit: parsed_unit = Unit(1, Dimensions()) unit_stack = parse_unit_stack(unit_str, longest_unit) for unit in unit_stack: - parsed_unit = combine_units(parsed_unit, unit) + # parsed_unit = combine_units(parsed_unit, unit) + parsed_unit *= unit return parsed_unit except KeyError: raise ValueError('Unit string contains an unrecognised pattern.') @@ -138,28 +118,37 @@ def parse_unit_from_group(unit_str: str, from_group: UnitGroup) -> Unit | None: else: return None -def parse_named_unit(unit: str | Unit) -> NamedUnit: +def parse_named_unit(unit_string: str, rtol: float=1e-14) -> NamedUnit: """Parses unit into a named unit. Parses unit into a Unit if it is not already, and then finds an equivaelent named unit. Please note that this might not be the expected unit from the string itself. E.g. 'kgm/2' will become - newtons.""" - if isinstance(unit, str): - generic_unit = parse_unit(unit) - elif isinstance(unit, Unit): - generic_unit = unit - else: - raise ValueError('Unit must be a string, or Unit') - for named_unit in all_units: - if named_unit == generic_unit: - return named_unit + newtons. + + :param unit_string: string describing the units, e.g. km/s + :param rtol: relative tolerance for matching scale factors + """ + unit = parse_unit(unit_string) + return find_named_unit(unit) + +def find_named_unit(unit: Unit, rtol: float=1e-14) -> NamedUnit: + """ Find a named unit matching the one provided """ + dimension_hash = hash(unit.dimensions) + if dimension_hash in unit_groups_by_dimension_hash: + unit_group = unit_groups_by_dimension_hash[hash(unit.dimensions)] + + for named_unit in unit_group.units: + if abs(named_unit.scale - unit.scale) < rtol*named_unit.scale: + return named_unit + raise ValueError('A named unit does not exist for this unit.') + def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUnit: """Parses unit_str into a named unit. The named unit found must be part of from_group. If two units are found, the unit that is present in from_group is returned. This is useful in cases of ambiguities.""" parsed_unit = parse_unit_from_group(unit_str, from_group) if parsed_unit is None: raise ValueError('That unit cannot be parsed from the specified group.') - return parse_named_unit(parsed_unit) + return find_named_unit(parsed_unit) if __name__ == "__main__": to_parse = input('Enter a unit to parse: ') diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index ebe738331..32faec793 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -236,7 +236,7 @@ def __hash__(self): 17**abs(self.moles_hint) * 19**abs(self.angle_hint) def __repr__(self): - s = "" + tokens = [] for name, size in [ ("length", self.length), ("time", self.time), @@ -249,14 +249,14 @@ def __repr__(self): if size == 0: pass elif size == 1: - s += f"{name}" + tokens.append(f"{name}") else: - s += f"{name}{int_as_unicode_superscript(size)}" + tokens.append(f"{name}{int_as_unicode_superscript(size)}") - return s + return ' '.join(tokens) def si_repr(self): - s = "" + tokens = [] for name, size in [ ("kg", self.mass), ("m", self.length), @@ -268,21 +268,21 @@ def si_repr(self): if size == 0: pass elif size == 1: - s += f"{name}" + tokens.append(f"{name}") else: - s += f"{name}{int_as_unicode_superscript(size)}" + tokens.append(f"{name}{int_as_unicode_superscript(size)}") match self.angle_hint: case 0: pass case 2: - s += "sr" + tokens.append("sr") case -2: - s += "sr" + int_as_unicode_superscript(-1) + tokens.append("sr" + int_as_unicode_superscript(-1)) case _: - s += "rad" + int_as_unicode_superscript(self.angle_hint) + tokens.append("rad" + int_as_unicode_superscript(self.angle_hint)) - return s + return ''.join(tokens) class Unit: @@ -804,443 +804,443 @@ def __init__(self, name: str, units: list[NamedUnit]): meters_per_second = NamedUnit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') meters_per_square_second = NamedUnit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') meters_per_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') -meters_per_square_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') +meters_per_square_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') meters_per_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') -meters_per_square_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') +meters_per_square_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') meters_per_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') -meters_per_square_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') +meters_per_square_nanosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') meters_per_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') -meters_per_square_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') +meters_per_square_picosecond = NamedUnit(1.0000000000000001e+24, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') meters_per_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') -meters_per_square_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') +meters_per_square_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') meters_per_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') -meters_per_square_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') +meters_per_square_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') meters_per_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') -meters_per_square_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') +meters_per_square_minute = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') meters_per_hour = NamedUnit(0.002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') -meters_per_square_hour = NamedUnit(0.002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') +meters_per_square_hour = NamedUnit(7.71604938271605e-06, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') meters_per_day = NamedUnit(0.00011574074074074075, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') -meters_per_square_day = NamedUnit(0.00011574074074074075, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') +meters_per_square_day = NamedUnit(1.3395919067215363e-08, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') meters_per_year = NamedUnit(3.168873850681143e-07, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') -meters_per_square_year = NamedUnit(3.168873850681143e-07, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') +meters_per_square_year = NamedUnit(1.0041761481530735e-13, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') exameters_per_second = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') exameters_per_square_second = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') exameters_per_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') -exameters_per_square_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') +exameters_per_square_millisecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') exameters_per_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') -exameters_per_square_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') +exameters_per_square_microsecond = NamedUnit(1e+30, Dimensions(length=1, time=-2), name='exameters_per_square_microsecond', ascii_symbol='Em/us^2', symbol='Emµs⁻²') exameters_per_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='exameters_per_nanosecond', ascii_symbol='Em/ns', symbol='Emns⁻¹') -exameters_per_square_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') +exameters_per_square_nanosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='exameters_per_square_nanosecond', ascii_symbol='Em/ns^2', symbol='Emns⁻²') exameters_per_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-1), name='exameters_per_picosecond', ascii_symbol='Em/ps', symbol='Emps⁻¹') -exameters_per_square_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-2), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') +exameters_per_square_picosecond = NamedUnit(1e+42, Dimensions(length=1, time=-2), name='exameters_per_square_picosecond', ascii_symbol='Em/ps^2', symbol='Emps⁻²') exameters_per_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='exameters_per_femtosecond', ascii_symbol='Em/fs', symbol='Emfs⁻¹') -exameters_per_square_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') +exameters_per_square_femtosecond = NamedUnit(9.999999999999999e+47, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') exameters_per_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-1), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') -exameters_per_square_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') +exameters_per_square_attosecond = NamedUnit(9.999999999999999e+53, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') exameters_per_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') -exameters_per_square_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') +exameters_per_square_minute = NamedUnit(277777777777777.78, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') exameters_per_hour = NamedUnit(2777777777777778.0, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') -exameters_per_square_hour = NamedUnit(2777777777777778.0, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') +exameters_per_square_hour = NamedUnit(7716049382716.05, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') exameters_per_day = NamedUnit(115740740740740.73, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') -exameters_per_square_day = NamedUnit(115740740740740.73, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') +exameters_per_square_day = NamedUnit(13395919067.215364, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') exameters_per_year = NamedUnit(316887385068.1143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') -exameters_per_square_year = NamedUnit(316887385068.1143, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') +exameters_per_square_year = NamedUnit(100417.61481530734, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') petameters_per_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') petameters_per_square_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') petameters_per_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') -petameters_per_square_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') +petameters_per_square_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') petameters_per_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') -petameters_per_square_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') +petameters_per_square_microsecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='petameters_per_square_microsecond', ascii_symbol='Pm/us^2', symbol='Pmµs⁻²') petameters_per_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='petameters_per_nanosecond', ascii_symbol='Pm/ns', symbol='Pmns⁻¹') -petameters_per_square_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') +petameters_per_square_nanosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='petameters_per_square_nanosecond', ascii_symbol='Pm/ns^2', symbol='Pmns⁻²') petameters_per_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-1), name='petameters_per_picosecond', ascii_symbol='Pm/ps', symbol='Pmps⁻¹') -petameters_per_square_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') +petameters_per_square_picosecond = NamedUnit(1.0000000000000001e+39, Dimensions(length=1, time=-2), name='petameters_per_square_picosecond', ascii_symbol='Pm/ps^2', symbol='Pmps⁻²') petameters_per_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='petameters_per_femtosecond', ascii_symbol='Pm/fs', symbol='Pmfs⁻¹') -petameters_per_square_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') +petameters_per_square_femtosecond = NamedUnit(1e+45, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') petameters_per_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') -petameters_per_square_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') +petameters_per_square_attosecond = NamedUnit(9.999999999999998e+50, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') petameters_per_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') -petameters_per_square_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') +petameters_per_square_minute = NamedUnit(277777777777.7778, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') petameters_per_hour = NamedUnit(2777777777777.778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') -petameters_per_square_hour = NamedUnit(2777777777777.778, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') +petameters_per_square_hour = NamedUnit(7716049382.716049, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') petameters_per_day = NamedUnit(115740740740.74074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') -petameters_per_square_day = NamedUnit(115740740740.74074, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') +petameters_per_square_day = NamedUnit(13395919.067215364, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') petameters_per_year = NamedUnit(316887385.0681143, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') -petameters_per_square_year = NamedUnit(316887385.0681143, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') +petameters_per_square_year = NamedUnit(100.41761481530735, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') terameters_per_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') terameters_per_square_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') terameters_per_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') -terameters_per_square_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') +terameters_per_square_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') terameters_per_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') -terameters_per_square_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') +terameters_per_square_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='terameters_per_square_microsecond', ascii_symbol='Tm/us^2', symbol='Tmµs⁻²') terameters_per_nanosecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='terameters_per_nanosecond', ascii_symbol='Tm/ns', symbol='Tmns⁻¹') -terameters_per_square_nanosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') +terameters_per_square_nanosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='terameters_per_square_nanosecond', ascii_symbol='Tm/ns^2', symbol='Tmns⁻²') terameters_per_picosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='terameters_per_picosecond', ascii_symbol='Tm/ps', symbol='Tmps⁻¹') -terameters_per_square_picosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') +terameters_per_square_picosecond = NamedUnit(1e+36, Dimensions(length=1, time=-2), name='terameters_per_square_picosecond', ascii_symbol='Tm/ps^2', symbol='Tmps⁻²') terameters_per_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='terameters_per_femtosecond', ascii_symbol='Tm/fs', symbol='Tmfs⁻¹') -terameters_per_square_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') +terameters_per_square_femtosecond = NamedUnit(9.999999999999999e+41, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') terameters_per_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') -terameters_per_square_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') +terameters_per_square_attosecond = NamedUnit(9.999999999999999e+47, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') terameters_per_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') -terameters_per_square_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') +terameters_per_square_minute = NamedUnit(277777777.7777778, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') terameters_per_hour = NamedUnit(2777777777.7777777, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') -terameters_per_square_hour = NamedUnit(2777777777.7777777, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') +terameters_per_square_hour = NamedUnit(7716049.382716049, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') terameters_per_day = NamedUnit(115740740.74074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') -terameters_per_square_day = NamedUnit(115740740.74074075, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') +terameters_per_square_day = NamedUnit(13395.919067215364, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') terameters_per_year = NamedUnit(316887.38506811426, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') -terameters_per_square_year = NamedUnit(316887.38506811426, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') +terameters_per_square_year = NamedUnit(0.10041761481530735, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') gigameters_per_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') gigameters_per_square_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') gigameters_per_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') -gigameters_per_square_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') +gigameters_per_square_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') gigameters_per_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') -gigameters_per_square_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') +gigameters_per_square_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='gigameters_per_square_microsecond', ascii_symbol='Gm/us^2', symbol='Gmµs⁻²') gigameters_per_nanosecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='gigameters_per_nanosecond', ascii_symbol='Gm/ns', symbol='Gmns⁻¹') -gigameters_per_square_nanosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') +gigameters_per_square_nanosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='gigameters_per_square_nanosecond', ascii_symbol='Gm/ns^2', symbol='Gmns⁻²') gigameters_per_picosecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='gigameters_per_picosecond', ascii_symbol='Gm/ps', symbol='Gmps⁻¹') -gigameters_per_square_picosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') +gigameters_per_square_picosecond = NamedUnit(1.0000000000000001e+33, Dimensions(length=1, time=-2), name='gigameters_per_square_picosecond', ascii_symbol='Gm/ps^2', symbol='Gmps⁻²') gigameters_per_femtosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='gigameters_per_femtosecond', ascii_symbol='Gm/fs', symbol='Gmfs⁻¹') -gigameters_per_square_femtosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') +gigameters_per_square_femtosecond = NamedUnit(1e+39, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') gigameters_per_attosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') -gigameters_per_square_attosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') +gigameters_per_square_attosecond = NamedUnit(1e+45, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') gigameters_per_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') -gigameters_per_square_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') +gigameters_per_square_minute = NamedUnit(277777.77777777775, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') gigameters_per_hour = NamedUnit(2777777.777777778, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') -gigameters_per_square_hour = NamedUnit(2777777.777777778, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') +gigameters_per_square_hour = NamedUnit(7716.049382716049, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') gigameters_per_day = NamedUnit(115740.74074074074, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') -gigameters_per_square_day = NamedUnit(115740.74074074074, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') +gigameters_per_square_day = NamedUnit(13.395919067215363, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') gigameters_per_year = NamedUnit(316.88738506811427, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') -gigameters_per_square_year = NamedUnit(316.88738506811427, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') +gigameters_per_square_year = NamedUnit(0.00010041761481530735, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') megameters_per_second = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') megameters_per_square_second = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') megameters_per_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') -megameters_per_square_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') +megameters_per_square_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') megameters_per_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') -megameters_per_square_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') +megameters_per_square_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='megameters_per_square_microsecond', ascii_symbol='Mm/us^2', symbol='Mmµs⁻²') megameters_per_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_nanosecond', ascii_symbol='Mm/ns', symbol='Mmns⁻¹') -megameters_per_square_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') +megameters_per_square_nanosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='megameters_per_square_nanosecond', ascii_symbol='Mm/ns^2', symbol='Mmns⁻²') megameters_per_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='megameters_per_picosecond', ascii_symbol='Mm/ps', symbol='Mmps⁻¹') -megameters_per_square_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') +megameters_per_square_picosecond = NamedUnit(1e+30, Dimensions(length=1, time=-2), name='megameters_per_square_picosecond', ascii_symbol='Mm/ps^2', symbol='Mmps⁻²') megameters_per_femtosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='megameters_per_femtosecond', ascii_symbol='Mm/fs', symbol='Mmfs⁻¹') -megameters_per_square_femtosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') +megameters_per_square_femtosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') megameters_per_attosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') -megameters_per_square_attosecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') +megameters_per_square_attosecond = NamedUnit(9.999999999999999e+41, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') megameters_per_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') -megameters_per_square_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') +megameters_per_square_minute = NamedUnit(277.77777777777777, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') megameters_per_hour = NamedUnit(2777.777777777778, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') -megameters_per_square_hour = NamedUnit(2777.777777777778, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') +megameters_per_square_hour = NamedUnit(7.716049382716049, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') megameters_per_day = NamedUnit(115.74074074074075, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') -megameters_per_square_day = NamedUnit(115.74074074074075, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') +megameters_per_square_day = NamedUnit(0.013395919067215363, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') megameters_per_year = NamedUnit(0.3168873850681143, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') -megameters_per_square_year = NamedUnit(0.3168873850681143, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') +megameters_per_square_year = NamedUnit(1.0041761481530735e-07, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') kilometers_per_second = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') kilometers_per_square_second = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') kilometers_per_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') -kilometers_per_square_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') +kilometers_per_square_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') kilometers_per_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') -kilometers_per_square_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') +kilometers_per_square_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_microsecond', ascii_symbol='km/us^2', symbol='kmµs⁻²') kilometers_per_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='kilometers_per_nanosecond', ascii_symbol='km/ns', symbol='kmns⁻¹') -kilometers_per_square_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-2), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') +kilometers_per_square_nanosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='kilometers_per_square_nanosecond', ascii_symbol='km/ns^2', symbol='kmns⁻²') kilometers_per_picosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_picosecond', ascii_symbol='km/ps', symbol='kmps⁻¹') -kilometers_per_square_picosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') +kilometers_per_square_picosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='kilometers_per_square_picosecond', ascii_symbol='km/ps^2', symbol='kmps⁻²') kilometers_per_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='kilometers_per_femtosecond', ascii_symbol='km/fs', symbol='kmfs⁻¹') -kilometers_per_square_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') +kilometers_per_square_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') kilometers_per_attosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') -kilometers_per_square_attosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') +kilometers_per_square_attosecond = NamedUnit(1e+39, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') kilometers_per_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') -kilometers_per_square_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') +kilometers_per_square_minute = NamedUnit(0.2777777777777778, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') kilometers_per_hour = NamedUnit(2.7777777777777777, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') -kilometers_per_square_hour = NamedUnit(2.7777777777777777, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') +kilometers_per_square_hour = NamedUnit(0.007716049382716049, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') kilometers_per_day = NamedUnit(0.11574074074074074, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') -kilometers_per_square_day = NamedUnit(0.11574074074074074, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') +kilometers_per_square_day = NamedUnit(1.3395919067215363e-05, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') kilometers_per_year = NamedUnit(0.0003168873850681143, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') -kilometers_per_square_year = NamedUnit(0.0003168873850681143, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') +kilometers_per_square_year = NamedUnit(1.0041761481530735e-10, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') millimeters_per_second = NamedUnit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') millimeters_per_square_second = NamedUnit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') millimeters_per_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') -millimeters_per_square_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') +millimeters_per_square_millisecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') millimeters_per_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') -millimeters_per_square_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') +millimeters_per_square_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_microsecond', ascii_symbol='mm/us^2', symbol='mmµs⁻²') millimeters_per_nanosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='millimeters_per_nanosecond', ascii_symbol='mm/ns', symbol='mmns⁻¹') -millimeters_per_square_nanosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') +millimeters_per_square_nanosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_nanosecond', ascii_symbol='mm/ns^2', symbol='mmns⁻²') millimeters_per_picosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_picosecond', ascii_symbol='mm/ps', symbol='mmps⁻¹') -millimeters_per_square_picosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') +millimeters_per_square_picosecond = NamedUnit(1.0000000000000001e+21, Dimensions(length=1, time=-2), name='millimeters_per_square_picosecond', ascii_symbol='mm/ps^2', symbol='mmps⁻²') millimeters_per_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_femtosecond', ascii_symbol='mm/fs', symbol='mmfs⁻¹') -millimeters_per_square_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') +millimeters_per_square_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') millimeters_per_attosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') -millimeters_per_square_attosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') +millimeters_per_square_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') millimeters_per_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') -millimeters_per_square_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') +millimeters_per_square_minute = NamedUnit(2.7777777777777776e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') millimeters_per_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') -millimeters_per_square_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') +millimeters_per_square_hour = NamedUnit(7.71604938271605e-09, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') millimeters_per_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') -millimeters_per_square_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') +millimeters_per_square_day = NamedUnit(1.3395919067215364e-11, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') millimeters_per_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') -millimeters_per_square_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') +millimeters_per_square_year = NamedUnit(1.0041761481530735e-16, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') micrometers_per_second = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') micrometers_per_square_second = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') micrometers_per_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') -micrometers_per_square_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') +micrometers_per_square_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') micrometers_per_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') -micrometers_per_square_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') +micrometers_per_square_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='micrometers_per_square_microsecond', ascii_symbol='um/us^2', symbol='µmµs⁻²') micrometers_per_nanosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-1), name='micrometers_per_nanosecond', ascii_symbol='um/ns', symbol='µmns⁻¹') -micrometers_per_square_nanosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-2), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') +micrometers_per_square_nanosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-2), name='micrometers_per_square_nanosecond', ascii_symbol='um/ns^2', symbol='µmns⁻²') micrometers_per_picosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='micrometers_per_picosecond', ascii_symbol='um/ps', symbol='µmps⁻¹') -micrometers_per_square_picosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') +micrometers_per_square_picosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='micrometers_per_square_picosecond', ascii_symbol='um/ps^2', symbol='µmps⁻²') micrometers_per_femtosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='micrometers_per_femtosecond', ascii_symbol='um/fs', symbol='µmfs⁻¹') -micrometers_per_square_femtosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') +micrometers_per_square_femtosecond = NamedUnit(9.999999999999998e+23, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') micrometers_per_attosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') -micrometers_per_square_attosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') +micrometers_per_square_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') micrometers_per_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') -micrometers_per_square_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') +micrometers_per_square_minute = NamedUnit(2.7777777777777777e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') micrometers_per_hour = NamedUnit(2.7777777777777776e-09, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') -micrometers_per_square_hour = NamedUnit(2.7777777777777776e-09, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') +micrometers_per_square_hour = NamedUnit(7.716049382716049e-12, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') micrometers_per_day = NamedUnit(1.1574074074074074e-10, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') -micrometers_per_square_day = NamedUnit(1.1574074074074074e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') +micrometers_per_square_day = NamedUnit(1.3395919067215363e-14, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') micrometers_per_year = NamedUnit(3.168873850681143e-13, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') -micrometers_per_square_year = NamedUnit(3.168873850681143e-13, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') +micrometers_per_square_year = NamedUnit(1.0041761481530734e-19, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') nanometers_per_second = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') nanometers_per_square_second = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') nanometers_per_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') -nanometers_per_square_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') +nanometers_per_square_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') nanometers_per_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') -nanometers_per_square_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') +nanometers_per_square_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='nanometers_per_square_microsecond', ascii_symbol='nm/us^2', symbol='nmµs⁻²') nanometers_per_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='nanometers_per_nanosecond', ascii_symbol='nm/ns', symbol='nmns⁻¹') -nanometers_per_square_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') +nanometers_per_square_nanosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_nanosecond', ascii_symbol='nm/ns^2', symbol='nmns⁻²') nanometers_per_picosecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='nanometers_per_picosecond', ascii_symbol='nm/ps', symbol='nmps⁻¹') -nanometers_per_square_picosecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') +nanometers_per_square_picosecond = NamedUnit(1000000000000000.1, Dimensions(length=1, time=-2), name='nanometers_per_square_picosecond', ascii_symbol='nm/ps^2', symbol='nmps⁻²') nanometers_per_femtosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='nanometers_per_femtosecond', ascii_symbol='nm/fs', symbol='nmfs⁻¹') -nanometers_per_square_femtosecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') +nanometers_per_square_femtosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') nanometers_per_attosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') -nanometers_per_square_attosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') +nanometers_per_square_attosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') nanometers_per_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') -nanometers_per_square_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') +nanometers_per_square_minute = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') nanometers_per_hour = NamedUnit(2.777777777777778e-12, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') -nanometers_per_square_hour = NamedUnit(2.777777777777778e-12, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') +nanometers_per_square_hour = NamedUnit(7.71604938271605e-15, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') nanometers_per_day = NamedUnit(1.1574074074074076e-13, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') -nanometers_per_square_day = NamedUnit(1.1574074074074076e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') +nanometers_per_square_day = NamedUnit(1.3395919067215365e-17, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') nanometers_per_year = NamedUnit(3.1688738506811433e-16, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') -nanometers_per_square_year = NamedUnit(3.1688738506811433e-16, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') +nanometers_per_square_year = NamedUnit(1.0041761481530736e-22, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') picometers_per_second = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') picometers_per_square_second = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') picometers_per_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') -picometers_per_square_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') +picometers_per_square_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') picometers_per_microsecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') -picometers_per_square_microsecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') +picometers_per_square_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='picometers_per_square_microsecond', ascii_symbol='pm/us^2', symbol='pmµs⁻²') picometers_per_nanosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='picometers_per_nanosecond', ascii_symbol='pm/ns', symbol='pmns⁻¹') -picometers_per_square_nanosecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') +picometers_per_square_nanosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-2), name='picometers_per_square_nanosecond', ascii_symbol='pm/ns^2', symbol='pmns⁻²') picometers_per_picosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='picometers_per_picosecond', ascii_symbol='pm/ps', symbol='pmps⁻¹') -picometers_per_square_picosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') +picometers_per_square_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='picometers_per_square_picosecond', ascii_symbol='pm/ps^2', symbol='pmps⁻²') picometers_per_femtosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-1), name='picometers_per_femtosecond', ascii_symbol='pm/fs', symbol='pmfs⁻¹') -picometers_per_square_femtosecond = NamedUnit(999.9999999999999, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') +picometers_per_square_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') picometers_per_attosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-1), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') -picometers_per_square_attosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') +picometers_per_square_attosecond = NamedUnit(9.999999999999998e+23, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') picometers_per_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') -picometers_per_square_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') +picometers_per_square_minute = NamedUnit(2.7777777777777775e-16, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') picometers_per_hour = NamedUnit(2.7777777777777776e-15, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') -picometers_per_square_hour = NamedUnit(2.7777777777777776e-15, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') +picometers_per_square_hour = NamedUnit(7.716049382716049e-18, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') picometers_per_day = NamedUnit(1.1574074074074073e-16, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') -picometers_per_square_day = NamedUnit(1.1574074074074073e-16, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') +picometers_per_square_day = NamedUnit(1.3395919067215364e-20, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') picometers_per_year = NamedUnit(3.168873850681143e-19, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') -picometers_per_square_year = NamedUnit(3.168873850681143e-19, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') +picometers_per_square_year = NamedUnit(1.0041761481530734e-25, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') femtometers_per_second = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') femtometers_per_square_second = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') femtometers_per_millisecond = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') -femtometers_per_square_millisecond = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') +femtometers_per_square_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') femtometers_per_microsecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') -femtometers_per_square_microsecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') +femtometers_per_square_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='femtometers_per_square_microsecond', ascii_symbol='fm/us^2', symbol='fmµs⁻²') femtometers_per_nanosecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='femtometers_per_nanosecond', ascii_symbol='fm/ns', symbol='fmns⁻¹') -femtometers_per_square_nanosecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') +femtometers_per_square_nanosecond = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_nanosecond', ascii_symbol='fm/ns^2', symbol='fmns⁻²') femtometers_per_picosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='femtometers_per_picosecond', ascii_symbol='fm/ps', symbol='fmps⁻¹') -femtometers_per_square_picosecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') +femtometers_per_square_picosecond = NamedUnit(1000000000.0000001, Dimensions(length=1, time=-2), name='femtometers_per_square_picosecond', ascii_symbol='fm/ps^2', symbol='fmps⁻²') femtometers_per_femtosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='femtometers_per_femtosecond', ascii_symbol='fm/fs', symbol='fmfs⁻¹') -femtometers_per_square_femtosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') +femtometers_per_square_femtosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') femtometers_per_attosecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') -femtometers_per_square_attosecond = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') +femtometers_per_square_attosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') femtometers_per_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') -femtometers_per_square_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') +femtometers_per_square_minute = NamedUnit(2.777777777777778e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') femtometers_per_hour = NamedUnit(2.777777777777778e-18, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') -femtometers_per_square_hour = NamedUnit(2.777777777777778e-18, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') +femtometers_per_square_hour = NamedUnit(7.71604938271605e-21, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') femtometers_per_day = NamedUnit(1.1574074074074075e-19, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') -femtometers_per_square_day = NamedUnit(1.1574074074074075e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') +femtometers_per_square_day = NamedUnit(1.3395919067215363e-23, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') femtometers_per_year = NamedUnit(3.168873850681143e-22, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') -femtometers_per_square_year = NamedUnit(3.168873850681143e-22, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') +femtometers_per_square_year = NamedUnit(1.0041761481530735e-28, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') attometers_per_second = NamedUnit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') attometers_per_square_second = NamedUnit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') attometers_per_millisecond = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') -attometers_per_square_millisecond = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') +attometers_per_square_millisecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') attometers_per_microsecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-1), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') -attometers_per_square_microsecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') +attometers_per_square_microsecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-2), name='attometers_per_square_microsecond', ascii_symbol='am/us^2', symbol='amµs⁻²') attometers_per_nanosecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='attometers_per_nanosecond', ascii_symbol='am/ns', symbol='amns⁻¹') -attometers_per_square_nanosecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') +attometers_per_square_nanosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='attometers_per_square_nanosecond', ascii_symbol='am/ns^2', symbol='amns⁻²') attometers_per_picosecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-1), name='attometers_per_picosecond', ascii_symbol='am/ps', symbol='amps⁻¹') -attometers_per_square_picosecond = NamedUnit(1.0000000000000002e-06, Dimensions(length=1, time=-2), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') +attometers_per_square_picosecond = NamedUnit(1000000.0000000001, Dimensions(length=1, time=-2), name='attometers_per_square_picosecond', ascii_symbol='am/ps^2', symbol='amps⁻²') attometers_per_femtosecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='attometers_per_femtosecond', ascii_symbol='am/fs', symbol='amfs⁻¹') -attometers_per_square_femtosecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') +attometers_per_square_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') attometers_per_attosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') -attometers_per_square_attosecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') +attometers_per_square_attosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') attometers_per_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') -attometers_per_square_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') +attometers_per_square_minute = NamedUnit(2.777777777777778e-22, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') attometers_per_hour = NamedUnit(2.7777777777777778e-21, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') -attometers_per_square_hour = NamedUnit(2.7777777777777778e-21, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') +attometers_per_square_hour = NamedUnit(7.71604938271605e-24, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') attometers_per_day = NamedUnit(1.1574074074074074e-22, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') -attometers_per_square_day = NamedUnit(1.1574074074074074e-22, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') +attometers_per_square_day = NamedUnit(1.3395919067215363e-26, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') attometers_per_year = NamedUnit(3.1688738506811433e-25, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') -attometers_per_square_year = NamedUnit(3.1688738506811433e-25, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') +attometers_per_square_year = NamedUnit(1.0041761481530734e-31, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') decimeters_per_second = NamedUnit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dmNone⁻¹') decimeters_per_square_second = NamedUnit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dmNone⁻²') decimeters_per_millisecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='decimeters_per_millisecond', ascii_symbol='dm/ms', symbol='dmms⁻¹') -decimeters_per_square_millisecond = NamedUnit(100.0, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') +decimeters_per_square_millisecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') decimeters_per_microsecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-1), name='decimeters_per_microsecond', ascii_symbol='dm/us', symbol='dmµs⁻¹') -decimeters_per_square_microsecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_microsecond', ascii_symbol='dm/us^2', symbol='dmµs⁻²') +decimeters_per_square_microsecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_microsecond', ascii_symbol='dm/us^2', symbol='dmµs⁻²') decimeters_per_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='decimeters_per_nanosecond', ascii_symbol='dm/ns', symbol='dmns⁻¹') -decimeters_per_square_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_nanosecond', ascii_symbol='dm/ns^2', symbol='dmns⁻²') +decimeters_per_square_nanosecond = NamedUnit(1e+17, Dimensions(length=1, time=-2), name='decimeters_per_square_nanosecond', ascii_symbol='dm/ns^2', symbol='dmns⁻²') decimeters_per_picosecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_picosecond', ascii_symbol='dm/ps', symbol='dmps⁻¹') -decimeters_per_square_picosecond = NamedUnit(100000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_picosecond', ascii_symbol='dm/ps^2', symbol='dmps⁻²') +decimeters_per_square_picosecond = NamedUnit(1.0000000000000001e+23, Dimensions(length=1, time=-2), name='decimeters_per_square_picosecond', ascii_symbol='dm/ps^2', symbol='dmps⁻²') decimeters_per_femtosecond = NamedUnit(100000000000000.0, Dimensions(length=1, time=-1), name='decimeters_per_femtosecond', ascii_symbol='dm/fs', symbol='dmfs⁻¹') -decimeters_per_square_femtosecond = NamedUnit(100000000000000.0, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') +decimeters_per_square_femtosecond = NamedUnit(1e+29, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') decimeters_per_attosecond = NamedUnit(1e+17, Dimensions(length=1, time=-1), name='decimeters_per_attosecond', ascii_symbol='dm/as', symbol='dmas⁻¹') -decimeters_per_square_attosecond = NamedUnit(1e+17, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') +decimeters_per_square_attosecond = NamedUnit(1e+35, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') decimeters_per_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmNone⁻¹') -decimeters_per_square_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmNone⁻²') +decimeters_per_square_minute = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmNone⁻²') decimeters_per_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmNone⁻¹') -decimeters_per_square_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmNone⁻²') +decimeters_per_square_hour = NamedUnit(7.71604938271605e-07, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmNone⁻²') decimeters_per_day = NamedUnit(1.1574074074074075e-05, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmNone⁻¹') -decimeters_per_square_day = NamedUnit(1.1574074074074075e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmNone⁻²') +decimeters_per_square_day = NamedUnit(1.3395919067215364e-09, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmNone⁻²') decimeters_per_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmNone⁻¹') -decimeters_per_square_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmNone⁻²') +decimeters_per_square_year = NamedUnit(1.0041761481530735e-14, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmNone⁻²') centimeters_per_second = NamedUnit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cmNone⁻¹') centimeters_per_square_second = NamedUnit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cmNone⁻²') centimeters_per_millisecond = NamedUnit(10.0, Dimensions(length=1, time=-1), name='centimeters_per_millisecond', ascii_symbol='cm/ms', symbol='cmms⁻¹') -centimeters_per_square_millisecond = NamedUnit(10.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') +centimeters_per_square_millisecond = NamedUnit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') centimeters_per_microsecond = NamedUnit(10000.0, Dimensions(length=1, time=-1), name='centimeters_per_microsecond', ascii_symbol='cm/us', symbol='cmµs⁻¹') -centimeters_per_square_microsecond = NamedUnit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_microsecond', ascii_symbol='cm/us^2', symbol='cmµs⁻²') +centimeters_per_square_microsecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_microsecond', ascii_symbol='cm/us^2', symbol='cmµs⁻²') centimeters_per_nanosecond = NamedUnit(10000000.0, Dimensions(length=1, time=-1), name='centimeters_per_nanosecond', ascii_symbol='cm/ns', symbol='cmns⁻¹') -centimeters_per_square_nanosecond = NamedUnit(10000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_nanosecond', ascii_symbol='cm/ns^2', symbol='cmns⁻²') +centimeters_per_square_nanosecond = NamedUnit(1e+16, Dimensions(length=1, time=-2), name='centimeters_per_square_nanosecond', ascii_symbol='cm/ns^2', symbol='cmns⁻²') centimeters_per_picosecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_picosecond', ascii_symbol='cm/ps', symbol='cmps⁻¹') -centimeters_per_square_picosecond = NamedUnit(10000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_picosecond', ascii_symbol='cm/ps^2', symbol='cmps⁻²') +centimeters_per_square_picosecond = NamedUnit(1e+22, Dimensions(length=1, time=-2), name='centimeters_per_square_picosecond', ascii_symbol='cm/ps^2', symbol='cmps⁻²') centimeters_per_femtosecond = NamedUnit(10000000000000.0, Dimensions(length=1, time=-1), name='centimeters_per_femtosecond', ascii_symbol='cm/fs', symbol='cmfs⁻¹') -centimeters_per_square_femtosecond = NamedUnit(10000000000000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') +centimeters_per_square_femtosecond = NamedUnit(1e+28, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') centimeters_per_attosecond = NamedUnit(1e+16, Dimensions(length=1, time=-1), name='centimeters_per_attosecond', ascii_symbol='cm/as', symbol='cmas⁻¹') -centimeters_per_square_attosecond = NamedUnit(1e+16, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') +centimeters_per_square_attosecond = NamedUnit(1e+34, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') centimeters_per_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmNone⁻¹') -centimeters_per_square_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmNone⁻²') +centimeters_per_square_minute = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmNone⁻²') centimeters_per_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmNone⁻¹') -centimeters_per_square_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmNone⁻²') +centimeters_per_square_hour = NamedUnit(7.71604938271605e-08, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmNone⁻²') centimeters_per_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmNone⁻¹') -centimeters_per_square_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmNone⁻²') +centimeters_per_square_day = NamedUnit(1.3395919067215363e-10, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmNone⁻²') centimeters_per_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmNone⁻¹') -centimeters_per_square_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmNone⁻²') +centimeters_per_square_year = NamedUnit(1.0041761481530735e-15, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmNone⁻²') angstroms_per_second = NamedUnit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') angstroms_per_square_second = NamedUnit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') angstroms_per_millisecond = NamedUnit(1e-07, Dimensions(length=1, time=-1), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') -angstroms_per_square_millisecond = NamedUnit(1e-07, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') +angstroms_per_square_millisecond = NamedUnit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') angstroms_per_microsecond = NamedUnit(0.0001, Dimensions(length=1, time=-1), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') -angstroms_per_square_microsecond = NamedUnit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') +angstroms_per_square_microsecond = NamedUnit(100.0, Dimensions(length=1, time=-2), name='angstroms_per_square_microsecond', ascii_symbol='Ang/us^2', symbol='ŵs⁻²') angstroms_per_nanosecond = NamedUnit(0.09999999999999999, Dimensions(length=1, time=-1), name='angstroms_per_nanosecond', ascii_symbol='Ang/ns', symbol='Åns⁻¹') -angstroms_per_square_nanosecond = NamedUnit(0.09999999999999999, Dimensions(length=1, time=-2), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') +angstroms_per_square_nanosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_nanosecond', ascii_symbol='Ang/ns^2', symbol='Åns⁻²') angstroms_per_picosecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='angstroms_per_picosecond', ascii_symbol='Ang/ps', symbol='Åps⁻¹') -angstroms_per_square_picosecond = NamedUnit(100.0, Dimensions(length=1, time=-2), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') +angstroms_per_square_picosecond = NamedUnit(100000000000000.02, Dimensions(length=1, time=-2), name='angstroms_per_square_picosecond', ascii_symbol='Ang/ps^2', symbol='Åps⁻²') angstroms_per_femtosecond = NamedUnit(100000.0, Dimensions(length=1, time=-1), name='angstroms_per_femtosecond', ascii_symbol='Ang/fs', symbol='Åfs⁻¹') -angstroms_per_square_femtosecond = NamedUnit(100000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') +angstroms_per_square_femtosecond = NamedUnit(1e+20, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') angstroms_per_attosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') -angstroms_per_square_attosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') +angstroms_per_square_attosecond = NamedUnit(9.999999999999999e+25, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') angstroms_per_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') -angstroms_per_square_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') +angstroms_per_square_minute = NamedUnit(2.7777777777777778e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') angstroms_per_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') -angstroms_per_square_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') +angstroms_per_square_hour = NamedUnit(7.716049382716049e-16, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') angstroms_per_day = NamedUnit(1.1574074074074074e-14, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') -angstroms_per_square_day = NamedUnit(1.1574074074074074e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') +angstroms_per_square_day = NamedUnit(1.3395919067215363e-18, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') angstroms_per_year = NamedUnit(3.168873850681143e-17, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') -angstroms_per_square_year = NamedUnit(3.168873850681143e-17, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') +angstroms_per_square_year = NamedUnit(1.0041761481530734e-23, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') miles_per_second = NamedUnit(1609.344, Dimensions(length=1, time=-1), name='miles_per_second', ascii_symbol='miles/s', symbol='NoneNone⁻¹') miles_per_square_second = NamedUnit(1609.344, Dimensions(length=1, time=-2), name='miles_per_square_second', ascii_symbol='miles/s^2', symbol='NoneNone⁻²') miles_per_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-1), name='miles_per_millisecond', ascii_symbol='miles/ms', symbol='Nonems⁻¹') -miles_per_square_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-2), name='miles_per_square_millisecond', ascii_symbol='miles/ms^2', symbol='Nonems⁻²') +miles_per_square_millisecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-2), name='miles_per_square_millisecond', ascii_symbol='miles/ms^2', symbol='Nonems⁻²') miles_per_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-1), name='miles_per_microsecond', ascii_symbol='miles/us', symbol='Noneµs⁻¹') -miles_per_square_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-2), name='miles_per_square_microsecond', ascii_symbol='miles/us^2', symbol='Noneµs⁻²') +miles_per_square_microsecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-2), name='miles_per_square_microsecond', ascii_symbol='miles/us^2', symbol='Noneµs⁻²') miles_per_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-1), name='miles_per_nanosecond', ascii_symbol='miles/ns', symbol='Nonens⁻¹') -miles_per_square_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-2), name='miles_per_square_nanosecond', ascii_symbol='miles/ns^2', symbol='Nonens⁻²') +miles_per_square_nanosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-2), name='miles_per_square_nanosecond', ascii_symbol='miles/ns^2', symbol='Nonens⁻²') miles_per_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-1), name='miles_per_picosecond', ascii_symbol='miles/ps', symbol='Noneps⁻¹') -miles_per_square_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-2), name='miles_per_square_picosecond', ascii_symbol='miles/ps^2', symbol='Noneps⁻²') +miles_per_square_picosecond = NamedUnit(1.609344e+27, Dimensions(length=1, time=-2), name='miles_per_square_picosecond', ascii_symbol='miles/ps^2', symbol='Noneps⁻²') miles_per_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-1), name='miles_per_femtosecond', ascii_symbol='miles/fs', symbol='Nonefs⁻¹') -miles_per_square_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-2), name='miles_per_square_femtosecond', ascii_symbol='miles/fs^2', symbol='Nonefs⁻²') +miles_per_square_femtosecond = NamedUnit(1.609344e+33, Dimensions(length=1, time=-2), name='miles_per_square_femtosecond', ascii_symbol='miles/fs^2', symbol='Nonefs⁻²') miles_per_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-1), name='miles_per_attosecond', ascii_symbol='miles/as', symbol='Noneas⁻¹') -miles_per_square_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-2), name='miles_per_square_attosecond', ascii_symbol='miles/as^2', symbol='Noneas⁻²') +miles_per_square_attosecond = NamedUnit(1.609344e+39, Dimensions(length=1, time=-2), name='miles_per_square_attosecond', ascii_symbol='miles/as^2', symbol='Noneas⁻²') miles_per_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-1), name='miles_per_minute', ascii_symbol='miles/min', symbol='NoneNone⁻¹') -miles_per_square_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-2), name='miles_per_square_minute', ascii_symbol='miles/min^2', symbol='NoneNone⁻²') +miles_per_square_minute = NamedUnit(0.44704, Dimensions(length=1, time=-2), name='miles_per_square_minute', ascii_symbol='miles/min^2', symbol='NoneNone⁻²') miles_per_hour = NamedUnit(4.4704, Dimensions(length=1, time=-1), name='miles_per_hour', ascii_symbol='miles/h', symbol='NoneNone⁻¹') -miles_per_square_hour = NamedUnit(4.4704, Dimensions(length=1, time=-2), name='miles_per_square_hour', ascii_symbol='miles/h^2', symbol='NoneNone⁻²') +miles_per_square_hour = NamedUnit(0.012417777777777778, Dimensions(length=1, time=-2), name='miles_per_square_hour', ascii_symbol='miles/h^2', symbol='NoneNone⁻²') miles_per_day = NamedUnit(0.18626666666666666, Dimensions(length=1, time=-1), name='miles_per_day', ascii_symbol='miles/d', symbol='NoneNone⁻¹') -miles_per_square_day = NamedUnit(0.18626666666666666, Dimensions(length=1, time=-2), name='miles_per_square_day', ascii_symbol='miles/d^2', symbol='NoneNone⁻²') +miles_per_square_day = NamedUnit(2.1558641975308643e-05, Dimensions(length=1, time=-2), name='miles_per_square_day', ascii_symbol='miles/d^2', symbol='NoneNone⁻²') miles_per_year = NamedUnit(0.0005099808118350593, Dimensions(length=1, time=-1), name='miles_per_year', ascii_symbol='miles/y', symbol='NoneNone⁻¹') -miles_per_square_year = NamedUnit(0.0005099808118350593, Dimensions(length=1, time=-2), name='miles_per_square_year', ascii_symbol='miles/y^2', symbol='NoneNone⁻²') +miles_per_square_year = NamedUnit(1.61606485897326e-10, Dimensions(length=1, time=-2), name='miles_per_square_year', ascii_symbol='miles/y^2', symbol='NoneNone⁻²') yards_per_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-1), name='yards_per_second', ascii_symbol='yrd/s', symbol='NoneNone⁻¹') yards_per_square_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-2), name='yards_per_square_second', ascii_symbol='yrd/s^2', symbol='NoneNone⁻²') yards_per_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-1), name='yards_per_millisecond', ascii_symbol='yrd/ms', symbol='Nonems⁻¹') -yards_per_square_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-2), name='yards_per_square_millisecond', ascii_symbol='yrd/ms^2', symbol='Nonems⁻²') +yards_per_square_millisecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-2), name='yards_per_square_millisecond', ascii_symbol='yrd/ms^2', symbol='Nonems⁻²') yards_per_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-1), name='yards_per_microsecond', ascii_symbol='yrd/us', symbol='Noneµs⁻¹') -yards_per_square_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-2), name='yards_per_square_microsecond', ascii_symbol='yrd/us^2', symbol='Noneµs⁻²') +yards_per_square_microsecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-2), name='yards_per_square_microsecond', ascii_symbol='yrd/us^2', symbol='Noneµs⁻²') yards_per_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-1), name='yards_per_nanosecond', ascii_symbol='yrd/ns', symbol='Nonens⁻¹') -yards_per_square_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-2), name='yards_per_square_nanosecond', ascii_symbol='yrd/ns^2', symbol='Nonens⁻²') +yards_per_square_nanosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-2), name='yards_per_square_nanosecond', ascii_symbol='yrd/ns^2', symbol='Nonens⁻²') yards_per_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-1), name='yards_per_picosecond', ascii_symbol='yrd/ps', symbol='Noneps⁻¹') -yards_per_square_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-2), name='yards_per_square_picosecond', ascii_symbol='yrd/ps^2', symbol='Noneps⁻²') +yards_per_square_picosecond = NamedUnit(9.144000000000002e+23, Dimensions(length=1, time=-2), name='yards_per_square_picosecond', ascii_symbol='yrd/ps^2', symbol='Noneps⁻²') yards_per_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-1), name='yards_per_femtosecond', ascii_symbol='yrd/fs', symbol='Nonefs⁻¹') -yards_per_square_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-2), name='yards_per_square_femtosecond', ascii_symbol='yrd/fs^2', symbol='Nonefs⁻²') +yards_per_square_femtosecond = NamedUnit(9.144e+29, Dimensions(length=1, time=-2), name='yards_per_square_femtosecond', ascii_symbol='yrd/fs^2', symbol='Nonefs⁻²') yards_per_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-1), name='yards_per_attosecond', ascii_symbol='yrd/as', symbol='Noneas⁻¹') -yards_per_square_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-2), name='yards_per_square_attosecond', ascii_symbol='yrd/as^2', symbol='Noneas⁻²') +yards_per_square_attosecond = NamedUnit(9.144e+35, Dimensions(length=1, time=-2), name='yards_per_square_attosecond', ascii_symbol='yrd/as^2', symbol='Noneas⁻²') yards_per_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-1), name='yards_per_minute', ascii_symbol='yrd/min', symbol='NoneNone⁻¹') -yards_per_square_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-2), name='yards_per_square_minute', ascii_symbol='yrd/min^2', symbol='NoneNone⁻²') +yards_per_square_minute = NamedUnit(0.00025400000000000005, Dimensions(length=1, time=-2), name='yards_per_square_minute', ascii_symbol='yrd/min^2', symbol='NoneNone⁻²') yards_per_hour = NamedUnit(0.00254, Dimensions(length=1, time=-1), name='yards_per_hour', ascii_symbol='yrd/h', symbol='NoneNone⁻¹') -yards_per_square_hour = NamedUnit(0.00254, Dimensions(length=1, time=-2), name='yards_per_square_hour', ascii_symbol='yrd/h^2', symbol='NoneNone⁻²') +yards_per_square_hour = NamedUnit(7.055555555555557e-06, Dimensions(length=1, time=-2), name='yards_per_square_hour', ascii_symbol='yrd/h^2', symbol='NoneNone⁻²') yards_per_day = NamedUnit(0.00010583333333333335, Dimensions(length=1, time=-1), name='yards_per_day', ascii_symbol='yrd/d', symbol='NoneNone⁻¹') -yards_per_square_day = NamedUnit(0.00010583333333333335, Dimensions(length=1, time=-2), name='yards_per_square_day', ascii_symbol='yrd/d^2', symbol='NoneNone⁻²') +yards_per_square_day = NamedUnit(1.224922839506173e-08, Dimensions(length=1, time=-2), name='yards_per_square_day', ascii_symbol='yrd/d^2', symbol='NoneNone⁻²') yards_per_year = NamedUnit(2.897618249062837e-07, Dimensions(length=1, time=-1), name='yards_per_year', ascii_symbol='yrd/y', symbol='NoneNone⁻¹') -yards_per_square_year = NamedUnit(2.897618249062837e-07, Dimensions(length=1, time=-2), name='yards_per_square_year', ascii_symbol='yrd/y^2', symbol='NoneNone⁻²') +yards_per_square_year = NamedUnit(9.182186698711705e-14, Dimensions(length=1, time=-2), name='yards_per_square_year', ascii_symbol='yrd/y^2', symbol='NoneNone⁻²') feet_per_second = NamedUnit(0.3048, Dimensions(length=1, time=-1), name='feet_per_second', ascii_symbol='ft/s', symbol='NoneNone⁻¹') feet_per_square_second = NamedUnit(0.3048, Dimensions(length=1, time=-2), name='feet_per_square_second', ascii_symbol='ft/s^2', symbol='NoneNone⁻²') feet_per_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-1), name='feet_per_millisecond', ascii_symbol='ft/ms', symbol='Nonems⁻¹') -feet_per_square_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-2), name='feet_per_square_millisecond', ascii_symbol='ft/ms^2', symbol='Nonems⁻²') +feet_per_square_millisecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-2), name='feet_per_square_millisecond', ascii_symbol='ft/ms^2', symbol='Nonems⁻²') feet_per_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-1), name='feet_per_microsecond', ascii_symbol='ft/us', symbol='Noneµs⁻¹') -feet_per_square_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-2), name='feet_per_square_microsecond', ascii_symbol='ft/us^2', symbol='Noneµs⁻²') +feet_per_square_microsecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-2), name='feet_per_square_microsecond', ascii_symbol='ft/us^2', symbol='Noneµs⁻²') feet_per_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-1), name='feet_per_nanosecond', ascii_symbol='ft/ns', symbol='Nonens⁻¹') -feet_per_square_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-2), name='feet_per_square_nanosecond', ascii_symbol='ft/ns^2', symbol='Nonens⁻²') +feet_per_square_nanosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-2), name='feet_per_square_nanosecond', ascii_symbol='ft/ns^2', symbol='Nonens⁻²') feet_per_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-1), name='feet_per_picosecond', ascii_symbol='ft/ps', symbol='Noneps⁻¹') -feet_per_square_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-2), name='feet_per_square_picosecond', ascii_symbol='ft/ps^2', symbol='Noneps⁻²') +feet_per_square_picosecond = NamedUnit(3.048e+23, Dimensions(length=1, time=-2), name='feet_per_square_picosecond', ascii_symbol='ft/ps^2', symbol='Noneps⁻²') feet_per_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-1), name='feet_per_femtosecond', ascii_symbol='ft/fs', symbol='Nonefs⁻¹') -feet_per_square_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-2), name='feet_per_square_femtosecond', ascii_symbol='ft/fs^2', symbol='Nonefs⁻²') +feet_per_square_femtosecond = NamedUnit(3.048e+29, Dimensions(length=1, time=-2), name='feet_per_square_femtosecond', ascii_symbol='ft/fs^2', symbol='Nonefs⁻²') feet_per_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-1), name='feet_per_attosecond', ascii_symbol='ft/as', symbol='Noneas⁻¹') -feet_per_square_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-2), name='feet_per_square_attosecond', ascii_symbol='ft/as^2', symbol='Noneas⁻²') +feet_per_square_attosecond = NamedUnit(3.0479999999999997e+35, Dimensions(length=1, time=-2), name='feet_per_square_attosecond', ascii_symbol='ft/as^2', symbol='Noneas⁻²') feet_per_minute = NamedUnit(0.00508, Dimensions(length=1, time=-1), name='feet_per_minute', ascii_symbol='ft/min', symbol='NoneNone⁻¹') -feet_per_square_minute = NamedUnit(0.00508, Dimensions(length=1, time=-2), name='feet_per_square_minute', ascii_symbol='ft/min^2', symbol='NoneNone⁻²') +feet_per_square_minute = NamedUnit(8.466666666666667e-05, Dimensions(length=1, time=-2), name='feet_per_square_minute', ascii_symbol='ft/min^2', symbol='NoneNone⁻²') feet_per_hour = NamedUnit(0.0008466666666666667, Dimensions(length=1, time=-1), name='feet_per_hour', ascii_symbol='ft/h', symbol='NoneNone⁻¹') -feet_per_square_hour = NamedUnit(0.0008466666666666667, Dimensions(length=1, time=-2), name='feet_per_square_hour', ascii_symbol='ft/h^2', symbol='NoneNone⁻²') +feet_per_square_hour = NamedUnit(2.351851851851852e-06, Dimensions(length=1, time=-2), name='feet_per_square_hour', ascii_symbol='ft/h^2', symbol='NoneNone⁻²') feet_per_day = NamedUnit(3.527777777777778e-05, Dimensions(length=1, time=-1), name='feet_per_day', ascii_symbol='ft/d', symbol='NoneNone⁻¹') -feet_per_square_day = NamedUnit(3.527777777777778e-05, Dimensions(length=1, time=-2), name='feet_per_square_day', ascii_symbol='ft/d^2', symbol='NoneNone⁻²') +feet_per_square_day = NamedUnit(4.083076131687243e-09, Dimensions(length=1, time=-2), name='feet_per_square_day', ascii_symbol='ft/d^2', symbol='NoneNone⁻²') feet_per_year = NamedUnit(9.658727496876123e-08, Dimensions(length=1, time=-1), name='feet_per_year', ascii_symbol='ft/y', symbol='NoneNone⁻¹') -feet_per_square_year = NamedUnit(9.658727496876123e-08, Dimensions(length=1, time=-2), name='feet_per_square_year', ascii_symbol='ft/y^2', symbol='NoneNone⁻²') +feet_per_square_year = NamedUnit(3.060728899570568e-14, Dimensions(length=1, time=-2), name='feet_per_square_year', ascii_symbol='ft/y^2', symbol='NoneNone⁻²') inches_per_second = NamedUnit(0.0254, Dimensions(length=1, time=-1), name='inches_per_second', ascii_symbol='in/s', symbol='NoneNone⁻¹') inches_per_square_second = NamedUnit(0.0254, Dimensions(length=1, time=-2), name='inches_per_square_second', ascii_symbol='in/s^2', symbol='NoneNone⁻²') inches_per_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-1), name='inches_per_millisecond', ascii_symbol='in/ms', symbol='Nonems⁻¹') -inches_per_square_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-2), name='inches_per_square_millisecond', ascii_symbol='in/ms^2', symbol='Nonems⁻²') +inches_per_square_millisecond = NamedUnit(25400.0, Dimensions(length=1, time=-2), name='inches_per_square_millisecond', ascii_symbol='in/ms^2', symbol='Nonems⁻²') inches_per_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-1), name='inches_per_microsecond', ascii_symbol='in/us', symbol='Noneµs⁻¹') -inches_per_square_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-2), name='inches_per_square_microsecond', ascii_symbol='in/us^2', symbol='Noneµs⁻²') +inches_per_square_microsecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-2), name='inches_per_square_microsecond', ascii_symbol='in/us^2', symbol='Noneµs⁻²') inches_per_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-1), name='inches_per_nanosecond', ascii_symbol='in/ns', symbol='Nonens⁻¹') -inches_per_square_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-2), name='inches_per_square_nanosecond', ascii_symbol='in/ns^2', symbol='Nonens⁻²') +inches_per_square_nanosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-2), name='inches_per_square_nanosecond', ascii_symbol='in/ns^2', symbol='Nonens⁻²') inches_per_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-1), name='inches_per_picosecond', ascii_symbol='in/ps', symbol='Noneps⁻¹') -inches_per_square_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-2), name='inches_per_square_picosecond', ascii_symbol='in/ps^2', symbol='Noneps⁻²') +inches_per_square_picosecond = NamedUnit(2.54e+22, Dimensions(length=1, time=-2), name='inches_per_square_picosecond', ascii_symbol='in/ps^2', symbol='Noneps⁻²') inches_per_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-1), name='inches_per_femtosecond', ascii_symbol='in/fs', symbol='Nonefs⁻¹') -inches_per_square_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-2), name='inches_per_square_femtosecond', ascii_symbol='in/fs^2', symbol='Nonefs⁻²') +inches_per_square_femtosecond = NamedUnit(2.54e+28, Dimensions(length=1, time=-2), name='inches_per_square_femtosecond', ascii_symbol='in/fs^2', symbol='Nonefs⁻²') inches_per_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-1), name='inches_per_attosecond', ascii_symbol='in/as', symbol='Noneas⁻¹') -inches_per_square_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-2), name='inches_per_square_attosecond', ascii_symbol='in/as^2', symbol='Noneas⁻²') +inches_per_square_attosecond = NamedUnit(2.5399999999999998e+34, Dimensions(length=1, time=-2), name='inches_per_square_attosecond', ascii_symbol='in/as^2', symbol='Noneas⁻²') inches_per_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-1), name='inches_per_minute', ascii_symbol='in/min', symbol='NoneNone⁻¹') -inches_per_square_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-2), name='inches_per_square_minute', ascii_symbol='in/min^2', symbol='NoneNone⁻²') +inches_per_square_minute = NamedUnit(7.055555555555555e-06, Dimensions(length=1, time=-2), name='inches_per_square_minute', ascii_symbol='in/min^2', symbol='NoneNone⁻²') inches_per_hour = NamedUnit(7.055555555555556e-05, Dimensions(length=1, time=-1), name='inches_per_hour', ascii_symbol='in/h', symbol='NoneNone⁻¹') -inches_per_square_hour = NamedUnit(7.055555555555556e-05, Dimensions(length=1, time=-2), name='inches_per_square_hour', ascii_symbol='in/h^2', symbol='NoneNone⁻²') +inches_per_square_hour = NamedUnit(1.9598765432098765e-07, Dimensions(length=1, time=-2), name='inches_per_square_hour', ascii_symbol='in/h^2', symbol='NoneNone⁻²') inches_per_day = NamedUnit(2.9398148148148147e-06, Dimensions(length=1, time=-1), name='inches_per_day', ascii_symbol='in/d', symbol='NoneNone⁻¹') -inches_per_square_day = NamedUnit(2.9398148148148147e-06, Dimensions(length=1, time=-2), name='inches_per_square_day', ascii_symbol='in/d^2', symbol='NoneNone⁻²') +inches_per_square_day = NamedUnit(3.4025634430727023e-10, Dimensions(length=1, time=-2), name='inches_per_square_day', ascii_symbol='in/d^2', symbol='NoneNone⁻²') inches_per_year = NamedUnit(8.048939580730103e-09, Dimensions(length=1, time=-1), name='inches_per_year', ascii_symbol='in/y', symbol='NoneNone⁻¹') -inches_per_square_year = NamedUnit(8.048939580730103e-09, Dimensions(length=1, time=-2), name='inches_per_square_year', ascii_symbol='in/y^2', symbol='NoneNone⁻²') +inches_per_square_year = NamedUnit(2.5506074163088065e-15, Dimensions(length=1, time=-2), name='inches_per_square_year', ascii_symbol='in/y^2', symbol='NoneNone⁻²') grams_per_cubic_meter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') exagrams_per_cubic_meter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') petagrams_per_cubic_meter = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') @@ -2011,6 +2011,8 @@ def __init__(self, name: str, units: list[NamedUnit]): "psi": pounds_force_per_square_inch, "percent": percent, "%": percent, + "Amps": amperes, + "amps": amperes, "yr": years, "year": years, "day": days, @@ -2019,6 +2021,9 @@ def __init__(self, name: str, units: list[NamedUnit]): "a.u.": atomic_mass_units, "amu": atomic_mass_units, "degr": degrees, + "Deg": degrees, + "degrees": degrees, + "Degrees": degrees, "Counts": none, "counts": none, "cnts": none, diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index d5fc0d5d1..afce93f13 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -1,32 +1,61 @@ from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group, parse_unit -from sasdata.quantities.units import meters, speed, meters_per_second, per_angstrom, kilometers_per_square_hour, newtons -from pytest import raises - - -def test_parse(): - parsed_metres = parse_named_unit('m') - assert parsed_metres == meters - # Have to specify a group because this is ambigious with inverse of milliseconds. - parsed_metres_per_second = parse_named_unit_from_group('ms-1', speed) - assert parsed_metres_per_second == meters_per_second - parsed_inverse_angstroms = parse_named_unit('A-1') - assert parsed_inverse_angstroms == per_angstrom - parsed_inverse_angstroms_slant = parse_named_unit('1/A') - assert parsed_inverse_angstroms_slant == per_angstrom - parsed_kilometers_per_square_hour = parse_named_unit('kmh-2') - assert parsed_kilometers_per_square_hour == kilometers_per_square_hour - parsed_kilometers_per_square_hour_slant = parse_named_unit('km/h2') - assert parsed_kilometers_per_square_hour_slant == kilometers_per_square_hour - parsed_newton = parse_named_unit('kgm/s2') - assert parsed_newton == newtons +from sasdata.quantities import units +from sasdata.quantities.units import Unit + +import pytest + +named_units_for_testing = [ + ('m', units.meters), + ('A-1', units.per_angstrom), + ('1/A', units.per_angstrom), + ('kmh-2', units.kilometers_per_square_hour), + ('km/h2', units.kilometers_per_square_hour), + ('kgm/s2', units.newtons), + ('m m', units.square_meters), + ('mm', units.millimeters), + ('A^-1', units.per_angstrom), + ('V/Amps', units.ohms), + ('Ω', units.ohms), + ('Å', units.angstroms), + ('%', units.percent) +] + +unnamed_units_for_testing = [ + ('m13', units.meters**13), + ('kW/sr', units.kilowatts/units.stradians) +] + + +@pytest.mark.parametrize("string, expected_units", named_units_for_testing) +def test_name_parse(string: str, expected_units: Unit): + """ Test basic parsing""" + assert parse_named_unit(string) == expected_units + +@pytest.mark.parametrize("string, expected_units", named_units_for_testing + unnamed_units_for_testing) +def test_equivalent(string: str, expected_units: Unit): + """ Check dimensions of parsed units""" + assert parse_unit(string).equivalent(expected_units) + + +@pytest.mark.parametrize("string, expected_units", named_units_for_testing + unnamed_units_for_testing) +def test_scale_same(string: str, expected_units: Unit): + """ Test basic parsing""" + assert parse_unit(string).scale == pytest.approx(expected_units.scale, rel=1e-14) + + +def test_parse_from_group(): + """ Test group based disambiguation""" + parsed_metres_per_second = parse_named_unit_from_group('ms-1', units.speed) + assert parsed_metres_per_second == units.meters_per_second + def test_parse_errors(): # Fails because the unit is not in that specific group. - with raises(ValueError, match='That unit cannot be parsed from the specified group.'): - parse_named_unit_from_group('km', speed) + with pytest.raises(ValueError, match='That unit cannot be parsed from the specified group.'): + parse_named_unit_from_group('km', units.speed) # Fails because part of the unit matches but there is an unknown unit '@' - with raises(ValueError, match='unit_str contains forbidden characters.'): + with pytest.raises(ValueError, match='unit_str contains forbidden characters.'): parse_unit('km@-1') # Fails because 'da' is not a unit. - with raises(ValueError, match='Unit string contains an unrecognised pattern.'): + with pytest.raises(ValueError, match='Unit string contains an unrecognised pattern.'): parse_unit('mmda2') From 6a6f0bdc2c510265796435dbc47df266bf98e877 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 1 Oct 2024 17:35:43 +0100 Subject: [PATCH 591/675] Parsing of units in HDF5 reader --- sasdata/quantities/unit_parser.py | 26 ++++++++++++++++++++++---- sasdata/temp_hdf5_reader.py | 21 +++++++++++---------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index cd3c10d71..0f7965a97 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -127,9 +127,13 @@ def parse_named_unit(unit_string: str, rtol: float=1e-14) -> NamedUnit: :param rtol: relative tolerance for matching scale factors """ unit = parse_unit(unit_string) - return find_named_unit(unit) + named_unit = find_named_unit(unit) + if named_unit is None: + raise ValueError(f"We don't have a for this unit: '{unit}'") + else: + return named_unit -def find_named_unit(unit: Unit, rtol: float=1e-14) -> NamedUnit: +def find_named_unit(unit: Unit, rtol: float=1e-14) -> NamedUnit | None: """ Find a named unit matching the one provided """ dimension_hash = hash(unit.dimensions) if dimension_hash in unit_groups_by_dimension_hash: @@ -139,7 +143,7 @@ def find_named_unit(unit: Unit, rtol: float=1e-14) -> NamedUnit: if abs(named_unit.scale - unit.scale) < rtol*named_unit.scale: return named_unit - raise ValueError('A named unit does not exist for this unit.') + return None def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUnit: @@ -150,12 +154,26 @@ def parse_named_unit_from_group(unit_str: str, from_group: UnitGroup) -> NamedUn raise ValueError('That unit cannot be parsed from the specified group.') return find_named_unit(parsed_unit) +def parse(string: str, + name_lookup: bool = True, + longest_unit: bool = True, + lookup_rtol: float = 1e-14): + + unit = parse_unit(string, longest_unit=longest_unit) + if name_lookup: + named = find_named_unit(unit, rtol=lookup_rtol) + if named is not None: + return named + + return unit + + if __name__ == "__main__": to_parse = input('Enter a unit to parse: ') try: generic_unit = parse_unit(to_parse) print(f'Generic Unit: {generic_unit}') - named_unit = parse_named_unit(generic_unit) + named_unit = find_named_unit(generic_unit) print(f'Named Unit: {named_unit}') except ValueError: print('There is no named unit available.') diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 4b746855f..32bb4f7ae 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -14,11 +14,12 @@ from sasdata.raw_form import RawData from sasdata.raw_form import Dataset as SASDataDataset, Group as SASDataGroup -from quantities.quantity import NamedQuantity -from quantities import units +from sasdata.quantities.quantity import NamedQuantity +from sasdata.quantities import units +from sasdata.quantities.unit_parser import parse -test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" -# test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" +# test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" +test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" logger = logging.getLogger(__name__) @@ -52,10 +53,7 @@ def recurse_hdf5(hdf5_entry): else: raise TypeError(f"Unknown type found during HDF5 parsing: {type(hdf5_entry)} ({hdf5_entry})") -def parse_units_placeholder(string: str) -> units.Unit: - #TODO: Remove when not needed - return units.meters - +GET_UNITS_FROM_ELSEWHERE = units.meters def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: """ In the context of NeXus files, load a group of data entries that are organised together match up the units and errors with their values""" @@ -68,8 +66,11 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: for name in node.children: child = node.children[name] - # TODO: Actual unit parser here - units = parse_units_placeholder(child.attributes["units"]) + + if "units" in child.attributes: + units = parse(child.attributes["units"]) + else: + units = GET_UNITS_FROM_ELSEWHERE quantity = NamedQuantity(name=name_prefix+child.name, value=child.data, From 93bc62a27447b04db8d1e92ca2d527ec790818d9 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 2 Oct 2024 11:09:44 +0100 Subject: [PATCH 592/675] Fixed moles potentially --- sasdata/quantities/_build_tables.py | 2 +- sasdata/quantities/units.py | 460 ++++++++++++++-------------- 2 files changed, 231 insertions(+), 231 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index f39628acb..302cd8697 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -185,7 +185,7 @@ def format_name(name: str): # Units dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) fid.write(f"{combined_name_plural} = NamedUnit({combined_scale}, " - f"Dimensions({length}, {time}, {mass}, {current}, {temperature})," + f"Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," f"name='{combined_name_plural}'," f"ascii_symbol='{combined_symbol}'," f"symbol='{combined_special_symbol}')\n") diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 32faec793..2bec4bc7e 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -430,235 +430,235 @@ def __init__(self, name: str, units: list[NamedUnit]): # meters = NamedUnit(1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') -exameters = NamedUnit(1e+18, Dimensions(1, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') -petameters = NamedUnit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') -terameters = NamedUnit(1000000000000.0, Dimensions(1, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') -gigameters = NamedUnit(1000000000.0, Dimensions(1, 0, 0, 0, 0),name='gigameters',ascii_symbol='Gm',symbol='Gm') -megameters = NamedUnit(1000000.0, Dimensions(1, 0, 0, 0, 0),name='megameters',ascii_symbol='Mm',symbol='Mm') -kilometers = NamedUnit(1000.0, Dimensions(1, 0, 0, 0, 0),name='kilometers',ascii_symbol='km',symbol='km') -millimeters = NamedUnit(0.001, Dimensions(1, 0, 0, 0, 0),name='millimeters',ascii_symbol='mm',symbol='mm') -micrometers = NamedUnit(1e-06, Dimensions(1, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',symbol='µm') -nanometers = NamedUnit(1e-09, Dimensions(1, 0, 0, 0, 0),name='nanometers',ascii_symbol='nm',symbol='nm') -picometers = NamedUnit(1e-12, Dimensions(1, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') -femtometers = NamedUnit(1e-15, Dimensions(1, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') -attometers = NamedUnit(1e-18, Dimensions(1, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') -decimeters = NamedUnit(0.1, Dimensions(1, 0, 0, 0, 0),name='decimeters',ascii_symbol='dm',symbol='dm') -centimeters = NamedUnit(0.01, Dimensions(1, 0, 0, 0, 0),name='centimeters',ascii_symbol='cm',symbol='cm') +exameters = NamedUnit(1e+18, Dimensions(1, 0, 0, 0, 0, 0, 0),name='exameters',ascii_symbol='Em',symbol='Em') +petameters = NamedUnit(1000000000000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='petameters',ascii_symbol='Pm',symbol='Pm') +terameters = NamedUnit(1000000000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='terameters',ascii_symbol='Tm',symbol='Tm') +gigameters = NamedUnit(1000000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='gigameters',ascii_symbol='Gm',symbol='Gm') +megameters = NamedUnit(1000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='megameters',ascii_symbol='Mm',symbol='Mm') +kilometers = NamedUnit(1000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='kilometers',ascii_symbol='km',symbol='km') +millimeters = NamedUnit(0.001, Dimensions(1, 0, 0, 0, 0, 0, 0),name='millimeters',ascii_symbol='mm',symbol='mm') +micrometers = NamedUnit(1e-06, Dimensions(1, 0, 0, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',symbol='µm') +nanometers = NamedUnit(1e-09, Dimensions(1, 0, 0, 0, 0, 0, 0),name='nanometers',ascii_symbol='nm',symbol='nm') +picometers = NamedUnit(1e-12, Dimensions(1, 0, 0, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') +femtometers = NamedUnit(1e-15, Dimensions(1, 0, 0, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') +attometers = NamedUnit(1e-18, Dimensions(1, 0, 0, 0, 0, 0, 0),name='attometers',ascii_symbol='am',symbol='am') +decimeters = NamedUnit(0.1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='decimeters',ascii_symbol='dm',symbol='dm') +centimeters = NamedUnit(0.01, Dimensions(1, 0, 0, 0, 0, 0, 0),name='centimeters',ascii_symbol='cm',symbol='cm') seconds = NamedUnit(1, Dimensions(0, 1, 0, 0, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') -milliseconds = NamedUnit(0.001, Dimensions(0, 1, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') -microseconds = NamedUnit(1e-06, Dimensions(0, 1, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') -nanoseconds = NamedUnit(1e-09, Dimensions(0, 1, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') -picoseconds = NamedUnit(1e-12, Dimensions(0, 1, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') -femtoseconds = NamedUnit(1e-15, Dimensions(0, 1, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') -attoseconds = NamedUnit(1e-18, Dimensions(0, 1, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') +milliseconds = NamedUnit(0.001, Dimensions(0, 1, 0, 0, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') +microseconds = NamedUnit(1e-06, Dimensions(0, 1, 0, 0, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') +nanoseconds = NamedUnit(1e-09, Dimensions(0, 1, 0, 0, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') +picoseconds = NamedUnit(1e-12, Dimensions(0, 1, 0, 0, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') +femtoseconds = NamedUnit(1e-15, Dimensions(0, 1, 0, 0, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') +attoseconds = NamedUnit(1e-18, Dimensions(0, 1, 0, 0, 0, 0, 0),name='attoseconds',ascii_symbol='as',symbol='as') grams = NamedUnit(0.001, Dimensions(0, 0, 1, 0, 0, 0, 0),name='grams',ascii_symbol='g',symbol='g') -exagrams = NamedUnit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') -petagrams = NamedUnit(1000000000000.0, Dimensions(0, 0, 1, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') -teragrams = NamedUnit(1000000000.0, Dimensions(0, 0, 1, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') -gigagrams = NamedUnit(1000000.0, Dimensions(0, 0, 1, 0, 0),name='gigagrams',ascii_symbol='Gg',symbol='Gg') -megagrams = NamedUnit(1000.0, Dimensions(0, 0, 1, 0, 0),name='megagrams',ascii_symbol='Mg',symbol='Mg') -kilograms = NamedUnit(1.0, Dimensions(0, 0, 1, 0, 0),name='kilograms',ascii_symbol='kg',symbol='kg') -milligrams = NamedUnit(1e-06, Dimensions(0, 0, 1, 0, 0),name='milligrams',ascii_symbol='mg',symbol='mg') -micrograms = NamedUnit(1e-09, Dimensions(0, 0, 1, 0, 0),name='micrograms',ascii_symbol='ug',symbol='µg') -nanograms = NamedUnit(1.0000000000000002e-12, Dimensions(0, 0, 1, 0, 0),name='nanograms',ascii_symbol='ng',symbol='ng') -picograms = NamedUnit(1e-15, Dimensions(0, 0, 1, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') -femtograms = NamedUnit(1e-18, Dimensions(0, 0, 1, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') -attograms = NamedUnit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') +exagrams = NamedUnit(1000000000000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='exagrams',ascii_symbol='Eg',symbol='Eg') +petagrams = NamedUnit(1000000000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='petagrams',ascii_symbol='Pg',symbol='Pg') +teragrams = NamedUnit(1000000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='teragrams',ascii_symbol='Tg',symbol='Tg') +gigagrams = NamedUnit(1000000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='gigagrams',ascii_symbol='Gg',symbol='Gg') +megagrams = NamedUnit(1000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='megagrams',ascii_symbol='Mg',symbol='Mg') +kilograms = NamedUnit(1.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='kilograms',ascii_symbol='kg',symbol='kg') +milligrams = NamedUnit(1e-06, Dimensions(0, 0, 1, 0, 0, 0, 0),name='milligrams',ascii_symbol='mg',symbol='mg') +micrograms = NamedUnit(1e-09, Dimensions(0, 0, 1, 0, 0, 0, 0),name='micrograms',ascii_symbol='ug',symbol='µg') +nanograms = NamedUnit(1.0000000000000002e-12, Dimensions(0, 0, 1, 0, 0, 0, 0),name='nanograms',ascii_symbol='ng',symbol='ng') +picograms = NamedUnit(1e-15, Dimensions(0, 0, 1, 0, 0, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') +femtograms = NamedUnit(1e-18, Dimensions(0, 0, 1, 0, 0, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') +attograms = NamedUnit(1.0000000000000001e-21, Dimensions(0, 0, 1, 0, 0, 0, 0),name='attograms',ascii_symbol='ag',symbol='ag') amperes = NamedUnit(1, Dimensions(0, 0, 0, 1, 0, 0, 0),name='amperes',ascii_symbol='A',symbol='A') -exaamperes = NamedUnit(1e+18, Dimensions(0, 0, 0, 1, 0),name='exaamperes',ascii_symbol='EA',symbol='EA') -petaamperes = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0),name='petaamperes',ascii_symbol='PA',symbol='PA') -teraamperes = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 1, 0),name='teraamperes',ascii_symbol='TA',symbol='TA') -gigaamperes = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 1, 0),name='gigaamperes',ascii_symbol='GA',symbol='GA') -megaamperes = NamedUnit(1000000.0, Dimensions(0, 0, 0, 1, 0),name='megaamperes',ascii_symbol='MA',symbol='MA') -kiloamperes = NamedUnit(1000.0, Dimensions(0, 0, 0, 1, 0),name='kiloamperes',ascii_symbol='kA',symbol='kA') -milliamperes = NamedUnit(0.001, Dimensions(0, 0, 0, 1, 0),name='milliamperes',ascii_symbol='mA',symbol='mA') -microamperes = NamedUnit(1e-06, Dimensions(0, 0, 0, 1, 0),name='microamperes',ascii_symbol='uA',symbol='µA') -nanoamperes = NamedUnit(1e-09, Dimensions(0, 0, 0, 1, 0),name='nanoamperes',ascii_symbol='nA',symbol='nA') -picoamperes = NamedUnit(1e-12, Dimensions(0, 0, 0, 1, 0),name='picoamperes',ascii_symbol='pA',symbol='pA') -femtoamperes = NamedUnit(1e-15, Dimensions(0, 0, 0, 1, 0),name='femtoamperes',ascii_symbol='fA',symbol='fA') -attoamperes = NamedUnit(1e-18, Dimensions(0, 0, 0, 1, 0),name='attoamperes',ascii_symbol='aA',symbol='aA') +exaamperes = NamedUnit(1e+18, Dimensions(0, 0, 0, 1, 0, 0, 0),name='exaamperes',ascii_symbol='EA',symbol='EA') +petaamperes = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='petaamperes',ascii_symbol='PA',symbol='PA') +teraamperes = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='teraamperes',ascii_symbol='TA',symbol='TA') +gigaamperes = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='gigaamperes',ascii_symbol='GA',symbol='GA') +megaamperes = NamedUnit(1000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='megaamperes',ascii_symbol='MA',symbol='MA') +kiloamperes = NamedUnit(1000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='kiloamperes',ascii_symbol='kA',symbol='kA') +milliamperes = NamedUnit(0.001, Dimensions(0, 0, 0, 1, 0, 0, 0),name='milliamperes',ascii_symbol='mA',symbol='mA') +microamperes = NamedUnit(1e-06, Dimensions(0, 0, 0, 1, 0, 0, 0),name='microamperes',ascii_symbol='uA',symbol='µA') +nanoamperes = NamedUnit(1e-09, Dimensions(0, 0, 0, 1, 0, 0, 0),name='nanoamperes',ascii_symbol='nA',symbol='nA') +picoamperes = NamedUnit(1e-12, Dimensions(0, 0, 0, 1, 0, 0, 0),name='picoamperes',ascii_symbol='pA',symbol='pA') +femtoamperes = NamedUnit(1e-15, Dimensions(0, 0, 0, 1, 0, 0, 0),name='femtoamperes',ascii_symbol='fA',symbol='fA') +attoamperes = NamedUnit(1e-18, Dimensions(0, 0, 0, 1, 0, 0, 0),name='attoamperes',ascii_symbol='aA',symbol='aA') kelvin = NamedUnit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kelvin',ascii_symbol='K',symbol='K') -exakelvin = NamedUnit(1e+18, Dimensions(0, 0, 0, 0, 1),name='exakelvin',ascii_symbol='EK',symbol='EK') -petakelvin = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1),name='petakelvin',ascii_symbol='PK',symbol='PK') -terakelvin = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 0, 1),name='terakelvin',ascii_symbol='TK',symbol='TK') -gigakelvin = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 0, 1),name='gigakelvin',ascii_symbol='GK',symbol='GK') -megakelvin = NamedUnit(1000000.0, Dimensions(0, 0, 0, 0, 1),name='megakelvin',ascii_symbol='MK',symbol='MK') -kilokelvin = NamedUnit(1000.0, Dimensions(0, 0, 0, 0, 1),name='kilokelvin',ascii_symbol='kK',symbol='kK') -millikelvin = NamedUnit(0.001, Dimensions(0, 0, 0, 0, 1),name='millikelvin',ascii_symbol='mK',symbol='mK') -microkelvin = NamedUnit(1e-06, Dimensions(0, 0, 0, 0, 1),name='microkelvin',ascii_symbol='uK',symbol='µK') -nanokelvin = NamedUnit(1e-09, Dimensions(0, 0, 0, 0, 1),name='nanokelvin',ascii_symbol='nK',symbol='nK') -picokelvin = NamedUnit(1e-12, Dimensions(0, 0, 0, 0, 1),name='picokelvin',ascii_symbol='pK',symbol='pK') -femtokelvin = NamedUnit(1e-15, Dimensions(0, 0, 0, 0, 1),name='femtokelvin',ascii_symbol='fK',symbol='fK') -attokelvin = NamedUnit(1e-18, Dimensions(0, 0, 0, 0, 1),name='attokelvin',ascii_symbol='aK',symbol='aK') +exakelvin = NamedUnit(1e+18, Dimensions(0, 0, 0, 0, 1, 0, 0),name='exakelvin',ascii_symbol='EK',symbol='EK') +petakelvin = NamedUnit(1000000000000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='petakelvin',ascii_symbol='PK',symbol='PK') +terakelvin = NamedUnit(1000000000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='terakelvin',ascii_symbol='TK',symbol='TK') +gigakelvin = NamedUnit(1000000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='gigakelvin',ascii_symbol='GK',symbol='GK') +megakelvin = NamedUnit(1000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='megakelvin',ascii_symbol='MK',symbol='MK') +kilokelvin = NamedUnit(1000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kilokelvin',ascii_symbol='kK',symbol='kK') +millikelvin = NamedUnit(0.001, Dimensions(0, 0, 0, 0, 1, 0, 0),name='millikelvin',ascii_symbol='mK',symbol='mK') +microkelvin = NamedUnit(1e-06, Dimensions(0, 0, 0, 0, 1, 0, 0),name='microkelvin',ascii_symbol='uK',symbol='µK') +nanokelvin = NamedUnit(1e-09, Dimensions(0, 0, 0, 0, 1, 0, 0),name='nanokelvin',ascii_symbol='nK',symbol='nK') +picokelvin = NamedUnit(1e-12, Dimensions(0, 0, 0, 0, 1, 0, 0),name='picokelvin',ascii_symbol='pK',symbol='pK') +femtokelvin = NamedUnit(1e-15, Dimensions(0, 0, 0, 0, 1, 0, 0),name='femtokelvin',ascii_symbol='fK',symbol='fK') +attokelvin = NamedUnit(1e-18, Dimensions(0, 0, 0, 0, 1, 0, 0),name='attokelvin',ascii_symbol='aK',symbol='aK') hertz = NamedUnit(1, Dimensions(0, -1, 0, 0, 0, 0, 0),name='hertz',ascii_symbol='Hz',symbol='Hz') -exahertz = NamedUnit(1e+18, Dimensions(0, -1, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') -petahertz = NamedUnit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') -terahertz = NamedUnit(1000000000000.0, Dimensions(0, -1, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') -gigahertz = NamedUnit(1000000000.0, Dimensions(0, -1, 0, 0, 0),name='gigahertz',ascii_symbol='GHz',symbol='GHz') -megahertz = NamedUnit(1000000.0, Dimensions(0, -1, 0, 0, 0),name='megahertz',ascii_symbol='MHz',symbol='MHz') -kilohertz = NamedUnit(1000.0, Dimensions(0, -1, 0, 0, 0),name='kilohertz',ascii_symbol='kHz',symbol='kHz') -millihertz = NamedUnit(0.001, Dimensions(0, -1, 0, 0, 0),name='millihertz',ascii_symbol='mHz',symbol='mHz') -microhertz = NamedUnit(1e-06, Dimensions(0, -1, 0, 0, 0),name='microhertz',ascii_symbol='uHz',symbol='µHz') -nanohertz = NamedUnit(1e-09, Dimensions(0, -1, 0, 0, 0),name='nanohertz',ascii_symbol='nHz',symbol='nHz') -picohertz = NamedUnit(1e-12, Dimensions(0, -1, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') -femtohertz = NamedUnit(1e-15, Dimensions(0, -1, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') -attohertz = NamedUnit(1e-18, Dimensions(0, -1, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') +exahertz = NamedUnit(1e+18, Dimensions(0, -1, 0, 0, 0, 0, 0),name='exahertz',ascii_symbol='EHz',symbol='EHz') +petahertz = NamedUnit(1000000000000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='petahertz',ascii_symbol='PHz',symbol='PHz') +terahertz = NamedUnit(1000000000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='terahertz',ascii_symbol='THz',symbol='THz') +gigahertz = NamedUnit(1000000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='gigahertz',ascii_symbol='GHz',symbol='GHz') +megahertz = NamedUnit(1000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='megahertz',ascii_symbol='MHz',symbol='MHz') +kilohertz = NamedUnit(1000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='kilohertz',ascii_symbol='kHz',symbol='kHz') +millihertz = NamedUnit(0.001, Dimensions(0, -1, 0, 0, 0, 0, 0),name='millihertz',ascii_symbol='mHz',symbol='mHz') +microhertz = NamedUnit(1e-06, Dimensions(0, -1, 0, 0, 0, 0, 0),name='microhertz',ascii_symbol='uHz',symbol='µHz') +nanohertz = NamedUnit(1e-09, Dimensions(0, -1, 0, 0, 0, 0, 0),name='nanohertz',ascii_symbol='nHz',symbol='nHz') +picohertz = NamedUnit(1e-12, Dimensions(0, -1, 0, 0, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') +femtohertz = NamedUnit(1e-15, Dimensions(0, -1, 0, 0, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') +attohertz = NamedUnit(1e-18, Dimensions(0, -1, 0, 0, 0, 0, 0),name='attohertz',ascii_symbol='aHz',symbol='aHz') newtons = NamedUnit(1, Dimensions(1, -2, 1, 0, 0, 0, 0),name='newtons',ascii_symbol='N',symbol='N') -exanewtons = NamedUnit(1e+18, Dimensions(1, -2, 1, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') -petanewtons = NamedUnit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') -teranewtons = NamedUnit(1000000000000.0, Dimensions(1, -2, 1, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') -giganewtons = NamedUnit(1000000000.0, Dimensions(1, -2, 1, 0, 0),name='giganewtons',ascii_symbol='GN',symbol='GN') -meganewtons = NamedUnit(1000000.0, Dimensions(1, -2, 1, 0, 0),name='meganewtons',ascii_symbol='MN',symbol='MN') -kilonewtons = NamedUnit(1000.0, Dimensions(1, -2, 1, 0, 0),name='kilonewtons',ascii_symbol='kN',symbol='kN') -millinewtons = NamedUnit(0.001, Dimensions(1, -2, 1, 0, 0),name='millinewtons',ascii_symbol='mN',symbol='mN') -micronewtons = NamedUnit(1e-06, Dimensions(1, -2, 1, 0, 0),name='micronewtons',ascii_symbol='uN',symbol='µN') -nanonewtons = NamedUnit(1e-09, Dimensions(1, -2, 1, 0, 0),name='nanonewtons',ascii_symbol='nN',symbol='nN') -piconewtons = NamedUnit(1e-12, Dimensions(1, -2, 1, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') -femtonewtons = NamedUnit(1e-15, Dimensions(1, -2, 1, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') -attonewtons = NamedUnit(1e-18, Dimensions(1, -2, 1, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') +exanewtons = NamedUnit(1e+18, Dimensions(1, -2, 1, 0, 0, 0, 0),name='exanewtons',ascii_symbol='EN',symbol='EN') +petanewtons = NamedUnit(1000000000000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='petanewtons',ascii_symbol='PN',symbol='PN') +teranewtons = NamedUnit(1000000000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='teranewtons',ascii_symbol='TN',symbol='TN') +giganewtons = NamedUnit(1000000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='giganewtons',ascii_symbol='GN',symbol='GN') +meganewtons = NamedUnit(1000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='meganewtons',ascii_symbol='MN',symbol='MN') +kilonewtons = NamedUnit(1000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kilonewtons',ascii_symbol='kN',symbol='kN') +millinewtons = NamedUnit(0.001, Dimensions(1, -2, 1, 0, 0, 0, 0),name='millinewtons',ascii_symbol='mN',symbol='mN') +micronewtons = NamedUnit(1e-06, Dimensions(1, -2, 1, 0, 0, 0, 0),name='micronewtons',ascii_symbol='uN',symbol='µN') +nanonewtons = NamedUnit(1e-09, Dimensions(1, -2, 1, 0, 0, 0, 0),name='nanonewtons',ascii_symbol='nN',symbol='nN') +piconewtons = NamedUnit(1e-12, Dimensions(1, -2, 1, 0, 0, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') +femtonewtons = NamedUnit(1e-15, Dimensions(1, -2, 1, 0, 0, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') +attonewtons = NamedUnit(1e-18, Dimensions(1, -2, 1, 0, 0, 0, 0),name='attonewtons',ascii_symbol='aN',symbol='aN') pascals = NamedUnit(1, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pascals',ascii_symbol='Pa',symbol='Pa') -exapascals = NamedUnit(1e+18, Dimensions(-1, -2, 1, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') -petapascals = NamedUnit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') -terapascals = NamedUnit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') -gigapascals = NamedUnit(1000000000.0, Dimensions(-1, -2, 1, 0, 0),name='gigapascals',ascii_symbol='GPa',symbol='GPa') -megapascals = NamedUnit(1000000.0, Dimensions(-1, -2, 1, 0, 0),name='megapascals',ascii_symbol='MPa',symbol='MPa') -kilopascals = NamedUnit(1000.0, Dimensions(-1, -2, 1, 0, 0),name='kilopascals',ascii_symbol='kPa',symbol='kPa') -millipascals = NamedUnit(0.001, Dimensions(-1, -2, 1, 0, 0),name='millipascals',ascii_symbol='mPa',symbol='mPa') -micropascals = NamedUnit(1e-06, Dimensions(-1, -2, 1, 0, 0),name='micropascals',ascii_symbol='uPa',symbol='µPa') -nanopascals = NamedUnit(1e-09, Dimensions(-1, -2, 1, 0, 0),name='nanopascals',ascii_symbol='nPa',symbol='nPa') -picopascals = NamedUnit(1e-12, Dimensions(-1, -2, 1, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') -femtopascals = NamedUnit(1e-15, Dimensions(-1, -2, 1, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') -attopascals = NamedUnit(1e-18, Dimensions(-1, -2, 1, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') +exapascals = NamedUnit(1e+18, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='exapascals',ascii_symbol='EPa',symbol='EPa') +petapascals = NamedUnit(1000000000000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='petapascals',ascii_symbol='PPa',symbol='PPa') +terapascals = NamedUnit(1000000000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='terapascals',ascii_symbol='TPa',symbol='TPa') +gigapascals = NamedUnit(1000000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='gigapascals',ascii_symbol='GPa',symbol='GPa') +megapascals = NamedUnit(1000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='megapascals',ascii_symbol='MPa',symbol='MPa') +kilopascals = NamedUnit(1000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='kilopascals',ascii_symbol='kPa',symbol='kPa') +millipascals = NamedUnit(0.001, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='millipascals',ascii_symbol='mPa',symbol='mPa') +micropascals = NamedUnit(1e-06, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='micropascals',ascii_symbol='uPa',symbol='µPa') +nanopascals = NamedUnit(1e-09, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='nanopascals',ascii_symbol='nPa',symbol='nPa') +picopascals = NamedUnit(1e-12, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') +femtopascals = NamedUnit(1e-15, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') +attopascals = NamedUnit(1e-18, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='attopascals',ascii_symbol='aPa',symbol='aPa') joules = NamedUnit(1, Dimensions(2, -2, 1, 0, 0, 0, 0),name='joules',ascii_symbol='J',symbol='J') -exajoules = NamedUnit(1e+18, Dimensions(2, -2, 1, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') -petajoules = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') -terajoules = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') -gigajoules = NamedUnit(1000000000.0, Dimensions(2, -2, 1, 0, 0),name='gigajoules',ascii_symbol='GJ',symbol='GJ') -megajoules = NamedUnit(1000000.0, Dimensions(2, -2, 1, 0, 0),name='megajoules',ascii_symbol='MJ',symbol='MJ') -kilojoules = NamedUnit(1000.0, Dimensions(2, -2, 1, 0, 0),name='kilojoules',ascii_symbol='kJ',symbol='kJ') -millijoules = NamedUnit(0.001, Dimensions(2, -2, 1, 0, 0),name='millijoules',ascii_symbol='mJ',symbol='mJ') -microjoules = NamedUnit(1e-06, Dimensions(2, -2, 1, 0, 0),name='microjoules',ascii_symbol='uJ',symbol='µJ') -nanojoules = NamedUnit(1e-09, Dimensions(2, -2, 1, 0, 0),name='nanojoules',ascii_symbol='nJ',symbol='nJ') -picojoules = NamedUnit(1e-12, Dimensions(2, -2, 1, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') -femtojoules = NamedUnit(1e-15, Dimensions(2, -2, 1, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') -attojoules = NamedUnit(1e-18, Dimensions(2, -2, 1, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') +exajoules = NamedUnit(1e+18, Dimensions(2, -2, 1, 0, 0, 0, 0),name='exajoules',ascii_symbol='EJ',symbol='EJ') +petajoules = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='petajoules',ascii_symbol='PJ',symbol='PJ') +terajoules = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='terajoules',ascii_symbol='TJ',symbol='TJ') +gigajoules = NamedUnit(1000000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='gigajoules',ascii_symbol='GJ',symbol='GJ') +megajoules = NamedUnit(1000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='megajoules',ascii_symbol='MJ',symbol='MJ') +kilojoules = NamedUnit(1000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='kilojoules',ascii_symbol='kJ',symbol='kJ') +millijoules = NamedUnit(0.001, Dimensions(2, -2, 1, 0, 0, 0, 0),name='millijoules',ascii_symbol='mJ',symbol='mJ') +microjoules = NamedUnit(1e-06, Dimensions(2, -2, 1, 0, 0, 0, 0),name='microjoules',ascii_symbol='uJ',symbol='µJ') +nanojoules = NamedUnit(1e-09, Dimensions(2, -2, 1, 0, 0, 0, 0),name='nanojoules',ascii_symbol='nJ',symbol='nJ') +picojoules = NamedUnit(1e-12, Dimensions(2, -2, 1, 0, 0, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') +femtojoules = NamedUnit(1e-15, Dimensions(2, -2, 1, 0, 0, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') +attojoules = NamedUnit(1e-18, Dimensions(2, -2, 1, 0, 0, 0, 0),name='attojoules',ascii_symbol='aJ',symbol='aJ') watts = NamedUnit(1, Dimensions(2, -3, 1, 0, 0, 0, 0),name='watts',ascii_symbol='W',symbol='W') -exawatts = NamedUnit(1e+18, Dimensions(2, -3, 1, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') -petawatts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') -terawatts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') -gigawatts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, 0, 0),name='gigawatts',ascii_symbol='GW',symbol='GW') -megawatts = NamedUnit(1000000.0, Dimensions(2, -3, 1, 0, 0),name='megawatts',ascii_symbol='MW',symbol='MW') -kilowatts = NamedUnit(1000.0, Dimensions(2, -3, 1, 0, 0),name='kilowatts',ascii_symbol='kW',symbol='kW') -milliwatts = NamedUnit(0.001, Dimensions(2, -3, 1, 0, 0),name='milliwatts',ascii_symbol='mW',symbol='mW') -microwatts = NamedUnit(1e-06, Dimensions(2, -3, 1, 0, 0),name='microwatts',ascii_symbol='uW',symbol='µW') -nanowatts = NamedUnit(1e-09, Dimensions(2, -3, 1, 0, 0),name='nanowatts',ascii_symbol='nW',symbol='nW') -picowatts = NamedUnit(1e-12, Dimensions(2, -3, 1, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') -femtowatts = NamedUnit(1e-15, Dimensions(2, -3, 1, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') -attowatts = NamedUnit(1e-18, Dimensions(2, -3, 1, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') +exawatts = NamedUnit(1e+18, Dimensions(2, -3, 1, 0, 0, 0, 0),name='exawatts',ascii_symbol='EW',symbol='EW') +petawatts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='petawatts',ascii_symbol='PW',symbol='PW') +terawatts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='terawatts',ascii_symbol='TW',symbol='TW') +gigawatts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='gigawatts',ascii_symbol='GW',symbol='GW') +megawatts = NamedUnit(1000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='megawatts',ascii_symbol='MW',symbol='MW') +kilowatts = NamedUnit(1000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='kilowatts',ascii_symbol='kW',symbol='kW') +milliwatts = NamedUnit(0.001, Dimensions(2, -3, 1, 0, 0, 0, 0),name='milliwatts',ascii_symbol='mW',symbol='mW') +microwatts = NamedUnit(1e-06, Dimensions(2, -3, 1, 0, 0, 0, 0),name='microwatts',ascii_symbol='uW',symbol='µW') +nanowatts = NamedUnit(1e-09, Dimensions(2, -3, 1, 0, 0, 0, 0),name='nanowatts',ascii_symbol='nW',symbol='nW') +picowatts = NamedUnit(1e-12, Dimensions(2, -3, 1, 0, 0, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') +femtowatts = NamedUnit(1e-15, Dimensions(2, -3, 1, 0, 0, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') +attowatts = NamedUnit(1e-18, Dimensions(2, -3, 1, 0, 0, 0, 0),name='attowatts',ascii_symbol='aW',symbol='aW') coulombs = NamedUnit(1, Dimensions(0, 1, 0, 1, 0, 0, 0),name='coulombs',ascii_symbol='C',symbol='C') -exacoulombs = NamedUnit(1e+18, Dimensions(0, 1, 0, 1, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') -petacoulombs = NamedUnit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') -teracoulombs = NamedUnit(1000000000000.0, Dimensions(0, 1, 0, 1, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') -gigacoulombs = NamedUnit(1000000000.0, Dimensions(0, 1, 0, 1, 0),name='gigacoulombs',ascii_symbol='GC',symbol='GC') -megacoulombs = NamedUnit(1000000.0, Dimensions(0, 1, 0, 1, 0),name='megacoulombs',ascii_symbol='MC',symbol='MC') -kilocoulombs = NamedUnit(1000.0, Dimensions(0, 1, 0, 1, 0),name='kilocoulombs',ascii_symbol='kC',symbol='kC') -millicoulombs = NamedUnit(0.001, Dimensions(0, 1, 0, 1, 0),name='millicoulombs',ascii_symbol='mC',symbol='mC') -microcoulombs = NamedUnit(1e-06, Dimensions(0, 1, 0, 1, 0),name='microcoulombs',ascii_symbol='uC',symbol='µC') -nanocoulombs = NamedUnit(1e-09, Dimensions(0, 1, 0, 1, 0),name='nanocoulombs',ascii_symbol='nC',symbol='nC') -picocoulombs = NamedUnit(1e-12, Dimensions(0, 1, 0, 1, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') -femtocoulombs = NamedUnit(1e-15, Dimensions(0, 1, 0, 1, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') -attocoulombs = NamedUnit(1e-18, Dimensions(0, 1, 0, 1, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') +exacoulombs = NamedUnit(1e+18, Dimensions(0, 1, 0, 1, 0, 0, 0),name='exacoulombs',ascii_symbol='EC',symbol='EC') +petacoulombs = NamedUnit(1000000000000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='petacoulombs',ascii_symbol='PC',symbol='PC') +teracoulombs = NamedUnit(1000000000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='teracoulombs',ascii_symbol='TC',symbol='TC') +gigacoulombs = NamedUnit(1000000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='gigacoulombs',ascii_symbol='GC',symbol='GC') +megacoulombs = NamedUnit(1000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='megacoulombs',ascii_symbol='MC',symbol='MC') +kilocoulombs = NamedUnit(1000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='kilocoulombs',ascii_symbol='kC',symbol='kC') +millicoulombs = NamedUnit(0.001, Dimensions(0, 1, 0, 1, 0, 0, 0),name='millicoulombs',ascii_symbol='mC',symbol='mC') +microcoulombs = NamedUnit(1e-06, Dimensions(0, 1, 0, 1, 0, 0, 0),name='microcoulombs',ascii_symbol='uC',symbol='µC') +nanocoulombs = NamedUnit(1e-09, Dimensions(0, 1, 0, 1, 0, 0, 0),name='nanocoulombs',ascii_symbol='nC',symbol='nC') +picocoulombs = NamedUnit(1e-12, Dimensions(0, 1, 0, 1, 0, 0, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') +femtocoulombs = NamedUnit(1e-15, Dimensions(0, 1, 0, 1, 0, 0, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') +attocoulombs = NamedUnit(1e-18, Dimensions(0, 1, 0, 1, 0, 0, 0),name='attocoulombs',ascii_symbol='aC',symbol='aC') volts = NamedUnit(1, Dimensions(2, -3, 1, -1, 0, 0, 0),name='volts',ascii_symbol='V',symbol='V') -exavolts = NamedUnit(1e+18, Dimensions(2, -3, 1, -1, 0),name='exavolts',ascii_symbol='EV',symbol='EV') -petavolts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0),name='petavolts',ascii_symbol='PV',symbol='PV') -teravolts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -1, 0),name='teravolts',ascii_symbol='TV',symbol='TV') -gigavolts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -1, 0),name='gigavolts',ascii_symbol='GV',symbol='GV') -megavolts = NamedUnit(1000000.0, Dimensions(2, -3, 1, -1, 0),name='megavolts',ascii_symbol='MV',symbol='MV') -kilovolts = NamedUnit(1000.0, Dimensions(2, -3, 1, -1, 0),name='kilovolts',ascii_symbol='kV',symbol='kV') -millivolts = NamedUnit(0.001, Dimensions(2, -3, 1, -1, 0),name='millivolts',ascii_symbol='mV',symbol='mV') -microvolts = NamedUnit(1e-06, Dimensions(2, -3, 1, -1, 0),name='microvolts',ascii_symbol='uV',symbol='µV') -nanovolts = NamedUnit(1e-09, Dimensions(2, -3, 1, -1, 0),name='nanovolts',ascii_symbol='nV',symbol='nV') -picovolts = NamedUnit(1e-12, Dimensions(2, -3, 1, -1, 0),name='picovolts',ascii_symbol='pV',symbol='pV') -femtovolts = NamedUnit(1e-15, Dimensions(2, -3, 1, -1, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') -attovolts = NamedUnit(1e-18, Dimensions(2, -3, 1, -1, 0),name='attovolts',ascii_symbol='aV',symbol='aV') +exavolts = NamedUnit(1e+18, Dimensions(2, -3, 1, -1, 0, 0, 0),name='exavolts',ascii_symbol='EV',symbol='EV') +petavolts = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='petavolts',ascii_symbol='PV',symbol='PV') +teravolts = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='teravolts',ascii_symbol='TV',symbol='TV') +gigavolts = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='gigavolts',ascii_symbol='GV',symbol='GV') +megavolts = NamedUnit(1000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='megavolts',ascii_symbol='MV',symbol='MV') +kilovolts = NamedUnit(1000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='kilovolts',ascii_symbol='kV',symbol='kV') +millivolts = NamedUnit(0.001, Dimensions(2, -3, 1, -1, 0, 0, 0),name='millivolts',ascii_symbol='mV',symbol='mV') +microvolts = NamedUnit(1e-06, Dimensions(2, -3, 1, -1, 0, 0, 0),name='microvolts',ascii_symbol='uV',symbol='µV') +nanovolts = NamedUnit(1e-09, Dimensions(2, -3, 1, -1, 0, 0, 0),name='nanovolts',ascii_symbol='nV',symbol='nV') +picovolts = NamedUnit(1e-12, Dimensions(2, -3, 1, -1, 0, 0, 0),name='picovolts',ascii_symbol='pV',symbol='pV') +femtovolts = NamedUnit(1e-15, Dimensions(2, -3, 1, -1, 0, 0, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') +attovolts = NamedUnit(1e-18, Dimensions(2, -3, 1, -1, 0, 0, 0),name='attovolts',ascii_symbol='aV',symbol='aV') ohms = NamedUnit(1, Dimensions(2, -3, 1, -2, 0, 0, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') -exaohms = NamedUnit(1e+18, Dimensions(2, -3, 1, -2, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') -petaohms = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') -teraohms = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -2, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') -gigaohms = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -2, 0),name='gigaohms',ascii_symbol='GOhm',symbol='GΩ') -megaohms = NamedUnit(1000000.0, Dimensions(2, -3, 1, -2, 0),name='megaohms',ascii_symbol='MOhm',symbol='MΩ') -kiloohms = NamedUnit(1000.0, Dimensions(2, -3, 1, -2, 0),name='kiloohms',ascii_symbol='kOhm',symbol='kΩ') -milliohms = NamedUnit(0.001, Dimensions(2, -3, 1, -2, 0),name='milliohms',ascii_symbol='mOhm',symbol='mΩ') -microohms = NamedUnit(1e-06, Dimensions(2, -3, 1, -2, 0),name='microohms',ascii_symbol='uOhm',symbol='µΩ') -nanoohms = NamedUnit(1e-09, Dimensions(2, -3, 1, -2, 0),name='nanoohms',ascii_symbol='nOhm',symbol='nΩ') -picoohms = NamedUnit(1e-12, Dimensions(2, -3, 1, -2, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') -femtoohms = NamedUnit(1e-15, Dimensions(2, -3, 1, -2, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') -attoohms = NamedUnit(1e-18, Dimensions(2, -3, 1, -2, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') +exaohms = NamedUnit(1e+18, Dimensions(2, -3, 1, -2, 0, 0, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') +petaohms = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') +teraohms = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') +gigaohms = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='gigaohms',ascii_symbol='GOhm',symbol='GΩ') +megaohms = NamedUnit(1000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='megaohms',ascii_symbol='MOhm',symbol='MΩ') +kiloohms = NamedUnit(1000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='kiloohms',ascii_symbol='kOhm',symbol='kΩ') +milliohms = NamedUnit(0.001, Dimensions(2, -3, 1, -2, 0, 0, 0),name='milliohms',ascii_symbol='mOhm',symbol='mΩ') +microohms = NamedUnit(1e-06, Dimensions(2, -3, 1, -2, 0, 0, 0),name='microohms',ascii_symbol='uOhm',symbol='µΩ') +nanoohms = NamedUnit(1e-09, Dimensions(2, -3, 1, -2, 0, 0, 0),name='nanoohms',ascii_symbol='nOhm',symbol='nΩ') +picoohms = NamedUnit(1e-12, Dimensions(2, -3, 1, -2, 0, 0, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') +femtoohms = NamedUnit(1e-15, Dimensions(2, -3, 1, -2, 0, 0, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') +attoohms = NamedUnit(1e-18, Dimensions(2, -3, 1, -2, 0, 0, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') farads = NamedUnit(1, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='farads',ascii_symbol='F',symbol='F') -exafarads = NamedUnit(1e+18, Dimensions(-2, 4, -1, 2, 0),name='exafarads',ascii_symbol='EF',symbol='EF') -petafarads = NamedUnit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='petafarads',ascii_symbol='PF',symbol='PF') -terafarads = NamedUnit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0),name='terafarads',ascii_symbol='TF',symbol='TF') -gigafarads = NamedUnit(1000000000.0, Dimensions(-2, 4, -1, 2, 0),name='gigafarads',ascii_symbol='GF',symbol='GF') -megafarads = NamedUnit(1000000.0, Dimensions(-2, 4, -1, 2, 0),name='megafarads',ascii_symbol='MF',symbol='MF') -kilofarads = NamedUnit(1000.0, Dimensions(-2, 4, -1, 2, 0),name='kilofarads',ascii_symbol='kF',symbol='kF') -millifarads = NamedUnit(0.001, Dimensions(-2, 4, -1, 2, 0),name='millifarads',ascii_symbol='mF',symbol='mF') -microfarads = NamedUnit(1e-06, Dimensions(-2, 4, -1, 2, 0),name='microfarads',ascii_symbol='uF',symbol='µF') -nanofarads = NamedUnit(1e-09, Dimensions(-2, 4, -1, 2, 0),name='nanofarads',ascii_symbol='nF',symbol='nF') -picofarads = NamedUnit(1e-12, Dimensions(-2, 4, -1, 2, 0),name='picofarads',ascii_symbol='pF',symbol='pF') -femtofarads = NamedUnit(1e-15, Dimensions(-2, 4, -1, 2, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') -attofarads = NamedUnit(1e-18, Dimensions(-2, 4, -1, 2, 0),name='attofarads',ascii_symbol='aF',symbol='aF') +exafarads = NamedUnit(1e+18, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='exafarads',ascii_symbol='EF',symbol='EF') +petafarads = NamedUnit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='petafarads',ascii_symbol='PF',symbol='PF') +terafarads = NamedUnit(1000000000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='terafarads',ascii_symbol='TF',symbol='TF') +gigafarads = NamedUnit(1000000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='gigafarads',ascii_symbol='GF',symbol='GF') +megafarads = NamedUnit(1000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='megafarads',ascii_symbol='MF',symbol='MF') +kilofarads = NamedUnit(1000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='kilofarads',ascii_symbol='kF',symbol='kF') +millifarads = NamedUnit(0.001, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='millifarads',ascii_symbol='mF',symbol='mF') +microfarads = NamedUnit(1e-06, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='microfarads',ascii_symbol='uF',symbol='µF') +nanofarads = NamedUnit(1e-09, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='nanofarads',ascii_symbol='nF',symbol='nF') +picofarads = NamedUnit(1e-12, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='picofarads',ascii_symbol='pF',symbol='pF') +femtofarads = NamedUnit(1e-15, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') +attofarads = NamedUnit(1e-18, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='attofarads',ascii_symbol='aF',symbol='aF') siemens = NamedUnit(1, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='siemens',ascii_symbol='S',symbol='S') -exasiemens = NamedUnit(1e+18, Dimensions(-2, 3, -1, 2, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') -petasiemens = NamedUnit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') -terasiemens = NamedUnit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') -gigasiemens = NamedUnit(1000000000.0, Dimensions(-2, 3, -1, 2, 0),name='gigasiemens',ascii_symbol='GS',symbol='GS') -megasiemens = NamedUnit(1000000.0, Dimensions(-2, 3, -1, 2, 0),name='megasiemens',ascii_symbol='MS',symbol='MS') -kilosiemens = NamedUnit(1000.0, Dimensions(-2, 3, -1, 2, 0),name='kilosiemens',ascii_symbol='kS',symbol='kS') -millisiemens = NamedUnit(0.001, Dimensions(-2, 3, -1, 2, 0),name='millisiemens',ascii_symbol='mS',symbol='mS') -microsiemens = NamedUnit(1e-06, Dimensions(-2, 3, -1, 2, 0),name='microsiemens',ascii_symbol='uS',symbol='µS') -nanosiemens = NamedUnit(1e-09, Dimensions(-2, 3, -1, 2, 0),name='nanosiemens',ascii_symbol='nS',symbol='nS') -picosiemens = NamedUnit(1e-12, Dimensions(-2, 3, -1, 2, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') -femtosiemens = NamedUnit(1e-15, Dimensions(-2, 3, -1, 2, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') -attosiemens = NamedUnit(1e-18, Dimensions(-2, 3, -1, 2, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') +exasiemens = NamedUnit(1e+18, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='exasiemens',ascii_symbol='ES',symbol='ES') +petasiemens = NamedUnit(1000000000000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='petasiemens',ascii_symbol='PS',symbol='PS') +terasiemens = NamedUnit(1000000000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='terasiemens',ascii_symbol='TS',symbol='TS') +gigasiemens = NamedUnit(1000000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='gigasiemens',ascii_symbol='GS',symbol='GS') +megasiemens = NamedUnit(1000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='megasiemens',ascii_symbol='MS',symbol='MS') +kilosiemens = NamedUnit(1000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='kilosiemens',ascii_symbol='kS',symbol='kS') +millisiemens = NamedUnit(0.001, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='millisiemens',ascii_symbol='mS',symbol='mS') +microsiemens = NamedUnit(1e-06, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='microsiemens',ascii_symbol='uS',symbol='µS') +nanosiemens = NamedUnit(1e-09, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='nanosiemens',ascii_symbol='nS',symbol='nS') +picosiemens = NamedUnit(1e-12, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') +femtosiemens = NamedUnit(1e-15, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') +attosiemens = NamedUnit(1e-18, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='attosiemens',ascii_symbol='aS',symbol='aS') webers = NamedUnit(1, Dimensions(2, -2, 1, -1, 0, 0, 0),name='webers',ascii_symbol='Wb',symbol='Wb') -exawebers = NamedUnit(1e+18, Dimensions(2, -2, 1, -1, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') -petawebers = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') -terawebers = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -1, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') -gigawebers = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -1, 0),name='gigawebers',ascii_symbol='GWb',symbol='GWb') -megawebers = NamedUnit(1000000.0, Dimensions(2, -2, 1, -1, 0),name='megawebers',ascii_symbol='MWb',symbol='MWb') -kilowebers = NamedUnit(1000.0, Dimensions(2, -2, 1, -1, 0),name='kilowebers',ascii_symbol='kWb',symbol='kWb') -milliwebers = NamedUnit(0.001, Dimensions(2, -2, 1, -1, 0),name='milliwebers',ascii_symbol='mWb',symbol='mWb') -microwebers = NamedUnit(1e-06, Dimensions(2, -2, 1, -1, 0),name='microwebers',ascii_symbol='uWb',symbol='µWb') -nanowebers = NamedUnit(1e-09, Dimensions(2, -2, 1, -1, 0),name='nanowebers',ascii_symbol='nWb',symbol='nWb') -picowebers = NamedUnit(1e-12, Dimensions(2, -2, 1, -1, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') -femtowebers = NamedUnit(1e-15, Dimensions(2, -2, 1, -1, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') -attowebers = NamedUnit(1e-18, Dimensions(2, -2, 1, -1, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') +exawebers = NamedUnit(1e+18, Dimensions(2, -2, 1, -1, 0, 0, 0),name='exawebers',ascii_symbol='EWb',symbol='EWb') +petawebers = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='petawebers',ascii_symbol='PWb',symbol='PWb') +terawebers = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='terawebers',ascii_symbol='TWb',symbol='TWb') +gigawebers = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='gigawebers',ascii_symbol='GWb',symbol='GWb') +megawebers = NamedUnit(1000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='megawebers',ascii_symbol='MWb',symbol='MWb') +kilowebers = NamedUnit(1000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='kilowebers',ascii_symbol='kWb',symbol='kWb') +milliwebers = NamedUnit(0.001, Dimensions(2, -2, 1, -1, 0, 0, 0),name='milliwebers',ascii_symbol='mWb',symbol='mWb') +microwebers = NamedUnit(1e-06, Dimensions(2, -2, 1, -1, 0, 0, 0),name='microwebers',ascii_symbol='uWb',symbol='µWb') +nanowebers = NamedUnit(1e-09, Dimensions(2, -2, 1, -1, 0, 0, 0),name='nanowebers',ascii_symbol='nWb',symbol='nWb') +picowebers = NamedUnit(1e-12, Dimensions(2, -2, 1, -1, 0, 0, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') +femtowebers = NamedUnit(1e-15, Dimensions(2, -2, 1, -1, 0, 0, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') +attowebers = NamedUnit(1e-18, Dimensions(2, -2, 1, -1, 0, 0, 0),name='attowebers',ascii_symbol='aWb',symbol='aWb') tesla = NamedUnit(1, Dimensions(0, -2, 1, -1, 0, 0, 0),name='tesla',ascii_symbol='T',symbol='T') -exatesla = NamedUnit(1e+18, Dimensions(0, -2, 1, -1, 0),name='exatesla',ascii_symbol='ET',symbol='ET') -petatesla = NamedUnit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0),name='petatesla',ascii_symbol='PT',symbol='PT') -teratesla = NamedUnit(1000000000000.0, Dimensions(0, -2, 1, -1, 0),name='teratesla',ascii_symbol='TT',symbol='TT') -gigatesla = NamedUnit(1000000000.0, Dimensions(0, -2, 1, -1, 0),name='gigatesla',ascii_symbol='GT',symbol='GT') -megatesla = NamedUnit(1000000.0, Dimensions(0, -2, 1, -1, 0),name='megatesla',ascii_symbol='MT',symbol='MT') -kilotesla = NamedUnit(1000.0, Dimensions(0, -2, 1, -1, 0),name='kilotesla',ascii_symbol='kT',symbol='kT') -millitesla = NamedUnit(0.001, Dimensions(0, -2, 1, -1, 0),name='millitesla',ascii_symbol='mT',symbol='mT') -microtesla = NamedUnit(1e-06, Dimensions(0, -2, 1, -1, 0),name='microtesla',ascii_symbol='uT',symbol='µT') -nanotesla = NamedUnit(1e-09, Dimensions(0, -2, 1, -1, 0),name='nanotesla',ascii_symbol='nT',symbol='nT') -picotesla = NamedUnit(1e-12, Dimensions(0, -2, 1, -1, 0),name='picotesla',ascii_symbol='pT',symbol='pT') -femtotesla = NamedUnit(1e-15, Dimensions(0, -2, 1, -1, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') -attotesla = NamedUnit(1e-18, Dimensions(0, -2, 1, -1, 0),name='attotesla',ascii_symbol='aT',symbol='aT') +exatesla = NamedUnit(1e+18, Dimensions(0, -2, 1, -1, 0, 0, 0),name='exatesla',ascii_symbol='ET',symbol='ET') +petatesla = NamedUnit(1000000000000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='petatesla',ascii_symbol='PT',symbol='PT') +teratesla = NamedUnit(1000000000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='teratesla',ascii_symbol='TT',symbol='TT') +gigatesla = NamedUnit(1000000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='gigatesla',ascii_symbol='GT',symbol='GT') +megatesla = NamedUnit(1000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='megatesla',ascii_symbol='MT',symbol='MT') +kilotesla = NamedUnit(1000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='kilotesla',ascii_symbol='kT',symbol='kT') +millitesla = NamedUnit(0.001, Dimensions(0, -2, 1, -1, 0, 0, 0),name='millitesla',ascii_symbol='mT',symbol='mT') +microtesla = NamedUnit(1e-06, Dimensions(0, -2, 1, -1, 0, 0, 0),name='microtesla',ascii_symbol='uT',symbol='µT') +nanotesla = NamedUnit(1e-09, Dimensions(0, -2, 1, -1, 0, 0, 0),name='nanotesla',ascii_symbol='nT',symbol='nT') +picotesla = NamedUnit(1e-12, Dimensions(0, -2, 1, -1, 0, 0, 0),name='picotesla',ascii_symbol='pT',symbol='pT') +femtotesla = NamedUnit(1e-15, Dimensions(0, -2, 1, -1, 0, 0, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') +attotesla = NamedUnit(1e-18, Dimensions(0, -2, 1, -1, 0, 0, 0),name='attotesla',ascii_symbol='aT',symbol='aT') henry = NamedUnit(1, Dimensions(2, -2, 1, -2, 0, 0, 0),name='henry',ascii_symbol='H',symbol='H') -exahenry = NamedUnit(1e+18, Dimensions(2, -2, 1, -2, 0),name='exahenry',ascii_symbol='EH',symbol='EH') -petahenry = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0),name='petahenry',ascii_symbol='PH',symbol='PH') -terahenry = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -2, 0),name='terahenry',ascii_symbol='TH',symbol='TH') -gigahenry = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -2, 0),name='gigahenry',ascii_symbol='GH',symbol='GH') -megahenry = NamedUnit(1000000.0, Dimensions(2, -2, 1, -2, 0),name='megahenry',ascii_symbol='MH',symbol='MH') -kilohenry = NamedUnit(1000.0, Dimensions(2, -2, 1, -2, 0),name='kilohenry',ascii_symbol='kH',symbol='kH') -millihenry = NamedUnit(0.001, Dimensions(2, -2, 1, -2, 0),name='millihenry',ascii_symbol='mH',symbol='mH') -microhenry = NamedUnit(1e-06, Dimensions(2, -2, 1, -2, 0),name='microhenry',ascii_symbol='uH',symbol='µH') -nanohenry = NamedUnit(1e-09, Dimensions(2, -2, 1, -2, 0),name='nanohenry',ascii_symbol='nH',symbol='nH') -picohenry = NamedUnit(1e-12, Dimensions(2, -2, 1, -2, 0),name='picohenry',ascii_symbol='pH',symbol='pH') -femtohenry = NamedUnit(1e-15, Dimensions(2, -2, 1, -2, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') -attohenry = NamedUnit(1e-18, Dimensions(2, -2, 1, -2, 0),name='attohenry',ascii_symbol='aH',symbol='aH') +exahenry = NamedUnit(1e+18, Dimensions(2, -2, 1, -2, 0, 0, 0),name='exahenry',ascii_symbol='EH',symbol='EH') +petahenry = NamedUnit(1000000000000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='petahenry',ascii_symbol='PH',symbol='PH') +terahenry = NamedUnit(1000000000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='terahenry',ascii_symbol='TH',symbol='TH') +gigahenry = NamedUnit(1000000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='gigahenry',ascii_symbol='GH',symbol='GH') +megahenry = NamedUnit(1000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='megahenry',ascii_symbol='MH',symbol='MH') +kilohenry = NamedUnit(1000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='kilohenry',ascii_symbol='kH',symbol='kH') +millihenry = NamedUnit(0.001, Dimensions(2, -2, 1, -2, 0, 0, 0),name='millihenry',ascii_symbol='mH',symbol='mH') +microhenry = NamedUnit(1e-06, Dimensions(2, -2, 1, -2, 0, 0, 0),name='microhenry',ascii_symbol='uH',symbol='µH') +nanohenry = NamedUnit(1e-09, Dimensions(2, -2, 1, -2, 0, 0, 0),name='nanohenry',ascii_symbol='nH',symbol='nH') +picohenry = NamedUnit(1e-12, Dimensions(2, -2, 1, -2, 0, 0, 0),name='picohenry',ascii_symbol='pH',symbol='pH') +femtohenry = NamedUnit(1e-15, Dimensions(2, -2, 1, -2, 0, 0, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') +attohenry = NamedUnit(1e-18, Dimensions(2, -2, 1, -2, 0, 0, 0),name='attohenry',ascii_symbol='aH',symbol='aH') angstroms = NamedUnit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') minutes = NamedUnit(60, Dimensions(0, 1, 0, 0, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') hours = NamedUnit(360, Dimensions(0, 1, 0, 0, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') @@ -669,26 +669,26 @@ def __init__(self, name: str, units: list[NamedUnit]): stradians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') litres = NamedUnit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') electronvolts = NamedUnit(1.602176634e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') -exaelectronvolts = NamedUnit(0.1602176634, Dimensions(2, -2, 1, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') -petaelectronvolts = NamedUnit(0.0001602176634, Dimensions(2, -2, 1, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') -teraelectronvolts = NamedUnit(1.602176634e-07, Dimensions(2, -2, 1, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') -gigaelectronvolts = NamedUnit(1.6021766339999998e-10, Dimensions(2, -2, 1, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') -megaelectronvolts = NamedUnit(1.6021766339999998e-13, Dimensions(2, -2, 1, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') -kiloelectronvolts = NamedUnit(1.602176634e-16, Dimensions(2, -2, 1, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') -millielectronvolts = NamedUnit(1.6021766339999998e-22, Dimensions(2, -2, 1, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') -microelectronvolts = NamedUnit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') -nanoelectronvolts = NamedUnit(1.602176634e-28, Dimensions(2, -2, 1, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') -picoelectronvolts = NamedUnit(1.6021766339999998e-31, Dimensions(2, -2, 1, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') -femtoelectronvolts = NamedUnit(1.602176634e-34, Dimensions(2, -2, 1, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') -attoelectronvolts = NamedUnit(1.602176634e-37, Dimensions(2, -2, 1, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') +exaelectronvolts = NamedUnit(0.1602176634, Dimensions(2, -2, 1, 0, 0, 0, 0),name='exaelectronvolts',ascii_symbol='EeV',symbol='EeV') +petaelectronvolts = NamedUnit(0.0001602176634, Dimensions(2, -2, 1, 0, 0, 0, 0),name='petaelectronvolts',ascii_symbol='PeV',symbol='PeV') +teraelectronvolts = NamedUnit(1.602176634e-07, Dimensions(2, -2, 1, 0, 0, 0, 0),name='teraelectronvolts',ascii_symbol='TeV',symbol='TeV') +gigaelectronvolts = NamedUnit(1.6021766339999998e-10, Dimensions(2, -2, 1, 0, 0, 0, 0),name='gigaelectronvolts',ascii_symbol='GeV',symbol='GeV') +megaelectronvolts = NamedUnit(1.6021766339999998e-13, Dimensions(2, -2, 1, 0, 0, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') +kiloelectronvolts = NamedUnit(1.602176634e-16, Dimensions(2, -2, 1, 0, 0, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') +millielectronvolts = NamedUnit(1.6021766339999998e-22, Dimensions(2, -2, 1, 0, 0, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') +microelectronvolts = NamedUnit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') +nanoelectronvolts = NamedUnit(1.602176634e-28, Dimensions(2, -2, 1, 0, 0, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') +picoelectronvolts = NamedUnit(1.6021766339999998e-31, Dimensions(2, -2, 1, 0, 0, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') +femtoelectronvolts = NamedUnit(1.602176634e-34, Dimensions(2, -2, 1, 0, 0, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') +attoelectronvolts = NamedUnit(1.602176634e-37, Dimensions(2, -2, 1, 0, 0, 0, 0),name='attoelectronvolts',ascii_symbol='aeV',symbol='aeV') atomic_mass_units = NamedUnit(1.660538921e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') moles = NamedUnit(6.02214076e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') -millimoles = NamedUnit(6.02214076e+20, Dimensions(0, 0, 0, 0, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') -micromoles = NamedUnit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') -nanomoles = NamedUnit(602214076000000.0, Dimensions(0, 0, 0, 0, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') -picomoles = NamedUnit(602214076000.0, Dimensions(0, 0, 0, 0, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') -femtomoles = NamedUnit(602214076.0, Dimensions(0, 0, 0, 0, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') -attomoles = NamedUnit(602214.076, Dimensions(0, 0, 0, 0, 0),name='attomoles',ascii_symbol='amol',symbol='amol') +millimoles = NamedUnit(6.02214076e+20, Dimensions(0, 0, 0, 0, 0, 1, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') +micromoles = NamedUnit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0, 1, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') +nanomoles = NamedUnit(602214076000000.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') +picomoles = NamedUnit(602214076000.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') +femtomoles = NamedUnit(602214076.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') +attomoles = NamedUnit(602214.076, Dimensions(0, 0, 0, 0, 0, 1, 0),name='attomoles',ascii_symbol='amol',symbol='amol') kg_force = NamedUnit(9.80665, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kg_force',ascii_symbol='kgForce',symbol='kgForce') degrees_celsius = NamedUnit(1, Dimensions(0, 0, 0, 0, 1, 0, 0),name='degrees_celsius',ascii_symbol='C',symbol='C') miles = NamedUnit(1609.344, Dimensions(1, 0, 0, 0, 0, 0, 0),name='miles',ascii_symbol='miles',symbol='miles') From f615426a1888fe588e1a847b83c6505a1cb0b509 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 2 Oct 2024 15:09:03 +0100 Subject: [PATCH 593/675] Unit name fixes --- sasdata/quantities/_build_tables.py | 17 +- sasdata/quantities/units.py | 900 ++++++++++++++-------------- 2 files changed, 463 insertions(+), 454 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 302cd8697..1af419395 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -237,18 +237,21 @@ def format_name(name: str): speed_dimensions = Dimensions(length=1, time=-1) accel_dimensions = Dimensions(length=1, time=-2) + length_special = length_special_symbol if length_special_symbol is not None else length_symbol + time_special = time_special_symbol if time_special_symbol is not None else time_symbol + fid.write(f"{speed_name} " f"= NamedUnit({length_scale / time_scale}, " f"Dimensions(length=1, time=-1), " f"name='{speed_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}', " - f"symbol='{length_special_symbol}{time_special_symbol}⁻¹')\n") + f"symbol='{length_special}{time_special}⁻¹')\n") fid.write(f"{accel_name} = NamedUnit({length_scale / time_scale**2}, " f"Dimensions(length=1, time=-2), " f"name='{accel_name}', " f"ascii_symbol='{length_symbol}/{time_symbol}^2', " - f"symbol='{length_special_symbol}{time_special_symbol}⁻²')\n") + f"symbol='{length_special}{time_special}⁻²')\n") unit_types[hash(speed_dimensions)].append(speed_name) unit_types[hash(accel_dimensions)].append(accel_name) @@ -261,12 +264,15 @@ def format_name(name: str): dimensions = Dimensions(length=-3, mass=1) + mass_special = mass_symbol if mass_special_symbol is None else mass_special_symbol + length_special = length_symbol if length_special_symbol is None else length_special_symbol + fid.write(f"{name} " f"= NamedUnit({mass_scale / length_scale**3}, " f"Dimensions(length=-3, mass=1), " f"name='{name}', " f"ascii_symbol='{mass_symbol} {length_symbol}^-3', " - f"symbol='{mass_special_symbol}{length_special_symbol}⁻³')\n") + f"symbol='{mass_special}{length_special}⁻³')\n") unit_types[hash(dimensions)].append(name) @@ -278,12 +284,15 @@ def format_name(name: str): dimensions = Dimensions(length=-3, moles_hint=1) + length_special = length_symbol if length_special_symbol is None else length_special_symbol + amount_special = amount_symbol if amount_special_symbol is None else amount_special_symbol + fid.write(f"{name} " f"= NamedUnit({amount_scale / length_scale**3}, " f"Dimensions(length=-3, moles_hint=1), " f"name='{name}', " f"ascii_symbol='{amount_symbol} {length_symbol}^-3', " - f"symbol='{amount_special_symbol}{length_special_symbol}⁻³')\n") + f"symbol='{amount_special}{length_special}⁻³')\n") unit_types[hash(dimensions)].append(name) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 2bec4bc7e..b02646aa3 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -801,30 +801,30 @@ def __init__(self, name: str, units: list[NamedUnit]): per_inch = NamedUnit(39.37007874015748, Dimensions(length=-1), name='per_inch', ascii_symbol='in^-1', symbol='in⁻¹') per_square_inch = NamedUnit(1550.0031000062002, Dimensions(length=-2), name='per_square_inch', ascii_symbol='in^-2', symbol='in⁻²') per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3), name='per_cubic_inch', ascii_symbol='in^-3', symbol='in⁻³') -meters_per_second = NamedUnit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='NoneNone⁻¹') -meters_per_square_second = NamedUnit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='NoneNone⁻²') -meters_per_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='Nonems⁻¹') -meters_per_square_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='Nonems⁻²') -meters_per_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='Noneµs⁻¹') -meters_per_square_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='Noneµs⁻²') -meters_per_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='Nonens⁻¹') -meters_per_square_nanosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='Nonens⁻²') -meters_per_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='Noneps⁻¹') -meters_per_square_picosecond = NamedUnit(1.0000000000000001e+24, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='Noneps⁻²') -meters_per_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='Nonefs⁻¹') -meters_per_square_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='Nonefs⁻²') -meters_per_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='Noneas⁻¹') -meters_per_square_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='Noneas⁻²') -meters_per_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='NoneNone⁻¹') -meters_per_square_minute = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='NoneNone⁻²') -meters_per_hour = NamedUnit(0.002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='NoneNone⁻¹') -meters_per_square_hour = NamedUnit(7.71604938271605e-06, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='NoneNone⁻²') -meters_per_day = NamedUnit(0.00011574074074074075, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='NoneNone⁻¹') -meters_per_square_day = NamedUnit(1.3395919067215363e-08, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='NoneNone⁻²') -meters_per_year = NamedUnit(3.168873850681143e-07, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='NoneNone⁻¹') -meters_per_square_year = NamedUnit(1.0041761481530735e-13, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='NoneNone⁻²') -exameters_per_second = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='EmNone⁻¹') -exameters_per_square_second = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='EmNone⁻²') +meters_per_second = NamedUnit(1.0, Dimensions(length=1, time=-1), name='meters_per_second', ascii_symbol='m/s', symbol='ms⁻¹') +meters_per_square_second = NamedUnit(1.0, Dimensions(length=1, time=-2), name='meters_per_square_second', ascii_symbol='m/s^2', symbol='ms⁻²') +meters_per_millisecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='meters_per_millisecond', ascii_symbol='m/ms', symbol='mms⁻¹') +meters_per_square_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='meters_per_square_millisecond', ascii_symbol='m/ms^2', symbol='mms⁻²') +meters_per_microsecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='meters_per_microsecond', ascii_symbol='m/us', symbol='mµs⁻¹') +meters_per_square_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='meters_per_square_microsecond', ascii_symbol='m/us^2', symbol='mµs⁻²') +meters_per_nanosecond = NamedUnit(999999999.9999999, Dimensions(length=1, time=-1), name='meters_per_nanosecond', ascii_symbol='m/ns', symbol='mns⁻¹') +meters_per_square_nanosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='meters_per_square_nanosecond', ascii_symbol='m/ns^2', symbol='mns⁻²') +meters_per_picosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='meters_per_picosecond', ascii_symbol='m/ps', symbol='mps⁻¹') +meters_per_square_picosecond = NamedUnit(1.0000000000000001e+24, Dimensions(length=1, time=-2), name='meters_per_square_picosecond', ascii_symbol='m/ps^2', symbol='mps⁻²') +meters_per_femtosecond = NamedUnit(999999999999999.9, Dimensions(length=1, time=-1), name='meters_per_femtosecond', ascii_symbol='m/fs', symbol='mfs⁻¹') +meters_per_square_femtosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='meters_per_square_femtosecond', ascii_symbol='m/fs^2', symbol='mfs⁻²') +meters_per_attosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-1), name='meters_per_attosecond', ascii_symbol='m/as', symbol='mas⁻¹') +meters_per_square_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='meters_per_square_attosecond', ascii_symbol='m/as^2', symbol='mas⁻²') +meters_per_minute = NamedUnit(0.016666666666666666, Dimensions(length=1, time=-1), name='meters_per_minute', ascii_symbol='m/min', symbol='mmin⁻¹') +meters_per_square_minute = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-2), name='meters_per_square_minute', ascii_symbol='m/min^2', symbol='mmin⁻²') +meters_per_hour = NamedUnit(0.002777777777777778, Dimensions(length=1, time=-1), name='meters_per_hour', ascii_symbol='m/h', symbol='mh⁻¹') +meters_per_square_hour = NamedUnit(7.71604938271605e-06, Dimensions(length=1, time=-2), name='meters_per_square_hour', ascii_symbol='m/h^2', symbol='mh⁻²') +meters_per_day = NamedUnit(0.00011574074074074075, Dimensions(length=1, time=-1), name='meters_per_day', ascii_symbol='m/d', symbol='md⁻¹') +meters_per_square_day = NamedUnit(1.3395919067215363e-08, Dimensions(length=1, time=-2), name='meters_per_square_day', ascii_symbol='m/d^2', symbol='md⁻²') +meters_per_year = NamedUnit(3.168873850681143e-07, Dimensions(length=1, time=-1), name='meters_per_year', ascii_symbol='m/y', symbol='my⁻¹') +meters_per_square_year = NamedUnit(1.0041761481530735e-13, Dimensions(length=1, time=-2), name='meters_per_square_year', ascii_symbol='m/y^2', symbol='my⁻²') +exameters_per_second = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='exameters_per_second', ascii_symbol='Em/s', symbol='Ems⁻¹') +exameters_per_square_second = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='exameters_per_square_second', ascii_symbol='Em/s^2', symbol='Ems⁻²') exameters_per_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='exameters_per_millisecond', ascii_symbol='Em/ms', symbol='Emms⁻¹') exameters_per_square_millisecond = NamedUnit(1e+24, Dimensions(length=1, time=-2), name='exameters_per_square_millisecond', ascii_symbol='Em/ms^2', symbol='Emms⁻²') exameters_per_microsecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='exameters_per_microsecond', ascii_symbol='Em/us', symbol='Emµs⁻¹') @@ -837,16 +837,16 @@ def __init__(self, name: str, units: list[NamedUnit]): exameters_per_square_femtosecond = NamedUnit(9.999999999999999e+47, Dimensions(length=1, time=-2), name='exameters_per_square_femtosecond', ascii_symbol='Em/fs^2', symbol='Emfs⁻²') exameters_per_attosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-1), name='exameters_per_attosecond', ascii_symbol='Em/as', symbol='Emas⁻¹') exameters_per_square_attosecond = NamedUnit(9.999999999999999e+53, Dimensions(length=1, time=-2), name='exameters_per_square_attosecond', ascii_symbol='Em/as^2', symbol='Emas⁻²') -exameters_per_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='EmNone⁻¹') -exameters_per_square_minute = NamedUnit(277777777777777.78, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='EmNone⁻²') -exameters_per_hour = NamedUnit(2777777777777778.0, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='EmNone⁻¹') -exameters_per_square_hour = NamedUnit(7716049382716.05, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='EmNone⁻²') -exameters_per_day = NamedUnit(115740740740740.73, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='EmNone⁻¹') -exameters_per_square_day = NamedUnit(13395919067.215364, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='EmNone⁻²') -exameters_per_year = NamedUnit(316887385068.1143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='EmNone⁻¹') -exameters_per_square_year = NamedUnit(100417.61481530734, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='EmNone⁻²') -petameters_per_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='PmNone⁻¹') -petameters_per_square_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='PmNone⁻²') +exameters_per_minute = NamedUnit(1.6666666666666666e+16, Dimensions(length=1, time=-1), name='exameters_per_minute', ascii_symbol='Em/min', symbol='Emmin⁻¹') +exameters_per_square_minute = NamedUnit(277777777777777.78, Dimensions(length=1, time=-2), name='exameters_per_square_minute', ascii_symbol='Em/min^2', symbol='Emmin⁻²') +exameters_per_hour = NamedUnit(2777777777777778.0, Dimensions(length=1, time=-1), name='exameters_per_hour', ascii_symbol='Em/h', symbol='Emh⁻¹') +exameters_per_square_hour = NamedUnit(7716049382716.05, Dimensions(length=1, time=-2), name='exameters_per_square_hour', ascii_symbol='Em/h^2', symbol='Emh⁻²') +exameters_per_day = NamedUnit(115740740740740.73, Dimensions(length=1, time=-1), name='exameters_per_day', ascii_symbol='Em/d', symbol='Emd⁻¹') +exameters_per_square_day = NamedUnit(13395919067.215364, Dimensions(length=1, time=-2), name='exameters_per_square_day', ascii_symbol='Em/d^2', symbol='Emd⁻²') +exameters_per_year = NamedUnit(316887385068.1143, Dimensions(length=1, time=-1), name='exameters_per_year', ascii_symbol='Em/y', symbol='Emy⁻¹') +exameters_per_square_year = NamedUnit(100417.61481530734, Dimensions(length=1, time=-2), name='exameters_per_square_year', ascii_symbol='Em/y^2', symbol='Emy⁻²') +petameters_per_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='petameters_per_second', ascii_symbol='Pm/s', symbol='Pms⁻¹') +petameters_per_square_second = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='petameters_per_square_second', ascii_symbol='Pm/s^2', symbol='Pms⁻²') petameters_per_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='petameters_per_millisecond', ascii_symbol='Pm/ms', symbol='Pmms⁻¹') petameters_per_square_millisecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='petameters_per_square_millisecond', ascii_symbol='Pm/ms^2', symbol='Pmms⁻²') petameters_per_microsecond = NamedUnit(1e+21, Dimensions(length=1, time=-1), name='petameters_per_microsecond', ascii_symbol='Pm/us', symbol='Pmµs⁻¹') @@ -859,16 +859,16 @@ def __init__(self, name: str, units: list[NamedUnit]): petameters_per_square_femtosecond = NamedUnit(1e+45, Dimensions(length=1, time=-2), name='petameters_per_square_femtosecond', ascii_symbol='Pm/fs^2', symbol='Pmfs⁻²') petameters_per_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-1), name='petameters_per_attosecond', ascii_symbol='Pm/as', symbol='Pmas⁻¹') petameters_per_square_attosecond = NamedUnit(9.999999999999998e+50, Dimensions(length=1, time=-2), name='petameters_per_square_attosecond', ascii_symbol='Pm/as^2', symbol='Pmas⁻²') -petameters_per_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='PmNone⁻¹') -petameters_per_square_minute = NamedUnit(277777777777.7778, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='PmNone⁻²') -petameters_per_hour = NamedUnit(2777777777777.778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='PmNone⁻¹') -petameters_per_square_hour = NamedUnit(7716049382.716049, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='PmNone⁻²') -petameters_per_day = NamedUnit(115740740740.74074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='PmNone⁻¹') -petameters_per_square_day = NamedUnit(13395919.067215364, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='PmNone⁻²') -petameters_per_year = NamedUnit(316887385.0681143, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='PmNone⁻¹') -petameters_per_square_year = NamedUnit(100.41761481530735, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='PmNone⁻²') -terameters_per_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='TmNone⁻¹') -terameters_per_square_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='TmNone⁻²') +petameters_per_minute = NamedUnit(16666666666666.666, Dimensions(length=1, time=-1), name='petameters_per_minute', ascii_symbol='Pm/min', symbol='Pmmin⁻¹') +petameters_per_square_minute = NamedUnit(277777777777.7778, Dimensions(length=1, time=-2), name='petameters_per_square_minute', ascii_symbol='Pm/min^2', symbol='Pmmin⁻²') +petameters_per_hour = NamedUnit(2777777777777.778, Dimensions(length=1, time=-1), name='petameters_per_hour', ascii_symbol='Pm/h', symbol='Pmh⁻¹') +petameters_per_square_hour = NamedUnit(7716049382.716049, Dimensions(length=1, time=-2), name='petameters_per_square_hour', ascii_symbol='Pm/h^2', symbol='Pmh⁻²') +petameters_per_day = NamedUnit(115740740740.74074, Dimensions(length=1, time=-1), name='petameters_per_day', ascii_symbol='Pm/d', symbol='Pmd⁻¹') +petameters_per_square_day = NamedUnit(13395919.067215364, Dimensions(length=1, time=-2), name='petameters_per_square_day', ascii_symbol='Pm/d^2', symbol='Pmd⁻²') +petameters_per_year = NamedUnit(316887385.0681143, Dimensions(length=1, time=-1), name='petameters_per_year', ascii_symbol='Pm/y', symbol='Pmy⁻¹') +petameters_per_square_year = NamedUnit(100.41761481530735, Dimensions(length=1, time=-2), name='petameters_per_square_year', ascii_symbol='Pm/y^2', symbol='Pmy⁻²') +terameters_per_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_second', ascii_symbol='Tm/s', symbol='Tms⁻¹') +terameters_per_square_second = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='terameters_per_square_second', ascii_symbol='Tm/s^2', symbol='Tms⁻²') terameters_per_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='terameters_per_millisecond', ascii_symbol='Tm/ms', symbol='Tmms⁻¹') terameters_per_square_millisecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='terameters_per_square_millisecond', ascii_symbol='Tm/ms^2', symbol='Tmms⁻²') terameters_per_microsecond = NamedUnit(1e+18, Dimensions(length=1, time=-1), name='terameters_per_microsecond', ascii_symbol='Tm/us', symbol='Tmµs⁻¹') @@ -881,16 +881,16 @@ def __init__(self, name: str, units: list[NamedUnit]): terameters_per_square_femtosecond = NamedUnit(9.999999999999999e+41, Dimensions(length=1, time=-2), name='terameters_per_square_femtosecond', ascii_symbol='Tm/fs^2', symbol='Tmfs⁻²') terameters_per_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-1), name='terameters_per_attosecond', ascii_symbol='Tm/as', symbol='Tmas⁻¹') terameters_per_square_attosecond = NamedUnit(9.999999999999999e+47, Dimensions(length=1, time=-2), name='terameters_per_square_attosecond', ascii_symbol='Tm/as^2', symbol='Tmas⁻²') -terameters_per_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='TmNone⁻¹') -terameters_per_square_minute = NamedUnit(277777777.7777778, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='TmNone⁻²') -terameters_per_hour = NamedUnit(2777777777.7777777, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='TmNone⁻¹') -terameters_per_square_hour = NamedUnit(7716049.382716049, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='TmNone⁻²') -terameters_per_day = NamedUnit(115740740.74074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='TmNone⁻¹') -terameters_per_square_day = NamedUnit(13395.919067215364, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='TmNone⁻²') -terameters_per_year = NamedUnit(316887.38506811426, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='TmNone⁻¹') -terameters_per_square_year = NamedUnit(0.10041761481530735, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='TmNone⁻²') -gigameters_per_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='GmNone⁻¹') -gigameters_per_square_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='GmNone⁻²') +terameters_per_minute = NamedUnit(16666666666.666666, Dimensions(length=1, time=-1), name='terameters_per_minute', ascii_symbol='Tm/min', symbol='Tmmin⁻¹') +terameters_per_square_minute = NamedUnit(277777777.7777778, Dimensions(length=1, time=-2), name='terameters_per_square_minute', ascii_symbol='Tm/min^2', symbol='Tmmin⁻²') +terameters_per_hour = NamedUnit(2777777777.7777777, Dimensions(length=1, time=-1), name='terameters_per_hour', ascii_symbol='Tm/h', symbol='Tmh⁻¹') +terameters_per_square_hour = NamedUnit(7716049.382716049, Dimensions(length=1, time=-2), name='terameters_per_square_hour', ascii_symbol='Tm/h^2', symbol='Tmh⁻²') +terameters_per_day = NamedUnit(115740740.74074075, Dimensions(length=1, time=-1), name='terameters_per_day', ascii_symbol='Tm/d', symbol='Tmd⁻¹') +terameters_per_square_day = NamedUnit(13395.919067215364, Dimensions(length=1, time=-2), name='terameters_per_square_day', ascii_symbol='Tm/d^2', symbol='Tmd⁻²') +terameters_per_year = NamedUnit(316887.38506811426, Dimensions(length=1, time=-1), name='terameters_per_year', ascii_symbol='Tm/y', symbol='Tmy⁻¹') +terameters_per_square_year = NamedUnit(0.10041761481530735, Dimensions(length=1, time=-2), name='terameters_per_square_year', ascii_symbol='Tm/y^2', symbol='Tmy⁻²') +gigameters_per_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_second', ascii_symbol='Gm/s', symbol='Gms⁻¹') +gigameters_per_square_second = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_second', ascii_symbol='Gm/s^2', symbol='Gms⁻²') gigameters_per_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_millisecond', ascii_symbol='Gm/ms', symbol='Gmms⁻¹') gigameters_per_square_millisecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='gigameters_per_square_millisecond', ascii_symbol='Gm/ms^2', symbol='Gmms⁻²') gigameters_per_microsecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='gigameters_per_microsecond', ascii_symbol='Gm/us', symbol='Gmµs⁻¹') @@ -903,16 +903,16 @@ def __init__(self, name: str, units: list[NamedUnit]): gigameters_per_square_femtosecond = NamedUnit(1e+39, Dimensions(length=1, time=-2), name='gigameters_per_square_femtosecond', ascii_symbol='Gm/fs^2', symbol='Gmfs⁻²') gigameters_per_attosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-1), name='gigameters_per_attosecond', ascii_symbol='Gm/as', symbol='Gmas⁻¹') gigameters_per_square_attosecond = NamedUnit(1e+45, Dimensions(length=1, time=-2), name='gigameters_per_square_attosecond', ascii_symbol='Gm/as^2', symbol='Gmas⁻²') -gigameters_per_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='GmNone⁻¹') -gigameters_per_square_minute = NamedUnit(277777.77777777775, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='GmNone⁻²') -gigameters_per_hour = NamedUnit(2777777.777777778, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='GmNone⁻¹') -gigameters_per_square_hour = NamedUnit(7716.049382716049, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='GmNone⁻²') -gigameters_per_day = NamedUnit(115740.74074074074, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='GmNone⁻¹') -gigameters_per_square_day = NamedUnit(13.395919067215363, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='GmNone⁻²') -gigameters_per_year = NamedUnit(316.88738506811427, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='GmNone⁻¹') -gigameters_per_square_year = NamedUnit(0.00010041761481530735, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='GmNone⁻²') -megameters_per_second = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='MmNone⁻¹') -megameters_per_square_second = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='MmNone⁻²') +gigameters_per_minute = NamedUnit(16666666.666666666, Dimensions(length=1, time=-1), name='gigameters_per_minute', ascii_symbol='Gm/min', symbol='Gmmin⁻¹') +gigameters_per_square_minute = NamedUnit(277777.77777777775, Dimensions(length=1, time=-2), name='gigameters_per_square_minute', ascii_symbol='Gm/min^2', symbol='Gmmin⁻²') +gigameters_per_hour = NamedUnit(2777777.777777778, Dimensions(length=1, time=-1), name='gigameters_per_hour', ascii_symbol='Gm/h', symbol='Gmh⁻¹') +gigameters_per_square_hour = NamedUnit(7716.049382716049, Dimensions(length=1, time=-2), name='gigameters_per_square_hour', ascii_symbol='Gm/h^2', symbol='Gmh⁻²') +gigameters_per_day = NamedUnit(115740.74074074074, Dimensions(length=1, time=-1), name='gigameters_per_day', ascii_symbol='Gm/d', symbol='Gmd⁻¹') +gigameters_per_square_day = NamedUnit(13.395919067215363, Dimensions(length=1, time=-2), name='gigameters_per_square_day', ascii_symbol='Gm/d^2', symbol='Gmd⁻²') +gigameters_per_year = NamedUnit(316.88738506811427, Dimensions(length=1, time=-1), name='gigameters_per_year', ascii_symbol='Gm/y', symbol='Gmy⁻¹') +gigameters_per_square_year = NamedUnit(0.00010041761481530735, Dimensions(length=1, time=-2), name='gigameters_per_square_year', ascii_symbol='Gm/y^2', symbol='Gmy⁻²') +megameters_per_second = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='megameters_per_second', ascii_symbol='Mm/s', symbol='Mms⁻¹') +megameters_per_square_second = NamedUnit(1000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_second', ascii_symbol='Mm/s^2', symbol='Mms⁻²') megameters_per_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='megameters_per_millisecond', ascii_symbol='Mm/ms', symbol='Mmms⁻¹') megameters_per_square_millisecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='megameters_per_square_millisecond', ascii_symbol='Mm/ms^2', symbol='Mmms⁻²') megameters_per_microsecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-1), name='megameters_per_microsecond', ascii_symbol='Mm/us', symbol='Mmµs⁻¹') @@ -925,16 +925,16 @@ def __init__(self, name: str, units: list[NamedUnit]): megameters_per_square_femtosecond = NamedUnit(9.999999999999999e+35, Dimensions(length=1, time=-2), name='megameters_per_square_femtosecond', ascii_symbol='Mm/fs^2', symbol='Mmfs⁻²') megameters_per_attosecond = NamedUnit(1e+24, Dimensions(length=1, time=-1), name='megameters_per_attosecond', ascii_symbol='Mm/as', symbol='Mmas⁻¹') megameters_per_square_attosecond = NamedUnit(9.999999999999999e+41, Dimensions(length=1, time=-2), name='megameters_per_square_attosecond', ascii_symbol='Mm/as^2', symbol='Mmas⁻²') -megameters_per_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='MmNone⁻¹') -megameters_per_square_minute = NamedUnit(277.77777777777777, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='MmNone⁻²') -megameters_per_hour = NamedUnit(2777.777777777778, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='MmNone⁻¹') -megameters_per_square_hour = NamedUnit(7.716049382716049, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='MmNone⁻²') -megameters_per_day = NamedUnit(115.74074074074075, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='MmNone⁻¹') -megameters_per_square_day = NamedUnit(0.013395919067215363, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='MmNone⁻²') -megameters_per_year = NamedUnit(0.3168873850681143, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='MmNone⁻¹') -megameters_per_square_year = NamedUnit(1.0041761481530735e-07, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='MmNone⁻²') -kilometers_per_second = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kmNone⁻¹') -kilometers_per_square_second = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kmNone⁻²') +megameters_per_minute = NamedUnit(16666.666666666668, Dimensions(length=1, time=-1), name='megameters_per_minute', ascii_symbol='Mm/min', symbol='Mmmin⁻¹') +megameters_per_square_minute = NamedUnit(277.77777777777777, Dimensions(length=1, time=-2), name='megameters_per_square_minute', ascii_symbol='Mm/min^2', symbol='Mmmin⁻²') +megameters_per_hour = NamedUnit(2777.777777777778, Dimensions(length=1, time=-1), name='megameters_per_hour', ascii_symbol='Mm/h', symbol='Mmh⁻¹') +megameters_per_square_hour = NamedUnit(7.716049382716049, Dimensions(length=1, time=-2), name='megameters_per_square_hour', ascii_symbol='Mm/h^2', symbol='Mmh⁻²') +megameters_per_day = NamedUnit(115.74074074074075, Dimensions(length=1, time=-1), name='megameters_per_day', ascii_symbol='Mm/d', symbol='Mmd⁻¹') +megameters_per_square_day = NamedUnit(0.013395919067215363, Dimensions(length=1, time=-2), name='megameters_per_square_day', ascii_symbol='Mm/d^2', symbol='Mmd⁻²') +megameters_per_year = NamedUnit(0.3168873850681143, Dimensions(length=1, time=-1), name='megameters_per_year', ascii_symbol='Mm/y', symbol='Mmy⁻¹') +megameters_per_square_year = NamedUnit(1.0041761481530735e-07, Dimensions(length=1, time=-2), name='megameters_per_square_year', ascii_symbol='Mm/y^2', symbol='Mmy⁻²') +kilometers_per_second = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='kilometers_per_second', ascii_symbol='km/s', symbol='kms⁻¹') +kilometers_per_square_second = NamedUnit(1000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_second', ascii_symbol='km/s^2', symbol='kms⁻²') kilometers_per_millisecond = NamedUnit(1000000.0, Dimensions(length=1, time=-1), name='kilometers_per_millisecond', ascii_symbol='km/ms', symbol='kmms⁻¹') kilometers_per_square_millisecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-2), name='kilometers_per_square_millisecond', ascii_symbol='km/ms^2', symbol='kmms⁻²') kilometers_per_microsecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='kilometers_per_microsecond', ascii_symbol='km/us', symbol='kmµs⁻¹') @@ -947,16 +947,16 @@ def __init__(self, name: str, units: list[NamedUnit]): kilometers_per_square_femtosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='kilometers_per_square_femtosecond', ascii_symbol='km/fs^2', symbol='kmfs⁻²') kilometers_per_attosecond = NamedUnit(9.999999999999999e+20, Dimensions(length=1, time=-1), name='kilometers_per_attosecond', ascii_symbol='km/as', symbol='kmas⁻¹') kilometers_per_square_attosecond = NamedUnit(1e+39, Dimensions(length=1, time=-2), name='kilometers_per_square_attosecond', ascii_symbol='km/as^2', symbol='kmas⁻²') -kilometers_per_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmNone⁻¹') -kilometers_per_square_minute = NamedUnit(0.2777777777777778, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmNone⁻²') -kilometers_per_hour = NamedUnit(2.7777777777777777, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmNone⁻¹') -kilometers_per_square_hour = NamedUnit(0.007716049382716049, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmNone⁻²') -kilometers_per_day = NamedUnit(0.11574074074074074, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmNone⁻¹') -kilometers_per_square_day = NamedUnit(1.3395919067215363e-05, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmNone⁻²') -kilometers_per_year = NamedUnit(0.0003168873850681143, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmNone⁻¹') -kilometers_per_square_year = NamedUnit(1.0041761481530735e-10, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmNone⁻²') -millimeters_per_second = NamedUnit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mmNone⁻¹') -millimeters_per_square_second = NamedUnit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mmNone⁻²') +kilometers_per_minute = NamedUnit(16.666666666666668, Dimensions(length=1, time=-1), name='kilometers_per_minute', ascii_symbol='km/min', symbol='kmmin⁻¹') +kilometers_per_square_minute = NamedUnit(0.2777777777777778, Dimensions(length=1, time=-2), name='kilometers_per_square_minute', ascii_symbol='km/min^2', symbol='kmmin⁻²') +kilometers_per_hour = NamedUnit(2.7777777777777777, Dimensions(length=1, time=-1), name='kilometers_per_hour', ascii_symbol='km/h', symbol='kmh⁻¹') +kilometers_per_square_hour = NamedUnit(0.007716049382716049, Dimensions(length=1, time=-2), name='kilometers_per_square_hour', ascii_symbol='km/h^2', symbol='kmh⁻²') +kilometers_per_day = NamedUnit(0.11574074074074074, Dimensions(length=1, time=-1), name='kilometers_per_day', ascii_symbol='km/d', symbol='kmd⁻¹') +kilometers_per_square_day = NamedUnit(1.3395919067215363e-05, Dimensions(length=1, time=-2), name='kilometers_per_square_day', ascii_symbol='km/d^2', symbol='kmd⁻²') +kilometers_per_year = NamedUnit(0.0003168873850681143, Dimensions(length=1, time=-1), name='kilometers_per_year', ascii_symbol='km/y', symbol='kmy⁻¹') +kilometers_per_square_year = NamedUnit(1.0041761481530735e-10, Dimensions(length=1, time=-2), name='kilometers_per_square_year', ascii_symbol='km/y^2', symbol='kmy⁻²') +millimeters_per_second = NamedUnit(0.001, Dimensions(length=1, time=-1), name='millimeters_per_second', ascii_symbol='mm/s', symbol='mms⁻¹') +millimeters_per_square_second = NamedUnit(0.001, Dimensions(length=1, time=-2), name='millimeters_per_square_second', ascii_symbol='mm/s^2', symbol='mms⁻²') millimeters_per_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='millimeters_per_millisecond', ascii_symbol='mm/ms', symbol='mmms⁻¹') millimeters_per_square_millisecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-2), name='millimeters_per_square_millisecond', ascii_symbol='mm/ms^2', symbol='mmms⁻²') millimeters_per_microsecond = NamedUnit(1000.0000000000001, Dimensions(length=1, time=-1), name='millimeters_per_microsecond', ascii_symbol='mm/us', symbol='mmµs⁻¹') @@ -969,16 +969,16 @@ def __init__(self, name: str, units: list[NamedUnit]): millimeters_per_square_femtosecond = NamedUnit(9.999999999999999e+26, Dimensions(length=1, time=-2), name='millimeters_per_square_femtosecond', ascii_symbol='mm/fs^2', symbol='mmfs⁻²') millimeters_per_attosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-1), name='millimeters_per_attosecond', ascii_symbol='mm/as', symbol='mmas⁻¹') millimeters_per_square_attosecond = NamedUnit(1e+33, Dimensions(length=1, time=-2), name='millimeters_per_square_attosecond', ascii_symbol='mm/as^2', symbol='mmas⁻²') -millimeters_per_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmNone⁻¹') -millimeters_per_square_minute = NamedUnit(2.7777777777777776e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmNone⁻²') -millimeters_per_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmNone⁻¹') -millimeters_per_square_hour = NamedUnit(7.71604938271605e-09, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmNone⁻²') -millimeters_per_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmNone⁻¹') -millimeters_per_square_day = NamedUnit(1.3395919067215364e-11, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmNone⁻²') -millimeters_per_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmNone⁻¹') -millimeters_per_square_year = NamedUnit(1.0041761481530735e-16, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmNone⁻²') -micrometers_per_second = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µmNone⁻¹') -micrometers_per_square_second = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µmNone⁻²') +millimeters_per_minute = NamedUnit(1.6666666666666667e-05, Dimensions(length=1, time=-1), name='millimeters_per_minute', ascii_symbol='mm/min', symbol='mmmin⁻¹') +millimeters_per_square_minute = NamedUnit(2.7777777777777776e-07, Dimensions(length=1, time=-2), name='millimeters_per_square_minute', ascii_symbol='mm/min^2', symbol='mmmin⁻²') +millimeters_per_hour = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-1), name='millimeters_per_hour', ascii_symbol='mm/h', symbol='mmh⁻¹') +millimeters_per_square_hour = NamedUnit(7.71604938271605e-09, Dimensions(length=1, time=-2), name='millimeters_per_square_hour', ascii_symbol='mm/h^2', symbol='mmh⁻²') +millimeters_per_day = NamedUnit(1.1574074074074074e-07, Dimensions(length=1, time=-1), name='millimeters_per_day', ascii_symbol='mm/d', symbol='mmd⁻¹') +millimeters_per_square_day = NamedUnit(1.3395919067215364e-11, Dimensions(length=1, time=-2), name='millimeters_per_square_day', ascii_symbol='mm/d^2', symbol='mmd⁻²') +millimeters_per_year = NamedUnit(3.168873850681143e-10, Dimensions(length=1, time=-1), name='millimeters_per_year', ascii_symbol='mm/y', symbol='mmy⁻¹') +millimeters_per_square_year = NamedUnit(1.0041761481530735e-16, Dimensions(length=1, time=-2), name='millimeters_per_square_year', ascii_symbol='mm/y^2', symbol='mmy⁻²') +micrometers_per_second = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='micrometers_per_second', ascii_symbol='um/s', symbol='µms⁻¹') +micrometers_per_square_second = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='micrometers_per_square_second', ascii_symbol='um/s^2', symbol='µms⁻²') micrometers_per_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='micrometers_per_millisecond', ascii_symbol='um/ms', symbol='µmms⁻¹') micrometers_per_square_millisecond = NamedUnit(1.0, Dimensions(length=1, time=-2), name='micrometers_per_square_millisecond', ascii_symbol='um/ms^2', symbol='µmms⁻²') micrometers_per_microsecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='micrometers_per_microsecond', ascii_symbol='um/us', symbol='µmµs⁻¹') @@ -991,16 +991,16 @@ def __init__(self, name: str, units: list[NamedUnit]): micrometers_per_square_femtosecond = NamedUnit(9.999999999999998e+23, Dimensions(length=1, time=-2), name='micrometers_per_square_femtosecond', ascii_symbol='um/fs^2', symbol='µmfs⁻²') micrometers_per_attosecond = NamedUnit(999999999999.9999, Dimensions(length=1, time=-1), name='micrometers_per_attosecond', ascii_symbol='um/as', symbol='µmas⁻¹') micrometers_per_square_attosecond = NamedUnit(9.999999999999999e+29, Dimensions(length=1, time=-2), name='micrometers_per_square_attosecond', ascii_symbol='um/as^2', symbol='µmas⁻²') -micrometers_per_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmNone⁻¹') -micrometers_per_square_minute = NamedUnit(2.7777777777777777e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmNone⁻²') -micrometers_per_hour = NamedUnit(2.7777777777777776e-09, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmNone⁻¹') -micrometers_per_square_hour = NamedUnit(7.716049382716049e-12, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmNone⁻²') -micrometers_per_day = NamedUnit(1.1574074074074074e-10, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmNone⁻¹') -micrometers_per_square_day = NamedUnit(1.3395919067215363e-14, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmNone⁻²') -micrometers_per_year = NamedUnit(3.168873850681143e-13, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmNone⁻¹') -micrometers_per_square_year = NamedUnit(1.0041761481530734e-19, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmNone⁻²') -nanometers_per_second = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nmNone⁻¹') -nanometers_per_square_second = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nmNone⁻²') +micrometers_per_minute = NamedUnit(1.6666666666666667e-08, Dimensions(length=1, time=-1), name='micrometers_per_minute', ascii_symbol='um/min', symbol='µmmin⁻¹') +micrometers_per_square_minute = NamedUnit(2.7777777777777777e-10, Dimensions(length=1, time=-2), name='micrometers_per_square_minute', ascii_symbol='um/min^2', symbol='µmmin⁻²') +micrometers_per_hour = NamedUnit(2.7777777777777776e-09, Dimensions(length=1, time=-1), name='micrometers_per_hour', ascii_symbol='um/h', symbol='µmh⁻¹') +micrometers_per_square_hour = NamedUnit(7.716049382716049e-12, Dimensions(length=1, time=-2), name='micrometers_per_square_hour', ascii_symbol='um/h^2', symbol='µmh⁻²') +micrometers_per_day = NamedUnit(1.1574074074074074e-10, Dimensions(length=1, time=-1), name='micrometers_per_day', ascii_symbol='um/d', symbol='µmd⁻¹') +micrometers_per_square_day = NamedUnit(1.3395919067215363e-14, Dimensions(length=1, time=-2), name='micrometers_per_square_day', ascii_symbol='um/d^2', symbol='µmd⁻²') +micrometers_per_year = NamedUnit(3.168873850681143e-13, Dimensions(length=1, time=-1), name='micrometers_per_year', ascii_symbol='um/y', symbol='µmy⁻¹') +micrometers_per_square_year = NamedUnit(1.0041761481530734e-19, Dimensions(length=1, time=-2), name='micrometers_per_square_year', ascii_symbol='um/y^2', symbol='µmy⁻²') +nanometers_per_second = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='nanometers_per_second', ascii_symbol='nm/s', symbol='nms⁻¹') +nanometers_per_square_second = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='nanometers_per_square_second', ascii_symbol='nm/s^2', symbol='nms⁻²') nanometers_per_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='nanometers_per_millisecond', ascii_symbol='nm/ms', symbol='nmms⁻¹') nanometers_per_square_millisecond = NamedUnit(0.001, Dimensions(length=1, time=-2), name='nanometers_per_square_millisecond', ascii_symbol='nm/ms^2', symbol='nmms⁻²') nanometers_per_microsecond = NamedUnit(0.001, Dimensions(length=1, time=-1), name='nanometers_per_microsecond', ascii_symbol='nm/us', symbol='nmµs⁻¹') @@ -1013,16 +1013,16 @@ def __init__(self, name: str, units: list[NamedUnit]): nanometers_per_square_femtosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='nanometers_per_square_femtosecond', ascii_symbol='nm/fs^2', symbol='nmfs⁻²') nanometers_per_attosecond = NamedUnit(1000000000.0, Dimensions(length=1, time=-1), name='nanometers_per_attosecond', ascii_symbol='nm/as', symbol='nmas⁻¹') nanometers_per_square_attosecond = NamedUnit(1e+27, Dimensions(length=1, time=-2), name='nanometers_per_square_attosecond', ascii_symbol='nm/as^2', symbol='nmas⁻²') -nanometers_per_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmNone⁻¹') -nanometers_per_square_minute = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmNone⁻²') -nanometers_per_hour = NamedUnit(2.777777777777778e-12, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmNone⁻¹') -nanometers_per_square_hour = NamedUnit(7.71604938271605e-15, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmNone⁻²') -nanometers_per_day = NamedUnit(1.1574074074074076e-13, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmNone⁻¹') -nanometers_per_square_day = NamedUnit(1.3395919067215365e-17, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmNone⁻²') -nanometers_per_year = NamedUnit(3.1688738506811433e-16, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmNone⁻¹') -nanometers_per_square_year = NamedUnit(1.0041761481530736e-22, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmNone⁻²') -picometers_per_second = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pmNone⁻¹') -picometers_per_square_second = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pmNone⁻²') +nanometers_per_minute = NamedUnit(1.6666666666666667e-11, Dimensions(length=1, time=-1), name='nanometers_per_minute', ascii_symbol='nm/min', symbol='nmmin⁻¹') +nanometers_per_square_minute = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-2), name='nanometers_per_square_minute', ascii_symbol='nm/min^2', symbol='nmmin⁻²') +nanometers_per_hour = NamedUnit(2.777777777777778e-12, Dimensions(length=1, time=-1), name='nanometers_per_hour', ascii_symbol='nm/h', symbol='nmh⁻¹') +nanometers_per_square_hour = NamedUnit(7.71604938271605e-15, Dimensions(length=1, time=-2), name='nanometers_per_square_hour', ascii_symbol='nm/h^2', symbol='nmh⁻²') +nanometers_per_day = NamedUnit(1.1574074074074076e-13, Dimensions(length=1, time=-1), name='nanometers_per_day', ascii_symbol='nm/d', symbol='nmd⁻¹') +nanometers_per_square_day = NamedUnit(1.3395919067215365e-17, Dimensions(length=1, time=-2), name='nanometers_per_square_day', ascii_symbol='nm/d^2', symbol='nmd⁻²') +nanometers_per_year = NamedUnit(3.1688738506811433e-16, Dimensions(length=1, time=-1), name='nanometers_per_year', ascii_symbol='nm/y', symbol='nmy⁻¹') +nanometers_per_square_year = NamedUnit(1.0041761481530736e-22, Dimensions(length=1, time=-2), name='nanometers_per_square_year', ascii_symbol='nm/y^2', symbol='nmy⁻²') +picometers_per_second = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='picometers_per_second', ascii_symbol='pm/s', symbol='pms⁻¹') +picometers_per_square_second = NamedUnit(1e-12, Dimensions(length=1, time=-2), name='picometers_per_square_second', ascii_symbol='pm/s^2', symbol='pms⁻²') picometers_per_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='picometers_per_millisecond', ascii_symbol='pm/ms', symbol='pmms⁻¹') picometers_per_square_millisecond = NamedUnit(1e-06, Dimensions(length=1, time=-2), name='picometers_per_square_millisecond', ascii_symbol='pm/ms^2', symbol='pmms⁻²') picometers_per_microsecond = NamedUnit(1e-06, Dimensions(length=1, time=-1), name='picometers_per_microsecond', ascii_symbol='pm/us', symbol='pmµs⁻¹') @@ -1035,16 +1035,16 @@ def __init__(self, name: str, units: list[NamedUnit]): picometers_per_square_femtosecond = NamedUnit(9.999999999999999e+17, Dimensions(length=1, time=-2), name='picometers_per_square_femtosecond', ascii_symbol='pm/fs^2', symbol='pmfs⁻²') picometers_per_attosecond = NamedUnit(999999.9999999999, Dimensions(length=1, time=-1), name='picometers_per_attosecond', ascii_symbol='pm/as', symbol='pmas⁻¹') picometers_per_square_attosecond = NamedUnit(9.999999999999998e+23, Dimensions(length=1, time=-2), name='picometers_per_square_attosecond', ascii_symbol='pm/as^2', symbol='pmas⁻²') -picometers_per_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmNone⁻¹') -picometers_per_square_minute = NamedUnit(2.7777777777777775e-16, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmNone⁻²') -picometers_per_hour = NamedUnit(2.7777777777777776e-15, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmNone⁻¹') -picometers_per_square_hour = NamedUnit(7.716049382716049e-18, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmNone⁻²') -picometers_per_day = NamedUnit(1.1574074074074073e-16, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmNone⁻¹') -picometers_per_square_day = NamedUnit(1.3395919067215364e-20, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmNone⁻²') -picometers_per_year = NamedUnit(3.168873850681143e-19, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmNone⁻¹') -picometers_per_square_year = NamedUnit(1.0041761481530734e-25, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmNone⁻²') -femtometers_per_second = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fmNone⁻¹') -femtometers_per_square_second = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fmNone⁻²') +picometers_per_minute = NamedUnit(1.6666666666666667e-14, Dimensions(length=1, time=-1), name='picometers_per_minute', ascii_symbol='pm/min', symbol='pmmin⁻¹') +picometers_per_square_minute = NamedUnit(2.7777777777777775e-16, Dimensions(length=1, time=-2), name='picometers_per_square_minute', ascii_symbol='pm/min^2', symbol='pmmin⁻²') +picometers_per_hour = NamedUnit(2.7777777777777776e-15, Dimensions(length=1, time=-1), name='picometers_per_hour', ascii_symbol='pm/h', symbol='pmh⁻¹') +picometers_per_square_hour = NamedUnit(7.716049382716049e-18, Dimensions(length=1, time=-2), name='picometers_per_square_hour', ascii_symbol='pm/h^2', symbol='pmh⁻²') +picometers_per_day = NamedUnit(1.1574074074074073e-16, Dimensions(length=1, time=-1), name='picometers_per_day', ascii_symbol='pm/d', symbol='pmd⁻¹') +picometers_per_square_day = NamedUnit(1.3395919067215364e-20, Dimensions(length=1, time=-2), name='picometers_per_square_day', ascii_symbol='pm/d^2', symbol='pmd⁻²') +picometers_per_year = NamedUnit(3.168873850681143e-19, Dimensions(length=1, time=-1), name='picometers_per_year', ascii_symbol='pm/y', symbol='pmy⁻¹') +picometers_per_square_year = NamedUnit(1.0041761481530734e-25, Dimensions(length=1, time=-2), name='picometers_per_square_year', ascii_symbol='pm/y^2', symbol='pmy⁻²') +femtometers_per_second = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='femtometers_per_second', ascii_symbol='fm/s', symbol='fms⁻¹') +femtometers_per_square_second = NamedUnit(1e-15, Dimensions(length=1, time=-2), name='femtometers_per_square_second', ascii_symbol='fm/s^2', symbol='fms⁻²') femtometers_per_millisecond = NamedUnit(1e-12, Dimensions(length=1, time=-1), name='femtometers_per_millisecond', ascii_symbol='fm/ms', symbol='fmms⁻¹') femtometers_per_square_millisecond = NamedUnit(1e-09, Dimensions(length=1, time=-2), name='femtometers_per_square_millisecond', ascii_symbol='fm/ms^2', symbol='fmms⁻²') femtometers_per_microsecond = NamedUnit(1e-09, Dimensions(length=1, time=-1), name='femtometers_per_microsecond', ascii_symbol='fm/us', symbol='fmµs⁻¹') @@ -1057,16 +1057,16 @@ def __init__(self, name: str, units: list[NamedUnit]): femtometers_per_square_femtosecond = NamedUnit(1000000000000000.0, Dimensions(length=1, time=-2), name='femtometers_per_square_femtosecond', ascii_symbol='fm/fs^2', symbol='fmfs⁻²') femtometers_per_attosecond = NamedUnit(1000.0, Dimensions(length=1, time=-1), name='femtometers_per_attosecond', ascii_symbol='fm/as', symbol='fmas⁻¹') femtometers_per_square_attosecond = NamedUnit(1e+21, Dimensions(length=1, time=-2), name='femtometers_per_square_attosecond', ascii_symbol='fm/as^2', symbol='fmas⁻²') -femtometers_per_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmNone⁻¹') -femtometers_per_square_minute = NamedUnit(2.777777777777778e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmNone⁻²') -femtometers_per_hour = NamedUnit(2.777777777777778e-18, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmNone⁻¹') -femtometers_per_square_hour = NamedUnit(7.71604938271605e-21, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmNone⁻²') -femtometers_per_day = NamedUnit(1.1574074074074075e-19, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmNone⁻¹') -femtometers_per_square_day = NamedUnit(1.3395919067215363e-23, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmNone⁻²') -femtometers_per_year = NamedUnit(3.168873850681143e-22, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmNone⁻¹') -femtometers_per_square_year = NamedUnit(1.0041761481530735e-28, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmNone⁻²') -attometers_per_second = NamedUnit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='amNone⁻¹') -attometers_per_square_second = NamedUnit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='amNone⁻²') +femtometers_per_minute = NamedUnit(1.6666666666666667e-17, Dimensions(length=1, time=-1), name='femtometers_per_minute', ascii_symbol='fm/min', symbol='fmmin⁻¹') +femtometers_per_square_minute = NamedUnit(2.777777777777778e-19, Dimensions(length=1, time=-2), name='femtometers_per_square_minute', ascii_symbol='fm/min^2', symbol='fmmin⁻²') +femtometers_per_hour = NamedUnit(2.777777777777778e-18, Dimensions(length=1, time=-1), name='femtometers_per_hour', ascii_symbol='fm/h', symbol='fmh⁻¹') +femtometers_per_square_hour = NamedUnit(7.71604938271605e-21, Dimensions(length=1, time=-2), name='femtometers_per_square_hour', ascii_symbol='fm/h^2', symbol='fmh⁻²') +femtometers_per_day = NamedUnit(1.1574074074074075e-19, Dimensions(length=1, time=-1), name='femtometers_per_day', ascii_symbol='fm/d', symbol='fmd⁻¹') +femtometers_per_square_day = NamedUnit(1.3395919067215363e-23, Dimensions(length=1, time=-2), name='femtometers_per_square_day', ascii_symbol='fm/d^2', symbol='fmd⁻²') +femtometers_per_year = NamedUnit(3.168873850681143e-22, Dimensions(length=1, time=-1), name='femtometers_per_year', ascii_symbol='fm/y', symbol='fmy⁻¹') +femtometers_per_square_year = NamedUnit(1.0041761481530735e-28, Dimensions(length=1, time=-2), name='femtometers_per_square_year', ascii_symbol='fm/y^2', symbol='fmy⁻²') +attometers_per_second = NamedUnit(1e-18, Dimensions(length=1, time=-1), name='attometers_per_second', ascii_symbol='am/s', symbol='ams⁻¹') +attometers_per_square_second = NamedUnit(1e-18, Dimensions(length=1, time=-2), name='attometers_per_square_second', ascii_symbol='am/s^2', symbol='ams⁻²') attometers_per_millisecond = NamedUnit(1e-15, Dimensions(length=1, time=-1), name='attometers_per_millisecond', ascii_symbol='am/ms', symbol='amms⁻¹') attometers_per_square_millisecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-2), name='attometers_per_square_millisecond', ascii_symbol='am/ms^2', symbol='amms⁻²') attometers_per_microsecond = NamedUnit(1.0000000000000002e-12, Dimensions(length=1, time=-1), name='attometers_per_microsecond', ascii_symbol='am/us', symbol='amµs⁻¹') @@ -1079,16 +1079,16 @@ def __init__(self, name: str, units: list[NamedUnit]): attometers_per_square_femtosecond = NamedUnit(1000000000000.0, Dimensions(length=1, time=-2), name='attometers_per_square_femtosecond', ascii_symbol='am/fs^2', symbol='amfs⁻²') attometers_per_attosecond = NamedUnit(1.0, Dimensions(length=1, time=-1), name='attometers_per_attosecond', ascii_symbol='am/as', symbol='amas⁻¹') attometers_per_square_attosecond = NamedUnit(1e+18, Dimensions(length=1, time=-2), name='attometers_per_square_attosecond', ascii_symbol='am/as^2', symbol='amas⁻²') -attometers_per_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='amNone⁻¹') -attometers_per_square_minute = NamedUnit(2.777777777777778e-22, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='amNone⁻²') -attometers_per_hour = NamedUnit(2.7777777777777778e-21, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amNone⁻¹') -attometers_per_square_hour = NamedUnit(7.71604938271605e-24, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amNone⁻²') -attometers_per_day = NamedUnit(1.1574074074074074e-22, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amNone⁻¹') -attometers_per_square_day = NamedUnit(1.3395919067215363e-26, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amNone⁻²') -attometers_per_year = NamedUnit(3.1688738506811433e-25, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amNone⁻¹') -attometers_per_square_year = NamedUnit(1.0041761481530734e-31, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amNone⁻²') -decimeters_per_second = NamedUnit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dmNone⁻¹') -decimeters_per_square_second = NamedUnit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dmNone⁻²') +attometers_per_minute = NamedUnit(1.6666666666666668e-20, Dimensions(length=1, time=-1), name='attometers_per_minute', ascii_symbol='am/min', symbol='ammin⁻¹') +attometers_per_square_minute = NamedUnit(2.777777777777778e-22, Dimensions(length=1, time=-2), name='attometers_per_square_minute', ascii_symbol='am/min^2', symbol='ammin⁻²') +attometers_per_hour = NamedUnit(2.7777777777777778e-21, Dimensions(length=1, time=-1), name='attometers_per_hour', ascii_symbol='am/h', symbol='amh⁻¹') +attometers_per_square_hour = NamedUnit(7.71604938271605e-24, Dimensions(length=1, time=-2), name='attometers_per_square_hour', ascii_symbol='am/h^2', symbol='amh⁻²') +attometers_per_day = NamedUnit(1.1574074074074074e-22, Dimensions(length=1, time=-1), name='attometers_per_day', ascii_symbol='am/d', symbol='amd⁻¹') +attometers_per_square_day = NamedUnit(1.3395919067215363e-26, Dimensions(length=1, time=-2), name='attometers_per_square_day', ascii_symbol='am/d^2', symbol='amd⁻²') +attometers_per_year = NamedUnit(3.1688738506811433e-25, Dimensions(length=1, time=-1), name='attometers_per_year', ascii_symbol='am/y', symbol='amy⁻¹') +attometers_per_square_year = NamedUnit(1.0041761481530734e-31, Dimensions(length=1, time=-2), name='attometers_per_square_year', ascii_symbol='am/y^2', symbol='amy⁻²') +decimeters_per_second = NamedUnit(0.1, Dimensions(length=1, time=-1), name='decimeters_per_second', ascii_symbol='dm/s', symbol='dms⁻¹') +decimeters_per_square_second = NamedUnit(0.1, Dimensions(length=1, time=-2), name='decimeters_per_square_second', ascii_symbol='dm/s^2', symbol='dms⁻²') decimeters_per_millisecond = NamedUnit(100.0, Dimensions(length=1, time=-1), name='decimeters_per_millisecond', ascii_symbol='dm/ms', symbol='dmms⁻¹') decimeters_per_square_millisecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-2), name='decimeters_per_square_millisecond', ascii_symbol='dm/ms^2', symbol='dmms⁻²') decimeters_per_microsecond = NamedUnit(100000.00000000001, Dimensions(length=1, time=-1), name='decimeters_per_microsecond', ascii_symbol='dm/us', symbol='dmµs⁻¹') @@ -1101,16 +1101,16 @@ def __init__(self, name: str, units: list[NamedUnit]): decimeters_per_square_femtosecond = NamedUnit(1e+29, Dimensions(length=1, time=-2), name='decimeters_per_square_femtosecond', ascii_symbol='dm/fs^2', symbol='dmfs⁻²') decimeters_per_attosecond = NamedUnit(1e+17, Dimensions(length=1, time=-1), name='decimeters_per_attosecond', ascii_symbol='dm/as', symbol='dmas⁻¹') decimeters_per_square_attosecond = NamedUnit(1e+35, Dimensions(length=1, time=-2), name='decimeters_per_square_attosecond', ascii_symbol='dm/as^2', symbol='dmas⁻²') -decimeters_per_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmNone⁻¹') -decimeters_per_square_minute = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmNone⁻²') -decimeters_per_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmNone⁻¹') -decimeters_per_square_hour = NamedUnit(7.71604938271605e-07, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmNone⁻²') -decimeters_per_day = NamedUnit(1.1574074074074075e-05, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmNone⁻¹') -decimeters_per_square_day = NamedUnit(1.3395919067215364e-09, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmNone⁻²') -decimeters_per_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmNone⁻¹') -decimeters_per_square_year = NamedUnit(1.0041761481530735e-14, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmNone⁻²') -centimeters_per_second = NamedUnit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cmNone⁻¹') -centimeters_per_square_second = NamedUnit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cmNone⁻²') +decimeters_per_minute = NamedUnit(0.0016666666666666668, Dimensions(length=1, time=-1), name='decimeters_per_minute', ascii_symbol='dm/min', symbol='dmmin⁻¹') +decimeters_per_square_minute = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-2), name='decimeters_per_square_minute', ascii_symbol='dm/min^2', symbol='dmmin⁻²') +decimeters_per_hour = NamedUnit(0.0002777777777777778, Dimensions(length=1, time=-1), name='decimeters_per_hour', ascii_symbol='dm/h', symbol='dmh⁻¹') +decimeters_per_square_hour = NamedUnit(7.71604938271605e-07, Dimensions(length=1, time=-2), name='decimeters_per_square_hour', ascii_symbol='dm/h^2', symbol='dmh⁻²') +decimeters_per_day = NamedUnit(1.1574074074074075e-05, Dimensions(length=1, time=-1), name='decimeters_per_day', ascii_symbol='dm/d', symbol='dmd⁻¹') +decimeters_per_square_day = NamedUnit(1.3395919067215364e-09, Dimensions(length=1, time=-2), name='decimeters_per_square_day', ascii_symbol='dm/d^2', symbol='dmd⁻²') +decimeters_per_year = NamedUnit(3.168873850681143e-08, Dimensions(length=1, time=-1), name='decimeters_per_year', ascii_symbol='dm/y', symbol='dmy⁻¹') +decimeters_per_square_year = NamedUnit(1.0041761481530735e-14, Dimensions(length=1, time=-2), name='decimeters_per_square_year', ascii_symbol='dm/y^2', symbol='dmy⁻²') +centimeters_per_second = NamedUnit(0.01, Dimensions(length=1, time=-1), name='centimeters_per_second', ascii_symbol='cm/s', symbol='cms⁻¹') +centimeters_per_square_second = NamedUnit(0.01, Dimensions(length=1, time=-2), name='centimeters_per_square_second', ascii_symbol='cm/s^2', symbol='cms⁻²') centimeters_per_millisecond = NamedUnit(10.0, Dimensions(length=1, time=-1), name='centimeters_per_millisecond', ascii_symbol='cm/ms', symbol='cmms⁻¹') centimeters_per_square_millisecond = NamedUnit(10000.0, Dimensions(length=1, time=-2), name='centimeters_per_square_millisecond', ascii_symbol='cm/ms^2', symbol='cmms⁻²') centimeters_per_microsecond = NamedUnit(10000.0, Dimensions(length=1, time=-1), name='centimeters_per_microsecond', ascii_symbol='cm/us', symbol='cmµs⁻¹') @@ -1123,16 +1123,16 @@ def __init__(self, name: str, units: list[NamedUnit]): centimeters_per_square_femtosecond = NamedUnit(1e+28, Dimensions(length=1, time=-2), name='centimeters_per_square_femtosecond', ascii_symbol='cm/fs^2', symbol='cmfs⁻²') centimeters_per_attosecond = NamedUnit(1e+16, Dimensions(length=1, time=-1), name='centimeters_per_attosecond', ascii_symbol='cm/as', symbol='cmas⁻¹') centimeters_per_square_attosecond = NamedUnit(1e+34, Dimensions(length=1, time=-2), name='centimeters_per_square_attosecond', ascii_symbol='cm/as^2', symbol='cmas⁻²') -centimeters_per_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmNone⁻¹') -centimeters_per_square_minute = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmNone⁻²') -centimeters_per_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmNone⁻¹') -centimeters_per_square_hour = NamedUnit(7.71604938271605e-08, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmNone⁻²') -centimeters_per_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmNone⁻¹') -centimeters_per_square_day = NamedUnit(1.3395919067215363e-10, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmNone⁻²') -centimeters_per_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmNone⁻¹') -centimeters_per_square_year = NamedUnit(1.0041761481530735e-15, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmNone⁻²') -angstroms_per_second = NamedUnit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='ÅNone⁻¹') -angstroms_per_square_second = NamedUnit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='ÅNone⁻²') +centimeters_per_minute = NamedUnit(0.00016666666666666666, Dimensions(length=1, time=-1), name='centimeters_per_minute', ascii_symbol='cm/min', symbol='cmmin⁻¹') +centimeters_per_square_minute = NamedUnit(2.777777777777778e-06, Dimensions(length=1, time=-2), name='centimeters_per_square_minute', ascii_symbol='cm/min^2', symbol='cmmin⁻²') +centimeters_per_hour = NamedUnit(2.777777777777778e-05, Dimensions(length=1, time=-1), name='centimeters_per_hour', ascii_symbol='cm/h', symbol='cmh⁻¹') +centimeters_per_square_hour = NamedUnit(7.71604938271605e-08, Dimensions(length=1, time=-2), name='centimeters_per_square_hour', ascii_symbol='cm/h^2', symbol='cmh⁻²') +centimeters_per_day = NamedUnit(1.1574074074074074e-06, Dimensions(length=1, time=-1), name='centimeters_per_day', ascii_symbol='cm/d', symbol='cmd⁻¹') +centimeters_per_square_day = NamedUnit(1.3395919067215363e-10, Dimensions(length=1, time=-2), name='centimeters_per_square_day', ascii_symbol='cm/d^2', symbol='cmd⁻²') +centimeters_per_year = NamedUnit(3.168873850681143e-09, Dimensions(length=1, time=-1), name='centimeters_per_year', ascii_symbol='cm/y', symbol='cmy⁻¹') +centimeters_per_square_year = NamedUnit(1.0041761481530735e-15, Dimensions(length=1, time=-2), name='centimeters_per_square_year', ascii_symbol='cm/y^2', symbol='cmy⁻²') +angstroms_per_second = NamedUnit(1e-10, Dimensions(length=1, time=-1), name='angstroms_per_second', ascii_symbol='Ang/s', symbol='Ås⁻¹') +angstroms_per_square_second = NamedUnit(1e-10, Dimensions(length=1, time=-2), name='angstroms_per_square_second', ascii_symbol='Ang/s^2', symbol='Ås⁻²') angstroms_per_millisecond = NamedUnit(1e-07, Dimensions(length=1, time=-1), name='angstroms_per_millisecond', ascii_symbol='Ang/ms', symbol='Åms⁻¹') angstroms_per_square_millisecond = NamedUnit(0.0001, Dimensions(length=1, time=-2), name='angstroms_per_square_millisecond', ascii_symbol='Ang/ms^2', symbol='Åms⁻²') angstroms_per_microsecond = NamedUnit(0.0001, Dimensions(length=1, time=-1), name='angstroms_per_microsecond', ascii_symbol='Ang/us', symbol='ŵs⁻¹') @@ -1145,119 +1145,119 @@ def __init__(self, name: str, units: list[NamedUnit]): angstroms_per_square_femtosecond = NamedUnit(1e+20, Dimensions(length=1, time=-2), name='angstroms_per_square_femtosecond', ascii_symbol='Ang/fs^2', symbol='Åfs⁻²') angstroms_per_attosecond = NamedUnit(100000000.0, Dimensions(length=1, time=-1), name='angstroms_per_attosecond', ascii_symbol='Ang/as', symbol='Åas⁻¹') angstroms_per_square_attosecond = NamedUnit(9.999999999999999e+25, Dimensions(length=1, time=-2), name='angstroms_per_square_attosecond', ascii_symbol='Ang/as^2', symbol='Åas⁻²') -angstroms_per_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='ÅNone⁻¹') -angstroms_per_square_minute = NamedUnit(2.7777777777777778e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='ÅNone⁻²') -angstroms_per_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='ÅNone⁻¹') -angstroms_per_square_hour = NamedUnit(7.716049382716049e-16, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='ÅNone⁻²') -angstroms_per_day = NamedUnit(1.1574074074074074e-14, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='ÅNone⁻¹') -angstroms_per_square_day = NamedUnit(1.3395919067215363e-18, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='ÅNone⁻²') -angstroms_per_year = NamedUnit(3.168873850681143e-17, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='ÅNone⁻¹') -angstroms_per_square_year = NamedUnit(1.0041761481530734e-23, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='ÅNone⁻²') -miles_per_second = NamedUnit(1609.344, Dimensions(length=1, time=-1), name='miles_per_second', ascii_symbol='miles/s', symbol='NoneNone⁻¹') -miles_per_square_second = NamedUnit(1609.344, Dimensions(length=1, time=-2), name='miles_per_square_second', ascii_symbol='miles/s^2', symbol='NoneNone⁻²') -miles_per_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-1), name='miles_per_millisecond', ascii_symbol='miles/ms', symbol='Nonems⁻¹') -miles_per_square_millisecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-2), name='miles_per_square_millisecond', ascii_symbol='miles/ms^2', symbol='Nonems⁻²') -miles_per_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-1), name='miles_per_microsecond', ascii_symbol='miles/us', symbol='Noneµs⁻¹') -miles_per_square_microsecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-2), name='miles_per_square_microsecond', ascii_symbol='miles/us^2', symbol='Noneµs⁻²') -miles_per_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-1), name='miles_per_nanosecond', ascii_symbol='miles/ns', symbol='Nonens⁻¹') -miles_per_square_nanosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-2), name='miles_per_square_nanosecond', ascii_symbol='miles/ns^2', symbol='Nonens⁻²') -miles_per_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-1), name='miles_per_picosecond', ascii_symbol='miles/ps', symbol='Noneps⁻¹') -miles_per_square_picosecond = NamedUnit(1.609344e+27, Dimensions(length=1, time=-2), name='miles_per_square_picosecond', ascii_symbol='miles/ps^2', symbol='Noneps⁻²') -miles_per_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-1), name='miles_per_femtosecond', ascii_symbol='miles/fs', symbol='Nonefs⁻¹') -miles_per_square_femtosecond = NamedUnit(1.609344e+33, Dimensions(length=1, time=-2), name='miles_per_square_femtosecond', ascii_symbol='miles/fs^2', symbol='Nonefs⁻²') -miles_per_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-1), name='miles_per_attosecond', ascii_symbol='miles/as', symbol='Noneas⁻¹') -miles_per_square_attosecond = NamedUnit(1.609344e+39, Dimensions(length=1, time=-2), name='miles_per_square_attosecond', ascii_symbol='miles/as^2', symbol='Noneas⁻²') -miles_per_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-1), name='miles_per_minute', ascii_symbol='miles/min', symbol='NoneNone⁻¹') -miles_per_square_minute = NamedUnit(0.44704, Dimensions(length=1, time=-2), name='miles_per_square_minute', ascii_symbol='miles/min^2', symbol='NoneNone⁻²') -miles_per_hour = NamedUnit(4.4704, Dimensions(length=1, time=-1), name='miles_per_hour', ascii_symbol='miles/h', symbol='NoneNone⁻¹') -miles_per_square_hour = NamedUnit(0.012417777777777778, Dimensions(length=1, time=-2), name='miles_per_square_hour', ascii_symbol='miles/h^2', symbol='NoneNone⁻²') -miles_per_day = NamedUnit(0.18626666666666666, Dimensions(length=1, time=-1), name='miles_per_day', ascii_symbol='miles/d', symbol='NoneNone⁻¹') -miles_per_square_day = NamedUnit(2.1558641975308643e-05, Dimensions(length=1, time=-2), name='miles_per_square_day', ascii_symbol='miles/d^2', symbol='NoneNone⁻²') -miles_per_year = NamedUnit(0.0005099808118350593, Dimensions(length=1, time=-1), name='miles_per_year', ascii_symbol='miles/y', symbol='NoneNone⁻¹') -miles_per_square_year = NamedUnit(1.61606485897326e-10, Dimensions(length=1, time=-2), name='miles_per_square_year', ascii_symbol='miles/y^2', symbol='NoneNone⁻²') -yards_per_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-1), name='yards_per_second', ascii_symbol='yrd/s', symbol='NoneNone⁻¹') -yards_per_square_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-2), name='yards_per_square_second', ascii_symbol='yrd/s^2', symbol='NoneNone⁻²') -yards_per_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-1), name='yards_per_millisecond', ascii_symbol='yrd/ms', symbol='Nonems⁻¹') -yards_per_square_millisecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-2), name='yards_per_square_millisecond', ascii_symbol='yrd/ms^2', symbol='Nonems⁻²') -yards_per_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-1), name='yards_per_microsecond', ascii_symbol='yrd/us', symbol='Noneµs⁻¹') -yards_per_square_microsecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-2), name='yards_per_square_microsecond', ascii_symbol='yrd/us^2', symbol='Noneµs⁻²') -yards_per_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-1), name='yards_per_nanosecond', ascii_symbol='yrd/ns', symbol='Nonens⁻¹') -yards_per_square_nanosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-2), name='yards_per_square_nanosecond', ascii_symbol='yrd/ns^2', symbol='Nonens⁻²') -yards_per_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-1), name='yards_per_picosecond', ascii_symbol='yrd/ps', symbol='Noneps⁻¹') -yards_per_square_picosecond = NamedUnit(9.144000000000002e+23, Dimensions(length=1, time=-2), name='yards_per_square_picosecond', ascii_symbol='yrd/ps^2', symbol='Noneps⁻²') -yards_per_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-1), name='yards_per_femtosecond', ascii_symbol='yrd/fs', symbol='Nonefs⁻¹') -yards_per_square_femtosecond = NamedUnit(9.144e+29, Dimensions(length=1, time=-2), name='yards_per_square_femtosecond', ascii_symbol='yrd/fs^2', symbol='Nonefs⁻²') -yards_per_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-1), name='yards_per_attosecond', ascii_symbol='yrd/as', symbol='Noneas⁻¹') -yards_per_square_attosecond = NamedUnit(9.144e+35, Dimensions(length=1, time=-2), name='yards_per_square_attosecond', ascii_symbol='yrd/as^2', symbol='Noneas⁻²') -yards_per_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-1), name='yards_per_minute', ascii_symbol='yrd/min', symbol='NoneNone⁻¹') -yards_per_square_minute = NamedUnit(0.00025400000000000005, Dimensions(length=1, time=-2), name='yards_per_square_minute', ascii_symbol='yrd/min^2', symbol='NoneNone⁻²') -yards_per_hour = NamedUnit(0.00254, Dimensions(length=1, time=-1), name='yards_per_hour', ascii_symbol='yrd/h', symbol='NoneNone⁻¹') -yards_per_square_hour = NamedUnit(7.055555555555557e-06, Dimensions(length=1, time=-2), name='yards_per_square_hour', ascii_symbol='yrd/h^2', symbol='NoneNone⁻²') -yards_per_day = NamedUnit(0.00010583333333333335, Dimensions(length=1, time=-1), name='yards_per_day', ascii_symbol='yrd/d', symbol='NoneNone⁻¹') -yards_per_square_day = NamedUnit(1.224922839506173e-08, Dimensions(length=1, time=-2), name='yards_per_square_day', ascii_symbol='yrd/d^2', symbol='NoneNone⁻²') -yards_per_year = NamedUnit(2.897618249062837e-07, Dimensions(length=1, time=-1), name='yards_per_year', ascii_symbol='yrd/y', symbol='NoneNone⁻¹') -yards_per_square_year = NamedUnit(9.182186698711705e-14, Dimensions(length=1, time=-2), name='yards_per_square_year', ascii_symbol='yrd/y^2', symbol='NoneNone⁻²') -feet_per_second = NamedUnit(0.3048, Dimensions(length=1, time=-1), name='feet_per_second', ascii_symbol='ft/s', symbol='NoneNone⁻¹') -feet_per_square_second = NamedUnit(0.3048, Dimensions(length=1, time=-2), name='feet_per_square_second', ascii_symbol='ft/s^2', symbol='NoneNone⁻²') -feet_per_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-1), name='feet_per_millisecond', ascii_symbol='ft/ms', symbol='Nonems⁻¹') -feet_per_square_millisecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-2), name='feet_per_square_millisecond', ascii_symbol='ft/ms^2', symbol='Nonems⁻²') -feet_per_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-1), name='feet_per_microsecond', ascii_symbol='ft/us', symbol='Noneµs⁻¹') -feet_per_square_microsecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-2), name='feet_per_square_microsecond', ascii_symbol='ft/us^2', symbol='Noneµs⁻²') -feet_per_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-1), name='feet_per_nanosecond', ascii_symbol='ft/ns', symbol='Nonens⁻¹') -feet_per_square_nanosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-2), name='feet_per_square_nanosecond', ascii_symbol='ft/ns^2', symbol='Nonens⁻²') -feet_per_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-1), name='feet_per_picosecond', ascii_symbol='ft/ps', symbol='Noneps⁻¹') -feet_per_square_picosecond = NamedUnit(3.048e+23, Dimensions(length=1, time=-2), name='feet_per_square_picosecond', ascii_symbol='ft/ps^2', symbol='Noneps⁻²') -feet_per_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-1), name='feet_per_femtosecond', ascii_symbol='ft/fs', symbol='Nonefs⁻¹') -feet_per_square_femtosecond = NamedUnit(3.048e+29, Dimensions(length=1, time=-2), name='feet_per_square_femtosecond', ascii_symbol='ft/fs^2', symbol='Nonefs⁻²') -feet_per_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-1), name='feet_per_attosecond', ascii_symbol='ft/as', symbol='Noneas⁻¹') -feet_per_square_attosecond = NamedUnit(3.0479999999999997e+35, Dimensions(length=1, time=-2), name='feet_per_square_attosecond', ascii_symbol='ft/as^2', symbol='Noneas⁻²') -feet_per_minute = NamedUnit(0.00508, Dimensions(length=1, time=-1), name='feet_per_minute', ascii_symbol='ft/min', symbol='NoneNone⁻¹') -feet_per_square_minute = NamedUnit(8.466666666666667e-05, Dimensions(length=1, time=-2), name='feet_per_square_minute', ascii_symbol='ft/min^2', symbol='NoneNone⁻²') -feet_per_hour = NamedUnit(0.0008466666666666667, Dimensions(length=1, time=-1), name='feet_per_hour', ascii_symbol='ft/h', symbol='NoneNone⁻¹') -feet_per_square_hour = NamedUnit(2.351851851851852e-06, Dimensions(length=1, time=-2), name='feet_per_square_hour', ascii_symbol='ft/h^2', symbol='NoneNone⁻²') -feet_per_day = NamedUnit(3.527777777777778e-05, Dimensions(length=1, time=-1), name='feet_per_day', ascii_symbol='ft/d', symbol='NoneNone⁻¹') -feet_per_square_day = NamedUnit(4.083076131687243e-09, Dimensions(length=1, time=-2), name='feet_per_square_day', ascii_symbol='ft/d^2', symbol='NoneNone⁻²') -feet_per_year = NamedUnit(9.658727496876123e-08, Dimensions(length=1, time=-1), name='feet_per_year', ascii_symbol='ft/y', symbol='NoneNone⁻¹') -feet_per_square_year = NamedUnit(3.060728899570568e-14, Dimensions(length=1, time=-2), name='feet_per_square_year', ascii_symbol='ft/y^2', symbol='NoneNone⁻²') -inches_per_second = NamedUnit(0.0254, Dimensions(length=1, time=-1), name='inches_per_second', ascii_symbol='in/s', symbol='NoneNone⁻¹') -inches_per_square_second = NamedUnit(0.0254, Dimensions(length=1, time=-2), name='inches_per_square_second', ascii_symbol='in/s^2', symbol='NoneNone⁻²') -inches_per_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-1), name='inches_per_millisecond', ascii_symbol='in/ms', symbol='Nonems⁻¹') -inches_per_square_millisecond = NamedUnit(25400.0, Dimensions(length=1, time=-2), name='inches_per_square_millisecond', ascii_symbol='in/ms^2', symbol='Nonems⁻²') -inches_per_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-1), name='inches_per_microsecond', ascii_symbol='in/us', symbol='Noneµs⁻¹') -inches_per_square_microsecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-2), name='inches_per_square_microsecond', ascii_symbol='in/us^2', symbol='Noneµs⁻²') -inches_per_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-1), name='inches_per_nanosecond', ascii_symbol='in/ns', symbol='Nonens⁻¹') -inches_per_square_nanosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-2), name='inches_per_square_nanosecond', ascii_symbol='in/ns^2', symbol='Nonens⁻²') -inches_per_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-1), name='inches_per_picosecond', ascii_symbol='in/ps', symbol='Noneps⁻¹') -inches_per_square_picosecond = NamedUnit(2.54e+22, Dimensions(length=1, time=-2), name='inches_per_square_picosecond', ascii_symbol='in/ps^2', symbol='Noneps⁻²') -inches_per_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-1), name='inches_per_femtosecond', ascii_symbol='in/fs', symbol='Nonefs⁻¹') -inches_per_square_femtosecond = NamedUnit(2.54e+28, Dimensions(length=1, time=-2), name='inches_per_square_femtosecond', ascii_symbol='in/fs^2', symbol='Nonefs⁻²') -inches_per_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-1), name='inches_per_attosecond', ascii_symbol='in/as', symbol='Noneas⁻¹') -inches_per_square_attosecond = NamedUnit(2.5399999999999998e+34, Dimensions(length=1, time=-2), name='inches_per_square_attosecond', ascii_symbol='in/as^2', symbol='Noneas⁻²') -inches_per_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-1), name='inches_per_minute', ascii_symbol='in/min', symbol='NoneNone⁻¹') -inches_per_square_minute = NamedUnit(7.055555555555555e-06, Dimensions(length=1, time=-2), name='inches_per_square_minute', ascii_symbol='in/min^2', symbol='NoneNone⁻²') -inches_per_hour = NamedUnit(7.055555555555556e-05, Dimensions(length=1, time=-1), name='inches_per_hour', ascii_symbol='in/h', symbol='NoneNone⁻¹') -inches_per_square_hour = NamedUnit(1.9598765432098765e-07, Dimensions(length=1, time=-2), name='inches_per_square_hour', ascii_symbol='in/h^2', symbol='NoneNone⁻²') -inches_per_day = NamedUnit(2.9398148148148147e-06, Dimensions(length=1, time=-1), name='inches_per_day', ascii_symbol='in/d', symbol='NoneNone⁻¹') -inches_per_square_day = NamedUnit(3.4025634430727023e-10, Dimensions(length=1, time=-2), name='inches_per_square_day', ascii_symbol='in/d^2', symbol='NoneNone⁻²') -inches_per_year = NamedUnit(8.048939580730103e-09, Dimensions(length=1, time=-1), name='inches_per_year', ascii_symbol='in/y', symbol='NoneNone⁻¹') -inches_per_square_year = NamedUnit(2.5506074163088065e-15, Dimensions(length=1, time=-2), name='inches_per_square_year', ascii_symbol='in/y^2', symbol='NoneNone⁻²') -grams_per_cubic_meter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_meter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='EgNone⁻³') -petagrams_per_cubic_meter = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='PgNone⁻³') -teragrams_per_cubic_meter = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_meter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='GgNone⁻³') -megagrams_per_cubic_meter = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='MgNone⁻³') -kilograms_per_cubic_meter = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgNone⁻³') -milligrams_per_cubic_meter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgNone⁻³') -micrograms_per_cubic_meter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgNone⁻³') -nanograms_per_cubic_meter = NamedUnit(1.0000000000000002e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngNone⁻³') -picograms_per_cubic_meter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgNone⁻³') -femtograms_per_cubic_meter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgNone⁻³') -attograms_per_cubic_meter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_meter = NamedUnit(1.660538921e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='NoneNone⁻³') -pounds_per_cubic_meter = NamedUnit(0.45359237, Dimensions(length=-3, mass=1), name='pounds_per_cubic_meter', ascii_symbol='lb m^-3', symbol='NoneNone⁻³') -ounces_per_cubic_meter = NamedUnit(0.028349523125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_meter', ascii_symbol='oz m^-3', symbol='NoneNone⁻³') -grams_per_cubic_exameter = NamedUnit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='NoneEm⁻³') +angstroms_per_minute = NamedUnit(1.6666666666666668e-12, Dimensions(length=1, time=-1), name='angstroms_per_minute', ascii_symbol='Ang/min', symbol='Åmin⁻¹') +angstroms_per_square_minute = NamedUnit(2.7777777777777778e-14, Dimensions(length=1, time=-2), name='angstroms_per_square_minute', ascii_symbol='Ang/min^2', symbol='Åmin⁻²') +angstroms_per_hour = NamedUnit(2.777777777777778e-13, Dimensions(length=1, time=-1), name='angstroms_per_hour', ascii_symbol='Ang/h', symbol='Åh⁻¹') +angstroms_per_square_hour = NamedUnit(7.716049382716049e-16, Dimensions(length=1, time=-2), name='angstroms_per_square_hour', ascii_symbol='Ang/h^2', symbol='Åh⁻²') +angstroms_per_day = NamedUnit(1.1574074074074074e-14, Dimensions(length=1, time=-1), name='angstroms_per_day', ascii_symbol='Ang/d', symbol='Åd⁻¹') +angstroms_per_square_day = NamedUnit(1.3395919067215363e-18, Dimensions(length=1, time=-2), name='angstroms_per_square_day', ascii_symbol='Ang/d^2', symbol='Åd⁻²') +angstroms_per_year = NamedUnit(3.168873850681143e-17, Dimensions(length=1, time=-1), name='angstroms_per_year', ascii_symbol='Ang/y', symbol='Åy⁻¹') +angstroms_per_square_year = NamedUnit(1.0041761481530734e-23, Dimensions(length=1, time=-2), name='angstroms_per_square_year', ascii_symbol='Ang/y^2', symbol='Åy⁻²') +miles_per_second = NamedUnit(1609.344, Dimensions(length=1, time=-1), name='miles_per_second', ascii_symbol='miles/s', symbol='miless⁻¹') +miles_per_square_second = NamedUnit(1609.344, Dimensions(length=1, time=-2), name='miles_per_square_second', ascii_symbol='miles/s^2', symbol='miless⁻²') +miles_per_millisecond = NamedUnit(1609344.0, Dimensions(length=1, time=-1), name='miles_per_millisecond', ascii_symbol='miles/ms', symbol='milesms⁻¹') +miles_per_square_millisecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-2), name='miles_per_square_millisecond', ascii_symbol='miles/ms^2', symbol='milesms⁻²') +miles_per_microsecond = NamedUnit(1609344000.0000002, Dimensions(length=1, time=-1), name='miles_per_microsecond', ascii_symbol='miles/us', symbol='milesµs⁻¹') +miles_per_square_microsecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-2), name='miles_per_square_microsecond', ascii_symbol='miles/us^2', symbol='milesµs⁻²') +miles_per_nanosecond = NamedUnit(1609344000000.0, Dimensions(length=1, time=-1), name='miles_per_nanosecond', ascii_symbol='miles/ns', symbol='milesns⁻¹') +miles_per_square_nanosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-2), name='miles_per_square_nanosecond', ascii_symbol='miles/ns^2', symbol='milesns⁻²') +miles_per_picosecond = NamedUnit(1609344000000000.0, Dimensions(length=1, time=-1), name='miles_per_picosecond', ascii_symbol='miles/ps', symbol='milesps⁻¹') +miles_per_square_picosecond = NamedUnit(1.609344e+27, Dimensions(length=1, time=-2), name='miles_per_square_picosecond', ascii_symbol='miles/ps^2', symbol='milesps⁻²') +miles_per_femtosecond = NamedUnit(1.609344e+18, Dimensions(length=1, time=-1), name='miles_per_femtosecond', ascii_symbol='miles/fs', symbol='milesfs⁻¹') +miles_per_square_femtosecond = NamedUnit(1.609344e+33, Dimensions(length=1, time=-2), name='miles_per_square_femtosecond', ascii_symbol='miles/fs^2', symbol='milesfs⁻²') +miles_per_attosecond = NamedUnit(1.609344e+21, Dimensions(length=1, time=-1), name='miles_per_attosecond', ascii_symbol='miles/as', symbol='milesas⁻¹') +miles_per_square_attosecond = NamedUnit(1.609344e+39, Dimensions(length=1, time=-2), name='miles_per_square_attosecond', ascii_symbol='miles/as^2', symbol='milesas⁻²') +miles_per_minute = NamedUnit(26.822400000000002, Dimensions(length=1, time=-1), name='miles_per_minute', ascii_symbol='miles/min', symbol='milesmin⁻¹') +miles_per_square_minute = NamedUnit(0.44704, Dimensions(length=1, time=-2), name='miles_per_square_minute', ascii_symbol='miles/min^2', symbol='milesmin⁻²') +miles_per_hour = NamedUnit(4.4704, Dimensions(length=1, time=-1), name='miles_per_hour', ascii_symbol='miles/h', symbol='milesh⁻¹') +miles_per_square_hour = NamedUnit(0.012417777777777778, Dimensions(length=1, time=-2), name='miles_per_square_hour', ascii_symbol='miles/h^2', symbol='milesh⁻²') +miles_per_day = NamedUnit(0.18626666666666666, Dimensions(length=1, time=-1), name='miles_per_day', ascii_symbol='miles/d', symbol='milesd⁻¹') +miles_per_square_day = NamedUnit(2.1558641975308643e-05, Dimensions(length=1, time=-2), name='miles_per_square_day', ascii_symbol='miles/d^2', symbol='milesd⁻²') +miles_per_year = NamedUnit(0.0005099808118350593, Dimensions(length=1, time=-1), name='miles_per_year', ascii_symbol='miles/y', symbol='milesy⁻¹') +miles_per_square_year = NamedUnit(1.61606485897326e-10, Dimensions(length=1, time=-2), name='miles_per_square_year', ascii_symbol='miles/y^2', symbol='milesy⁻²') +yards_per_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-1), name='yards_per_second', ascii_symbol='yrd/s', symbol='yrds⁻¹') +yards_per_square_second = NamedUnit(0.9144000000000001, Dimensions(length=1, time=-2), name='yards_per_square_second', ascii_symbol='yrd/s^2', symbol='yrds⁻²') +yards_per_millisecond = NamedUnit(914.4000000000001, Dimensions(length=1, time=-1), name='yards_per_millisecond', ascii_symbol='yrd/ms', symbol='yrdms⁻¹') +yards_per_square_millisecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-2), name='yards_per_square_millisecond', ascii_symbol='yrd/ms^2', symbol='yrdms⁻²') +yards_per_microsecond = NamedUnit(914400.0000000001, Dimensions(length=1, time=-1), name='yards_per_microsecond', ascii_symbol='yrd/us', symbol='yrdµs⁻¹') +yards_per_square_microsecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-2), name='yards_per_square_microsecond', ascii_symbol='yrd/us^2', symbol='yrdµs⁻²') +yards_per_nanosecond = NamedUnit(914400000.0, Dimensions(length=1, time=-1), name='yards_per_nanosecond', ascii_symbol='yrd/ns', symbol='yrdns⁻¹') +yards_per_square_nanosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-2), name='yards_per_square_nanosecond', ascii_symbol='yrd/ns^2', symbol='yrdns⁻²') +yards_per_picosecond = NamedUnit(914400000000.0001, Dimensions(length=1, time=-1), name='yards_per_picosecond', ascii_symbol='yrd/ps', symbol='yrdps⁻¹') +yards_per_square_picosecond = NamedUnit(9.144000000000002e+23, Dimensions(length=1, time=-2), name='yards_per_square_picosecond', ascii_symbol='yrd/ps^2', symbol='yrdps⁻²') +yards_per_femtosecond = NamedUnit(914400000000000.0, Dimensions(length=1, time=-1), name='yards_per_femtosecond', ascii_symbol='yrd/fs', symbol='yrdfs⁻¹') +yards_per_square_femtosecond = NamedUnit(9.144e+29, Dimensions(length=1, time=-2), name='yards_per_square_femtosecond', ascii_symbol='yrd/fs^2', symbol='yrdfs⁻²') +yards_per_attosecond = NamedUnit(9.144e+17, Dimensions(length=1, time=-1), name='yards_per_attosecond', ascii_symbol='yrd/as', symbol='yrdas⁻¹') +yards_per_square_attosecond = NamedUnit(9.144e+35, Dimensions(length=1, time=-2), name='yards_per_square_attosecond', ascii_symbol='yrd/as^2', symbol='yrdas⁻²') +yards_per_minute = NamedUnit(0.015240000000000002, Dimensions(length=1, time=-1), name='yards_per_minute', ascii_symbol='yrd/min', symbol='yrdmin⁻¹') +yards_per_square_minute = NamedUnit(0.00025400000000000005, Dimensions(length=1, time=-2), name='yards_per_square_minute', ascii_symbol='yrd/min^2', symbol='yrdmin⁻²') +yards_per_hour = NamedUnit(0.00254, Dimensions(length=1, time=-1), name='yards_per_hour', ascii_symbol='yrd/h', symbol='yrdh⁻¹') +yards_per_square_hour = NamedUnit(7.055555555555557e-06, Dimensions(length=1, time=-2), name='yards_per_square_hour', ascii_symbol='yrd/h^2', symbol='yrdh⁻²') +yards_per_day = NamedUnit(0.00010583333333333335, Dimensions(length=1, time=-1), name='yards_per_day', ascii_symbol='yrd/d', symbol='yrdd⁻¹') +yards_per_square_day = NamedUnit(1.224922839506173e-08, Dimensions(length=1, time=-2), name='yards_per_square_day', ascii_symbol='yrd/d^2', symbol='yrdd⁻²') +yards_per_year = NamedUnit(2.897618249062837e-07, Dimensions(length=1, time=-1), name='yards_per_year', ascii_symbol='yrd/y', symbol='yrdy⁻¹') +yards_per_square_year = NamedUnit(9.182186698711705e-14, Dimensions(length=1, time=-2), name='yards_per_square_year', ascii_symbol='yrd/y^2', symbol='yrdy⁻²') +feet_per_second = NamedUnit(0.3048, Dimensions(length=1, time=-1), name='feet_per_second', ascii_symbol='ft/s', symbol='fts⁻¹') +feet_per_square_second = NamedUnit(0.3048, Dimensions(length=1, time=-2), name='feet_per_square_second', ascii_symbol='ft/s^2', symbol='fts⁻²') +feet_per_millisecond = NamedUnit(304.8, Dimensions(length=1, time=-1), name='feet_per_millisecond', ascii_symbol='ft/ms', symbol='ftms⁻¹') +feet_per_square_millisecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-2), name='feet_per_square_millisecond', ascii_symbol='ft/ms^2', symbol='ftms⁻²') +feet_per_microsecond = NamedUnit(304800.00000000006, Dimensions(length=1, time=-1), name='feet_per_microsecond', ascii_symbol='ft/us', symbol='ftµs⁻¹') +feet_per_square_microsecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-2), name='feet_per_square_microsecond', ascii_symbol='ft/us^2', symbol='ftµs⁻²') +feet_per_nanosecond = NamedUnit(304800000.0, Dimensions(length=1, time=-1), name='feet_per_nanosecond', ascii_symbol='ft/ns', symbol='ftns⁻¹') +feet_per_square_nanosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-2), name='feet_per_square_nanosecond', ascii_symbol='ft/ns^2', symbol='ftns⁻²') +feet_per_picosecond = NamedUnit(304800000000.0, Dimensions(length=1, time=-1), name='feet_per_picosecond', ascii_symbol='ft/ps', symbol='ftps⁻¹') +feet_per_square_picosecond = NamedUnit(3.048e+23, Dimensions(length=1, time=-2), name='feet_per_square_picosecond', ascii_symbol='ft/ps^2', symbol='ftps⁻²') +feet_per_femtosecond = NamedUnit(304800000000000.0, Dimensions(length=1, time=-1), name='feet_per_femtosecond', ascii_symbol='ft/fs', symbol='ftfs⁻¹') +feet_per_square_femtosecond = NamedUnit(3.048e+29, Dimensions(length=1, time=-2), name='feet_per_square_femtosecond', ascii_symbol='ft/fs^2', symbol='ftfs⁻²') +feet_per_attosecond = NamedUnit(3.048e+17, Dimensions(length=1, time=-1), name='feet_per_attosecond', ascii_symbol='ft/as', symbol='ftas⁻¹') +feet_per_square_attosecond = NamedUnit(3.0479999999999997e+35, Dimensions(length=1, time=-2), name='feet_per_square_attosecond', ascii_symbol='ft/as^2', symbol='ftas⁻²') +feet_per_minute = NamedUnit(0.00508, Dimensions(length=1, time=-1), name='feet_per_minute', ascii_symbol='ft/min', symbol='ftmin⁻¹') +feet_per_square_minute = NamedUnit(8.466666666666667e-05, Dimensions(length=1, time=-2), name='feet_per_square_minute', ascii_symbol='ft/min^2', symbol='ftmin⁻²') +feet_per_hour = NamedUnit(0.0008466666666666667, Dimensions(length=1, time=-1), name='feet_per_hour', ascii_symbol='ft/h', symbol='fth⁻¹') +feet_per_square_hour = NamedUnit(2.351851851851852e-06, Dimensions(length=1, time=-2), name='feet_per_square_hour', ascii_symbol='ft/h^2', symbol='fth⁻²') +feet_per_day = NamedUnit(3.527777777777778e-05, Dimensions(length=1, time=-1), name='feet_per_day', ascii_symbol='ft/d', symbol='ftd⁻¹') +feet_per_square_day = NamedUnit(4.083076131687243e-09, Dimensions(length=1, time=-2), name='feet_per_square_day', ascii_symbol='ft/d^2', symbol='ftd⁻²') +feet_per_year = NamedUnit(9.658727496876123e-08, Dimensions(length=1, time=-1), name='feet_per_year', ascii_symbol='ft/y', symbol='fty⁻¹') +feet_per_square_year = NamedUnit(3.060728899570568e-14, Dimensions(length=1, time=-2), name='feet_per_square_year', ascii_symbol='ft/y^2', symbol='fty⁻²') +inches_per_second = NamedUnit(0.0254, Dimensions(length=1, time=-1), name='inches_per_second', ascii_symbol='in/s', symbol='ins⁻¹') +inches_per_square_second = NamedUnit(0.0254, Dimensions(length=1, time=-2), name='inches_per_square_second', ascii_symbol='in/s^2', symbol='ins⁻²') +inches_per_millisecond = NamedUnit(25.4, Dimensions(length=1, time=-1), name='inches_per_millisecond', ascii_symbol='in/ms', symbol='inms⁻¹') +inches_per_square_millisecond = NamedUnit(25400.0, Dimensions(length=1, time=-2), name='inches_per_square_millisecond', ascii_symbol='in/ms^2', symbol='inms⁻²') +inches_per_microsecond = NamedUnit(25400.0, Dimensions(length=1, time=-1), name='inches_per_microsecond', ascii_symbol='in/us', symbol='inµs⁻¹') +inches_per_square_microsecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-2), name='inches_per_square_microsecond', ascii_symbol='in/us^2', symbol='inµs⁻²') +inches_per_nanosecond = NamedUnit(25399999.999999996, Dimensions(length=1, time=-1), name='inches_per_nanosecond', ascii_symbol='in/ns', symbol='inns⁻¹') +inches_per_square_nanosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-2), name='inches_per_square_nanosecond', ascii_symbol='in/ns^2', symbol='inns⁻²') +inches_per_picosecond = NamedUnit(25400000000.0, Dimensions(length=1, time=-1), name='inches_per_picosecond', ascii_symbol='in/ps', symbol='inps⁻¹') +inches_per_square_picosecond = NamedUnit(2.54e+22, Dimensions(length=1, time=-2), name='inches_per_square_picosecond', ascii_symbol='in/ps^2', symbol='inps⁻²') +inches_per_femtosecond = NamedUnit(25399999999999.996, Dimensions(length=1, time=-1), name='inches_per_femtosecond', ascii_symbol='in/fs', symbol='infs⁻¹') +inches_per_square_femtosecond = NamedUnit(2.54e+28, Dimensions(length=1, time=-2), name='inches_per_square_femtosecond', ascii_symbol='in/fs^2', symbol='infs⁻²') +inches_per_attosecond = NamedUnit(2.5399999999999996e+16, Dimensions(length=1, time=-1), name='inches_per_attosecond', ascii_symbol='in/as', symbol='inas⁻¹') +inches_per_square_attosecond = NamedUnit(2.5399999999999998e+34, Dimensions(length=1, time=-2), name='inches_per_square_attosecond', ascii_symbol='in/as^2', symbol='inas⁻²') +inches_per_minute = NamedUnit(0.00042333333333333334, Dimensions(length=1, time=-1), name='inches_per_minute', ascii_symbol='in/min', symbol='inmin⁻¹') +inches_per_square_minute = NamedUnit(7.055555555555555e-06, Dimensions(length=1, time=-2), name='inches_per_square_minute', ascii_symbol='in/min^2', symbol='inmin⁻²') +inches_per_hour = NamedUnit(7.055555555555556e-05, Dimensions(length=1, time=-1), name='inches_per_hour', ascii_symbol='in/h', symbol='inh⁻¹') +inches_per_square_hour = NamedUnit(1.9598765432098765e-07, Dimensions(length=1, time=-2), name='inches_per_square_hour', ascii_symbol='in/h^2', symbol='inh⁻²') +inches_per_day = NamedUnit(2.9398148148148147e-06, Dimensions(length=1, time=-1), name='inches_per_day', ascii_symbol='in/d', symbol='ind⁻¹') +inches_per_square_day = NamedUnit(3.4025634430727023e-10, Dimensions(length=1, time=-2), name='inches_per_square_day', ascii_symbol='in/d^2', symbol='ind⁻²') +inches_per_year = NamedUnit(8.048939580730103e-09, Dimensions(length=1, time=-1), name='inches_per_year', ascii_symbol='in/y', symbol='iny⁻¹') +inches_per_square_year = NamedUnit(2.5506074163088065e-15, Dimensions(length=1, time=-2), name='inches_per_square_year', ascii_symbol='in/y^2', symbol='iny⁻²') +grams_per_cubic_meter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='grams_per_cubic_meter', ascii_symbol='g m^-3', symbol='gm⁻³') +exagrams_per_cubic_meter = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_meter', ascii_symbol='Eg m^-3', symbol='Egm⁻³') +petagrams_per_cubic_meter = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_meter', ascii_symbol='Pg m^-3', symbol='Pgm⁻³') +teragrams_per_cubic_meter = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_meter', ascii_symbol='Tg m^-3', symbol='Tgm⁻³') +gigagrams_per_cubic_meter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_meter', ascii_symbol='Gg m^-3', symbol='Ggm⁻³') +megagrams_per_cubic_meter = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_meter', ascii_symbol='Mg m^-3', symbol='Mgm⁻³') +kilograms_per_cubic_meter = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_meter', ascii_symbol='kg m^-3', symbol='kgm⁻³') +milligrams_per_cubic_meter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_meter', ascii_symbol='mg m^-3', symbol='mgm⁻³') +micrograms_per_cubic_meter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_meter', ascii_symbol='ug m^-3', symbol='µgm⁻³') +nanograms_per_cubic_meter = NamedUnit(1.0000000000000002e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_meter', ascii_symbol='ng m^-3', symbol='ngm⁻³') +picograms_per_cubic_meter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_meter', ascii_symbol='pg m^-3', symbol='pgm⁻³') +femtograms_per_cubic_meter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_meter', ascii_symbol='fg m^-3', symbol='fgm⁻³') +attograms_per_cubic_meter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_meter', ascii_symbol='ag m^-3', symbol='agm⁻³') +atomic_mass_units_per_cubic_meter = NamedUnit(1.660538921e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_meter', ascii_symbol='au m^-3', symbol='aum⁻³') +pounds_per_cubic_meter = NamedUnit(0.45359237, Dimensions(length=-3, mass=1), name='pounds_per_cubic_meter', ascii_symbol='lb m^-3', symbol='lbm⁻³') +ounces_per_cubic_meter = NamedUnit(0.028349523125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_meter', ascii_symbol='oz m^-3', symbol='ozm⁻³') +grams_per_cubic_exameter = NamedUnit(1e-57, Dimensions(length=-3, mass=1), name='grams_per_cubic_exameter', ascii_symbol='g Em^-3', symbol='gEm⁻³') exagrams_per_cubic_exameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_exameter', ascii_symbol='Eg Em^-3', symbol='EgEm⁻³') petagrams_per_cubic_exameter = NamedUnit(9.999999999999999e-43, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_exameter', ascii_symbol='Pg Em^-3', symbol='PgEm⁻³') teragrams_per_cubic_exameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_exameter', ascii_symbol='Tg Em^-3', symbol='TgEm⁻³') @@ -1270,10 +1270,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_exameter = NamedUnit(1e-69, Dimensions(length=-3, mass=1), name='picograms_per_cubic_exameter', ascii_symbol='pg Em^-3', symbol='pgEm⁻³') femtograms_per_cubic_exameter = NamedUnit(1e-72, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_exameter', ascii_symbol='fg Em^-3', symbol='fgEm⁻³') attograms_per_cubic_exameter = NamedUnit(1e-75, Dimensions(length=-3, mass=1), name='attograms_per_cubic_exameter', ascii_symbol='ag Em^-3', symbol='agEm⁻³') -atomic_mass_units_per_cubic_exameter = NamedUnit(1.6605389209999996e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='NoneEm⁻³') -pounds_per_cubic_exameter = NamedUnit(4.5359237e-55, Dimensions(length=-3, mass=1), name='pounds_per_cubic_exameter', ascii_symbol='lb Em^-3', symbol='NoneEm⁻³') -ounces_per_cubic_exameter = NamedUnit(2.8349523125e-56, Dimensions(length=-3, mass=1), name='ounces_per_cubic_exameter', ascii_symbol='oz Em^-3', symbol='NoneEm⁻³') -grams_per_cubic_petameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='NonePm⁻³') +atomic_mass_units_per_cubic_exameter = NamedUnit(1.6605389209999996e-81, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_exameter', ascii_symbol='au Em^-3', symbol='auEm⁻³') +pounds_per_cubic_exameter = NamedUnit(4.5359237e-55, Dimensions(length=-3, mass=1), name='pounds_per_cubic_exameter', ascii_symbol='lb Em^-3', symbol='lbEm⁻³') +ounces_per_cubic_exameter = NamedUnit(2.8349523125e-56, Dimensions(length=-3, mass=1), name='ounces_per_cubic_exameter', ascii_symbol='oz Em^-3', symbol='ozEm⁻³') +grams_per_cubic_petameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='grams_per_cubic_petameter', ascii_symbol='g Pm^-3', symbol='gPm⁻³') exagrams_per_cubic_petameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_petameter', ascii_symbol='Eg Pm^-3', symbol='EgPm⁻³') petagrams_per_cubic_petameter = NamedUnit(1e-33, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_petameter', ascii_symbol='Pg Pm^-3', symbol='PgPm⁻³') teragrams_per_cubic_petameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_petameter', ascii_symbol='Tg Pm^-3', symbol='TgPm⁻³') @@ -1286,10 +1286,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-60, Dimensions(length=-3, mass=1), name='picograms_per_cubic_petameter', ascii_symbol='pg Pm^-3', symbol='pgPm⁻³') femtograms_per_cubic_petameter = NamedUnit(1.0000000000000002e-63, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_petameter', ascii_symbol='fg Pm^-3', symbol='fgPm⁻³') attograms_per_cubic_petameter = NamedUnit(1.0000000000000001e-66, Dimensions(length=-3, mass=1), name='attograms_per_cubic_petameter', ascii_symbol='ag Pm^-3', symbol='agPm⁻³') -atomic_mass_units_per_cubic_petameter = NamedUnit(1.660538921e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='NonePm⁻³') -pounds_per_cubic_petameter = NamedUnit(4.5359237000000005e-46, Dimensions(length=-3, mass=1), name='pounds_per_cubic_petameter', ascii_symbol='lb Pm^-3', symbol='NonePm⁻³') -ounces_per_cubic_petameter = NamedUnit(2.8349523125000003e-47, Dimensions(length=-3, mass=1), name='ounces_per_cubic_petameter', ascii_symbol='oz Pm^-3', symbol='NonePm⁻³') -grams_per_cubic_terameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='NoneTm⁻³') +atomic_mass_units_per_cubic_petameter = NamedUnit(1.660538921e-72, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_petameter', ascii_symbol='au Pm^-3', symbol='auPm⁻³') +pounds_per_cubic_petameter = NamedUnit(4.5359237000000005e-46, Dimensions(length=-3, mass=1), name='pounds_per_cubic_petameter', ascii_symbol='lb Pm^-3', symbol='lbPm⁻³') +ounces_per_cubic_petameter = NamedUnit(2.8349523125000003e-47, Dimensions(length=-3, mass=1), name='ounces_per_cubic_petameter', ascii_symbol='oz Pm^-3', symbol='ozPm⁻³') +grams_per_cubic_terameter = NamedUnit(1e-39, Dimensions(length=-3, mass=1), name='grams_per_cubic_terameter', ascii_symbol='g Tm^-3', symbol='gTm⁻³') exagrams_per_cubic_terameter = NamedUnit(1e-21, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_terameter', ascii_symbol='Eg Tm^-3', symbol='EgTm⁻³') petagrams_per_cubic_terameter = NamedUnit(1e-24, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_terameter', ascii_symbol='Pg Tm^-3', symbol='PgTm⁻³') teragrams_per_cubic_terameter = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_terameter', ascii_symbol='Tg Tm^-3', symbol='TgTm⁻³') @@ -1302,10 +1302,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_terameter = NamedUnit(1e-51, Dimensions(length=-3, mass=1), name='picograms_per_cubic_terameter', ascii_symbol='pg Tm^-3', symbol='pgTm⁻³') femtograms_per_cubic_terameter = NamedUnit(1e-54, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_terameter', ascii_symbol='fg Tm^-3', symbol='fgTm⁻³') attograms_per_cubic_terameter = NamedUnit(1.0000000000000001e-57, Dimensions(length=-3, mass=1), name='attograms_per_cubic_terameter', ascii_symbol='ag Tm^-3', symbol='agTm⁻³') -atomic_mass_units_per_cubic_terameter = NamedUnit(1.6605389209999997e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='NoneTm⁻³') -pounds_per_cubic_terameter = NamedUnit(4.5359237e-37, Dimensions(length=-3, mass=1), name='pounds_per_cubic_terameter', ascii_symbol='lb Tm^-3', symbol='NoneTm⁻³') -ounces_per_cubic_terameter = NamedUnit(2.8349523125e-38, Dimensions(length=-3, mass=1), name='ounces_per_cubic_terameter', ascii_symbol='oz Tm^-3', symbol='NoneTm⁻³') -grams_per_cubic_gigameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='NoneGm⁻³') +atomic_mass_units_per_cubic_terameter = NamedUnit(1.6605389209999997e-63, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_terameter', ascii_symbol='au Tm^-3', symbol='auTm⁻³') +pounds_per_cubic_terameter = NamedUnit(4.5359237e-37, Dimensions(length=-3, mass=1), name='pounds_per_cubic_terameter', ascii_symbol='lb Tm^-3', symbol='lbTm⁻³') +ounces_per_cubic_terameter = NamedUnit(2.8349523125e-38, Dimensions(length=-3, mass=1), name='ounces_per_cubic_terameter', ascii_symbol='oz Tm^-3', symbol='ozTm⁻³') +grams_per_cubic_gigameter = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='grams_per_cubic_gigameter', ascii_symbol='g Gm^-3', symbol='gGm⁻³') exagrams_per_cubic_gigameter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_gigameter', ascii_symbol='Eg Gm^-3', symbol='EgGm⁻³') petagrams_per_cubic_gigameter = NamedUnit(1e-15, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_gigameter', ascii_symbol='Pg Gm^-3', symbol='PgGm⁻³') teragrams_per_cubic_gigameter = NamedUnit(1e-18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_gigameter', ascii_symbol='Tg Gm^-3', symbol='TgGm⁻³') @@ -1318,10 +1318,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_gigameter = NamedUnit(1e-42, Dimensions(length=-3, mass=1), name='picograms_per_cubic_gigameter', ascii_symbol='pg Gm^-3', symbol='pgGm⁻³') femtograms_per_cubic_gigameter = NamedUnit(1e-45, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_gigameter', ascii_symbol='fg Gm^-3', symbol='fgGm⁻³') attograms_per_cubic_gigameter = NamedUnit(1.0000000000000001e-48, Dimensions(length=-3, mass=1), name='attograms_per_cubic_gigameter', ascii_symbol='ag Gm^-3', symbol='agGm⁻³') -atomic_mass_units_per_cubic_gigameter = NamedUnit(1.660538921e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='NoneGm⁻³') -pounds_per_cubic_gigameter = NamedUnit(4.5359237e-28, Dimensions(length=-3, mass=1), name='pounds_per_cubic_gigameter', ascii_symbol='lb Gm^-3', symbol='NoneGm⁻³') -ounces_per_cubic_gigameter = NamedUnit(2.8349523125e-29, Dimensions(length=-3, mass=1), name='ounces_per_cubic_gigameter', ascii_symbol='oz Gm^-3', symbol='NoneGm⁻³') -grams_per_cubic_megameter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='NoneMm⁻³') +atomic_mass_units_per_cubic_gigameter = NamedUnit(1.660538921e-54, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_gigameter', ascii_symbol='au Gm^-3', symbol='auGm⁻³') +pounds_per_cubic_gigameter = NamedUnit(4.5359237e-28, Dimensions(length=-3, mass=1), name='pounds_per_cubic_gigameter', ascii_symbol='lb Gm^-3', symbol='lbGm⁻³') +ounces_per_cubic_gigameter = NamedUnit(2.8349523125e-29, Dimensions(length=-3, mass=1), name='ounces_per_cubic_gigameter', ascii_symbol='oz Gm^-3', symbol='ozGm⁻³') +grams_per_cubic_megameter = NamedUnit(1.0000000000000001e-21, Dimensions(length=-3, mass=1), name='grams_per_cubic_megameter', ascii_symbol='g Mm^-3', symbol='gMm⁻³') exagrams_per_cubic_megameter = NamedUnit(0.001, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_megameter', ascii_symbol='Eg Mm^-3', symbol='EgMm⁻³') petagrams_per_cubic_megameter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_megameter', ascii_symbol='Pg Mm^-3', symbol='PgMm⁻³') teragrams_per_cubic_megameter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_megameter', ascii_symbol='Tg Mm^-3', symbol='TgMm⁻³') @@ -1334,10 +1334,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_megameter = NamedUnit(1e-33, Dimensions(length=-3, mass=1), name='picograms_per_cubic_megameter', ascii_symbol='pg Mm^-3', symbol='pgMm⁻³') femtograms_per_cubic_megameter = NamedUnit(1.0000000000000001e-36, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_megameter', ascii_symbol='fg Mm^-3', symbol='fgMm⁻³') attograms_per_cubic_megameter = NamedUnit(1.0000000000000001e-39, Dimensions(length=-3, mass=1), name='attograms_per_cubic_megameter', ascii_symbol='ag Mm^-3', symbol='agMm⁻³') -atomic_mass_units_per_cubic_megameter = NamedUnit(1.6605389209999997e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='NoneMm⁻³') -pounds_per_cubic_megameter = NamedUnit(4.535923700000001e-19, Dimensions(length=-3, mass=1), name='pounds_per_cubic_megameter', ascii_symbol='lb Mm^-3', symbol='NoneMm⁻³') -ounces_per_cubic_megameter = NamedUnit(2.8349523125000004e-20, Dimensions(length=-3, mass=1), name='ounces_per_cubic_megameter', ascii_symbol='oz Mm^-3', symbol='NoneMm⁻³') -grams_per_cubic_kilometer = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='Nonekm⁻³') +atomic_mass_units_per_cubic_megameter = NamedUnit(1.6605389209999997e-45, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_megameter', ascii_symbol='au Mm^-3', symbol='auMm⁻³') +pounds_per_cubic_megameter = NamedUnit(4.535923700000001e-19, Dimensions(length=-3, mass=1), name='pounds_per_cubic_megameter', ascii_symbol='lb Mm^-3', symbol='lbMm⁻³') +ounces_per_cubic_megameter = NamedUnit(2.8349523125000004e-20, Dimensions(length=-3, mass=1), name='ounces_per_cubic_megameter', ascii_symbol='oz Mm^-3', symbol='ozMm⁻³') +grams_per_cubic_kilometer = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='grams_per_cubic_kilometer', ascii_symbol='g km^-3', symbol='gkm⁻³') exagrams_per_cubic_kilometer = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_kilometer', ascii_symbol='Eg km^-3', symbol='Egkm⁻³') petagrams_per_cubic_kilometer = NamedUnit(1000.0, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_kilometer', ascii_symbol='Pg km^-3', symbol='Pgkm⁻³') teragrams_per_cubic_kilometer = NamedUnit(1.0, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_kilometer', ascii_symbol='Tg km^-3', symbol='Tgkm⁻³') @@ -1350,10 +1350,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_kilometer = NamedUnit(1.0000000000000001e-24, Dimensions(length=-3, mass=1), name='picograms_per_cubic_kilometer', ascii_symbol='pg km^-3', symbol='pgkm⁻³') femtograms_per_cubic_kilometer = NamedUnit(1e-27, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_kilometer', ascii_symbol='fg km^-3', symbol='fgkm⁻³') attograms_per_cubic_kilometer = NamedUnit(1e-30, Dimensions(length=-3, mass=1), name='attograms_per_cubic_kilometer', ascii_symbol='ag km^-3', symbol='agkm⁻³') -atomic_mass_units_per_cubic_kilometer = NamedUnit(1.6605389209999997e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='Nonekm⁻³') -pounds_per_cubic_kilometer = NamedUnit(4.5359237000000004e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_kilometer', ascii_symbol='lb km^-3', symbol='Nonekm⁻³') -ounces_per_cubic_kilometer = NamedUnit(2.8349523125000003e-11, Dimensions(length=-3, mass=1), name='ounces_per_cubic_kilometer', ascii_symbol='oz km^-3', symbol='Nonekm⁻³') -grams_per_cubic_millimeter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='Nonemm⁻³') +atomic_mass_units_per_cubic_kilometer = NamedUnit(1.6605389209999997e-36, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_kilometer', ascii_symbol='au km^-3', symbol='aukm⁻³') +pounds_per_cubic_kilometer = NamedUnit(4.5359237000000004e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_kilometer', ascii_symbol='lb km^-3', symbol='lbkm⁻³') +ounces_per_cubic_kilometer = NamedUnit(2.8349523125000003e-11, Dimensions(length=-3, mass=1), name='ounces_per_cubic_kilometer', ascii_symbol='oz km^-3', symbol='ozkm⁻³') +grams_per_cubic_millimeter = NamedUnit(1000000.0, Dimensions(length=-3, mass=1), name='grams_per_cubic_millimeter', ascii_symbol='g mm^-3', symbol='gmm⁻³') exagrams_per_cubic_millimeter = NamedUnit(1e+24, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_millimeter', ascii_symbol='Eg mm^-3', symbol='Egmm⁻³') petagrams_per_cubic_millimeter = NamedUnit(1e+21, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_millimeter', ascii_symbol='Pg mm^-3', symbol='Pgmm⁻³') teragrams_per_cubic_millimeter = NamedUnit(1e+18, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_millimeter', ascii_symbol='Tg mm^-3', symbol='Tgmm⁻³') @@ -1366,10 +1366,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_millimeter = NamedUnit(1e-06, Dimensions(length=-3, mass=1), name='picograms_per_cubic_millimeter', ascii_symbol='pg mm^-3', symbol='pgmm⁻³') femtograms_per_cubic_millimeter = NamedUnit(1e-09, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_millimeter', ascii_symbol='fg mm^-3', symbol='fgmm⁻³') attograms_per_cubic_millimeter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='attograms_per_cubic_millimeter', ascii_symbol='ag mm^-3', symbol='agmm⁻³') -atomic_mass_units_per_cubic_millimeter = NamedUnit(1.6605389209999997e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='Nonemm⁻³') -pounds_per_cubic_millimeter = NamedUnit(453592370.0, Dimensions(length=-3, mass=1), name='pounds_per_cubic_millimeter', ascii_symbol='lb mm^-3', symbol='Nonemm⁻³') -ounces_per_cubic_millimeter = NamedUnit(28349523.125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_millimeter', ascii_symbol='oz mm^-3', symbol='Nonemm⁻³') -grams_per_cubic_micrometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='Noneµm⁻³') +atomic_mass_units_per_cubic_millimeter = NamedUnit(1.6605389209999997e-18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_millimeter', ascii_symbol='au mm^-3', symbol='aumm⁻³') +pounds_per_cubic_millimeter = NamedUnit(453592370.0, Dimensions(length=-3, mass=1), name='pounds_per_cubic_millimeter', ascii_symbol='lb mm^-3', symbol='lbmm⁻³') +ounces_per_cubic_millimeter = NamedUnit(28349523.125, Dimensions(length=-3, mass=1), name='ounces_per_cubic_millimeter', ascii_symbol='oz mm^-3', symbol='ozmm⁻³') +grams_per_cubic_micrometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='grams_per_cubic_micrometer', ascii_symbol='g um^-3', symbol='gµm⁻³') exagrams_per_cubic_micrometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_micrometer', ascii_symbol='Eg um^-3', symbol='Egµm⁻³') petagrams_per_cubic_micrometer = NamedUnit(1.0000000000000002e+30, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_micrometer', ascii_symbol='Pg um^-3', symbol='Pgµm⁻³') teragrams_per_cubic_micrometer = NamedUnit(1.0000000000000002e+27, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_micrometer', ascii_symbol='Tg um^-3', symbol='Tgµm⁻³') @@ -1382,10 +1382,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_micrometer = NamedUnit(1000.0000000000002, Dimensions(length=-3, mass=1), name='picograms_per_cubic_micrometer', ascii_symbol='pg um^-3', symbol='pgµm⁻³') femtograms_per_cubic_micrometer = NamedUnit(1.0000000000000002, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_micrometer', ascii_symbol='fg um^-3', symbol='fgµm⁻³') attograms_per_cubic_micrometer = NamedUnit(0.0010000000000000002, Dimensions(length=-3, mass=1), name='attograms_per_cubic_micrometer', ascii_symbol='ag um^-3', symbol='agµm⁻³') -atomic_mass_units_per_cubic_micrometer = NamedUnit(1.660538921e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='Noneµm⁻³') -pounds_per_cubic_micrometer = NamedUnit(4.5359237000000006e+17, Dimensions(length=-3, mass=1), name='pounds_per_cubic_micrometer', ascii_symbol='lb um^-3', symbol='Noneµm⁻³') -ounces_per_cubic_micrometer = NamedUnit(2.8349523125000004e+16, Dimensions(length=-3, mass=1), name='ounces_per_cubic_micrometer', ascii_symbol='oz um^-3', symbol='Noneµm⁻³') -grams_per_cubic_nanometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='Nonenm⁻³') +atomic_mass_units_per_cubic_micrometer = NamedUnit(1.660538921e-09, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_micrometer', ascii_symbol='au um^-3', symbol='auµm⁻³') +pounds_per_cubic_micrometer = NamedUnit(4.5359237000000006e+17, Dimensions(length=-3, mass=1), name='pounds_per_cubic_micrometer', ascii_symbol='lb um^-3', symbol='lbµm⁻³') +ounces_per_cubic_micrometer = NamedUnit(2.8349523125000004e+16, Dimensions(length=-3, mass=1), name='ounces_per_cubic_micrometer', ascii_symbol='oz um^-3', symbol='ozµm⁻³') +grams_per_cubic_nanometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='grams_per_cubic_nanometer', ascii_symbol='g nm^-3', symbol='gnm⁻³') exagrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_nanometer', ascii_symbol='Eg nm^-3', symbol='Egnm⁻³') petagrams_per_cubic_nanometer = NamedUnit(9.999999999999998e+38, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_nanometer', ascii_symbol='Pg nm^-3', symbol='Pgnm⁻³') teragrams_per_cubic_nanometer = NamedUnit(9.999999999999997e+35, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_nanometer', ascii_symbol='Tg nm^-3', symbol='Tgnm⁻³') @@ -1398,10 +1398,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_nanometer = NamedUnit(999999999999.9999, Dimensions(length=-3, mass=1), name='picograms_per_cubic_nanometer', ascii_symbol='pg nm^-3', symbol='pgnm⁻³') femtograms_per_cubic_nanometer = NamedUnit(999999999.9999999, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_nanometer', ascii_symbol='fg nm^-3', symbol='fgnm⁻³') attograms_per_cubic_nanometer = NamedUnit(999999.9999999999, Dimensions(length=-3, mass=1), name='attograms_per_cubic_nanometer', ascii_symbol='ag nm^-3', symbol='agnm⁻³') -atomic_mass_units_per_cubic_nanometer = NamedUnit(1.6605389209999994, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='Nonenm⁻³') -pounds_per_cubic_nanometer = NamedUnit(4.535923699999999e+26, Dimensions(length=-3, mass=1), name='pounds_per_cubic_nanometer', ascii_symbol='lb nm^-3', symbol='Nonenm⁻³') -ounces_per_cubic_nanometer = NamedUnit(2.8349523124999993e+25, Dimensions(length=-3, mass=1), name='ounces_per_cubic_nanometer', ascii_symbol='oz nm^-3', symbol='Nonenm⁻³') -grams_per_cubic_picometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='Nonepm⁻³') +atomic_mass_units_per_cubic_nanometer = NamedUnit(1.6605389209999994, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_nanometer', ascii_symbol='au nm^-3', symbol='aunm⁻³') +pounds_per_cubic_nanometer = NamedUnit(4.535923699999999e+26, Dimensions(length=-3, mass=1), name='pounds_per_cubic_nanometer', ascii_symbol='lb nm^-3', symbol='lbnm⁻³') +ounces_per_cubic_nanometer = NamedUnit(2.8349523124999993e+25, Dimensions(length=-3, mass=1), name='ounces_per_cubic_nanometer', ascii_symbol='oz nm^-3', symbol='oznm⁻³') +grams_per_cubic_picometer = NamedUnit(1.0000000000000001e+33, Dimensions(length=-3, mass=1), name='grams_per_cubic_picometer', ascii_symbol='g pm^-3', symbol='gpm⁻³') exagrams_per_cubic_picometer = NamedUnit(1e+51, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_picometer', ascii_symbol='Eg pm^-3', symbol='Egpm⁻³') petagrams_per_cubic_picometer = NamedUnit(1e+48, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_picometer', ascii_symbol='Pg pm^-3', symbol='Pgpm⁻³') teragrams_per_cubic_picometer = NamedUnit(1.0000000000000001e+45, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_picometer', ascii_symbol='Tg pm^-3', symbol='Tgpm⁻³') @@ -1414,10 +1414,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_picometer = NamedUnit(1.0000000000000001e+21, Dimensions(length=-3, mass=1), name='picograms_per_cubic_picometer', ascii_symbol='pg pm^-3', symbol='pgpm⁻³') femtograms_per_cubic_picometer = NamedUnit(1.0000000000000001e+18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_picometer', ascii_symbol='fg pm^-3', symbol='fgpm⁻³') attograms_per_cubic_picometer = NamedUnit(1000000000000000.1, Dimensions(length=-3, mass=1), name='attograms_per_cubic_picometer', ascii_symbol='ag pm^-3', symbol='agpm⁻³') -atomic_mass_units_per_cubic_picometer = NamedUnit(1660538921.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='Nonepm⁻³') -pounds_per_cubic_picometer = NamedUnit(4.5359237000000005e+35, Dimensions(length=-3, mass=1), name='pounds_per_cubic_picometer', ascii_symbol='lb pm^-3', symbol='Nonepm⁻³') -ounces_per_cubic_picometer = NamedUnit(2.8349523125000003e+34, Dimensions(length=-3, mass=1), name='ounces_per_cubic_picometer', ascii_symbol='oz pm^-3', symbol='Nonepm⁻³') -grams_per_cubic_femtometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='Nonefm⁻³') +atomic_mass_units_per_cubic_picometer = NamedUnit(1660538921.0, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_picometer', ascii_symbol='au pm^-3', symbol='aupm⁻³') +pounds_per_cubic_picometer = NamedUnit(4.5359237000000005e+35, Dimensions(length=-3, mass=1), name='pounds_per_cubic_picometer', ascii_symbol='lb pm^-3', symbol='lbpm⁻³') +ounces_per_cubic_picometer = NamedUnit(2.8349523125000003e+34, Dimensions(length=-3, mass=1), name='ounces_per_cubic_picometer', ascii_symbol='oz pm^-3', symbol='ozpm⁻³') +grams_per_cubic_femtometer = NamedUnit(9.999999999999997e+41, Dimensions(length=-3, mass=1), name='grams_per_cubic_femtometer', ascii_symbol='g fm^-3', symbol='gfm⁻³') exagrams_per_cubic_femtometer = NamedUnit(9.999999999999998e+59, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_femtometer', ascii_symbol='Eg fm^-3', symbol='Egfm⁻³') petagrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+56, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_femtometer', ascii_symbol='Pg fm^-3', symbol='Pgfm⁻³') teragrams_per_cubic_femtometer = NamedUnit(9.999999999999997e+53, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_femtometer', ascii_symbol='Tg fm^-3', symbol='Tgfm⁻³') @@ -1430,10 +1430,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+29, Dimensions(length=-3, mass=1), name='picograms_per_cubic_femtometer', ascii_symbol='pg fm^-3', symbol='pgfm⁻³') femtograms_per_cubic_femtometer = NamedUnit(9.999999999999997e+26, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_femtometer', ascii_symbol='fg fm^-3', symbol='fgfm⁻³') attograms_per_cubic_femtometer = NamedUnit(9.999999999999998e+23, Dimensions(length=-3, mass=1), name='attograms_per_cubic_femtometer', ascii_symbol='ag fm^-3', symbol='agfm⁻³') -atomic_mass_units_per_cubic_femtometer = NamedUnit(1.6605389209999992e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='Nonefm⁻³') -pounds_per_cubic_femtometer = NamedUnit(4.5359236999999985e+44, Dimensions(length=-3, mass=1), name='pounds_per_cubic_femtometer', ascii_symbol='lb fm^-3', symbol='Nonefm⁻³') -ounces_per_cubic_femtometer = NamedUnit(2.834952312499999e+43, Dimensions(length=-3, mass=1), name='ounces_per_cubic_femtometer', ascii_symbol='oz fm^-3', symbol='Nonefm⁻³') -grams_per_cubic_attometer = NamedUnit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='Noneam⁻³') +atomic_mass_units_per_cubic_femtometer = NamedUnit(1.6605389209999992e+18, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_femtometer', ascii_symbol='au fm^-3', symbol='aufm⁻³') +pounds_per_cubic_femtometer = NamedUnit(4.5359236999999985e+44, Dimensions(length=-3, mass=1), name='pounds_per_cubic_femtometer', ascii_symbol='lb fm^-3', symbol='lbfm⁻³') +ounces_per_cubic_femtometer = NamedUnit(2.834952312499999e+43, Dimensions(length=-3, mass=1), name='ounces_per_cubic_femtometer', ascii_symbol='oz fm^-3', symbol='ozfm⁻³') +grams_per_cubic_attometer = NamedUnit(9.999999999999998e+50, Dimensions(length=-3, mass=1), name='grams_per_cubic_attometer', ascii_symbol='g am^-3', symbol='gam⁻³') exagrams_per_cubic_attometer = NamedUnit(9.999999999999999e+68, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_attometer', ascii_symbol='Eg am^-3', symbol='Egam⁻³') petagrams_per_cubic_attometer = NamedUnit(9.999999999999998e+65, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_attometer', ascii_symbol='Pg am^-3', symbol='Pgam⁻³') teragrams_per_cubic_attometer = NamedUnit(9.999999999999999e+62, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_attometer', ascii_symbol='Tg am^-3', symbol='Tgam⁻³') @@ -1446,10 +1446,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_attometer = NamedUnit(1e+39, Dimensions(length=-3, mass=1), name='picograms_per_cubic_attometer', ascii_symbol='pg am^-3', symbol='pgam⁻³') femtograms_per_cubic_attometer = NamedUnit(9.999999999999999e+35, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_attometer', ascii_symbol='fg am^-3', symbol='fgam⁻³') attograms_per_cubic_attometer = NamedUnit(1e+33, Dimensions(length=-3, mass=1), name='attograms_per_cubic_attometer', ascii_symbol='ag am^-3', symbol='agam⁻³') -atomic_mass_units_per_cubic_attometer = NamedUnit(1.6605389209999997e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='Noneam⁻³') -pounds_per_cubic_attometer = NamedUnit(4.5359237e+53, Dimensions(length=-3, mass=1), name='pounds_per_cubic_attometer', ascii_symbol='lb am^-3', symbol='Noneam⁻³') -ounces_per_cubic_attometer = NamedUnit(2.8349523125e+52, Dimensions(length=-3, mass=1), name='ounces_per_cubic_attometer', ascii_symbol='oz am^-3', symbol='Noneam⁻³') -grams_per_cubic_decimeter = NamedUnit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='Nonedm⁻³') +atomic_mass_units_per_cubic_attometer = NamedUnit(1.6605389209999997e+27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_attometer', ascii_symbol='au am^-3', symbol='auam⁻³') +pounds_per_cubic_attometer = NamedUnit(4.5359237e+53, Dimensions(length=-3, mass=1), name='pounds_per_cubic_attometer', ascii_symbol='lb am^-3', symbol='lbam⁻³') +ounces_per_cubic_attometer = NamedUnit(2.8349523125e+52, Dimensions(length=-3, mass=1), name='ounces_per_cubic_attometer', ascii_symbol='oz am^-3', symbol='ozam⁻³') +grams_per_cubic_decimeter = NamedUnit(0.9999999999999998, Dimensions(length=-3, mass=1), name='grams_per_cubic_decimeter', ascii_symbol='g dm^-3', symbol='gdm⁻³') exagrams_per_cubic_decimeter = NamedUnit(9.999999999999997e+17, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_decimeter', ascii_symbol='Eg dm^-3', symbol='Egdm⁻³') petagrams_per_cubic_decimeter = NamedUnit(999999999999999.8, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_decimeter', ascii_symbol='Pg dm^-3', symbol='Pgdm⁻³') teragrams_per_cubic_decimeter = NamedUnit(999999999999.9998, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_decimeter', ascii_symbol='Tg dm^-3', symbol='Tgdm⁻³') @@ -1462,10 +1462,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_decimeter = NamedUnit(9.999999999999998e-13, Dimensions(length=-3, mass=1), name='picograms_per_cubic_decimeter', ascii_symbol='pg dm^-3', symbol='pgdm⁻³') femtograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_decimeter', ascii_symbol='fg dm^-3', symbol='fgdm⁻³') attograms_per_cubic_decimeter = NamedUnit(9.999999999999999e-19, Dimensions(length=-3, mass=1), name='attograms_per_cubic_decimeter', ascii_symbol='ag dm^-3', symbol='agdm⁻³') -atomic_mass_units_per_cubic_decimeter = NamedUnit(1.6605389209999993e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='Nonedm⁻³') -pounds_per_cubic_decimeter = NamedUnit(453.5923699999999, Dimensions(length=-3, mass=1), name='pounds_per_cubic_decimeter', ascii_symbol='lb dm^-3', symbol='Nonedm⁻³') -ounces_per_cubic_decimeter = NamedUnit(28.349523124999994, Dimensions(length=-3, mass=1), name='ounces_per_cubic_decimeter', ascii_symbol='oz dm^-3', symbol='Nonedm⁻³') -grams_per_cubic_centimeter = NamedUnit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='Nonecm⁻³') +atomic_mass_units_per_cubic_decimeter = NamedUnit(1.6605389209999993e-24, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_decimeter', ascii_symbol='au dm^-3', symbol='audm⁻³') +pounds_per_cubic_decimeter = NamedUnit(453.5923699999999, Dimensions(length=-3, mass=1), name='pounds_per_cubic_decimeter', ascii_symbol='lb dm^-3', symbol='lbdm⁻³') +ounces_per_cubic_decimeter = NamedUnit(28.349523124999994, Dimensions(length=-3, mass=1), name='ounces_per_cubic_decimeter', ascii_symbol='oz dm^-3', symbol='ozdm⁻³') +grams_per_cubic_centimeter = NamedUnit(999.9999999999999, Dimensions(length=-3, mass=1), name='grams_per_cubic_centimeter', ascii_symbol='g cm^-3', symbol='gcm⁻³') exagrams_per_cubic_centimeter = NamedUnit(9.999999999999999e+20, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_centimeter', ascii_symbol='Eg cm^-3', symbol='Egcm⁻³') petagrams_per_cubic_centimeter = NamedUnit(9.999999999999999e+17, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_centimeter', ascii_symbol='Pg cm^-3', symbol='Pgcm⁻³') teragrams_per_cubic_centimeter = NamedUnit(999999999999999.9, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_centimeter', ascii_symbol='Tg cm^-3', symbol='Tgcm⁻³') @@ -1478,10 +1478,10 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_centimeter = NamedUnit(9.999999999999999e-10, Dimensions(length=-3, mass=1), name='picograms_per_cubic_centimeter', ascii_symbol='pg cm^-3', symbol='pgcm⁻³') femtograms_per_cubic_centimeter = NamedUnit(1e-12, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_centimeter', ascii_symbol='fg cm^-3', symbol='fgcm⁻³') attograms_per_cubic_centimeter = NamedUnit(9.999999999999999e-16, Dimensions(length=-3, mass=1), name='attograms_per_cubic_centimeter', ascii_symbol='ag cm^-3', symbol='agcm⁻³') -atomic_mass_units_per_cubic_centimeter = NamedUnit(1.6605389209999996e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='Nonecm⁻³') -pounds_per_cubic_centimeter = NamedUnit(453592.36999999994, Dimensions(length=-3, mass=1), name='pounds_per_cubic_centimeter', ascii_symbol='lb cm^-3', symbol='Nonecm⁻³') -ounces_per_cubic_centimeter = NamedUnit(28349.523124999996, Dimensions(length=-3, mass=1), name='ounces_per_cubic_centimeter', ascii_symbol='oz cm^-3', symbol='Nonecm⁻³') -grams_per_cubic_angstrom = NamedUnit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='NoneÅ⁻³') +atomic_mass_units_per_cubic_centimeter = NamedUnit(1.6605389209999996e-21, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_centimeter', ascii_symbol='au cm^-3', symbol='aucm⁻³') +pounds_per_cubic_centimeter = NamedUnit(453592.36999999994, Dimensions(length=-3, mass=1), name='pounds_per_cubic_centimeter', ascii_symbol='lb cm^-3', symbol='lbcm⁻³') +ounces_per_cubic_centimeter = NamedUnit(28349.523124999996, Dimensions(length=-3, mass=1), name='ounces_per_cubic_centimeter', ascii_symbol='oz cm^-3', symbol='ozcm⁻³') +grams_per_cubic_angstrom = NamedUnit(9.999999999999999e+26, Dimensions(length=-3, mass=1), name='grams_per_cubic_angstrom', ascii_symbol='g Ang^-3', symbol='gÅ⁻³') exagrams_per_cubic_angstrom = NamedUnit(1e+45, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_angstrom', ascii_symbol='Eg Ang^-3', symbol='EgÅ⁻³') petagrams_per_cubic_angstrom = NamedUnit(9.999999999999999e+41, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_angstrom', ascii_symbol='Pg Ang^-3', symbol='PgÅ⁻³') teragrams_per_cubic_angstrom = NamedUnit(1e+39, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_angstrom', ascii_symbol='Tg Ang^-3', symbol='TgÅ⁻³') @@ -1494,213 +1494,213 @@ def __init__(self, name: str, units: list[NamedUnit]): picograms_per_cubic_angstrom = NamedUnit(1000000000000000.0, Dimensions(length=-3, mass=1), name='picograms_per_cubic_angstrom', ascii_symbol='pg Ang^-3', symbol='pgÅ⁻³') femtograms_per_cubic_angstrom = NamedUnit(1000000000000.0, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_angstrom', ascii_symbol='fg Ang^-3', symbol='fgÅ⁻³') attograms_per_cubic_angstrom = NamedUnit(1000000000.0, Dimensions(length=-3, mass=1), name='attograms_per_cubic_angstrom', ascii_symbol='ag Ang^-3', symbol='agÅ⁻³') -atomic_mass_units_per_cubic_angstrom = NamedUnit(1660.5389209999996, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='NoneÅ⁻³') -pounds_per_cubic_angstrom = NamedUnit(4.5359237e+29, Dimensions(length=-3, mass=1), name='pounds_per_cubic_angstrom', ascii_symbol='lb Ang^-3', symbol='NoneÅ⁻³') -ounces_per_cubic_angstrom = NamedUnit(2.8349523125e+28, Dimensions(length=-3, mass=1), name='ounces_per_cubic_angstrom', ascii_symbol='oz Ang^-3', symbol='NoneÅ⁻³') -grams_per_cubic_mile = NamedUnit(2.399127585789277e-13, Dimensions(length=-3, mass=1), name='grams_per_cubic_mile', ascii_symbol='g miles^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_mile = NamedUnit(239912.7585789277, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_mile', ascii_symbol='Eg miles^-3', symbol='EgNone⁻³') -petagrams_per_cubic_mile = NamedUnit(239.9127585789277, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_mile', ascii_symbol='Pg miles^-3', symbol='PgNone⁻³') -teragrams_per_cubic_mile = NamedUnit(0.2399127585789277, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_mile', ascii_symbol='Tg miles^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_mile = NamedUnit(0.0002399127585789277, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_mile', ascii_symbol='Gg miles^-3', symbol='GgNone⁻³') -megagrams_per_cubic_mile = NamedUnit(2.399127585789277e-07, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_mile', ascii_symbol='Mg miles^-3', symbol='MgNone⁻³') -kilograms_per_cubic_mile = NamedUnit(2.399127585789277e-10, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_mile', ascii_symbol='kg miles^-3', symbol='kgNone⁻³') -milligrams_per_cubic_mile = NamedUnit(2.399127585789277e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_mile', ascii_symbol='mg miles^-3', symbol='mgNone⁻³') -micrograms_per_cubic_mile = NamedUnit(2.3991275857892774e-19, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_mile', ascii_symbol='ug miles^-3', symbol='µgNone⁻³') -nanograms_per_cubic_mile = NamedUnit(2.3991275857892774e-22, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_mile', ascii_symbol='ng miles^-3', symbol='ngNone⁻³') -picograms_per_cubic_mile = NamedUnit(2.399127585789277e-25, Dimensions(length=-3, mass=1), name='picograms_per_cubic_mile', ascii_symbol='pg miles^-3', symbol='pgNone⁻³') -femtograms_per_cubic_mile = NamedUnit(2.3991275857892772e-28, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_mile', ascii_symbol='fg miles^-3', symbol='fgNone⁻³') -attograms_per_cubic_mile = NamedUnit(2.399127585789277e-31, Dimensions(length=-3, mass=1), name='attograms_per_cubic_mile', ascii_symbol='ag miles^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_mile = NamedUnit(3.98384473264786e-37, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_mile', ascii_symbol='au miles^-3', symbol='NoneNone⁻³') -pounds_per_cubic_mile = NamedUnit(1.0882259675705365e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_mile', ascii_symbol='lb miles^-3', symbol='NoneNone⁻³') -ounces_per_cubic_mile = NamedUnit(6.801412297315853e-12, Dimensions(length=-3, mass=1), name='ounces_per_cubic_mile', ascii_symbol='oz miles^-3', symbol='NoneNone⁻³') -grams_per_cubic_yard = NamedUnit(0.0013079506193143919, Dimensions(length=-3, mass=1), name='grams_per_cubic_yard', ascii_symbol='g yrd^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_yard = NamedUnit(1307950619314391.8, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_yard', ascii_symbol='Eg yrd^-3', symbol='EgNone⁻³') -petagrams_per_cubic_yard = NamedUnit(1307950619314.3918, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_yard', ascii_symbol='Pg yrd^-3', symbol='PgNone⁻³') -teragrams_per_cubic_yard = NamedUnit(1307950619.3143919, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_yard', ascii_symbol='Tg yrd^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_yard = NamedUnit(1307950.6193143919, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_yard', ascii_symbol='Gg yrd^-3', symbol='GgNone⁻³') -megagrams_per_cubic_yard = NamedUnit(1307.9506193143918, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_yard', ascii_symbol='Mg yrd^-3', symbol='MgNone⁻³') -kilograms_per_cubic_yard = NamedUnit(1.3079506193143917, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_yard', ascii_symbol='kg yrd^-3', symbol='kgNone⁻³') -milligrams_per_cubic_yard = NamedUnit(1.3079506193143917e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_yard', ascii_symbol='mg yrd^-3', symbol='mgNone⁻³') -micrograms_per_cubic_yard = NamedUnit(1.3079506193143919e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_yard', ascii_symbol='ug yrd^-3', symbol='µgNone⁻³') -nanograms_per_cubic_yard = NamedUnit(1.307950619314392e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_yard', ascii_symbol='ng yrd^-3', symbol='ngNone⁻³') -picograms_per_cubic_yard = NamedUnit(1.3079506193143919e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_yard', ascii_symbol='pg yrd^-3', symbol='pgNone⁻³') -femtograms_per_cubic_yard = NamedUnit(1.3079506193143918e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_yard', ascii_symbol='fg yrd^-3', symbol='fgNone⁻³') -attograms_per_cubic_yard = NamedUnit(1.307950619314392e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_yard', ascii_symbol='ag yrd^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_yard = NamedUnit(2.1719029101176016e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_yard', ascii_symbol='au yrd^-3', symbol='NoneNone⁻³') -pounds_per_cubic_yard = NamedUnit(0.5932764212577828, Dimensions(length=-3, mass=1), name='pounds_per_cubic_yard', ascii_symbol='lb yrd^-3', symbol='NoneNone⁻³') -ounces_per_cubic_yard = NamedUnit(0.037079776328611425, Dimensions(length=-3, mass=1), name='ounces_per_cubic_yard', ascii_symbol='oz yrd^-3', symbol='NoneNone⁻³') -grams_per_cubic_foot = NamedUnit(0.035314666721488586, Dimensions(length=-3, mass=1), name='grams_per_cubic_foot', ascii_symbol='g ft^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_foot = NamedUnit(3.5314666721488584e+16, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_foot', ascii_symbol='Eg ft^-3', symbol='EgNone⁻³') -petagrams_per_cubic_foot = NamedUnit(35314666721488.586, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_foot', ascii_symbol='Pg ft^-3', symbol='PgNone⁻³') -teragrams_per_cubic_foot = NamedUnit(35314666721.48859, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_foot', ascii_symbol='Tg ft^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_foot = NamedUnit(35314666.72148859, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_foot', ascii_symbol='Gg ft^-3', symbol='GgNone⁻³') -megagrams_per_cubic_foot = NamedUnit(35314.66672148858, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_foot', ascii_symbol='Mg ft^-3', symbol='MgNone⁻³') -kilograms_per_cubic_foot = NamedUnit(35.314666721488585, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_foot', ascii_symbol='kg ft^-3', symbol='kgNone⁻³') -milligrams_per_cubic_foot = NamedUnit(3.5314666721488586e-05, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_foot', ascii_symbol='mg ft^-3', symbol='mgNone⁻³') -micrograms_per_cubic_foot = NamedUnit(3.5314666721488584e-08, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_foot', ascii_symbol='ug ft^-3', symbol='µgNone⁻³') -nanograms_per_cubic_foot = NamedUnit(3.531466672148859e-11, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_foot', ascii_symbol='ng ft^-3', symbol='ngNone⁻³') -picograms_per_cubic_foot = NamedUnit(3.531466672148859e-14, Dimensions(length=-3, mass=1), name='picograms_per_cubic_foot', ascii_symbol='pg ft^-3', symbol='pgNone⁻³') -femtograms_per_cubic_foot = NamedUnit(3.5314666721488585e-17, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_foot', ascii_symbol='fg ft^-3', symbol='fgNone⁻³') -attograms_per_cubic_foot = NamedUnit(3.531466672148859e-20, Dimensions(length=-3, mass=1), name='attograms_per_cubic_foot', ascii_symbol='ag ft^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_foot = NamedUnit(5.864137857317526e-26, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_foot', ascii_symbol='au ft^-3', symbol='NoneNone⁻³') -pounds_per_cubic_foot = NamedUnit(16.018463373960138, Dimensions(length=-3, mass=1), name='pounds_per_cubic_foot', ascii_symbol='lb ft^-3', symbol='NoneNone⁻³') -ounces_per_cubic_foot = NamedUnit(1.0011539608725086, Dimensions(length=-3, mass=1), name='ounces_per_cubic_foot', ascii_symbol='oz ft^-3', symbol='NoneNone⁻³') -grams_per_cubic_inch = NamedUnit(61.02374409473229, Dimensions(length=-3, mass=1), name='grams_per_cubic_inch', ascii_symbol='g in^-3', symbol='NoneNone⁻³') -exagrams_per_cubic_inch = NamedUnit(6.102374409473229e+19, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_inch', ascii_symbol='Eg in^-3', symbol='EgNone⁻³') -petagrams_per_cubic_inch = NamedUnit(6.102374409473229e+16, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_inch', ascii_symbol='Pg in^-3', symbol='PgNone⁻³') -teragrams_per_cubic_inch = NamedUnit(61023744094732.29, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_inch', ascii_symbol='Tg in^-3', symbol='TgNone⁻³') -gigagrams_per_cubic_inch = NamedUnit(61023744094.732285, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_inch', ascii_symbol='Gg in^-3', symbol='GgNone⁻³') -megagrams_per_cubic_inch = NamedUnit(61023744.094732285, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_inch', ascii_symbol='Mg in^-3', symbol='MgNone⁻³') -kilograms_per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_inch', ascii_symbol='kg in^-3', symbol='kgNone⁻³') -milligrams_per_cubic_inch = NamedUnit(0.06102374409473228, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_inch', ascii_symbol='mg in^-3', symbol='mgNone⁻³') -micrograms_per_cubic_inch = NamedUnit(6.102374409473229e-05, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_inch', ascii_symbol='ug in^-3', symbol='µgNone⁻³') -nanograms_per_cubic_inch = NamedUnit(6.10237440947323e-08, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_inch', ascii_symbol='ng in^-3', symbol='ngNone⁻³') -picograms_per_cubic_inch = NamedUnit(6.102374409473229e-11, Dimensions(length=-3, mass=1), name='picograms_per_cubic_inch', ascii_symbol='pg in^-3', symbol='pgNone⁻³') -femtograms_per_cubic_inch = NamedUnit(6.10237440947323e-14, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_inch', ascii_symbol='fg in^-3', symbol='fgNone⁻³') -attograms_per_cubic_inch = NamedUnit(6.10237440947323e-17, Dimensions(length=-3, mass=1), name='attograms_per_cubic_inch', ascii_symbol='ag in^-3', symbol='agNone⁻³') -atomic_mass_units_per_cubic_inch = NamedUnit(1.0133230217444687e-22, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_inch', ascii_symbol='au in^-3', symbol='NoneNone⁻³') -pounds_per_cubic_inch = NamedUnit(27679.904710203125, Dimensions(length=-3, mass=1), name='pounds_per_cubic_inch', ascii_symbol='lb in^-3', symbol='NoneNone⁻³') -ounces_per_cubic_inch = NamedUnit(1729.9940443876953, Dimensions(length=-3, mass=1), name='ounces_per_cubic_inch', ascii_symbol='oz in^-3', symbol='NoneNone⁻³') -moles_per_cubic_meter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_meter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_meter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_meter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_meter = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_meter = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_meter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolNone⁻³') -moles_per_cubic_exameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='NoneEm⁻³') +atomic_mass_units_per_cubic_angstrom = NamedUnit(1660.5389209999996, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_angstrom', ascii_symbol='au Ang^-3', symbol='auÅ⁻³') +pounds_per_cubic_angstrom = NamedUnit(4.5359237e+29, Dimensions(length=-3, mass=1), name='pounds_per_cubic_angstrom', ascii_symbol='lb Ang^-3', symbol='lbÅ⁻³') +ounces_per_cubic_angstrom = NamedUnit(2.8349523125e+28, Dimensions(length=-3, mass=1), name='ounces_per_cubic_angstrom', ascii_symbol='oz Ang^-3', symbol='ozÅ⁻³') +grams_per_cubic_mile = NamedUnit(2.399127585789277e-13, Dimensions(length=-3, mass=1), name='grams_per_cubic_mile', ascii_symbol='g miles^-3', symbol='gmiles⁻³') +exagrams_per_cubic_mile = NamedUnit(239912.7585789277, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_mile', ascii_symbol='Eg miles^-3', symbol='Egmiles⁻³') +petagrams_per_cubic_mile = NamedUnit(239.9127585789277, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_mile', ascii_symbol='Pg miles^-3', symbol='Pgmiles⁻³') +teragrams_per_cubic_mile = NamedUnit(0.2399127585789277, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_mile', ascii_symbol='Tg miles^-3', symbol='Tgmiles⁻³') +gigagrams_per_cubic_mile = NamedUnit(0.0002399127585789277, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_mile', ascii_symbol='Gg miles^-3', symbol='Ggmiles⁻³') +megagrams_per_cubic_mile = NamedUnit(2.399127585789277e-07, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_mile', ascii_symbol='Mg miles^-3', symbol='Mgmiles⁻³') +kilograms_per_cubic_mile = NamedUnit(2.399127585789277e-10, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_mile', ascii_symbol='kg miles^-3', symbol='kgmiles⁻³') +milligrams_per_cubic_mile = NamedUnit(2.399127585789277e-16, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_mile', ascii_symbol='mg miles^-3', symbol='mgmiles⁻³') +micrograms_per_cubic_mile = NamedUnit(2.3991275857892774e-19, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_mile', ascii_symbol='ug miles^-3', symbol='µgmiles⁻³') +nanograms_per_cubic_mile = NamedUnit(2.3991275857892774e-22, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_mile', ascii_symbol='ng miles^-3', symbol='ngmiles⁻³') +picograms_per_cubic_mile = NamedUnit(2.399127585789277e-25, Dimensions(length=-3, mass=1), name='picograms_per_cubic_mile', ascii_symbol='pg miles^-3', symbol='pgmiles⁻³') +femtograms_per_cubic_mile = NamedUnit(2.3991275857892772e-28, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_mile', ascii_symbol='fg miles^-3', symbol='fgmiles⁻³') +attograms_per_cubic_mile = NamedUnit(2.399127585789277e-31, Dimensions(length=-3, mass=1), name='attograms_per_cubic_mile', ascii_symbol='ag miles^-3', symbol='agmiles⁻³') +atomic_mass_units_per_cubic_mile = NamedUnit(3.98384473264786e-37, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_mile', ascii_symbol='au miles^-3', symbol='aumiles⁻³') +pounds_per_cubic_mile = NamedUnit(1.0882259675705365e-10, Dimensions(length=-3, mass=1), name='pounds_per_cubic_mile', ascii_symbol='lb miles^-3', symbol='lbmiles⁻³') +ounces_per_cubic_mile = NamedUnit(6.801412297315853e-12, Dimensions(length=-3, mass=1), name='ounces_per_cubic_mile', ascii_symbol='oz miles^-3', symbol='ozmiles⁻³') +grams_per_cubic_yard = NamedUnit(0.0013079506193143919, Dimensions(length=-3, mass=1), name='grams_per_cubic_yard', ascii_symbol='g yrd^-3', symbol='gyrd⁻³') +exagrams_per_cubic_yard = NamedUnit(1307950619314391.8, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_yard', ascii_symbol='Eg yrd^-3', symbol='Egyrd⁻³') +petagrams_per_cubic_yard = NamedUnit(1307950619314.3918, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_yard', ascii_symbol='Pg yrd^-3', symbol='Pgyrd⁻³') +teragrams_per_cubic_yard = NamedUnit(1307950619.3143919, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_yard', ascii_symbol='Tg yrd^-3', symbol='Tgyrd⁻³') +gigagrams_per_cubic_yard = NamedUnit(1307950.6193143919, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_yard', ascii_symbol='Gg yrd^-3', symbol='Ggyrd⁻³') +megagrams_per_cubic_yard = NamedUnit(1307.9506193143918, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_yard', ascii_symbol='Mg yrd^-3', symbol='Mgyrd⁻³') +kilograms_per_cubic_yard = NamedUnit(1.3079506193143917, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_yard', ascii_symbol='kg yrd^-3', symbol='kgyrd⁻³') +milligrams_per_cubic_yard = NamedUnit(1.3079506193143917e-06, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_yard', ascii_symbol='mg yrd^-3', symbol='mgyrd⁻³') +micrograms_per_cubic_yard = NamedUnit(1.3079506193143919e-09, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_yard', ascii_symbol='ug yrd^-3', symbol='µgyrd⁻³') +nanograms_per_cubic_yard = NamedUnit(1.307950619314392e-12, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_yard', ascii_symbol='ng yrd^-3', symbol='ngyrd⁻³') +picograms_per_cubic_yard = NamedUnit(1.3079506193143919e-15, Dimensions(length=-3, mass=1), name='picograms_per_cubic_yard', ascii_symbol='pg yrd^-3', symbol='pgyrd⁻³') +femtograms_per_cubic_yard = NamedUnit(1.3079506193143918e-18, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_yard', ascii_symbol='fg yrd^-3', symbol='fgyrd⁻³') +attograms_per_cubic_yard = NamedUnit(1.307950619314392e-21, Dimensions(length=-3, mass=1), name='attograms_per_cubic_yard', ascii_symbol='ag yrd^-3', symbol='agyrd⁻³') +atomic_mass_units_per_cubic_yard = NamedUnit(2.1719029101176016e-27, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_yard', ascii_symbol='au yrd^-3', symbol='auyrd⁻³') +pounds_per_cubic_yard = NamedUnit(0.5932764212577828, Dimensions(length=-3, mass=1), name='pounds_per_cubic_yard', ascii_symbol='lb yrd^-3', symbol='lbyrd⁻³') +ounces_per_cubic_yard = NamedUnit(0.037079776328611425, Dimensions(length=-3, mass=1), name='ounces_per_cubic_yard', ascii_symbol='oz yrd^-3', symbol='ozyrd⁻³') +grams_per_cubic_foot = NamedUnit(0.035314666721488586, Dimensions(length=-3, mass=1), name='grams_per_cubic_foot', ascii_symbol='g ft^-3', symbol='gft⁻³') +exagrams_per_cubic_foot = NamedUnit(3.5314666721488584e+16, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_foot', ascii_symbol='Eg ft^-3', symbol='Egft⁻³') +petagrams_per_cubic_foot = NamedUnit(35314666721488.586, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_foot', ascii_symbol='Pg ft^-3', symbol='Pgft⁻³') +teragrams_per_cubic_foot = NamedUnit(35314666721.48859, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_foot', ascii_symbol='Tg ft^-3', symbol='Tgft⁻³') +gigagrams_per_cubic_foot = NamedUnit(35314666.72148859, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_foot', ascii_symbol='Gg ft^-3', symbol='Ggft⁻³') +megagrams_per_cubic_foot = NamedUnit(35314.66672148858, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_foot', ascii_symbol='Mg ft^-3', symbol='Mgft⁻³') +kilograms_per_cubic_foot = NamedUnit(35.314666721488585, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_foot', ascii_symbol='kg ft^-3', symbol='kgft⁻³') +milligrams_per_cubic_foot = NamedUnit(3.5314666721488586e-05, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_foot', ascii_symbol='mg ft^-3', symbol='mgft⁻³') +micrograms_per_cubic_foot = NamedUnit(3.5314666721488584e-08, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_foot', ascii_symbol='ug ft^-3', symbol='µgft⁻³') +nanograms_per_cubic_foot = NamedUnit(3.531466672148859e-11, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_foot', ascii_symbol='ng ft^-3', symbol='ngft⁻³') +picograms_per_cubic_foot = NamedUnit(3.531466672148859e-14, Dimensions(length=-3, mass=1), name='picograms_per_cubic_foot', ascii_symbol='pg ft^-3', symbol='pgft⁻³') +femtograms_per_cubic_foot = NamedUnit(3.5314666721488585e-17, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_foot', ascii_symbol='fg ft^-3', symbol='fgft⁻³') +attograms_per_cubic_foot = NamedUnit(3.531466672148859e-20, Dimensions(length=-3, mass=1), name='attograms_per_cubic_foot', ascii_symbol='ag ft^-3', symbol='agft⁻³') +atomic_mass_units_per_cubic_foot = NamedUnit(5.864137857317526e-26, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_foot', ascii_symbol='au ft^-3', symbol='auft⁻³') +pounds_per_cubic_foot = NamedUnit(16.018463373960138, Dimensions(length=-3, mass=1), name='pounds_per_cubic_foot', ascii_symbol='lb ft^-3', symbol='lbft⁻³') +ounces_per_cubic_foot = NamedUnit(1.0011539608725086, Dimensions(length=-3, mass=1), name='ounces_per_cubic_foot', ascii_symbol='oz ft^-3', symbol='ozft⁻³') +grams_per_cubic_inch = NamedUnit(61.02374409473229, Dimensions(length=-3, mass=1), name='grams_per_cubic_inch', ascii_symbol='g in^-3', symbol='gin⁻³') +exagrams_per_cubic_inch = NamedUnit(6.102374409473229e+19, Dimensions(length=-3, mass=1), name='exagrams_per_cubic_inch', ascii_symbol='Eg in^-3', symbol='Egin⁻³') +petagrams_per_cubic_inch = NamedUnit(6.102374409473229e+16, Dimensions(length=-3, mass=1), name='petagrams_per_cubic_inch', ascii_symbol='Pg in^-3', symbol='Pgin⁻³') +teragrams_per_cubic_inch = NamedUnit(61023744094732.29, Dimensions(length=-3, mass=1), name='teragrams_per_cubic_inch', ascii_symbol='Tg in^-3', symbol='Tgin⁻³') +gigagrams_per_cubic_inch = NamedUnit(61023744094.732285, Dimensions(length=-3, mass=1), name='gigagrams_per_cubic_inch', ascii_symbol='Gg in^-3', symbol='Ggin⁻³') +megagrams_per_cubic_inch = NamedUnit(61023744.094732285, Dimensions(length=-3, mass=1), name='megagrams_per_cubic_inch', ascii_symbol='Mg in^-3', symbol='Mgin⁻³') +kilograms_per_cubic_inch = NamedUnit(61023.74409473229, Dimensions(length=-3, mass=1), name='kilograms_per_cubic_inch', ascii_symbol='kg in^-3', symbol='kgin⁻³') +milligrams_per_cubic_inch = NamedUnit(0.06102374409473228, Dimensions(length=-3, mass=1), name='milligrams_per_cubic_inch', ascii_symbol='mg in^-3', symbol='mgin⁻³') +micrograms_per_cubic_inch = NamedUnit(6.102374409473229e-05, Dimensions(length=-3, mass=1), name='micrograms_per_cubic_inch', ascii_symbol='ug in^-3', symbol='µgin⁻³') +nanograms_per_cubic_inch = NamedUnit(6.10237440947323e-08, Dimensions(length=-3, mass=1), name='nanograms_per_cubic_inch', ascii_symbol='ng in^-3', symbol='ngin⁻³') +picograms_per_cubic_inch = NamedUnit(6.102374409473229e-11, Dimensions(length=-3, mass=1), name='picograms_per_cubic_inch', ascii_symbol='pg in^-3', symbol='pgin⁻³') +femtograms_per_cubic_inch = NamedUnit(6.10237440947323e-14, Dimensions(length=-3, mass=1), name='femtograms_per_cubic_inch', ascii_symbol='fg in^-3', symbol='fgin⁻³') +attograms_per_cubic_inch = NamedUnit(6.10237440947323e-17, Dimensions(length=-3, mass=1), name='attograms_per_cubic_inch', ascii_symbol='ag in^-3', symbol='agin⁻³') +atomic_mass_units_per_cubic_inch = NamedUnit(1.0133230217444687e-22, Dimensions(length=-3, mass=1), name='atomic_mass_units_per_cubic_inch', ascii_symbol='au in^-3', symbol='auin⁻³') +pounds_per_cubic_inch = NamedUnit(27679.904710203125, Dimensions(length=-3, mass=1), name='pounds_per_cubic_inch', ascii_symbol='lb in^-3', symbol='lbin⁻³') +ounces_per_cubic_inch = NamedUnit(1729.9940443876953, Dimensions(length=-3, mass=1), name='ounces_per_cubic_inch', ascii_symbol='oz in^-3', symbol='ozin⁻³') +moles_per_cubic_meter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_meter', ascii_symbol='mol m^-3', symbol='molm⁻³') +millimoles_per_cubic_meter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_meter', ascii_symbol='mmol m^-3', symbol='mmolm⁻³') +micromoles_per_cubic_meter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_meter', ascii_symbol='umol m^-3', symbol='µmolm⁻³') +nanomoles_per_cubic_meter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_meter', ascii_symbol='nmol m^-3', symbol='nmolm⁻³') +picomoles_per_cubic_meter = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_meter', ascii_symbol='pmol m^-3', symbol='pmolm⁻³') +femtomoles_per_cubic_meter = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_meter', ascii_symbol='fmol m^-3', symbol='fmolm⁻³') +attomoles_per_cubic_meter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_meter', ascii_symbol='amol m^-3', symbol='amolm⁻³') +moles_per_cubic_exameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_exameter', ascii_symbol='mol Em^-3', symbol='molEm⁻³') millimoles_per_cubic_exameter = NamedUnit(6.02214076e-34, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_exameter', ascii_symbol='mmol Em^-3', symbol='mmolEm⁻³') micromoles_per_cubic_exameter = NamedUnit(6.02214076e-37, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_exameter', ascii_symbol='umol Em^-3', symbol='µmolEm⁻³') nanomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-40, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_exameter', ascii_symbol='nmol Em^-3', symbol='nmolEm⁻³') picomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-43, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_exameter', ascii_symbol='pmol Em^-3', symbol='pmolEm⁻³') femtomoles_per_cubic_exameter = NamedUnit(6.02214076e-46, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_exameter', ascii_symbol='fmol Em^-3', symbol='fmolEm⁻³') attomoles_per_cubic_exameter = NamedUnit(6.022140759999999e-49, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_exameter', ascii_symbol='amol Em^-3', symbol='amolEm⁻³') -moles_per_cubic_petameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='NonePm⁻³') +moles_per_cubic_petameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_petameter', ascii_symbol='mol Pm^-3', symbol='molPm⁻³') millimoles_per_cubic_petameter = NamedUnit(6.0221407600000005e-25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_petameter', ascii_symbol='mmol Pm^-3', symbol='mmolPm⁻³') micromoles_per_cubic_petameter = NamedUnit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_petameter', ascii_symbol='umol Pm^-3', symbol='µmolPm⁻³') nanomoles_per_cubic_petameter = NamedUnit(6.02214076e-31, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_petameter', ascii_symbol='nmol Pm^-3', symbol='nmolPm⁻³') picomoles_per_cubic_petameter = NamedUnit(6.0221407600000005e-34, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_petameter', ascii_symbol='pmol Pm^-3', symbol='pmolPm⁻³') femtomoles_per_cubic_petameter = NamedUnit(6.022140760000001e-37, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_petameter', ascii_symbol='fmol Pm^-3', symbol='fmolPm⁻³') attomoles_per_cubic_petameter = NamedUnit(6.022140760000001e-40, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_petameter', ascii_symbol='amol Pm^-3', symbol='amolPm⁻³') -moles_per_cubic_terameter = NamedUnit(6.022140759999999e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='NoneTm⁻³') +moles_per_cubic_terameter = NamedUnit(6.022140759999999e-13, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_terameter', ascii_symbol='mol Tm^-3', symbol='molTm⁻³') millimoles_per_cubic_terameter = NamedUnit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_terameter', ascii_symbol='mmol Tm^-3', symbol='mmolTm⁻³') micromoles_per_cubic_terameter = NamedUnit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_terameter', ascii_symbol='umol Tm^-3', symbol='µmolTm⁻³') nanomoles_per_cubic_terameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_terameter', ascii_symbol='nmol Tm^-3', symbol='nmolTm⁻³') picomoles_per_cubic_terameter = NamedUnit(6.02214076e-25, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_terameter', ascii_symbol='pmol Tm^-3', symbol='pmolTm⁻³') femtomoles_per_cubic_terameter = NamedUnit(6.02214076e-28, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_terameter', ascii_symbol='fmol Tm^-3', symbol='fmolTm⁻³') attomoles_per_cubic_terameter = NamedUnit(6.0221407599999995e-31, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_terameter', ascii_symbol='amol Tm^-3', symbol='amolTm⁻³') -moles_per_cubic_gigameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='NoneGm⁻³') +moles_per_cubic_gigameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_gigameter', ascii_symbol='mol Gm^-3', symbol='molGm⁻³') millimoles_per_cubic_gigameter = NamedUnit(6.022140760000001e-07, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_gigameter', ascii_symbol='mmol Gm^-3', symbol='mmolGm⁻³') micromoles_per_cubic_gigameter = NamedUnit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_gigameter', ascii_symbol='umol Gm^-3', symbol='µmolGm⁻³') nanomoles_per_cubic_gigameter = NamedUnit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_gigameter', ascii_symbol='nmol Gm^-3', symbol='nmolGm⁻³') picomoles_per_cubic_gigameter = NamedUnit(6.02214076e-16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_gigameter', ascii_symbol='pmol Gm^-3', symbol='pmolGm⁻³') femtomoles_per_cubic_gigameter = NamedUnit(6.02214076e-19, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_gigameter', ascii_symbol='fmol Gm^-3', symbol='fmolGm⁻³') attomoles_per_cubic_gigameter = NamedUnit(6.02214076e-22, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_gigameter', ascii_symbol='amol Gm^-3', symbol='amolGm⁻³') -moles_per_cubic_megameter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='NoneMm⁻³') +moles_per_cubic_megameter = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_megameter', ascii_symbol='mol Mm^-3', symbol='molMm⁻³') millimoles_per_cubic_megameter = NamedUnit(602.214076, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_megameter', ascii_symbol='mmol Mm^-3', symbol='mmolMm⁻³') micromoles_per_cubic_megameter = NamedUnit(0.602214076, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_megameter', ascii_symbol='umol Mm^-3', symbol='µmolMm⁻³') nanomoles_per_cubic_megameter = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_megameter', ascii_symbol='nmol Mm^-3', symbol='nmolMm⁻³') picomoles_per_cubic_megameter = NamedUnit(6.02214076e-07, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_megameter', ascii_symbol='pmol Mm^-3', symbol='pmolMm⁻³') femtomoles_per_cubic_megameter = NamedUnit(6.02214076e-10, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_megameter', ascii_symbol='fmol Mm^-3', symbol='fmolMm⁻³') attomoles_per_cubic_megameter = NamedUnit(6.02214076e-13, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_megameter', ascii_symbol='amol Mm^-3', symbol='amolMm⁻³') -moles_per_cubic_kilometer = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='Nonekm⁻³') +moles_per_cubic_kilometer = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_kilometer', ascii_symbol='mol km^-3', symbol='molkm⁻³') millimoles_per_cubic_kilometer = NamedUnit(602214076000.0, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_kilometer', ascii_symbol='mmol km^-3', symbol='mmolkm⁻³') micromoles_per_cubic_kilometer = NamedUnit(602214076.0, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_kilometer', ascii_symbol='umol km^-3', symbol='µmolkm⁻³') nanomoles_per_cubic_kilometer = NamedUnit(602214.076, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_kilometer', ascii_symbol='nmol km^-3', symbol='nmolkm⁻³') picomoles_per_cubic_kilometer = NamedUnit(602.214076, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_kilometer', ascii_symbol='pmol km^-3', symbol='pmolkm⁻³') femtomoles_per_cubic_kilometer = NamedUnit(0.602214076, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_kilometer', ascii_symbol='fmol km^-3', symbol='fmolkm⁻³') attomoles_per_cubic_kilometer = NamedUnit(0.000602214076, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_kilometer', ascii_symbol='amol km^-3', symbol='amolkm⁻³') -moles_per_cubic_millimeter = NamedUnit(6.0221407599999995e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='Nonemm⁻³') +moles_per_cubic_millimeter = NamedUnit(6.0221407599999995e+32, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_millimeter', ascii_symbol='mol mm^-3', symbol='molmm⁻³') millimoles_per_cubic_millimeter = NamedUnit(6.02214076e+29, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_millimeter', ascii_symbol='mmol mm^-3', symbol='mmolmm⁻³') micromoles_per_cubic_millimeter = NamedUnit(6.0221407599999996e+26, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_millimeter', ascii_symbol='umol mm^-3', symbol='µmolmm⁻³') nanomoles_per_cubic_millimeter = NamedUnit(6.02214076e+23, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_millimeter', ascii_symbol='nmol mm^-3', symbol='nmolmm⁻³') picomoles_per_cubic_millimeter = NamedUnit(6.02214076e+20, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_millimeter', ascii_symbol='pmol mm^-3', symbol='pmolmm⁻³') femtomoles_per_cubic_millimeter = NamedUnit(6.02214076e+17, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_millimeter', ascii_symbol='fmol mm^-3', symbol='fmolmm⁻³') attomoles_per_cubic_millimeter = NamedUnit(602214076000000.0, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_millimeter', ascii_symbol='amol mm^-3', symbol='amolmm⁻³') -moles_per_cubic_micrometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='Noneµm⁻³') +moles_per_cubic_micrometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_micrometer', ascii_symbol='mol um^-3', symbol='molµm⁻³') millimoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+38, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_micrometer', ascii_symbol='mmol um^-3', symbol='mmolµm⁻³') micromoles_per_cubic_micrometer = NamedUnit(6.0221407600000004e+35, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_micrometer', ascii_symbol='umol um^-3', symbol='µmolµm⁻³') nanomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+32, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_micrometer', ascii_symbol='nmol um^-3', symbol='nmolµm⁻³') picomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+29, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_micrometer', ascii_symbol='pmol um^-3', symbol='pmolµm⁻³') femtomoles_per_cubic_micrometer = NamedUnit(6.022140760000001e+26, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_micrometer', ascii_symbol='fmol um^-3', symbol='fmolµm⁻³') attomoles_per_cubic_micrometer = NamedUnit(6.0221407600000005e+23, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_micrometer', ascii_symbol='amol um^-3', symbol='amolµm⁻³') -moles_per_cubic_nanometer = NamedUnit(6.022140759999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='Nonenm⁻³') +moles_per_cubic_nanometer = NamedUnit(6.022140759999999e+50, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_nanometer', ascii_symbol='mol nm^-3', symbol='molnm⁻³') millimoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_nanometer', ascii_symbol='mmol nm^-3', symbol='mmolnm⁻³') micromoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+44, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_nanometer', ascii_symbol='umol nm^-3', symbol='µmolnm⁻³') nanomoles_per_cubic_nanometer = NamedUnit(6.022140759999998e+41, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_nanometer', ascii_symbol='nmol nm^-3', symbol='nmolnm⁻³') picomoles_per_cubic_nanometer = NamedUnit(6.0221407599999985e+38, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_nanometer', ascii_symbol='pmol nm^-3', symbol='pmolnm⁻³') femtomoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+35, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_nanometer', ascii_symbol='fmol nm^-3', symbol='fmolnm⁻³') attomoles_per_cubic_nanometer = NamedUnit(6.022140759999999e+32, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_nanometer', ascii_symbol='amol nm^-3', symbol='amolnm⁻³') -moles_per_cubic_picometer = NamedUnit(6.0221407600000005e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='Nonepm⁻³') +moles_per_cubic_picometer = NamedUnit(6.0221407600000005e+59, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_picometer', ascii_symbol='mol pm^-3', symbol='molpm⁻³') millimoles_per_cubic_picometer = NamedUnit(6.0221407600000005e+56, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_picometer', ascii_symbol='mmol pm^-3', symbol='mmolpm⁻³') micromoles_per_cubic_picometer = NamedUnit(6.022140760000001e+53, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_picometer', ascii_symbol='umol pm^-3', symbol='µmolpm⁻³') nanomoles_per_cubic_picometer = NamedUnit(6.0221407600000005e+50, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_picometer', ascii_symbol='nmol pm^-3', symbol='nmolpm⁻³') picomoles_per_cubic_picometer = NamedUnit(6.02214076e+47, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_picometer', ascii_symbol='pmol pm^-3', symbol='pmolpm⁻³') femtomoles_per_cubic_picometer = NamedUnit(6.022140760000001e+44, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_picometer', ascii_symbol='fmol pm^-3', symbol='fmolpm⁻³') attomoles_per_cubic_picometer = NamedUnit(6.022140760000001e+41, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_picometer', ascii_symbol='amol pm^-3', symbol='amolpm⁻³') -moles_per_cubic_femtometer = NamedUnit(6.022140759999998e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='Nonefm⁻³') +moles_per_cubic_femtometer = NamedUnit(6.022140759999998e+68, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_femtometer', ascii_symbol='mol fm^-3', symbol='molfm⁻³') millimoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+65, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_femtometer', ascii_symbol='mmol fm^-3', symbol='mmolfm⁻³') micromoles_per_cubic_femtometer = NamedUnit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_femtometer', ascii_symbol='umol fm^-3', symbol='µmolfm⁻³') nanomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+59, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_femtometer', ascii_symbol='nmol fm^-3', symbol='nmolfm⁻³') picomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+56, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_femtometer', ascii_symbol='pmol fm^-3', symbol='pmolfm⁻³') femtomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+53, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_femtometer', ascii_symbol='fmol fm^-3', symbol='fmolfm⁻³') attomoles_per_cubic_femtometer = NamedUnit(6.022140759999998e+50, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_femtometer', ascii_symbol='amol fm^-3', symbol='amolfm⁻³') -moles_per_cubic_attometer = NamedUnit(6.022140759999998e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='Noneam⁻³') +moles_per_cubic_attometer = NamedUnit(6.022140759999998e+77, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_attometer', ascii_symbol='mol am^-3', symbol='molam⁻³') millimoles_per_cubic_attometer = NamedUnit(6.022140759999999e+74, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_attometer', ascii_symbol='mmol am^-3', symbol='mmolam⁻³') micromoles_per_cubic_attometer = NamedUnit(6.022140759999999e+71, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_attometer', ascii_symbol='umol am^-3', symbol='µmolam⁻³') nanomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+68, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_attometer', ascii_symbol='nmol am^-3', symbol='nmolam⁻³') picomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+65, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_attometer', ascii_symbol='pmol am^-3', symbol='pmolam⁻³') femtomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+62, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_attometer', ascii_symbol='fmol am^-3', symbol='fmolam⁻³') attomoles_per_cubic_attometer = NamedUnit(6.022140759999999e+59, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_attometer', ascii_symbol='amol am^-3', symbol='amolam⁻³') -moles_per_cubic_decimeter = NamedUnit(6.022140759999998e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='Nonedm⁻³') +moles_per_cubic_decimeter = NamedUnit(6.022140759999998e+26, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_decimeter', ascii_symbol='mol dm^-3', symbol='moldm⁻³') millimoles_per_cubic_decimeter = NamedUnit(6.0221407599999985e+23, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_decimeter', ascii_symbol='mmol dm^-3', symbol='mmoldm⁻³') micromoles_per_cubic_decimeter = NamedUnit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_decimeter', ascii_symbol='umol dm^-3', symbol='µmoldm⁻³') nanomoles_per_cubic_decimeter = NamedUnit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_decimeter', ascii_symbol='nmol dm^-3', symbol='nmoldm⁻³') picomoles_per_cubic_decimeter = NamedUnit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_decimeter', ascii_symbol='pmol dm^-3', symbol='pmoldm⁻³') femtomoles_per_cubic_decimeter = NamedUnit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_decimeter', ascii_symbol='fmol dm^-3', symbol='fmoldm⁻³') attomoles_per_cubic_decimeter = NamedUnit(602214075.9999999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_decimeter', ascii_symbol='amol dm^-3', symbol='amoldm⁻³') -moles_per_cubic_centimeter = NamedUnit(6.022140759999999e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='Nonecm⁻³') +moles_per_cubic_centimeter = NamedUnit(6.022140759999999e+29, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_centimeter', ascii_symbol='mol cm^-3', symbol='molcm⁻³') millimoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+26, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_centimeter', ascii_symbol='mmol cm^-3', symbol='mmolcm⁻³') micromoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+23, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_centimeter', ascii_symbol='umol cm^-3', symbol='µmolcm⁻³') nanomoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+20, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_centimeter', ascii_symbol='nmol cm^-3', symbol='nmolcm⁻³') picomoles_per_cubic_centimeter = NamedUnit(6.022140759999999e+17, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_centimeter', ascii_symbol='pmol cm^-3', symbol='pmolcm⁻³') femtomoles_per_cubic_centimeter = NamedUnit(602214075999999.9, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_centimeter', ascii_symbol='fmol cm^-3', symbol='fmolcm⁻³') attomoles_per_cubic_centimeter = NamedUnit(602214075999.9999, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_centimeter', ascii_symbol='amol cm^-3', symbol='amolcm⁻³') -moles_per_cubic_angstrom = NamedUnit(6.022140759999999e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='NoneÅ⁻³') +moles_per_cubic_angstrom = NamedUnit(6.022140759999999e+53, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_angstrom', ascii_symbol='mol Ang^-3', symbol='molÅ⁻³') millimoles_per_cubic_angstrom = NamedUnit(6.02214076e+50, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_angstrom', ascii_symbol='mmol Ang^-3', symbol='mmolÅ⁻³') micromoles_per_cubic_angstrom = NamedUnit(6.022140759999999e+47, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_angstrom', ascii_symbol='umol Ang^-3', symbol='µmolÅ⁻³') nanomoles_per_cubic_angstrom = NamedUnit(6.02214076e+44, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_angstrom', ascii_symbol='nmol Ang^-3', symbol='nmolÅ⁻³') picomoles_per_cubic_angstrom = NamedUnit(6.02214076e+41, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_angstrom', ascii_symbol='pmol Ang^-3', symbol='pmolÅ⁻³') femtomoles_per_cubic_angstrom = NamedUnit(6.022140759999999e+38, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_angstrom', ascii_symbol='fmol Ang^-3', symbol='fmolÅ⁻³') attomoles_per_cubic_angstrom = NamedUnit(6.02214076e+35, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_angstrom', ascii_symbol='amol Ang^-3', symbol='amolÅ⁻³') -moles_per_cubic_mile = NamedUnit(144478840228220.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_mile', ascii_symbol='mol miles^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_mile = NamedUnit(144478840228.22003, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_mile', ascii_symbol='mmol miles^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_mile = NamedUnit(144478840.22822002, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_mile', ascii_symbol='umol miles^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_mile = NamedUnit(144478.84022822, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_mile', ascii_symbol='nmol miles^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_mile = NamedUnit(144.47884022822, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_mile', ascii_symbol='pmol miles^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_mile = NamedUnit(0.14447884022822002, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_mile', ascii_symbol='fmol miles^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_mile = NamedUnit(0.00014447884022822003, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_mile', ascii_symbol='amol miles^-3', symbol='amolNone⁻³') -moles_per_cubic_yard = NamedUnit(7.876662736640442e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_yard', ascii_symbol='mol yrd^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_yard = NamedUnit(7.876662736640442e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_yard', ascii_symbol='mmol yrd^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_yard = NamedUnit(7.876662736640442e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_yard', ascii_symbol='umol yrd^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_yard = NamedUnit(787666273664044.2, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_yard', ascii_symbol='nmol yrd^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_yard = NamedUnit(787666273664.0442, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_yard', ascii_symbol='pmol yrd^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_yard = NamedUnit(787666273.6640443, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_yard', ascii_symbol='fmol yrd^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_yard = NamedUnit(787666.2736640442, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_yard', ascii_symbol='amol yrd^-3', symbol='amolNone⁻³') -moles_per_cubic_foot = NamedUnit(2.1266989388929195e+25, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_foot', ascii_symbol='mol ft^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_foot = NamedUnit(2.1266989388929197e+22, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_foot', ascii_symbol='mmol ft^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_foot = NamedUnit(2.1266989388929196e+19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_foot', ascii_symbol='umol ft^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_foot = NamedUnit(2.1266989388929196e+16, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_foot', ascii_symbol='nmol ft^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_foot = NamedUnit(21266989388929.2, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_foot', ascii_symbol='pmol ft^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_foot = NamedUnit(21266989388.9292, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_foot', ascii_symbol='fmol ft^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_foot = NamedUnit(21266989.388929196, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_foot', ascii_symbol='amol ft^-3', symbol='amolNone⁻³') -moles_per_cubic_inch = NamedUnit(3.6749357664069658e+28, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_inch', ascii_symbol='mol in^-3', symbol='NoneNone⁻³') -millimoles_per_cubic_inch = NamedUnit(3.674935766406966e+25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_inch', ascii_symbol='mmol in^-3', symbol='mmolNone⁻³') -micromoles_per_cubic_inch = NamedUnit(3.674935766406966e+22, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_inch', ascii_symbol='umol in^-3', symbol='µmolNone⁻³') -nanomoles_per_cubic_inch = NamedUnit(3.674935766406966e+19, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_inch', ascii_symbol='nmol in^-3', symbol='nmolNone⁻³') -picomoles_per_cubic_inch = NamedUnit(3.674935766406966e+16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_inch', ascii_symbol='pmol in^-3', symbol='pmolNone⁻³') -femtomoles_per_cubic_inch = NamedUnit(36749357664069.664, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_inch', ascii_symbol='fmol in^-3', symbol='fmolNone⁻³') -attomoles_per_cubic_inch = NamedUnit(36749357664.069664, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_inch', ascii_symbol='amol in^-3', symbol='amolNone⁻³') +moles_per_cubic_mile = NamedUnit(144478840228220.0, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_mile', ascii_symbol='mol miles^-3', symbol='molmiles⁻³') +millimoles_per_cubic_mile = NamedUnit(144478840228.22003, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_mile', ascii_symbol='mmol miles^-3', symbol='mmolmiles⁻³') +micromoles_per_cubic_mile = NamedUnit(144478840.22822002, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_mile', ascii_symbol='umol miles^-3', symbol='µmolmiles⁻³') +nanomoles_per_cubic_mile = NamedUnit(144478.84022822, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_mile', ascii_symbol='nmol miles^-3', symbol='nmolmiles⁻³') +picomoles_per_cubic_mile = NamedUnit(144.47884022822, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_mile', ascii_symbol='pmol miles^-3', symbol='pmolmiles⁻³') +femtomoles_per_cubic_mile = NamedUnit(0.14447884022822002, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_mile', ascii_symbol='fmol miles^-3', symbol='fmolmiles⁻³') +attomoles_per_cubic_mile = NamedUnit(0.00014447884022822003, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_mile', ascii_symbol='amol miles^-3', symbol='amolmiles⁻³') +moles_per_cubic_yard = NamedUnit(7.876662736640442e+23, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_yard', ascii_symbol='mol yrd^-3', symbol='molyrd⁻³') +millimoles_per_cubic_yard = NamedUnit(7.876662736640442e+20, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_yard', ascii_symbol='mmol yrd^-3', symbol='mmolyrd⁻³') +micromoles_per_cubic_yard = NamedUnit(7.876662736640442e+17, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_yard', ascii_symbol='umol yrd^-3', symbol='µmolyrd⁻³') +nanomoles_per_cubic_yard = NamedUnit(787666273664044.2, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_yard', ascii_symbol='nmol yrd^-3', symbol='nmolyrd⁻³') +picomoles_per_cubic_yard = NamedUnit(787666273664.0442, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_yard', ascii_symbol='pmol yrd^-3', symbol='pmolyrd⁻³') +femtomoles_per_cubic_yard = NamedUnit(787666273.6640443, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_yard', ascii_symbol='fmol yrd^-3', symbol='fmolyrd⁻³') +attomoles_per_cubic_yard = NamedUnit(787666.2736640442, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_yard', ascii_symbol='amol yrd^-3', symbol='amolyrd⁻³') +moles_per_cubic_foot = NamedUnit(2.1266989388929195e+25, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_foot', ascii_symbol='mol ft^-3', symbol='molft⁻³') +millimoles_per_cubic_foot = NamedUnit(2.1266989388929197e+22, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_foot', ascii_symbol='mmol ft^-3', symbol='mmolft⁻³') +micromoles_per_cubic_foot = NamedUnit(2.1266989388929196e+19, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_foot', ascii_symbol='umol ft^-3', symbol='µmolft⁻³') +nanomoles_per_cubic_foot = NamedUnit(2.1266989388929196e+16, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_foot', ascii_symbol='nmol ft^-3', symbol='nmolft⁻³') +picomoles_per_cubic_foot = NamedUnit(21266989388929.2, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_foot', ascii_symbol='pmol ft^-3', symbol='pmolft⁻³') +femtomoles_per_cubic_foot = NamedUnit(21266989388.9292, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_foot', ascii_symbol='fmol ft^-3', symbol='fmolft⁻³') +attomoles_per_cubic_foot = NamedUnit(21266989.388929196, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_foot', ascii_symbol='amol ft^-3', symbol='amolft⁻³') +moles_per_cubic_inch = NamedUnit(3.6749357664069658e+28, Dimensions(length=-3, moles_hint=1), name='moles_per_cubic_inch', ascii_symbol='mol in^-3', symbol='molin⁻³') +millimoles_per_cubic_inch = NamedUnit(3.674935766406966e+25, Dimensions(length=-3, moles_hint=1), name='millimoles_per_cubic_inch', ascii_symbol='mmol in^-3', symbol='mmolin⁻³') +micromoles_per_cubic_inch = NamedUnit(3.674935766406966e+22, Dimensions(length=-3, moles_hint=1), name='micromoles_per_cubic_inch', ascii_symbol='umol in^-3', symbol='µmolin⁻³') +nanomoles_per_cubic_inch = NamedUnit(3.674935766406966e+19, Dimensions(length=-3, moles_hint=1), name='nanomoles_per_cubic_inch', ascii_symbol='nmol in^-3', symbol='nmolin⁻³') +picomoles_per_cubic_inch = NamedUnit(3.674935766406966e+16, Dimensions(length=-3, moles_hint=1), name='picomoles_per_cubic_inch', ascii_symbol='pmol in^-3', symbol='pmolin⁻³') +femtomoles_per_cubic_inch = NamedUnit(36749357664069.664, Dimensions(length=-3, moles_hint=1), name='femtomoles_per_cubic_inch', ascii_symbol='fmol in^-3', symbol='fmolin⁻³') +attomoles_per_cubic_inch = NamedUnit(36749357664.069664, Dimensions(length=-3, moles_hint=1), name='attomoles_per_cubic_inch', ascii_symbol='amol in^-3', symbol='amolin⁻³') # # Lookup table from symbols to units From 4af3da32b552efed76342ee8e2c4dca3400e4d88 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 3 Oct 2024 12:11:57 +0100 Subject: [PATCH 594/675] Filling in some of the working for the accessors --- sasdata/quantities/_accessor_base.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 42d14fc10..f8f0927bb 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -4,13 +4,36 @@ import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit +from sasdata.raw_form import Group, Dataset DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") +class AccessorTarget: + def __init__(self, data: Group): + self._data = data + + def get_value(self, path: str): + + tokens = path.split(".") + + # Navigate the tree from the entry we need + + current_tree_position: Group | Dataset = self._data + + for token in tokens: + if isinstance(current_tree_position, Group): + current_tree_position = current_tree_position.children[token] + elif isinstance(current_tree_position, Dataset): + current_tree_position = current_tree_position.attributes[token] + + + + + class Accessor[DataType, OutputType]: """ Base class """ - def __init__(self, target_object, value_target: str): + def __init__(self, target_object: AccessorTarget, value_target: str): self.target_object = target_object self.value_target = value_target @@ -35,7 +58,7 @@ def value(self) -> float | None: class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): """ Base class for accessors that work with quantities that have units """ - def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): + def __init__(self, target_object: AccessorTarget, value_target: str, unit_target: str, default_unit=units.none): super().__init__(target_object, value_target) self._unit_target = unit_target self.default_unit = default_unit From d0cf6b1339edea944b3f9c2e46c7a33547b569bb Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 2 Oct 2024 09:46:04 +0100 Subject: [PATCH 595/675] Fixed bug where ohms, and angstroms were forbidden For now, I'm just specifying all of them in the regex. This should work for now because there aren't many symbols to specify, and I'm not sure what else would work without allowing symbols that shouldn't really be allowed. --- sasdata/quantities/unit_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 0f7965a97..a87c50467 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -11,12 +11,12 @@ def split_unit_str(unit_str: str) -> list[str]: """Separate the letters from the numbers in unit_str""" - return findall(r'[A-Za-z]+|[-\d]+|/', unit_str) + return findall(r'[A-Za-zΩ%Å]+|[-\d]+|/', unit_str) def validate_unit_str(unit_str: str) -> bool: """Validate whether unit_str is valid. This doesn't mean that the unit specified in unit_str exists but rather it only consists of letters, and numbers as a unit string should.""" - return not fullmatch(r'[A-Za-z1-9\-\+/\ \.]+', unit_str) is None + return not fullmatch(r'[A-Za-zΩ%Å1-9\-\+/\ \.]+', unit_str) is None def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit From bda8b9ad7ebf3f37928626421fe5a0a340bae780 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 2 Oct 2024 09:50:15 +0100 Subject: [PATCH 596/675] Accept the ^ char but don't do anything with it. --- sasdata/quantities/unit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index a87c50467..082e6e340 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -16,7 +16,7 @@ def split_unit_str(unit_str: str) -> list[str]: def validate_unit_str(unit_str: str) -> bool: """Validate whether unit_str is valid. This doesn't mean that the unit specified in unit_str exists but rather it only consists of letters, and numbers as a unit string should.""" - return not fullmatch(r'[A-Za-zΩ%Å1-9\-\+/\ \.]+', unit_str) is None + return not fullmatch(r'[A-Za-zΩ%Å^1-9\-\+/\ \.]+', unit_str) is None def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit From b5c14095ea191f1bb595d088550069bbdcd3fd24 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 10:25:07 +0100 Subject: [PATCH 597/675] Connecting metadata --- sasdata/metadata.py | 28 +++++++++++++++++++--------- sasdata/quantities/_accessor_base.py | 4 ++-- sasdata/quantities/accessors.py | 27 +++++++++++++++++++++++++-- sasdata/temp_hdf5_reader.py | 13 +++++++------ 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 1ba8718c0..4df6e89bb 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -4,7 +4,7 @@ import sasdata.quantities.units as units from quantities.absolute_temperature import AbsoluteTemperatureAccessor from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor, \ - DimensionlessAccessor, FloatAccessor, TemperatureAccessor + DimensionlessAccessor, FloatAccessor, TemperatureAccessor, AccessorTarget class Detector: @@ -12,7 +12,7 @@ class Detector: Detector information """ - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): # Name of the instrument [string] self.name = StringAccessor(target_object, "detector.name") @@ -64,7 +64,7 @@ def summary(self): class Aperture: - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): # Name self.name = StringAccessor(target_object, "aperture.name") @@ -99,7 +99,7 @@ class Collimation: Class to hold collimation information """ - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): # Name self.name = StringAccessor(target_object, "collimation.name") @@ -127,7 +127,7 @@ class Source: Class to hold source information """ - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): # Name self.name = StringAccessor(target_object, "source.name") @@ -209,7 +209,7 @@ class Sample: """ Class to hold the sample description """ - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): # Short name for sample self.name = StringAccessor(target_object, "sample.name") @@ -272,7 +272,7 @@ class Process: Class that holds information about the processes performed on the data. """ - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): self.name = StringAccessor(target_object, "process.name") self.date = StringAccessor(target_object, "process.date") self.description = StringAccessor(target_object, "process.description") @@ -297,7 +297,7 @@ def __str__(self): f" Notes: {self.notes.value}" ) -class TransmissionSpectrum: +class TransmissionSpectrum(AccessorTarget): """ Class that holds information about transmission spectrum for white beams and spallation sources. @@ -332,5 +332,15 @@ def summary(self) -> str: f" Wavelengths: {self.wavelength.value}\n" f" Transmission: {self.transmission.value}\n") + class Metadata: - pass \ No newline at end of file + def __init__(self, target: AccessorTarget): + self._target = target + + self.aperture = Aperture(target) + self.collimation = Collimation(target) + self.detector = Detector(target) + self.process = Process(target) + self.sample = Sample(target) + self.source = Source(target) + self.transmission_spectrum = TransmissionSpectrum(target) \ No newline at end of file diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index f8f0927bb..78fc4d740 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -20,14 +20,14 @@ def get_value(self, path: str): # Navigate the tree from the entry we need current_tree_position: Group | Dataset = self._data - + for token in tokens: if isinstance(current_tree_position, Group): current_tree_position = current_tree_position.children[token] elif isinstance(current_tree_position, Dataset): current_tree_position = current_tree_position.attributes[token] - + return current_tree_position diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 92ef82bd1..3d9943b04 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -84,13 +84,36 @@ import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit +from sasdata.raw_form import Group, Dataset DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") +class AccessorTarget: + def __init__(self, data: Group): + self._data = data + + def get_value(self, path: str): + + tokens = path.split(".") + + # Navigate the tree from the entry we need + + current_tree_position: Group | Dataset = self._data + + for token in tokens: + if isinstance(current_tree_position, Group): + current_tree_position = current_tree_position.children[token] + elif isinstance(current_tree_position, Dataset): + current_tree_position = current_tree_position.attributes[token] + + return current_tree_position + + + class Accessor[DataType, OutputType]: """ Base class """ - def __init__(self, target_object, value_target: str): + def __init__(self, target_object: AccessorTarget, value_target: str): self.target_object = target_object self.value_target = value_target @@ -115,7 +138,7 @@ def value(self) -> float | None: class QuantityAccessor[DataType](Accessor[DataType, Quantity[DataType]]): """ Base class for accessors that work with quantities that have units """ - def __init__(self, target_object, value_target: str, unit_target: str, default_unit=None): + def __init__(self, target_object: AccessorTarget, value_target: str, unit_target: str, default_unit=units.none): super().__init__(target_object, value_target) self._unit_target = unit_target self.default_unit = default_unit diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 32bb4f7ae..383e0b744 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -10,8 +10,9 @@ from h5py._hl.dataset import Dataset as HDF5Dataset from h5py._hl.group import Group as HDF5Group - -from sasdata.raw_form import RawData +from sasdata.metadata import Metadata +from sasdata.quantities.accessors import AccessorTarget +from sasdata.raw_form import RawData, Dataset from sasdata.raw_form import Dataset as SASDataDataset, Group as SASDataGroup from sasdata.quantities.quantity import NamedQuantity @@ -125,11 +126,11 @@ def load_data(filename) -> list[RawData]: raw_metadata[key] = recurse_hdf5(component) + target = AccessorTarget(SASDataGroup("root", raw_metadata)) + metadata = Metadata(target) + loaded_data.append( - RawData( - name=root_key, - data_contents=data_contents, - raw_metadata=raw_metadata)) + SasData) return loaded_data From 297a2aeaebda3982c31bfc23a2e9388d43915d00 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 3 Oct 2024 14:07:18 +0100 Subject: [PATCH 598/675] Remove target data object attempt --- sasdata/target_data_object.py | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 sasdata/target_data_object.py diff --git a/sasdata/target_data_object.py b/sasdata/target_data_object.py deleted file mode 100644 index d88581752..000000000 --- a/sasdata/target_data_object.py +++ /dev/null @@ -1,3 +0,0 @@ -class TargetData: - def __init__(self): - self.reference_string = \ No newline at end of file From 5a2bf05e55cb9ca877121841d8bdd126ffa3e363 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 10:32:02 +0100 Subject: [PATCH 599/675] Accessor changes --- sasdata/data.py | 132 +++++++++++++++++++++++++-- sasdata/quantities/_accessor_base.py | 2 +- sasdata/quantities/_build_tables.py | 8 +- sasdata/quantities/accessors.py | 6 +- sasdata/quantities/units.py | 8 +- sasdata/raw_form.py | 67 -------------- sasdata/temp_hdf5_reader.py | 20 ++-- 7 files changed, 146 insertions(+), 97 deletions(-) delete mode 100644 sasdata/raw_form.py diff --git a/sasdata/data.py b/sasdata/data.py index 2c6e07b0c..6d42d987d 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,11 +1,12 @@ +from enum import Enum +from typing import TypeVar, Any, Self from dataclasses import dataclass -from sasdata.quantities.quantity import Quantity, NamedQuantity -from sasdata.metadata import Metadata -import numpy as np +from quantities.quantity import NamedQuantity -from sasdata.model_requirements import ModellingRequirements +DataType = TypeVar("DataType") +""" Sasdata metadata tree """ class SasData: def __init__(self, name: str, @@ -20,10 +21,121 @@ def __init__(self, name: str, self._verbose = verbose @dataclass -class DataSet: - abscissae: list[NamedQuantity[np.ndarray]] - ordinate: NamedQuantity[np.ndarray] - other: list[NamedQuantity[np.ndarray]] +class Dataset[DataType]: + name: str + data: DataType + attributes: dict[str, Self | str] - metadata: Metadata - model_requirements: ModellingRequirements + def summary(self, indent_amount: int = 0, indent: str = " ") -> str: + + s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" + s += f"{indent*(indent_amount+1)}{shorten_string(str(self.data))}\n" + for key in self.attributes: + value = self.attributes[key] + if isinstance(value, (Group, Dataset)): + value_string = value.summary(indent_amount+1, indent) + else: + value_string = f"{indent * (indent_amount+1)}{key}: {shorten_string(repr(value))}\n" + + s += value_string + + return s + +@dataclass +class Group: + name: str + children: dict[str, Self | Dataset] + + def summary(self, indent_amount: int=0, indent=" "): + s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" + for key in self.children: + s += self.children[key].summary(indent_amount+1, indent) + + return s + +class Function: + """ Representation of a (data driven) function, such as I vs Q """ + + def __init__(self, abscissae: list[NamedQuantity], ordinate: NamedQuantity): + self.abscissae = abscissae + self.ordinate = ordinate + + +class FunctionType(Enum): + """ What kind of function is this, should not be relied upon to be perfectly descriptive + + The functions might be parametrised by more variables than the specification + """ + UNKNOWN = 0 + SCATTERING_INTENSITY_VS_Q = 1 + SCATTERING_INTENSITY_VS_Q_2D = 2 + SCATTERING_INTENSITY_VS_Q_3D = 3 + SCATTERING_INTENSITY_VS_ANGLE = 4 + UNKNOWN_METADATA = 20 + TRANSMISSION = 21 + POLARISATION_EFFICIENCY = 22 + UNKNOWN_REALSPACE = 30 + SESANS = 31 + CORRELATION_FUNCTION_1D = 32 + CORRELATION_FUNCTION_2D = 33 + CORRELATION_FUNCTION_3D = 34 + INTERFACE_DISTRIBUTION_FUNCTION = 35 + PROBABILITY_DISTRIBUTION = 40 + PROBABILITY_DENSITY = 41 + +def function_type_identification_key(names): + """ Create a key from the names of data objects that can be used to assign a function type""" + return ":".join([s.lower() for s in sorted(names)]) + +function_fields_to_type = [ + (["Q"], "I", FunctionType.SCATTERING_INTENSITY_VS_Q), + (["Qx", "Qy"], "I", FunctionType.SCATTERING_INTENSITY_VS_Q_2D), + (["Qx", "Qy", "Qz"], "I", FunctionType.SCATTERING_INTENSITY_VS_Q_3D), + (["Z"], "G", FunctionType.SESANS), + (["lambda"], "T", FunctionType.TRANSMISSION) +] + +function_fields_lookup = { + function_type_identification_key(inputs + [output]): function_type for (inputs, output), function_type in function_fields_to_type +} + +def build_main_data(data: list[NamedQuantity]) -> Function: + names = [datum.name for datum in data] + identifier = function_type_identification_key(names) + + if identifier in function_fields_lookup: + function_type = function_fields_lookup[identifier] + else: + function_type = FunctionType.UNKNOWN + + match function_type: + case FunctionType.UNKNOWN: + + case _: + raise NotImplementedError("Unknown ") +class SasData: + def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: Group): + self.name = name + self._data_contents = data_contents + self._raw_metadata = raw_metadata + + # TO IMPLEMENT + + # abscissae: list[NamedQuantity[np.ndarray]] + # ordinate: NamedQuantity[np.ndarray] + # other: list[NamedQuantity[np.ndarray]] + # + # metadata: Metadata + # model_requirements: ModellingRequirements + + def summary(self, indent = " "): + s = f"{self.name}\n" + + for data in self._data_contents: + s += f"{indent}{data}\n" + + s += f"{indent}Metadata:\n" + for key in self._raw_metadata.children: + s += self._raw_metadata.children[key].summary(2, indent) + + return s \ No newline at end of file diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 78fc4d740..84a305466 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -4,7 +4,7 @@ import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit -from sasdata.raw_form import Group, Dataset +from sasdata.data import Group, Dataset DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 1af419395..c302802d6 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -93,7 +93,8 @@ # Two stages of aliases, to make sure units don't get lost aliases_1 = { - "A": ["Amps", "amps"] + "A": ["Amps", "amps"], + "C": ["Coulombs", "coulombs"] } aliases_2 = { @@ -101,10 +102,11 @@ "d": ["day"], "h": ["hr", "hour"], "Ang": ["A", "Å"], - "au": ["a.u.", "amu"], + "au": ["amu"], "percent": ["%"], "deg": ["degr", "Deg", "degrees", "Degrees"], - "none": ["Counts", "counts", "cnts", "Cnts"] + "none": ["Counts", "counts", "cnts", "Cnts", "a.u.", "fraction", "Fraction"], + "K": ["C"] # Ugh, cansas } diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 3d9943b04..5b808eec8 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -84,7 +84,7 @@ import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit -from sasdata.raw_form import Group, Dataset +from sasdata.data import Group, Dataset DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") @@ -100,14 +100,14 @@ def get_value(self, path: str): # Navigate the tree from the entry we need current_tree_position: Group | Dataset = self._data - + for token in tokens: if isinstance(current_tree_position, Group): current_tree_position = current_tree_position.children[token] elif isinstance(current_tree_position, Dataset): current_tree_position = current_tree_position.attributes[token] - return current_tree_position + diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index b02646aa3..834bb2436 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -1843,7 +1843,7 @@ def __init__(self, name: str, units: list[NamedUnit]): "pW": picowatts, "fW": femtowatts, "aW": attowatts, - "C": degrees_celsius, + "C": kelvin, "EC": exacoulombs, "PC": petacoulombs, "TC": teracoulombs, @@ -2013,12 +2013,13 @@ def __init__(self, name: str, units: list[NamedUnit]): "%": percent, "Amps": amperes, "amps": amperes, + "Coulombs": degrees_celsius, + "coulombs": degrees_celsius, "yr": years, "year": years, "day": days, "hr": hours, "hour": hours, - "a.u.": atomic_mass_units, "amu": atomic_mass_units, "degr": degrees, "Deg": degrees, @@ -2028,6 +2029,9 @@ def __init__(self, name: str, units: list[NamedUnit]): "counts": none, "cnts": none, "Cnts": none, + "a.u.": none, + "fraction": none, + "Fraction": none, } diff --git a/sasdata/raw_form.py b/sasdata/raw_form.py deleted file mode 100644 index e1883381d..000000000 --- a/sasdata/raw_form.py +++ /dev/null @@ -1,67 +0,0 @@ -from typing import TypeVar, Any, Self -from dataclasses import dataclass - -from quantities.quantity import NamedQuantity - -DataType = TypeVar("DataType") - -""" Sasdata metadata tree """ - -def shorten_string(string): - lines = string.split("\n") - if len(lines) <= 1: - return string - else: - return lines[0][:30] + " ... " + lines[-1][-30:] - -@dataclass -class Dataset[DataType]: - name: str - data: DataType - attributes: dict[str, Self | str] - - def summary(self, indent_amount: int = 0, indent: str = " ") -> str: - - s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" - s += f"{indent*(indent_amount+1)}{shorten_string(str(self.data))}\n" - for key in self.attributes: - value = self.attributes[key] - if isinstance(value, (Group, Dataset)): - value_string = value.summary(indent_amount+1, indent) - else: - value_string = f"{indent * (indent_amount+1)}{key}: {shorten_string(repr(value))}\n" - - s += value_string - - return s - -@dataclass -class Group: - name: str - children: dict[str, Self | Dataset] - - - def summary(self, indent_amount: int=0, indent=" "): - s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" - for key in self.children: - s += self.children[key].summary(indent_amount+1, indent) - - return s - -@dataclass -class RawData: - name: str - data_contents: list[NamedQuantity] - raw_metadata: dict[str, Dataset | Group] - - def summary(self, indent = " "): - s = f"{self.name}\n" - - for data in self.data_contents: - s += f"{indent}{data}\n" - - s += f"{indent}Metadata:\n" - for key in self.raw_metadata: - s += self.raw_metadata[key].summary(2, indent) - - return s \ No newline at end of file diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 383e0b744..a680e7875 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -10,10 +10,9 @@ from h5py._hl.dataset import Dataset as HDF5Dataset from h5py._hl.group import Group as HDF5Group -from sasdata.metadata import Metadata -from sasdata.quantities.accessors import AccessorTarget -from sasdata.raw_form import RawData, Dataset -from sasdata.raw_form import Dataset as SASDataDataset, Group as SASDataGroup + +from sasdata.data import SasData +from sasdata.data import Dataset as SASDataDataset, Group as SASDataGroup from sasdata.quantities.quantity import NamedQuantity from sasdata.quantities import units @@ -98,10 +97,10 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: return output -def load_data(filename) -> list[RawData]: +def load_data(filename) -> list[SasData]: with h5py.File(filename, 'r') as f: - loaded_data: list[RawData] = [] + loaded_data: list[SasData] = [] for root_key in f.keys(): @@ -125,12 +124,11 @@ def load_data(filename) -> list[RawData]: else: raw_metadata[key] = recurse_hdf5(component) - - target = AccessorTarget(SASDataGroup("root", raw_metadata)) - metadata = Metadata(target) - loaded_data.append( - SasData) + SasData( + name=root_key, + data_contents=data_contents, + raw_metadata=SASDataGroup("root", raw_metadata))) return loaded_data From 74864f01272cfc0d8bb9418f5cd30a19a844a749 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 10:47:21 +0100 Subject: [PATCH 600/675] Merge tidying --- sasdata/data.py | 114 ++------------------------- sasdata/data_backing.py | 65 --------------- sasdata/quantities/_accessor_base.py | 2 +- sasdata/quantities/accessors.py | 6 +- sasdata/temp_hdf5_reader.py | 3 +- 5 files changed, 11 insertions(+), 179 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 6d42d987d..f4accd924 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -3,122 +3,18 @@ from dataclasses import dataclass from quantities.quantity import NamedQuantity +from sasdata.metadata import Metadata +from sasdata.quantities.accessors import AccessorTarget +from sasdata.data_backing import Group -DataType = TypeVar("DataType") - -""" Sasdata metadata tree """ - -class SasData: - def __init__(self, name: str, - data_contents: list[Quantity], - raw_metadata: Group, - instrument: Instrument, - verbose: bool=False): - - self.name = name - self._data_contents = data_contents - self._raw_metadata = raw_metadata - self._verbose = verbose - -@dataclass -class Dataset[DataType]: - name: str - data: DataType - attributes: dict[str, Self | str] - - def summary(self, indent_amount: int = 0, indent: str = " ") -> str: - - s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" - s += f"{indent*(indent_amount+1)}{shorten_string(str(self.data))}\n" - for key in self.attributes: - value = self.attributes[key] - if isinstance(value, (Group, Dataset)): - value_string = value.summary(indent_amount+1, indent) - else: - value_string = f"{indent * (indent_amount+1)}{key}: {shorten_string(repr(value))}\n" - - s += value_string - - return s - -@dataclass -class Group: - name: str - children: dict[str, Self | Dataset] - - def summary(self, indent_amount: int=0, indent=" "): - s = f"{indent*indent_amount}{self.name.split("/")[-1]}:\n" - for key in self.children: - s += self.children[key].summary(indent_amount+1, indent) - - return s - -class Function: - """ Representation of a (data driven) function, such as I vs Q """ - - def __init__(self, abscissae: list[NamedQuantity], ordinate: NamedQuantity): - self.abscissae = abscissae - self.ordinate = ordinate - - -class FunctionType(Enum): - """ What kind of function is this, should not be relied upon to be perfectly descriptive - - The functions might be parametrised by more variables than the specification - """ - UNKNOWN = 0 - SCATTERING_INTENSITY_VS_Q = 1 - SCATTERING_INTENSITY_VS_Q_2D = 2 - SCATTERING_INTENSITY_VS_Q_3D = 3 - SCATTERING_INTENSITY_VS_ANGLE = 4 - UNKNOWN_METADATA = 20 - TRANSMISSION = 21 - POLARISATION_EFFICIENCY = 22 - UNKNOWN_REALSPACE = 30 - SESANS = 31 - CORRELATION_FUNCTION_1D = 32 - CORRELATION_FUNCTION_2D = 33 - CORRELATION_FUNCTION_3D = 34 - INTERFACE_DISTRIBUTION_FUNCTION = 35 - PROBABILITY_DISTRIBUTION = 40 - PROBABILITY_DENSITY = 41 - -def function_type_identification_key(names): - """ Create a key from the names of data objects that can be used to assign a function type""" - return ":".join([s.lower() for s in sorted(names)]) - -function_fields_to_type = [ - (["Q"], "I", FunctionType.SCATTERING_INTENSITY_VS_Q), - (["Qx", "Qy"], "I", FunctionType.SCATTERING_INTENSITY_VS_Q_2D), - (["Qx", "Qy", "Qz"], "I", FunctionType.SCATTERING_INTENSITY_VS_Q_3D), - (["Z"], "G", FunctionType.SESANS), - (["lambda"], "T", FunctionType.TRANSMISSION) -] - -function_fields_lookup = { - function_type_identification_key(inputs + [output]): function_type for (inputs, output), function_type in function_fields_to_type -} - -def build_main_data(data: list[NamedQuantity]) -> Function: - names = [datum.name for datum in data] - identifier = function_type_identification_key(names) - - if identifier in function_fields_lookup: - function_type = function_fields_lookup[identifier] - else: - function_type = FunctionType.UNKNOWN - - match function_type: - case FunctionType.UNKNOWN: - - case _: - raise NotImplementedError("Unknown ") class SasData: def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: Group): self.name = name self._data_contents = data_contents self._raw_metadata = raw_metadata + self.metadata = Metadata(AccessorTarget(raw_metadata)) + # TO IMPLEMENT # abscissae: list[NamedQuantity[np.ndarray]] diff --git a/sasdata/data_backing.py b/sasdata/data_backing.py index 315212628..dadfc194f 100644 --- a/sasdata/data_backing.py +++ b/sasdata/data_backing.py @@ -36,34 +36,6 @@ def summary(self, indent_amount: int = 0, indent: str = " ") -> str: return s - @staticmethod - def deserialise_json(json_data: dict) -> "Dataset": - name = json_data["name"] - data = "" # TODO: figure out QuantityType serialisation - attributes = {} - for key in json_data["attributes"]: - value = json_data["attributes"][key] - if isinstance(value, dict): - attributes[key] = Dataset.deserialise_json(value) - else: - attributes[key] = value - return Dataset(name, data, attributes) - - def serialise_json(self): - content = { - "name": self.name, - "data": "", # TODO: figure out QuantityType serialisation - "attributes": {}, - "type": "dataset" - } - for key in self.attributes: - value = self.attributes[key] - if isinstance(value, (Group, Dataset)): - content["attributes"]["key"] = value.serialise_json() - else: - content["attributes"]["key"] = value - return content - @dataclass class Group: name: str @@ -76,27 +48,6 @@ def summary(self, indent_amount: int=0, indent=" "): return s - @staticmethod - def deserialise_json(json_data: dict) -> "Group": - name = json_data["name"] - children = {} - for key in json_data["children"]: - value = json_data["children"][key] - if value["type"] == "group": - children[key] = Group.deserialise_json(value) - else: - children[key] = Dataset.deserialise_json(value) - return Group(name, children) - - def serialise_json(self): - return { - "name": self.name, - "children": { - key: self.children[key].serialise_json() for key in self.children - }, - "type": "group" - } - class Function: """ Representation of a (data driven) function, such as I vs Q """ @@ -157,19 +108,3 @@ def build_main_data(data: list[NamedQuantity]) -> Function: pass case _: raise NotImplementedError("Unknown ") - -def key_tree(data: Group | Dataset, indent_amount=0, indent: str = " ") -> str: - """ Show a metadata tree, showing the names of they keys used to access them""" - s = "" - if isinstance(data, Group): - for key in data.children: - s += indent*indent_amount + key + "\n" - s += key_tree(data.children[key], indent_amount=indent_amount+1, indent=indent) - - if isinstance(data, Dataset): - s += indent*indent_amount + "[data]\n" - for key in data.attributes: - s += indent*indent_amount + key + "\n" - s += key_tree(data.attributes[key], indent_amount=indent_amount+1, indent=indent) - - return s \ No newline at end of file diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 84a305466..2f84e962d 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -4,7 +4,7 @@ import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit -from sasdata.data import Group, Dataset +from sasdata.data_backing import Group, Dataset DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 5b808eec8..cf15bed98 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -84,7 +84,7 @@ import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit -from sasdata.data import Group, Dataset +from sasdata.data_backing import Group, Dataset DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") @@ -100,14 +100,14 @@ def get_value(self, path: str): # Navigate the tree from the entry we need current_tree_position: Group | Dataset = self._data - + for token in tokens: if isinstance(current_tree_position, Group): current_tree_position = current_tree_position.children[token] elif isinstance(current_tree_position, Dataset): current_tree_position = current_tree_position.attributes[token] - + return current_tree_position diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index a680e7875..b5bc1f707 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -12,7 +12,7 @@ from sasdata.data import SasData -from sasdata.data import Dataset as SASDataDataset, Group as SASDataGroup +from sasdata.data_backing import Dataset as SASDataDataset, Group as SASDataGroup from sasdata.quantities.quantity import NamedQuantity from sasdata.quantities import units @@ -124,6 +124,7 @@ def load_data(filename) -> list[SasData]: else: raw_metadata[key] = recurse_hdf5(component) + loaded_data.append( SasData( name=root_key, From f6c30d613a6b88aafd600b806e92966a30ae1699 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 11:31:19 +0100 Subject: [PATCH 601/675] Metadata linked up, just not pointing in the right place right now --- sasdata/data.py | 3 +-- sasdata/metadata.py | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index f4accd924..fea32fd18 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -31,7 +31,6 @@ def summary(self, indent = " "): s += f"{indent}{data}\n" s += f"{indent}Metadata:\n" - for key in self._raw_metadata.children: - s += self._raw_metadata.children[key].summary(2, indent) + s += self.metadata.summary() return s \ No newline at end of file diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 4df6e89bb..c3e81332d 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -92,7 +92,7 @@ def summary(self): return (f"Aperture:\n" f" Name: {self.name.value}\n" f" Aperture size: {self.size.value}\n" - f" Aperture distance: {self.distance.value}") + f" Aperture distance: {self.distance.value}\n") class Collimation: """ @@ -111,7 +111,7 @@ def __init__(self, target_object: AccessorTarget): # Todo - how do we handle this - self.collimator = Collimation(target_object) + # self.collimator = Collimation(target_object) def summary(self): @@ -192,7 +192,7 @@ def summary(self) -> str: f" Min. Wavelength: {self.wavelength_min.value}\n" f" Max. Wavelength: {self.wavelength_max.value}\n" f" Wavelength Spread: {self.wavelength_spread.value}\n" - f" Beam Size: {self.beam_size}\n") + f" Beam Size: {self.beam_size.value}\n") @@ -288,13 +288,13 @@ def single_line_desc(self): """ return f"{self.name.value} {self.date.value} {self.description.value}" - def __str__(self): + def summary(self): return (f"Process:\n" f" Name: {self.name.value}\n" f" Date: {self.date.value}\n" f" Description: {self.description.value}\n" f" Term: {self.term.value}\n" - f" Notes: {self.notes.value}" + f" Notes: {self.notes.value}\n" ) class TransmissionSpectrum(AccessorTarget): @@ -322,7 +322,7 @@ def __init__(self, target_object): self.transmission_deviation = DimensionlessAccessor[ArrayLike](target_object, "transmission.transmission_deviation", "transmission.transmission_deviation.units", - default_units=units.none) + default_unit=units.none) def summary(self) -> str: @@ -343,4 +343,13 @@ def __init__(self, target: AccessorTarget): self.process = Process(target) self.sample = Sample(target) self.source = Source(target) - self.transmission_spectrum = TransmissionSpectrum(target) \ No newline at end of file + self.transmission_spectrum = TransmissionSpectrum(target) def summary(self): + return ( + self.aperture.summary() + + self.collimation.summary() + + self.detector.summary() + + self.process.summary() + + self.sample.summary() + + self.source.summary() + + self.transmission_spectrum.summary() + ) \ No newline at end of file From 49e1db1de4f6c6b7930b27f140e4fc36115846be Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 11:56:54 +0100 Subject: [PATCH 602/675] Added some debugging info to the summary --- sasdata/data.py | 5 ++++- sasdata/data_backing.py | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/sasdata/data.py b/sasdata/data.py index fea32fd18..f50fb61a9 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -5,7 +5,8 @@ from quantities.quantity import NamedQuantity from sasdata.metadata import Metadata from sasdata.quantities.accessors import AccessorTarget -from sasdata.data_backing import Group +from sasdata.data_backing import Group, key_tree + class SasData: def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: Group): @@ -33,4 +34,6 @@ def summary(self, indent = " "): s += f"{indent}Metadata:\n" s += self.metadata.summary() + s += key_tree(self._raw_metadata) + return s \ No newline at end of file diff --git a/sasdata/data_backing.py b/sasdata/data_backing.py index dadfc194f..564f466a6 100644 --- a/sasdata/data_backing.py +++ b/sasdata/data_backing.py @@ -108,3 +108,19 @@ def build_main_data(data: list[NamedQuantity]) -> Function: pass case _: raise NotImplementedError("Unknown ") + +def key_tree(data: Group | Dataset, indent_amount=0, indent: str = " ") -> str: + """ Show a metadata tree, showing the names of they keys used to access them""" + s = "" + if isinstance(data, Group): + for key in data.children: + s += indent*indent_amount + key + "\n" + s += key_tree(data.children[key], indent_amount=indent_amount+1, indent=indent) + + if isinstance(data, Dataset): + s += indent*indent_amount + "[data]\n" + for key in data.attributes: + s += indent*indent_amount + key + "\n" + s += key_tree(data.attributes[key], indent_amount=indent_amount+1, indent=indent) + + return s \ No newline at end of file From e286cc7cdbfb0f1c2922fa87b0f1bd82ceccd3fd Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 15:25:00 +0100 Subject: [PATCH 603/675] Debugging metadata --- sasdata/data.py | 10 +++-- sasdata/metadata.py | 34 ++++++++--------- sasdata/quantities/_accessor_base.py | 56 +++++++++++++++++++++++----- sasdata/quantities/accessors.py | 56 +++++++++++++++++++++++----- sasdata/temp_hdf5_reader.py | 5 ++- 5 files changed, 120 insertions(+), 41 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index f50fb61a9..b30864a91 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -9,12 +9,13 @@ class SasData: - def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: Group): + def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: Group, verbose: bool=False): self.name = name self._data_contents = data_contents self._raw_metadata = raw_metadata + self._verbose = verbose - self.metadata = Metadata(AccessorTarget(raw_metadata)) + self.metadata = Metadata(AccessorTarget(raw_metadata, verbose=verbose)) # TO IMPLEMENT @@ -25,7 +26,7 @@ def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: # metadata: Metadata # model_requirements: ModellingRequirements - def summary(self, indent = " "): + def summary(self, indent = " ", include_raw=False): s = f"{self.name}\n" for data in self._data_contents: @@ -34,6 +35,7 @@ def summary(self, indent = " "): s += f"{indent}Metadata:\n" s += self.metadata.summary() - s += key_tree(self._raw_metadata) + if include_raw: + s += key_tree(self._raw_metadata) return s \ No newline at end of file diff --git a/sasdata/metadata.py b/sasdata/metadata.py index c3e81332d..3f927bf9b 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -129,34 +129,34 @@ class Source: def __init__(self, target_object: AccessorTarget): # Name - self.name = StringAccessor(target_object, "source.name") + self.name = StringAccessor(target_object, "sassource.name") # Generic radiation type (Type and probe give more specific info) [string] - self.radiation = StringAccessor(target_object, "source.radiation") + self.radiation = StringAccessor(target_object, "sassource.radiation") # Type and probe are only written to by the NXcanSAS reader # Specific radiation type (Synchotron X-ray, Reactor neutron, etc) [string] - self.type = StringAccessor(target_object, "source.type") + self.type = StringAccessor(target_object, "sassource.type") # Radiation probe (generic probe such as neutron, x-ray, muon, etc) [string] - self.probe_particle = StringAccessor(target_object, "source.probe") + self.probe_particle = StringAccessor(target_object, "sassource.probe") # Beam size name - self.beam_size_name = StringAccessor(target_object, "source.beam_size_name") + self.beam_size_name = StringAccessor(target_object, "sassource.beam_size_name") # Beam size [Vector] [mm] self.beam_size = LengthAccessor[ArrayLike](target_object, - "source.beam_size", - "source.beam_size.units", + "sassource.beam_size", + "sassource.beam_size.units", default_unit=units.millimeters) # Beam shape [string] - self.beam_shape = StringAccessor(target_object, "source.beam_shape") + self.beam_shape = StringAccessor(target_object, "sassource.beam_shape") # Wavelength [float] [Angstrom] self.wavelength = LengthAccessor[float](target_object, - "source.wavelength", - "source.wavelength.units", + "sassource.wavelength", + "sassource.wavelength.units", default_unit=units.angstroms) # Minimum wavelength [float] [Angstrom] @@ -273,14 +273,14 @@ class Process: performed on the data. """ def __init__(self, target_object: AccessorTarget): - self.name = StringAccessor(target_object, "process.name") - self.date = StringAccessor(target_object, "process.date") - self.description = StringAccessor(target_object, "process.description") + self.name = StringAccessor(target_object, "sasprocess.name") + self.date = StringAccessor(target_object, "sasprocess.date") + self.description = StringAccessor(target_object, "sasprocess.description") #TODO: It seems like these might be lists of strings, this should be checked - self.term = StringAccessor(target_object, "process.term") - self.notes = StringAccessor(target_object, "process.notes") + self.term = StringAccessor(target_object, "sasprocess.term") + self.notes = StringAccessor(target_object, "sasprocess.notes") def single_line_desc(self): """ @@ -297,12 +297,12 @@ def summary(self): f" Notes: {self.notes.value}\n" ) -class TransmissionSpectrum(AccessorTarget): +class TransmissionSpectrum: """ Class that holds information about transmission spectrum for white beams and spallation sources. """ - def __init__(self, target_object): + def __init__(self, target_object: AccessorTarget): # TODO: Needs to be multiple instances self.name = StringAccessor(target_object, "transmission.") self.timestamp = StringAccessor(target_object, "transmission.timestamp") diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 2f84e962d..192628fda 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -3,18 +3,31 @@ from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit +from sasdata.quantities.unit_parser import parse_unit, parse_unit_from_group from sasdata.data_backing import Group, Dataset +import logging +# logger = logging.getLogger("Accessors") +class LoggerDummy: + def info(self, data): + print(data) +logger = LoggerDummy() + DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") + class AccessorTarget: - def __init__(self, data: Group): + def __init__(self, data: Group, verbose=False): self._data = data + self.verbose = verbose def get_value(self, path: str): + if self.verbose: + logger.info(f"Finding: {path}") + tokens = path.split(".") # Navigate the tree from the entry we need @@ -23,11 +36,27 @@ def get_value(self, path: str): for token in tokens: if isinstance(current_tree_position, Group): - current_tree_position = current_tree_position.children[token] + if token in current_tree_position.children: + current_tree_position = current_tree_position.children[token] + else: + if self.verbose: + logger.info(f"Failed at token {token} on group {current_tree_position.name}. Options: " + + ",".join([key for key in current_tree_position.children])) + return None + elif isinstance(current_tree_position, Dataset): - current_tree_position = current_tree_position.attributes[token] + if token in current_tree_position.attributes: + current_tree_position = current_tree_position.attributes[token] + else: + if self.verbose: + logger.info(f"Failed at token {token} on attribute {current_tree_position.name}. Options: " + + ",".join([key for key in current_tree_position.attributes])) + return None - return current_tree_position + if self.verbose: + logger.info(f"Found value: {current_tree_position}") + + return current_tree_position.data @@ -39,19 +68,19 @@ def __init__(self, target_object: AccessorTarget, value_target: str): @property def value(self) -> OutputType | None: - pass + self.target_object.get_value(self.value_target) class StringAccessor(Accessor[str, str]): """ String based fields """ @property def value(self) -> str | None: - pass + self.target_object.get_value(self.value_target) class FloatAccessor(Accessor[float, float]): """ Float based fields """ @property def value(self) -> float | None: - pass + self.target_object.get_value(self.value_target) @@ -65,20 +94,29 @@ def __init__(self, target_object: AccessorTarget, value_target: str, unit_target def _numerical_part(self) -> DataType | None: """ Numerical part of the data """ + return self.target_object.get_value(self.value_target) def _unit_part(self) -> str | None: """ String form of units for the data """ + return self.target_object.get_value(self._unit_target) @property def unit(self) -> Unit: - if self._unit_part() is None: + u = self._unit_part() + if u is None: return self.default_unit else: - return Unit.parse(self._unit_part()) + return parse_unit(u) @property def value(self) -> Quantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: return Quantity(self._numerical_part(), self.unit) + return None + @property + def quantity(self): + if self._unit_part() is not None and self._numerical_part() is not None: + return Quantity(self._numerical_part(), self.unit) + return None diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index cf15bed98..46947a25d 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -83,18 +83,31 @@ from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units from sasdata.quantities.units import Dimensions, Unit +from sasdata.quantities.unit_parser import parse_unit, parse_unit_from_group from sasdata.data_backing import Group, Dataset +import logging +# logger = logging.getLogger("Accessors") +class LoggerDummy: + def info(self, data): + print(data) +logger = LoggerDummy() + DataType = TypeVar("DataType") OutputType = TypeVar("OutputType") + class AccessorTarget: - def __init__(self, data: Group): + def __init__(self, data: Group, verbose=False): self._data = data + self.verbose = verbose def get_value(self, path: str): + if self.verbose: + logger.info(f"Finding: {path}") + tokens = path.split(".") # Navigate the tree from the entry we need @@ -103,11 +116,27 @@ def get_value(self, path: str): for token in tokens: if isinstance(current_tree_position, Group): - current_tree_position = current_tree_position.children[token] + if token in current_tree_position.children: + current_tree_position = current_tree_position.children[token] + else: + if self.verbose: + logger.info(f"Failed at token {token} on group {current_tree_position.name}. Options: " + + ",".join([key for key in current_tree_position.children])) + return None + elif isinstance(current_tree_position, Dataset): - current_tree_position = current_tree_position.attributes[token] + if token in current_tree_position.attributes: + current_tree_position = current_tree_position.attributes[token] + else: + if self.verbose: + logger.info(f"Failed at token {token} on attribute {current_tree_position.name}. Options: " + + ",".join([key for key in current_tree_position.attributes])) + return None - return current_tree_position + if self.verbose: + logger.info(f"Found value: {current_tree_position}") + + return current_tree_position.data @@ -119,19 +148,19 @@ def __init__(self, target_object: AccessorTarget, value_target: str): @property def value(self) -> OutputType | None: - pass + self.target_object.get_value(self.value_target) class StringAccessor(Accessor[str, str]): """ String based fields """ @property def value(self) -> str | None: - pass + self.target_object.get_value(self.value_target) class FloatAccessor(Accessor[float, float]): """ Float based fields """ @property def value(self) -> float | None: - pass + self.target_object.get_value(self.value_target) @@ -145,22 +174,31 @@ def __init__(self, target_object: AccessorTarget, value_target: str, unit_target def _numerical_part(self) -> DataType | None: """ Numerical part of the data """ + return self.target_object.get_value(self.value_target) def _unit_part(self) -> str | None: """ String form of units for the data """ + return self.target_object.get_value(self._unit_target) @property def unit(self) -> Unit: - if self._unit_part() is None: + u = self._unit_part() + if u is None: return self.default_unit else: - return Unit.parse(self._unit_part()) + return parse_unit(u) @property def value(self) -> Quantity[DataType] | None: if self._unit_part() is not None and self._numerical_part() is not None: return Quantity(self._numerical_part(), self.unit) + return None + @property + def quantity(self): + if self._unit_part() is not None and self._numerical_part() is not None: + return Quantity(self._numerical_part(), self.unit) + return None class LengthAccessor[T](QuantityAccessor[T]): diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index b5bc1f707..8b38431ba 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -129,7 +129,8 @@ def load_data(filename) -> list[SasData]: SasData( name=root_key, data_contents=data_contents, - raw_metadata=SASDataGroup("root", raw_metadata))) + raw_metadata=SASDataGroup("root", raw_metadata), + verbose=True)) return loaded_data @@ -139,4 +140,4 @@ def load_data(filename) -> list[SasData]: data = load_data(test_file) for dataset in data: - print(dataset.summary()) \ No newline at end of file + print(dataset.summary(include_raw=True)) \ No newline at end of file From 8d13974b40b1ecfc9bfcd866db9a56fe19e6ab24 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 7 Oct 2024 16:15:44 +0100 Subject: [PATCH 604/675] Better reference methods --- sasdata/data.py | 3 +- sasdata/metadata.py | 189 +++++++++++++++------------ sasdata/quantities/_accessor_base.py | 24 +++- sasdata/quantities/accessors.py | 24 +++- sasdata/temp_hdf5_reader.py | 2 +- 5 files changed, 145 insertions(+), 97 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index b30864a91..7f0cbfb2f 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -32,7 +32,8 @@ def summary(self, indent = " ", include_raw=False): for data in self._data_contents: s += f"{indent}{data}\n" - s += f"{indent}Metadata:\n" + s += f"Metadata:\n" + s += "\n" s += self.metadata.summary() if include_raw: diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 3f927bf9b..c42b89ac0 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -1,3 +1,5 @@ +from tokenize import String + import numpy as np from numpy.typing import ArrayLike @@ -15,41 +17,41 @@ class Detector: def __init__(self, target_object: AccessorTarget): # Name of the instrument [string] - self.name = StringAccessor(target_object, "detector.name") + self.name = StringAccessor(target_object, "name") # Sample to detector distance [float] [mm] self.distance = LengthAccessor[float](target_object, - "detector.distance", - "detector.distance.units", + "distance", + "distance.units", default_unit=units.millimeters) # Offset of this detector position in X, Y, # (and Z if necessary) [Vector] [mm] self.offset = LengthAccessor[ArrayLike](target_object, - "detector.offset", - "detector.offset.units", + "offset", + "offset.units", default_unit=units.millimeters) self.orientation = AngleAccessor[ArrayLike](target_object, - "detector.orientation", - "detector.orientation.units", + "orientation", + "orientation.units", default_unit=units.degrees) self.beam_center = LengthAccessor[ArrayLike](target_object, - "detector.beam_center", - "detector.beam_center.units", + "beam_center", + "beam_center.units", default_unit=units.millimeters) # Pixel size in X, Y, (and Z if necessary) [Vector] [mm] self.pixel_size = LengthAccessor[ArrayLike](target_object, - "detector.pixel_size", - "detector.pixel_size.units", + "pixel_size", + "pixel_size.units", default_unit=units.millimeters) # Slit length of the instrument for this detector.[float] [mm] self.slit_length = LengthAccessor[float](target_object, - "detector.slit_length", - "detector.slit_length.units", + "slit_length", + "slit_length.units", default_unit=units.millimeters) def summary(self): @@ -67,24 +69,24 @@ class Aperture: def __init__(self, target_object: AccessorTarget): # Name - self.name = StringAccessor(target_object, "aperture.name") + self.name = StringAccessor(target_object, "name") # Type - self.type = StringAccessor(target_object, "aperture.type") + self.type = StringAccessor(target_object, "type") # Size name - TODO: What is the name of a size - self.size_name = StringAccessor(target_object, "aperture.size_name") + self.size_name = StringAccessor(target_object, "size_name") # Aperture size [Vector] # TODO: Wat!?! self.size = QuantityAccessor[ArrayLike](target_object, - "aperture.size", - "aperture.size.units", + "size", + "size.units", default_unit=units.millimeters) # Aperture distance [float] self.distance = LengthAccessor[float](target_object, - "apature.distance", - "apature.distance.units", + "distance", + "distance.units", default_unit=units.millimeters) @@ -102,11 +104,11 @@ class Collimation: def __init__(self, target_object: AccessorTarget): # Name - self.name = StringAccessor(target_object, "collimation.name") + self.name = StringAccessor(target_object, "name") # Length [float] [mm] self.length = LengthAccessor[float](target_object, - "collimation.length", - "collimation.length.units", + "length", + "length.units", default_unit=units.millimeters) @@ -129,53 +131,53 @@ class Source: def __init__(self, target_object: AccessorTarget): # Name - self.name = StringAccessor(target_object, "sassource.name") + self.name = StringAccessor(target_object, "name") # Generic radiation type (Type and probe give more specific info) [string] - self.radiation = StringAccessor(target_object, "sassource.radiation") + self.radiation = StringAccessor(target_object, "radiation") # Type and probe are only written to by the NXcanSAS reader # Specific radiation type (Synchotron X-ray, Reactor neutron, etc) [string] - self.type = StringAccessor(target_object, "sassource.type") + self.type = StringAccessor(target_object, "type") # Radiation probe (generic probe such as neutron, x-ray, muon, etc) [string] - self.probe_particle = StringAccessor(target_object, "sassource.probe") + self.probe_particle = StringAccessor(target_object, "probe") # Beam size name - self.beam_size_name = StringAccessor(target_object, "sassource.beam_size_name") + self.beam_size_name = StringAccessor(target_object, "beam_size_name") # Beam size [Vector] [mm] self.beam_size = LengthAccessor[ArrayLike](target_object, - "sassource.beam_size", - "sassource.beam_size.units", + "beam_size", + "beam_size.units", default_unit=units.millimeters) # Beam shape [string] - self.beam_shape = StringAccessor(target_object, "sassource.beam_shape") + self.beam_shape = StringAccessor(target_object, "beam_shape") # Wavelength [float] [Angstrom] self.wavelength = LengthAccessor[float](target_object, - "sassource.wavelength", - "sassource.wavelength.units", + "wavelength", + "wavelength.units", default_unit=units.angstroms) # Minimum wavelength [float] [Angstrom] self.wavelength_min = LengthAccessor[float](target_object, - "source.wavelength_min", - "source.wavelength_min.units", + "wavelength_min", + "wavelength_min.units", default_unit=units.angstroms) # Maximum wavelength [float] [Angstrom] self.wavelength_max = LengthAccessor[float](target_object, - "source.wavelength_min", - "source.wavelength_max.units", + "wavelength_min", + "wavelength_max.units", default_unit=units.angstroms) # Wavelength spread [float] [Angstrom] # Quantity because it might have other units, such as percent self.wavelength_spread = QuantityAccessor[float](target_object, - "source.wavelength_spread", - "source.wavelength_spread.units", + "wavelength_spread", + "wavelength_spread.units", default_unit=units.angstroms) def summary(self) -> str: @@ -186,13 +188,13 @@ def summary(self) -> str: radiation = f"{self.radiation.value}" return (f"Source:\n" - f" Radiation: {radiation}\n" - f" Shape: {self.beam_shape.value}\n" - f" Wavelength: {self.wavelength.value}\n" - f" Min. Wavelength: {self.wavelength_min.value}\n" - f" Max. Wavelength: {self.wavelength_max.value}\n" + f" Radiation: {radiation}\n" + f" Shape: {self.beam_shape.value}\n" + f" Wavelength: {self.wavelength.value}\n" + f" Min. Wavelength: {self.wavelength_min.value}\n" + f" Max. Wavelength: {self.wavelength_max.value}\n" f" Wavelength Spread: {self.wavelength_spread.value}\n" - f" Beam Size: {self.beam_size.value}\n") + f" Beam Size: {self.beam_size.value}\n") @@ -212,39 +214,39 @@ class Sample: def __init__(self, target_object: AccessorTarget): # Short name for sample - self.name = StringAccessor(target_object, "sample.name") + self.name = StringAccessor(target_object, "name") # ID - self.sample_id = StringAccessor(target_object, "sample.id") + self.sample_id = StringAccessor(target_object, "id") # Thickness [float] [mm] self.thickness = LengthAccessor(target_object, - "sample.thickness", - "sample.thickness.units", + "thickness", + "thickness.units", default_unit=units.millimeters) # Transmission [float] [fraction] - self.transmission = FloatAccessor(target_object,"sample.transmission") + self.transmission = FloatAccessor(target_object,"transmission") # Temperature [float] [No Default] self.temperature = AbsoluteTemperatureAccessor(target_object, - "sample.temperature", - "sample.temperature.unit", + "temperature", + "temperature.unit", default_unit=units.kelvin) # Position [Vector] [mm] self.position = LengthAccessor[ArrayLike](target_object, - "sample.position", - "sample.position.unit", + "position", + "position.unit", default_unit=units.millimeters) # Orientation [Vector] [degrees] self.orientation = AngleAccessor[ArrayLike](target_object, - "sample.orientation", - "sample.orientation.unit", + "orientation", + "orientation.unit", default_unit=units.degrees) # Details - self.details = StringAccessor(target_object, "sample.details") + self.details = StringAccessor(target_object, "details") # SESANS zacceptance @@ -273,14 +275,14 @@ class Process: performed on the data. """ def __init__(self, target_object: AccessorTarget): - self.name = StringAccessor(target_object, "sasprocess.name") - self.date = StringAccessor(target_object, "sasprocess.date") - self.description = StringAccessor(target_object, "sasprocess.description") + self.name = StringAccessor(target_object, "name") + self.date = StringAccessor(target_object, "date") + self.description = StringAccessor(target_object, "description") #TODO: It seems like these might be lists of strings, this should be checked - self.term = StringAccessor(target_object, "sasprocess.term") - self.notes = StringAccessor(target_object, "sasprocess.notes") + self.term = StringAccessor(target_object, "term") + self.notes = StringAccessor(target_object, "notes") def single_line_desc(self): """ @@ -304,24 +306,24 @@ class TransmissionSpectrum: """ def __init__(self, target_object: AccessorTarget): # TODO: Needs to be multiple instances - self.name = StringAccessor(target_object, "transmission.") - self.timestamp = StringAccessor(target_object, "transmission.timestamp") + self.name = StringAccessor(target_object, "name") + self.timestamp = StringAccessor(target_object, "timestamp") # Wavelength (float) [A] self.wavelength = LengthAccessor[ArrayLike](target_object, - "transmission.wavelength", - "transmission.wavelength.units") + "wavelength", + "wavelength.units") # Transmission (float) [unit less] self.transmission = DimensionlessAccessor[ArrayLike](target_object, - "transmission.transmission", - "transmission.units", + "transmission", + "units", default_unit=units.none) # Transmission Deviation (float) [unit less] self.transmission_deviation = DimensionlessAccessor[ArrayLike](target_object, - "transmission.transmission_deviation", - "transmission.transmission_deviation.units", + "transmission_deviation", + "transmission_deviation.units", default_unit=units.none) @@ -333,23 +335,44 @@ def summary(self) -> str: f" Transmission: {self.transmission.value}\n") -class Metadata: +class Instrument: def __init__(self, target: AccessorTarget): - self._target = target - - self.aperture = Aperture(target) - self.collimation = Collimation(target) - self.detector = Detector(target) - self.process = Process(target) - self.sample = Sample(target) - self.source = Source(target) - self.transmission_spectrum = TransmissionSpectrum(target) def summary(self): + self.aperture = Aperture(target.with_path_prefix("sasaperture")) + self.collimation = Collimation(target.with_path_prefix("sascollimation")) + self.detector = Detector(target.with_path_prefix("sasdetector")) + self.source = Source(target.with_path_prefix("sassource")) + def summary(self): return ( self.aperture.summary() + self.collimation.summary() + self.detector.summary() + + self.source.summary()) + + +class Metadata: + def __init__(self, target: AccessorTarget): + self._target = target + + self.instrument = Instrument(target.with_path_prefix("sasinstrument")) + self.process = Process(target.with_path_prefix("sasprocess")) + self.sample = Sample(target.with_path_prefix("sassample")) + self.transmission_spectrum = TransmissionSpectrum(target.with_path_prefix("sastransmission_spectrum")) + + self._title = StringAccessor(target, "title") + self._run = StringAccessor(target, "run") + self._definitiion = StringAccessor(target, "definition") + + self.title: str = self._title.value + self.run: str = self._run.value + self.definitiion: str = self._definitiion.value + + def summary(self): + return ( + f" {self.title}, Run: {self.run}\n" + " " + "="*len(self.title) + + "=======" + + "="*len(self.run) + "\n\n" + + f"Definition: {self.title}\n" + self.process.summary() + self.sample.summary() + - self.source.summary() + - self.transmission_spectrum.summary() - ) \ No newline at end of file + self.instrument.summary() + + self.transmission_spectrum.summary()) \ No newline at end of file diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 192628fda..f750623bc 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -19,16 +19,28 @@ def info(self, data): class AccessorTarget: - def __init__(self, data: Group, verbose=False): + def __init__(self, data: Group, verbose=False, prefix_tokens: tuple=()): self._data = data self.verbose = verbose + self.prefix_tokens = list(prefix_tokens) + + def with_path_prefix(self, path_prexix: str): + """ Get an accessor that looks at a subtree of the metadata with the supplied prefix + + For example, accessors aiming at a.b, when the target it c.d will look at c.d.a.b + """ + return AccessorTarget(self._data, + verbose=self.verbose, + prefix_tokens=tuple(self.prefix_tokens + [path_prexix])) + def get_value(self, path: str): + tokens = self.prefix_tokens + path.split(".") + if self.verbose: logger.info(f"Finding: {path}") - - tokens = path.split(".") + logger.info(f"Full path: {tokens}") # Navigate the tree from the entry we need @@ -68,19 +80,19 @@ def __init__(self, target_object: AccessorTarget, value_target: str): @property def value(self) -> OutputType | None: - self.target_object.get_value(self.value_target) + return self.target_object.get_value(self.value_target) class StringAccessor(Accessor[str, str]): """ String based fields """ @property def value(self) -> str | None: - self.target_object.get_value(self.value_target) + return self.target_object.get_value(self.value_target) class FloatAccessor(Accessor[float, float]): """ Float based fields """ @property def value(self) -> float | None: - self.target_object.get_value(self.value_target) + return self.target_object.get_value(self.value_target) diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 46947a25d..71f6b0da3 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -99,16 +99,28 @@ def info(self, data): class AccessorTarget: - def __init__(self, data: Group, verbose=False): + def __init__(self, data: Group, verbose=False, prefix_tokens: tuple=()): self._data = data self.verbose = verbose + self.prefix_tokens = list(prefix_tokens) + + def with_path_prefix(self, path_prexix: str): + """ Get an accessor that looks at a subtree of the metadata with the supplied prefix + + For example, accessors aiming at a.b, when the target it c.d will look at c.d.a.b + """ + return AccessorTarget(self._data, + verbose=self.verbose, + prefix_tokens=tuple(self.prefix_tokens + [path_prexix])) + def get_value(self, path: str): + tokens = self.prefix_tokens + path.split(".") + if self.verbose: logger.info(f"Finding: {path}") - - tokens = path.split(".") + logger.info(f"Full path: {tokens}") # Navigate the tree from the entry we need @@ -148,19 +160,19 @@ def __init__(self, target_object: AccessorTarget, value_target: str): @property def value(self) -> OutputType | None: - self.target_object.get_value(self.value_target) + return self.target_object.get_value(self.value_target) class StringAccessor(Accessor[str, str]): """ String based fields """ @property def value(self) -> str | None: - self.target_object.get_value(self.value_target) + return self.target_object.get_value(self.value_target) class FloatAccessor(Accessor[float, float]): """ Float based fields """ @property def value(self) -> float | None: - self.target_object.get_value(self.value_target) + return self.target_object.get_value(self.value_target) diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 8b38431ba..b0bf6e423 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -130,7 +130,7 @@ def load_data(filename) -> list[SasData]: name=root_key, data_contents=data_contents, raw_metadata=SASDataGroup("root", raw_metadata), - verbose=True)) + verbose=False)) return loaded_data From ec3c45961f52adb09f1a3fac2973669da73973a9 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 10 Oct 2024 13:04:08 +0100 Subject: [PATCH 605/675] Added matrix operations, needs tests --- sasdata/quantities/operations.py | 112 ++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py index 724e55d0e..67cb636c8 100644 --- a/sasdata/quantities/operations.py +++ b/sasdata/quantities/operations.py @@ -702,9 +702,119 @@ def __eq__(self, other): if isinstance(other, Pow): return self.a == other.a and self.power == other.power + + +# +# Matrix operations +# + +class Transpose(UnaryOperation): + """ Transpose operation - as per numpy""" + + serialisation_name = "transpose" + + def evaluate(self, variables: dict[int, T]) -> T: + return np.transpose(self.a.evaluate(variables)) + + def _derivative(self, hash_value: int) -> Operation: + return Transpose(self.a.derivative(hash_value)) # TODO: Check! + + def _clean(self): + clean_a = self.a._clean() + return Transpose(clean_a) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Transpose(Operation.deserialise_json(parameters["a"])) + + def _summary_open(self): + return "Transpose" + + def __eq__(self, other): + if isinstance(other, Transpose): + return other.a == self.a + + +class Dot(BinaryOperation): + """ Dot product - backed by numpy's dot method""" + + serialisation_name = "dot" + + def _self_cls(self) -> type: + return Dot + + def evaluate(self, variables: dict[int, T]) -> T: + return np.dot(self.a.evaluate(variables) + self.b.evaluate(variables)) + + def _derivative(self, hash_value: int) -> Operation: + return Add( + Dot(self.a, + self.b._derivative(hash_value)), + Dot(self.a._derivative(hash_value), + self.b)) + + def _clean_ab(self, a, b): + return Dot(a, b) # Do nothing for now + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Dot(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Dot" + + +# TODO: Add to base operation class, and to quantities +class MatMul(BinaryOperation): + """ Matrix multiplication, using __matmul__ dunder""" + + serialisation_name = "matmul" + + def _self_cls(self) -> type: + return MatMul + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) @ self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add( + MatMul(self.a, + self.b._derivative(hash_value)), + MatMul(self.a._derivative(hash_value), + self.b)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): + # Convert 0*b or a*0 to 0 + return AdditiveIdentity() + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"@"b" to "a@b" + return Constant(a.evaluate({}) @ b.evaluate({}))._clean() + + elif isinstance(a, Neg): + return Neg(Mul(a.a, b)) + + elif isinstance(b, Neg): + return Neg(Mul(a, b.a)) + + return MatMul(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return MatMul(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "MatMul" + + + _serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, Variable, Neg, Inv, - Add, Sub, Mul, Div, Pow] + Add, Sub, Mul, Div, Pow, + Transpose, Dot, MatMul] _serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} From ecc81b5c1a95b99580debd39d5339c98d867a4ab Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 10 Oct 2024 13:05:22 +0100 Subject: [PATCH 606/675] Numpy import --- sasdata/quantities/operations.py | 1 + sasdata/quantities/quantity.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py index 67cb636c8..35f6fc7ef 100644 --- a/sasdata/quantities/operations.py +++ b/sasdata/quantities/operations.py @@ -1,4 +1,5 @@ from typing import Any, TypeVar, Union +import numpy as np import json diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 7572ea9e8..30ef9720c 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -235,6 +235,9 @@ def __rmul__(self: Self, other: ArrayLike | Self): self.history.operation_tree), self.history.references)) + + + def __truediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): return DerivedQuantity( From a86fa700cac7d5b5a7fe12558272d2e73485ba14 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 10 Oct 2024 13:12:27 +0100 Subject: [PATCH 607/675] Added __matmul__ and __rmatmul__ to quantities --- sasdata/quantities/quantity.py | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 30ef9720c..cecd50057 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -236,6 +236,42 @@ def __rmul__(self: Self, other: ArrayLike | Self): self.history.references)) + def __matmul__(self, other: ArrayLike | Self): + if isinstance(other, Quantity): + return DerivedQuantity( + self.value @ other.value, + self.units * other.units, + history=QuantityHistory.apply_operation( + operations.MatMul, + self.history, + other.history)) + else: + return DerivedQuantity( + self.value @ other, + self.units, + QuantityHistory( + operations.MatMul( + self.history.operation_tree, + operations.Constant(other)), + self.history.references)) + + def __rmatmul__(self, other: ArrayLike | Self): + if isinstance(other, Quantity): + return DerivedQuantity( + other.value @ self.value, + other.units * self.units, + history=QuantityHistory.apply_operation( + operations.MatMul, + other.history, + self.history)) + + else: + return DerivedQuantity(other @ self.value, self.units, + QuantityHistory( + operations.MatMul( + operations.Constant(other), + self.history.operation_tree), + self.history.references)) def __truediv__(self: Self, other: float | Self) -> Self: From 5316f79e8b54ae491f80009e7ac6aca2abf448f6 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 10 Oct 2024 14:36:27 +0100 Subject: [PATCH 608/675] Entrypoint for rebinning --- sasdata/model_requirements.py | 2 +- sasdata/transforms/operation.py | 19 --- sasdata/transforms/rebinning.py | 198 +------------------------------- 3 files changed, 2 insertions(+), 217 deletions(-) delete mode 100644 sasdata/transforms/operation.py diff --git a/sasdata/model_requirements.py b/sasdata/model_requirements.py index 5d68ad1b4..40b6ac728 100644 --- a/sasdata/model_requirements.py +++ b/sasdata/model_requirements.py @@ -3,7 +3,7 @@ import numpy as np from sasdata.metadata import Metadata -from transforms.operation import Operation +from sasdata.quantities.operations import Operation @dataclass diff --git a/sasdata/transforms/operation.py b/sasdata/transforms/operation.py deleted file mode 100644 index c06bb379a..000000000 --- a/sasdata/transforms/operation.py +++ /dev/null @@ -1,19 +0,0 @@ -import numpy as np -from sasdata.quantities.quantity import Quantity - -class Operation: - """ Sketch of what model post-processing classes might look like """ - - children: list["Operation"] - named_children: dict[str, "Operation"] - - @property - def name(self) -> str: - raise NotImplementedError("No name for transform") - - def evaluate(self) -> Quantity[np.ndarray]: - pass - - def __call__(self, *children, **named_children): - self.children = children - self.named_children = named_children \ No newline at end of file diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index fe96bcb72..58d191254 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -1,205 +1,9 @@ """ Algorithms for interpolation and rebinning """ from typing import TypeVar -import numpy as np from numpy._typing import ArrayLike -from scipy.interpolate import interp1d from sasdata.quantities.quantity import Quantity -from scipy.sparse import coo_matrix - -from enum import Enum - -class InterpolationOptions(Enum): - NEAREST_NEIGHBOUR = 0 - LINEAR = 1 - CUBIC = 3 - -class InterpolationError(Exception): - """ We probably want to raise exceptions because interpolation is not appropriate/well-defined, - not the same as numerical issues that will raise ValueErrors""" - - -def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], - output_axis: Quantity[ArrayLike], - mask: ArrayLike | None = None, - order: InterpolationOptions = InterpolationOptions.LINEAR, - is_density=False): - - """ Calculate the matrix that converts values recorded at points specified by input_axis to - values recorded at points specified by output_axis""" - - # We want the input values in terms of the output units, will implicitly check compatability - # TODO: incorporate mask - - working_units = output_axis.units - - input_x = input_axis.in_units_of(working_units) - output_x = output_axis.in_units_of(working_units) - - # Get the array indices that will map the array to a sorted one - input_sort = np.argsort(input_x) - output_sort = np.argsort(output_x) - - input_unsort = np.arange(len(input_x), dtype=int)[input_sort] - output_unsort = np.arange(len(output_x), dtype=int)[output_sort] - - sorted_in = input_x[input_sort] - sorted_out = output_x[output_sort] - - n_in = len(sorted_in) - n_out = len(sorted_out) - - conversion_matrix = None # output - - match order: - case InterpolationOptions.NEAREST_NEIGHBOUR: - - # COO Sparse matrix definition data - i_entries = [] - j_entries = [] - - crossing_points = 0.5*(sorted_out[1:] + sorted_out[:-1]) - - # Find the output values nearest to each of the input values - i=0 - for k, crossing_point in enumerate(crossing_points): - while i < n_in and sorted_in[i] < crossing_point: - i_entries.append(i) - j_entries.append(k) - i += 1 - - # All the rest in the last bin - while i < n_in: - i_entries.append(i) - j_entries.append(n_out-1) - i += 1 - - i_entries = input_unsort[np.array(i_entries, dtype=int)] - j_entries = output_unsort[np.array(j_entries, dtype=int)] - values = np.ones_like(i_entries, dtype=float) - - conversion_matrix = coo_matrix((values, (i_entries, j_entries)), shape=(n_in, n_out)) - - case InterpolationOptions.LINEAR: - - # Leverage existing linear interpolation methods to get the mapping - # do a linear interpolation on indices - # the floor should give the left bin - # the ceil should give the right bin - # the fractional part should give the relative weightings - - input_indices = np.arange(n_in, dtype=int) - output_indices = np.arange(n_out, dtype=int) - - fractional = np.interp(x=sorted_out, xp=sorted_in, fp=input_indices, left=0, right=n_in-1) - - left_bins = np.floor(fractional).astype(int) - right_bins = np.ceil(fractional).astype(int) - - right_weight = fractional % 1 - left_weight = 1 - right_weight - - # There *should* be no repeated entries for both i and j in the main part, but maybe at the ends - # If left bin is the same as right bin, then we only want one entry, and the weight should be 1 - - same = left_bins == right_bins - not_same = ~same - - same_bins = left_bins[same] # could equally be right bins, they're the same - - same_indices = output_indices[same] - not_same_indices = output_indices[not_same] - - j_entries_sorted = np.concatenate((same_indices, not_same_indices, not_same_indices)) - i_entries_sorted = np.concatenate((same_bins, left_bins[not_same], right_bins[not_same])) - - i_entries = input_unsort[i_entries_sorted] - j_entries = output_unsort[j_entries_sorted] - - # weights don't need to be unsorted # TODO: check this is right, it should become obvious if we use unsorted data - weights = np.concatenate((np.ones_like(same_bins, dtype=float), left_weight[not_same], right_weight[not_same])) - - conversion_matrix = coo_matrix((weights, (i_entries, j_entries)), shape=(n_in, n_out)) - - case InterpolationOptions.CUBIC: - # Cubic interpolation, much harder to implement because we can't just cheat and use numpy - raise NotImplementedError("Cubic interpolation not implemented yet") - - case _: - raise InterpolationError(f"Unsupported interpolation order: {order}") - - - if mask is None: - return conversion_matrix, None - - else: - # Create a new mask - - # Convert to numerical values - # Conservative masking: anything touched by the previous mask is now masked - new_mask = (np.array(mask, dtype=float) @ conversion_matrix) != 0.0 - - return conversion_matrix, new_mask - - -def calculate_interpolation_matrix_2d_axis_axis(input_1: Quantity[ArrayLike], - input_2: Quantity[ArrayLike], - output_1: Quantity[ArrayLike], - output_2: Quantity[ArrayLike], - mask, - order: InterpolationOptions = InterpolationOptions.LINEAR, - is_density: bool = False): - - # This is just the same 1D matrices things - - match order: - case InterpolationOptions.NEAREST_NEIGHBOUR: - pass - - case InterpolationOptions.LINEAR: - pass - - case InterpolationOptions.CUBIC: - pass - - case _: - pass - - -def calculate_interpolation_matrix(input_axes: list[Quantity[ArrayLike]], - output_axes: list[Quantity[ArrayLike]], - data: ArrayLike | None = None, - mask: ArrayLike | None = None): - - # TODO: We probably should delete this, but lets keep it for now - - if len(input_axes) not in (1, 2): - raise InterpolationError("Interpolation is only supported for 1D and 2D data") - - if len(input_axes) == 1 and len(output_axes) == 1: - # Check for dimensionality - input_axis = input_axes[0] - output_axis = output_axes[0] - - if len(input_axis.value.shape) == 1: - if len(output_axis.value.shape) == 1: - calculate_interpolation_matrix_1d() - - if len(output_axes) != len(input_axes): - # Input or output axes might be 2D matrices - pass - - - -def rebin(data: Quantity[ArrayLike], - axes: list[Quantity[ArrayLike]], - new_axes: list[Quantity[ArrayLike]], - mask: ArrayLike | None = None, - interpolation_order: int = 1): - - """ This algorithm is only for operations that preserve dimensionality, - i.e. non-projective rebinning. - """ +def rebin(data: Quantity[ArrayLike], axes: list[Quantity[ArrayLike]], new_axes: list[Quantity[ArrayLike]], interpolation_order=1): pass \ No newline at end of file From f5746098d77c59ca661763521352bf1c13268552 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 10 Oct 2024 14:42:42 +0100 Subject: [PATCH 609/675] Some better commenting on Quantity --- sasdata/quantities/quantity.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index cecd50057..8fa421dbf 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -134,10 +134,10 @@ def __init__(self, self.hash_value = -1 """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ - """ Contains the variance if it is data driven, else it is """ + self._variance = None + """ Contains the variance if it is data driven """ if standard_error is None: - self._variance = None self.hash_value = hash_data_via_numpy(hash_seed, value) else: self._variance = standard_error ** 2 From 7681e95918a04f7fa04e432253f7e0cfaabe2265 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 10 Oct 2024 18:10:36 +0100 Subject: [PATCH 610/675] Work towards rebinning methods --- sasdata/data.py | 21 +++++----- sasdata/transforms/rebinning.py | 68 ++++++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 11 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 7f0cbfb2f..544ba27d3 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -2,6 +2,8 @@ from typing import TypeVar, Any, Self from dataclasses import dataclass +import numpy as np + from quantities.quantity import NamedQuantity from sasdata.metadata import Metadata from sasdata.quantities.accessors import AccessorTarget @@ -9,7 +11,11 @@ class SasData: - def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: Group, verbose: bool=False): + def __init__(self, name: str, + data_contents: list[NamedQuantity], + raw_metadata: Group, + verbose: bool=False): + self.name = name self._data_contents = data_contents self._raw_metadata = raw_metadata @@ -17,14 +23,11 @@ def __init__(self, name: str, data_contents: list[NamedQuantity], raw_metadata: self.metadata = Metadata(AccessorTarget(raw_metadata, verbose=verbose)) - # TO IMPLEMENT - - # abscissae: list[NamedQuantity[np.ndarray]] - # ordinate: NamedQuantity[np.ndarray] - # other: list[NamedQuantity[np.ndarray]] - # - # metadata: Metadata - # model_requirements: ModellingRequirements + # Components that need to be organised after creation + self.ordinate: NamedQuantity[np.ndarray] = None # TODO: fill out + self.abscissae: list[NamedQuantity[np.ndarray]] = None # TODO: fill out + self.mask = None # TODO: fill out + self.model_requirements = None # TODO: fill out def summary(self, indent = " ", include_raw=False): s = f"{self.name}\n" diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index 58d191254..3a8c3e773 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -1,9 +1,73 @@ """ Algorithms for interpolation and rebinning """ from typing import TypeVar +import numpy as np from numpy._typing import ArrayLike from sasdata.quantities.quantity import Quantity +from scipy.sparse import coo_matrix -def rebin(data: Quantity[ArrayLike], axes: list[Quantity[ArrayLike]], new_axes: list[Quantity[ArrayLike]], interpolation_order=1): - pass \ No newline at end of file +from enum import Enum + +class InterpolationOptions(Enum): + NEAREST_NEIGHBOUR = 0 + LINEAR = 1 + + + +def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], + output_axis: Quantity[ArrayLike], + mask: ArrayLike | None = None, + order: InterpolationOptions = InterpolationOptions.NEAREST_NEIGHBOUR, + is_density=False): + + # We want the input values in terms of the output units, will implicitly check compatability + + working_units = output_axis.units + + input_x = input_axis.in_units_of(working_units) + output_x = output_axis.in_units_of(working_units) + + # Get the array indices that will map the array to a sorted one + input_sort = np.argsort(input_x) + output_sort = np.argsort(output_x) + + output_unsort = np.arange(len(input_x), dtype=int)[output_sort] + sorted_in = input_x[input_sort] + sorted_out = output_x[output_sort] + + match order: + case InterpolationOptions.NEAREST_NEIGHBOUR: + + # COO Sparse matrix definition data + values = [] + j_entries = [] + i_entries = [] + + # Find the output values nearest to each of the input values + for x_in in sorted_in: + + + case _: + raise ValueError(f"Unsupported interpolation order: {order}") + +def calculate_interpolation_matrix(input_axes: list[Quantity[ArrayLike]], + output_axes: list[Quantity[ArrayLike]], + data: ArrayLike | None = None, + mask: ArrayLike | None = None): + + pass + + + +def rebin(data: Quantity[ArrayLike], + axes: list[Quantity[ArrayLike]], + new_axes: list[Quantity[ArrayLike]], + mask: ArrayLike | None = None, + interpolation_order: int = 1): + + """ This algorithm is only for operations that preserve dimensionality, + i.e. non-projective rebinning. + """ + + pass From 1a1fc0cf5f64dad8a8fdadf5c562fe549e6fdd63 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 11 Oct 2024 11:27:53 +0100 Subject: [PATCH 611/675] Zeroth order rebinning sketch --- sasdata/transforms/rebinning.py | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index 3a8c3e773..c490f8275 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -32,7 +32,9 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], input_sort = np.argsort(input_x) output_sort = np.argsort(output_x) + input_unsort = np.arange(len(output_x), dtype=int)[input_sort] output_unsort = np.arange(len(input_x), dtype=int)[output_sort] + sorted_in = input_x[input_sort] sorted_out = output_x[output_sort] @@ -40,13 +42,33 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], case InterpolationOptions.NEAREST_NEIGHBOUR: # COO Sparse matrix definition data - values = [] - j_entries = [] i_entries = [] + j_entries = [] + + crossing_points = 0.5*(sorted_out[1:] + sorted_out[:-1]) # Find the output values nearest to each of the input values - for x_in in sorted_in: - + n_i = len(sorted_in) + n_j = len(sorted_out) + i=0 + for k, crossing_point in enumerate(crossing_points): + while i < n_i and sorted_in[i] < crossing_point: + i_entries.append(i) + j_entries.append(k) + i += 1 + + # All the rest in the last bin + while i < n_i: + i_entries.append(i) + j_entries.append(n_j-1) + i += 1 + + i_entries = input_unsort[np.array(i_entries, dtype=int)] + j_entries = output_unsort[np.array(j_entries, dtype=int)] + values = np.ones_like(i_entries, dtype=float) + + return coo_matrix((values, (i_entries, j_entries)), shape=(n_i, n_j)) + case _: raise ValueError(f"Unsupported interpolation order: {order}") @@ -70,4 +92,4 @@ def rebin(data: Quantity[ArrayLike], i.e. non-projective rebinning. """ - pass + pass \ No newline at end of file From 6d54f2793553add0e0e740695c1f9d4c8e4f898e Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 11 Oct 2024 12:08:04 +0100 Subject: [PATCH 612/675] First order rebinning --- sasdata/transforms/rebinning.py | 59 +++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index c490f8275..cd05cfc92 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -3,6 +3,7 @@ import numpy as np from numpy._typing import ArrayLike +from scipy.interpolate import interp1d from sasdata.quantities.quantity import Quantity from scipy.sparse import coo_matrix @@ -38,6 +39,11 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], sorted_in = input_x[input_sort] sorted_out = output_x[output_sort] + n_in = len(sorted_in) + n_out = len(sorted_out) + + conversion_matrix = None # output + match order: case InterpolationOptions.NEAREST_NEIGHBOUR: @@ -48,31 +54,72 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], crossing_points = 0.5*(sorted_out[1:] + sorted_out[:-1]) # Find the output values nearest to each of the input values - n_i = len(sorted_in) - n_j = len(sorted_out) i=0 for k, crossing_point in enumerate(crossing_points): - while i < n_i and sorted_in[i] < crossing_point: + while i < n_in and sorted_in[i] < crossing_point: i_entries.append(i) j_entries.append(k) i += 1 # All the rest in the last bin - while i < n_i: + while i < n_in: i_entries.append(i) - j_entries.append(n_j-1) + j_entries.append(n_out-1) i += 1 i_entries = input_unsort[np.array(i_entries, dtype=int)] j_entries = output_unsort[np.array(j_entries, dtype=int)] values = np.ones_like(i_entries, dtype=float) - return coo_matrix((values, (i_entries, j_entries)), shape=(n_i, n_j)) + conversion_matrix = coo_matrix((values, (i_entries, j_entries)), shape=(n_in, n_out)) + + case InterpolationOptions.LINEAR: + + # Leverage existing linear interpolation methods to get the mapping + # do a linear interpolation on indices + # the floor should give the left bin + # the ceil should give the right bin + # the fractional part should give the relative weightings + + input_indices = np.arange(n_in, dtype=int) + output_indices = np.arange(n_out, dtype=int) + + fractional = np.interp(x=sorted_out, xp=sorted_in, fp=input_indices, left=0, right=n_in-1) + + left_bins = np.floor(fractional, dtype=int) + right_bins = np.ceil(fractional, dtype=int) + + right_weight = fractional % 1 + left_weight = 1 - right_weight + + # There *should* be no repeated entries for both i and j in the main part, but maybe at the ends + # If left bin is the same as right bin, then we only want one entry, and the weight should be 1 + same = left_bins == right_bins + not_same = ~same + + same_bins = left_bins[same] # could equally be right bins, they're the same + + same_indices = output_indices[same] + not_same_indices = output_indices[not_same] + + j_entries_sorted = np.concatenate((same_indices, not_same_indices, not_same_indices)) + i_entries_sorted = np.concatenate((same_bins, left_bins[not_same], right_bins[not_same])) + + i_entries = input_unsort[i_entries_sorted] + j_entries = output_unsort[j_entries_sorted] + + # weights don't need to be unsorted # TODO: check this is right, it should become obvious if we use unsorted data + weights = np.concatenate((np.ones_like(same_bins, dtype=float), left_weight[not_same], right_weight[not_same])) + + conversion_matrix = coo_matrix((weights, (i_entries, j_entries)), shape=(n_in, n_out)) case _: raise ValueError(f"Unsupported interpolation order: {order}") + + return conversion_matrix + def calculate_interpolation_matrix(input_axes: list[Quantity[ArrayLike]], output_axes: list[Quantity[ArrayLike]], data: ArrayLike | None = None, From 6ead27543d1973d415fe9703eeaf55eab15a9092 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 15 Oct 2024 16:26:44 +0100 Subject: [PATCH 613/675] Rebinning tests and extensions --- sasdata/manual_tests/interpolation.py | 4 +- sasdata/quantities/math.py | 5 ++ sasdata/quantities/quantity.py | 18 +++++ sasdata/transforms/rebinning.py | 60 ++++++++++++++-- sasdata/transforms/test_interpolation.py | 91 ++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 9 deletions(-) create mode 100644 sasdata/quantities/math.py create mode 100644 sasdata/transforms/test_interpolation.py diff --git a/sasdata/manual_tests/interpolation.py b/sasdata/manual_tests/interpolation.py index c46078ba7..9edee8a2e 100644 --- a/sasdata/manual_tests/interpolation.py +++ b/sasdata/manual_tests/interpolation.py @@ -34,11 +34,11 @@ def linear_interpolation_check(): quantity_plot(new_x, new_y) - print(new_y.history.summary()) + # print(new_y.history.summary()) plt.show() -linear_interpolation_check() \ No newline at end of file +linear_interpolation_check() diff --git a/sasdata/quantities/math.py b/sasdata/quantities/math.py new file mode 100644 index 000000000..d252ccc0d --- /dev/null +++ b/sasdata/quantities/math.py @@ -0,0 +1,5 @@ +""" Math module extended to allow operations on quantities """ + +# TODO Implementations for trig and exp +# TODO Implementations for linear algebra stuff + diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 8fa421dbf..7bbd8359a 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -112,6 +112,16 @@ def has_variance(self): return False + def summary(self): + + variable_strings = [self.references[key].string_repr for key in self.references] + + s = "Variables: "+",".join(variable_strings) + s += "\n" + s += self.operation_tree.summary() + + return s + class Quantity[QuantityType]: @@ -400,6 +410,10 @@ def __repr__(self): def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass + @property + def string_repr(self): + return str(self.hash_value) + class NamedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, @@ -434,6 +448,10 @@ def with_standard_error(self, standard_error: Quantity): f"are not compatible with value units ({self.units})") + @property + def string_repr(self): + return self.name + class DerivedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): super().__init__(value, units, standard_error=None) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index cd05cfc92..3335216ac 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -13,13 +13,17 @@ class InterpolationOptions(Enum): NEAREST_NEIGHBOUR = 0 LINEAR = 1 + CUBIC = 3 +class InterpolationError(Exception): + """ We probably want to raise exceptions because interpolation is not appropriate/well-defined, + not the same as numerical issues that will raise ValueErrors""" def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], output_axis: Quantity[ArrayLike], mask: ArrayLike | None = None, - order: InterpolationOptions = InterpolationOptions.NEAREST_NEIGHBOUR, + order: InterpolationOptions = InterpolationOptions.LINEAR, is_density=False): # We want the input values in terms of the output units, will implicitly check compatability @@ -33,8 +37,8 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], input_sort = np.argsort(input_x) output_sort = np.argsort(output_x) - input_unsort = np.arange(len(output_x), dtype=int)[input_sort] - output_unsort = np.arange(len(input_x), dtype=int)[output_sort] + input_unsort = np.arange(len(input_x), dtype=int)[input_sort] + output_unsort = np.arange(len(output_x), dtype=int)[output_sort] sorted_in = input_x[input_sort] sorted_out = output_x[output_sort] @@ -86,8 +90,8 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], fractional = np.interp(x=sorted_out, xp=sorted_in, fp=input_indices, left=0, right=n_in-1) - left_bins = np.floor(fractional, dtype=int) - right_bins = np.ceil(fractional, dtype=int) + left_bins = np.floor(fractional).astype(int) + right_bins = np.ceil(fractional).astype(int) right_weight = fractional % 1 left_weight = 1 - right_weight @@ -114,18 +118,60 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], conversion_matrix = coo_matrix((weights, (i_entries, j_entries)), shape=(n_in, n_out)) + case InterpolationOptions.CUBIC: + # Cubic interpolation, much harder to implement because we can't just cheat and use numpy + raise NotImplementedError("Cubic interpolation not implemented yet") + case _: - raise ValueError(f"Unsupported interpolation order: {order}") + raise InterpolationError(f"Unsupported interpolation order: {order}") return conversion_matrix +def calculate_interpolation_matrix_2d_axis_axis(input_1: Quantity[ArrayLike], + input_2: Quantity[ArrayLike], + output_1: Quantity[ArrayLike], + output_2: Quantity[ArrayLike], + mask, + order: InterpolationOptions = InterpolationOptions.LINEAR, + is_density: bool = False): + + match order: + case InterpolationOptions.NEAREST_NEIGHBOUR: + pass + + case InterpolationOptions.LINEAR: + pass + + case InterpolationOptions.CUBIC: + pass + + case _: + pass + + def calculate_interpolation_matrix(input_axes: list[Quantity[ArrayLike]], output_axes: list[Quantity[ArrayLike]], data: ArrayLike | None = None, mask: ArrayLike | None = None): - pass + # TODO: We probably should delete this, but lets keep it for now + + if len(input_axes) not in (1, 2): + raise InterpolationError("Interpolation is only supported for 1D and 2D data") + + if len(input_axes) == 1 and len(output_axes) == 1: + # Check for dimensionality + input_axis = input_axes[0] + output_axis = output_axes[0] + + if len(input_axis.value.shape) == 1: + if len(output_axis.value.shape) == 1: + calculate_interpolation_matrix_1d() + + if len(output_axes) != len(input_axes): + # Input or output axes might be 2D matrices + diff --git a/sasdata/transforms/test_interpolation.py b/sasdata/transforms/test_interpolation.py new file mode 100644 index 000000000..688da65fd --- /dev/null +++ b/sasdata/transforms/test_interpolation.py @@ -0,0 +1,91 @@ +import pytest +import numpy as np +from matplotlib import pyplot as plt +from numpy.typing import ArrayLike +from typing import Callable + +from sasdata.quantities.plotting import quantity_plot +from sasdata.quantities.quantity import NamedQuantity, Quantity +from sasdata.quantities import units + +from sasdata.transforms.rebinning import calculate_interpolation_matrix_1d, InterpolationOptions + +test_functions = [ + lambda x: x**2, + lambda x: 2*x, + lambda x: x**3 +] + + +@pytest.mark.parametrize("fun", test_functions) +def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]]): + original_points = NamedQuantity("x_base", np.linspace(-10,10, 31), units.meters) + test_points = NamedQuantity("x_test", np.linspace(-5, 5, 11), units.meters) + + + mapping = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) + + y_original = fun(original_points) + y_test = y_original @ mapping + y_expected = fun(test_points) + + test_units = y_expected.units + + y_values_test = y_test.in_units_of(test_units) + y_values_expected = y_expected.in_units_of(test_units) + + # print(y_values_test) + # print(y_values_expected) + # + # quantity_plot(original_points, y_original) + # quantity_plot(test_points, y_test) + # quantity_plot(test_points, y_expected) + # plt.show() + + assert len(y_values_test) == len(y_values_expected) + + for t, e in zip(y_values_test, y_values_expected): + assert t == pytest.approx(e, abs=2) + + +@pytest.mark.parametrize("fun", test_functions) +def test_linear_interpolate_different_units(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]]): + original_points = NamedQuantity("x_base", np.linspace(-10,10, 107), units.meters) + test_points = NamedQuantity("x_test", np.linspace(-5000, 5000, 11), units.millimeters) + + mapping = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) + + y_original = fun(original_points) + y_test = y_original @ mapping + y_expected = fun(test_points) + + test_units = y_expected.units + + y_values_test = y_test.in_units_of(test_units) + y_values_expected = y_expected.in_units_of(test_units) + # + # print(y_values_test) + # print(y_test.in_si()) + # print(y_values_expected) + # + # plt.plot(original_points.in_si(), y_original.in_si()) + # plt.plot(test_points.in_si(), y_test.in_si(), "x") + # plt.plot(test_points.in_si(), y_expected.in_si(), "o") + # plt.show() + + assert len(y_values_test) == len(y_values_expected) + + for t, e in zip(y_values_test, y_values_expected): + assert t == pytest.approx(e, rel=5e-2) + +def test_linearity_linear(): + """ Test linear interpolation between two points""" + x_and_y = NamedQuantity("x_base", np.linspace(-10, 10, 2), units.meters) + new_x = NamedQuantity("x_test", np.linspace(-5000, 5000, 101), units.millimeters) + + mapping = calculate_interpolation_matrix_1d(x_and_y, new_x, order=InterpolationOptions.LINEAR) + + linear_points = x_and_y @ mapping + + for t, e in zip(new_x.in_si(), linear_points.in_si()): + assert t == pytest.approx(e, rel=1e-3) \ No newline at end of file From 9785da5568cd21033bf06f57a380a33bd1b952fd Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 20 Sep 2023 13:16:45 +0100 Subject: [PATCH 614/675] Work towards fraction binning --- sasdata/data_util/geometry.py | 0 sasdata/data_util/meshmerge.py | 89 ++++++++++++++++++++++++++++ sasdata/data_util/sample_polygons.py | 31 ++++++++++ sasdata/data_util/transforms.py | 58 ++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 sasdata/data_util/geometry.py create mode 100644 sasdata/data_util/meshmerge.py create mode 100644 sasdata/data_util/sample_polygons.py create mode 100644 sasdata/data_util/transforms.py diff --git a/sasdata/data_util/geometry.py b/sasdata/data_util/geometry.py new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/data_util/meshmerge.py b/sasdata/data_util/meshmerge.py new file mode 100644 index 000000000..eef12bf99 --- /dev/null +++ b/sasdata/data_util/meshmerge.py @@ -0,0 +1,89 @@ +from typing import Sequence +from scipy.spatial import Delaunay + +import numpy as np + +from dataclasses import dataclass + +@dataclass +class Mesh: + points: np.ndarray + edges: Sequence[Sequence[int]] # List of pairs of points forming edges + cells: Sequence[Sequence[int]] # List of edges constituting a cell + + +def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: + """ Take two lists of polygons and find their intersections + + Polygons in each of the input variables should not overlap i.e. a point in space should be assignable to + at most one polygon in mesh_a and at most one polygon in mesh_b + + Mesh topology should be sensible, otherwise bad things might happen + + :returns: + 1) A triangulated mesh based on both sets of polygons together + 2) The indices of the mesh_a polygon that corresponds to each triangle, -1 for nothing + 3) The indices of the mesh_b polygon that corresponds to each triangle, -1 for nothing + + """ + + # Find intersections of all edges in mesh one with edges in mesh two + + new_points = [] + for edge_a in mesh_a.edges: + for edge_b in mesh_b.edges: + # + # Parametric description of intersection in terms of position along lines + # + # Simultaneous eqns (to reflect current wiki notation) + # s(x2 - x1) - t(x4 - x3) = x3 - x1 + # s(y2 - y1) - t(y4 - y3) = y3 - y1 + # + # in matrix form: + # m.(s,t) = v + # + + p1 = mesh_a.points[edge_a[0]] + p2 = mesh_a.points[edge_a[1]] + p3 = mesh_b.points[edge_b[0]] + p4 = mesh_b.points[edge_b[1]] + + m = np.array([ + [p2[0] - p1[0], p3[0] - p4[0]], + [p2[1] - p1[1], p3[1] - p4[1]]]) + + v = np.array([p3[0] - p1[0], p3[1] - p1[1]]) + + if np.linalg.det(m) == 0: + # Lines don't intersect + break + + st = np.linalg.solve(m, v) + + # As the purpose of this is finding new points for the merged mesh, we don't + # want new points if they are right at the end of the lines, hence non strict + # inequalities here + if np.any(st <= 0) or np.any(st >= 1): + # Exclude intection points, that are not on the *segments* + break + + x = p1[0] + (p2[0] - p1[1])*st[0] + y = p1[1] + (p2[1] - p1[1])*st[1] + + new_points.append((x, y)) + + # Build list of all input points, in a way that we can check for coincident points + + + + # Remove coincident points + + + # Triangulate based on these intersections + + # Find centroids of all output triangles, and find which source cells they belong to + + ## Assign -1 to all cells + ## Find centroids + ## Check whether within bounding box + ## If in bounding box, check cell properly using winding number, if inside, assign diff --git a/sasdata/data_util/sample_polygons.py b/sasdata/data_util/sample_polygons.py new file mode 100644 index 000000000..e12fb1e80 --- /dev/null +++ b/sasdata/data_util/sample_polygons.py @@ -0,0 +1,31 @@ +import numpy as np + +def wedge(q0, q1, theta0, theta1, clockwise=False, n_points_per_degree=2): + + # Traverse a rectangle in curvilinear coordinates (q0, theta0), (q0, theta1), (q1, theta1), (q1, theta0) + if clockwise: + if theta1 > theta0: + theta0 += 2*np.pi + + else: + if theta0 > theta1: + theta1 += 2*np.pi + + subtended_angle = np.abs(theta1 - theta0) + n_points = int(subtended_angle*180*n_points_per_degree/np.pi)+1 + + angles = np.linspace(theta0, theta1, n_points) + + xs = np.concatenate((q0*np.cos(angles), q1*np.cos(angles[::-1]))) + ys = np.concatenate((q0*np.sin(angles), q1*np.sin(angles[::-1]))) + + return np.array((xs, ys)).T + + +if __name__ == "__main__": + import matplotlib.pyplot as plt + xy = wedge(0.3, 0.6, 2, 3) + + plt.plot(xy[:,0], xy[:,1]) + plt.show() + diff --git a/sasdata/data_util/transforms.py b/sasdata/data_util/transforms.py new file mode 100644 index 000000000..d04742d3c --- /dev/null +++ b/sasdata/data_util/transforms.py @@ -0,0 +1,58 @@ +import numpy as np +from scipy.spatial import Voronoi, Delaunay +import matplotlib.pyplot as plt +from matplotlib import cm + + +# Some test data + +qx_base_values = np.linspace(-10, 10, 21) +qy_base_values = np.linspace(-10, 10, 21) + +qx, qy = np.meshgrid(qx_base_values, qy_base_values) + +include = np.logical_not((np.abs(qx) < 2) & (np.abs(qy) < 2)) + +qx = qx[include] +qy = qy[include] + +r = np.sqrt(qx**2 + qy**2) + +data = np.log((1+np.cos(3*r))*np.exp(-r*r)) + +colormap = cm.get_cmap('winter', 256) + +def get_data_mesh(x, y, data): + + input_data = np.array((x, y)).T + voronoi = Voronoi(input_data) + + # plt.scatter(voronoi.vertices[:,0], voronoi.vertices[:,1]) + # plt.scatter(voronoi.points[:,0], voronoi.points[:,1]) + + cmin = np.min(data) + cmax = np.max(data) + + color_index_map = np.array(255 * (data - cmin) / (cmax - cmin), dtype=int) + + for point_index, points in enumerate(voronoi.points): + + region_index = voronoi.point_region[point_index] + region = voronoi.regions[region_index] + + if len(region) > 0: + + if -1 in region: + + pass + + else: + + color = colormap(color_index_map[point_index]) + + circly = region + [region[0]] + plt.fill(voronoi.vertices[circly, 0], voronoi.vertices[circly, 1], color=color, edgecolor="white") + + plt.show() + +get_data_mesh(qx.reshape(-1), qy.reshape(-1), data) \ No newline at end of file From 47c08e6f36cc30283f4e33cd392377b960ffdac5 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 22 Sep 2023 13:50:25 +0100 Subject: [PATCH 615/675] Mesh merging and some refactoring --- sasdata/data_util/meshmerge.py | 89 --------- .../{geometry.py => slicing/__init__.py} | 0 sasdata/data_util/slicing/geometry.py | 0 sasdata/data_util/slicing/mesh.py | 28 +++ sasdata/data_util/slicing/meshmerge.py | 170 ++++++++++++++++++ .../{ => slicing}/sample_polygons.py | 0 sasdata/data_util/{ => slicing}/transforms.py | 0 sasdata/data_util/slicing/voronoi_mesh.py | 37 ++++ 8 files changed, 235 insertions(+), 89 deletions(-) delete mode 100644 sasdata/data_util/meshmerge.py rename sasdata/data_util/{geometry.py => slicing/__init__.py} (100%) create mode 100644 sasdata/data_util/slicing/geometry.py create mode 100644 sasdata/data_util/slicing/mesh.py create mode 100644 sasdata/data_util/slicing/meshmerge.py rename sasdata/data_util/{ => slicing}/sample_polygons.py (100%) rename sasdata/data_util/{ => slicing}/transforms.py (100%) create mode 100644 sasdata/data_util/slicing/voronoi_mesh.py diff --git a/sasdata/data_util/meshmerge.py b/sasdata/data_util/meshmerge.py deleted file mode 100644 index eef12bf99..000000000 --- a/sasdata/data_util/meshmerge.py +++ /dev/null @@ -1,89 +0,0 @@ -from typing import Sequence -from scipy.spatial import Delaunay - -import numpy as np - -from dataclasses import dataclass - -@dataclass -class Mesh: - points: np.ndarray - edges: Sequence[Sequence[int]] # List of pairs of points forming edges - cells: Sequence[Sequence[int]] # List of edges constituting a cell - - -def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: - """ Take two lists of polygons and find their intersections - - Polygons in each of the input variables should not overlap i.e. a point in space should be assignable to - at most one polygon in mesh_a and at most one polygon in mesh_b - - Mesh topology should be sensible, otherwise bad things might happen - - :returns: - 1) A triangulated mesh based on both sets of polygons together - 2) The indices of the mesh_a polygon that corresponds to each triangle, -1 for nothing - 3) The indices of the mesh_b polygon that corresponds to each triangle, -1 for nothing - - """ - - # Find intersections of all edges in mesh one with edges in mesh two - - new_points = [] - for edge_a in mesh_a.edges: - for edge_b in mesh_b.edges: - # - # Parametric description of intersection in terms of position along lines - # - # Simultaneous eqns (to reflect current wiki notation) - # s(x2 - x1) - t(x4 - x3) = x3 - x1 - # s(y2 - y1) - t(y4 - y3) = y3 - y1 - # - # in matrix form: - # m.(s,t) = v - # - - p1 = mesh_a.points[edge_a[0]] - p2 = mesh_a.points[edge_a[1]] - p3 = mesh_b.points[edge_b[0]] - p4 = mesh_b.points[edge_b[1]] - - m = np.array([ - [p2[0] - p1[0], p3[0] - p4[0]], - [p2[1] - p1[1], p3[1] - p4[1]]]) - - v = np.array([p3[0] - p1[0], p3[1] - p1[1]]) - - if np.linalg.det(m) == 0: - # Lines don't intersect - break - - st = np.linalg.solve(m, v) - - # As the purpose of this is finding new points for the merged mesh, we don't - # want new points if they are right at the end of the lines, hence non strict - # inequalities here - if np.any(st <= 0) or np.any(st >= 1): - # Exclude intection points, that are not on the *segments* - break - - x = p1[0] + (p2[0] - p1[1])*st[0] - y = p1[1] + (p2[1] - p1[1])*st[1] - - new_points.append((x, y)) - - # Build list of all input points, in a way that we can check for coincident points - - - - # Remove coincident points - - - # Triangulate based on these intersections - - # Find centroids of all output triangles, and find which source cells they belong to - - ## Assign -1 to all cells - ## Find centroids - ## Check whether within bounding box - ## If in bounding box, check cell properly using winding number, if inside, assign diff --git a/sasdata/data_util/geometry.py b/sasdata/data_util/slicing/__init__.py similarity index 100% rename from sasdata/data_util/geometry.py rename to sasdata/data_util/slicing/__init__.py diff --git a/sasdata/data_util/slicing/geometry.py b/sasdata/data_util/slicing/geometry.py new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/data_util/slicing/mesh.py b/sasdata/data_util/slicing/mesh.py new file mode 100644 index 000000000..c27be6030 --- /dev/null +++ b/sasdata/data_util/slicing/mesh.py @@ -0,0 +1,28 @@ +from typing import Sequence + +import numpy as np + +import matplotlib.pyplot as plt +from matplotlib.collections import LineCollection + +class Mesh: + def __init__(self, points: np.ndarray, edges: Sequence[Sequence[int]], cells: Sequence[Sequence[int]]): + self.points = points + self.edges = edges + self.cells = cells + + self._cells_to_points = None + + + def show(self, actually_show=True, **kwargs): + + ax = plt.gca() + segments = [[self.points[edge[0]], self.points[edge[1]]] for edge in self.edges] + line_collection = LineCollection(segments=segments, **kwargs) + ax.add_collection(line_collection) + + if actually_show: + plt.show() + + def show_data(self, data: np.ndarray): + raise NotImplementedError("Show data not implemented") \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshmerge.py b/sasdata/data_util/slicing/meshmerge.py new file mode 100644 index 000000000..32cd8e1f5 --- /dev/null +++ b/sasdata/data_util/slicing/meshmerge.py @@ -0,0 +1,170 @@ +from typing import Sequence +from scipy.spatial import Delaunay + +import numpy as np + +from dataclasses import dataclass + +from sasdata.data_util.slicing.mesh import Mesh + +import matplotlib.pyplot as plt + +def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: + """ Take two lists of polygons and find their intersections + + Polygons in each of the input variables should not overlap i.e. a point in space should be assignable to + at most one polygon in mesh_a and at most one polygon in mesh_b + + Mesh topology should be sensible, otherwise bad things might happen + + :returns: + 1) A triangulated mesh based on both sets of polygons together + 2) The indices of the mesh_a polygon that corresponds to each triangle, -1 for nothing + 3) The indices of the mesh_b polygon that corresponds to each triangle, -1 for nothing + + """ + + # Find intersections of all edges in mesh one with edges in mesh two + + new_x = [] + new_y = [] + for edge_a in mesh_a.edges: + for edge_b in mesh_b.edges: + + p1 = mesh_a.points[edge_a[0]] + p2 = mesh_a.points[edge_a[1]] + p3 = mesh_b.points[edge_b[0]] + p4 = mesh_b.points[edge_b[1]] + + # Bounding box check + + # First edge entirely to left of other + if max((p1[0], p2[0])) < min((p3[0], p4[0])): + continue + + # First edge entirely below other + if max((p1[1], p2[1])) < min((p3[1], p4[1])): + continue + + # First edge entirely to right of other + if min((p1[0], p2[0])) > max((p3[0], p4[0])): + continue + + # First edge entirely above other + if min((p1[1], p2[1])) > max((p3[1], p4[1])): + continue + + # + # Parametric description of intersection in terms of position along lines + # + # Simultaneous eqns (to reflect current wiki notation) + # s(x2 - x1) - t(x4 - x3) = x3 - x1 + # s(y2 - y1) - t(y4 - y3) = y3 - y1 + # + # in matrix form: + # m.(s,t) = v + # + + + m = np.array([ + [p2[0] - p1[0], p3[0] - p4[0]], + [p2[1] - p1[1], p3[1] - p4[1]]]) + + v = np.array([p3[0] - p1[0], p3[1] - p1[1]]) + + if np.linalg.det(m) == 0: + # Lines don't intersect, or are colinear in a way that doesn't matter + continue + + st = np.linalg.solve(m, v) + + # As the purpose of this is finding new points for the merged mesh, we don't + # want new points if they are right at the end of the lines, hence non-strict + # inequalities here + if np.any(st <= 0) or np.any(st >= 1): + # Exclude intection points, that are not on the *segments* + continue + + x = p1[0] + (p2[0] - p1[0])*st[0] + y = p1[1] + (p2[1] - p1[1])*st[0] + + new_x.append(x) + new_y.append(y) + + + + # Build list of all input points, in a way that we can check for coincident points + + # plt.scatter(mesh_a.points[:,0], mesh_a.points[:,1]) + # plt.scatter(mesh_b.points[:,0], mesh_b.points[:,1]) + # plt.scatter(new_x, new_y) + # + # mesh_a.show(False) + # mesh_b.show(False, color=(.8, .5, 0)) + # + # plt.xlim([0,1]) + # plt.ylim([0,1]) + # + # plt.show() + + points = np.concatenate(( + mesh_a.points, + mesh_b.points, + np.array((new_x, new_y)).T + )) + + # plt.scatter(points[:,0], points[:,1]) + # plt.show() + + # Remove coincident points + + points = np.unique(points, axis=0) + + # Triangulate based on these intersections + + # Find centroids of all output triangles, and find which source cells they belong to + + ## Assign -1 to all cells + ## Find centroids - they're just the closed voronoi cells? + ## Check whether within bounding box + ## If in bounding box, check cell properly using winding number, if inside, assign + + +def simple_intersection(): + mesh_a = Mesh( + np.array([[0, 0.5],[1,0.5]], dtype=float), + [[0, 1]], []) + + mesh_b = Mesh( + np.array([[0.5, 0], [0.5, 1]], dtype=float), + [[0, 1]], []) + + meshmerge(mesh_a, mesh_b) + + + +def simple_intersection_2(): + mesh_a = Mesh( + np.array([[4,3],[1,3]], dtype=float), + [[0, 1]], []) + + mesh_b = Mesh( + np.array([[3, 4], [3, 1]], dtype=float), + [[0, 1]], []) + + meshmerge(mesh_a, mesh_b) +def main(): + from voronoi_mesh import voronoi_mesh + + n1 = 100 + n2 = 100 + + m1 = voronoi_mesh(np.random.random(n1), np.random.random(n1)) + m2 = voronoi_mesh(np.random.random(n2), np.random.random(n2)) + + + meshmerge(m1, m2) + +if __name__ == "__main__": + main() + # simple_intersection() \ No newline at end of file diff --git a/sasdata/data_util/sample_polygons.py b/sasdata/data_util/slicing/sample_polygons.py similarity index 100% rename from sasdata/data_util/sample_polygons.py rename to sasdata/data_util/slicing/sample_polygons.py diff --git a/sasdata/data_util/transforms.py b/sasdata/data_util/slicing/transforms.py similarity index 100% rename from sasdata/data_util/transforms.py rename to sasdata/data_util/slicing/transforms.py diff --git a/sasdata/data_util/slicing/voronoi_mesh.py b/sasdata/data_util/slicing/voronoi_mesh.py new file mode 100644 index 000000000..34a8fd7d6 --- /dev/null +++ b/sasdata/data_util/slicing/voronoi_mesh.py @@ -0,0 +1,37 @@ +import numpy as np +from scipy.spatial import Voronoi + + +from sasdata.data_util.slicing.mesh import Mesh + +def voronoi_mesh(x, y) -> Mesh: + + input_data = np.array((x, y)).T + voronoi = Voronoi(input_data) + + edges = set() + + for point_index, points in enumerate(voronoi.points): + + region_index = voronoi.point_region[point_index] + region = voronoi.regions[region_index] + + wrapped = region + [region[0]] + for a, b in zip(wrapped[:-1], wrapped[1:]): + if not a == -1 and not b == -1: + + # make sure the representation is unique + if a > b: + edges.add((a, b)) + else: + edges.add((b, a)) + + edges = list(edges) + + return Mesh(points=voronoi.vertices, edges=edges, cells=[]) + + +if __name__ == "__main__": + points = np.random.random((100, 2)) + mesh = voronoi_mesh(points[:,0], points[:,1]) + mesh.show() \ No newline at end of file From 3e43907ef28da0321912643af55b078693df0ae1 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Sun, 24 Sep 2023 12:54:29 +0100 Subject: [PATCH 616/675] Triangulated mesh --- sasdata/data_util/slicing/delaunay_mesh.py | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 sasdata/data_util/slicing/delaunay_mesh.py diff --git a/sasdata/data_util/slicing/delaunay_mesh.py b/sasdata/data_util/slicing/delaunay_mesh.py new file mode 100644 index 000000000..ef90e44df --- /dev/null +++ b/sasdata/data_util/slicing/delaunay_mesh.py @@ -0,0 +1,34 @@ +import numpy as np + +from scipy.spatial import Delaunay + +from sasdata.data_util.slicing.mesh import Mesh + + +def delaunay_mesh(x, y) -> Mesh: + + input_data = np.array((x, y)).T + delaunay = Delaunay(input_data) + + edges = set() + + for simplex_index, simplex in enumerate(delaunay.simplices): + + wrapped = list(simplex) + [simplex[0]] + + for a, b in zip(wrapped[:-1], wrapped[1:]): + # make sure the representation is unique + if a > b: + edges.add((a, b)) + else: + edges.add((b, a)) + + edges = list(edges) + + return Mesh(points=input_data, edges=edges, cells=[]) + + +if __name__ == "__main__": + points = np.random.random((100, 2)) + mesh = delaunay_mesh(points[:,0], points[:,1]) + mesh.show() \ No newline at end of file From 3ac0415443cd250fc08f8e78ca4900ae7571202a Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 25 Sep 2023 01:28:00 +0100 Subject: [PATCH 617/675] Mesh merging works --- sasdata/data_util/slicing/delaunay_mesh.py | 34 ------ sasdata/data_util/slicing/mesh.py | 28 ----- sasdata/data_util/slicing/meshes/__init__.py | 0 .../data_util/slicing/meshes/delaunay_mesh.py | 32 +++++ sasdata/data_util/slicing/meshes/mesh.py | 96 +++++++++++++++ .../slicing/{ => meshes}/meshmerge.py | 110 +++++++++++------- sasdata/data_util/slicing/meshes/util.py | 10 ++ .../data_util/slicing/meshes/voronoi_mesh.py | 20 ++++ sasdata/data_util/slicing/voronoi_mesh.py | 37 ------ test/slicers/meshes_for_testing.py | 80 ++++--------- test/slicers/utest_meshmerge.py | 13 +-- 11 files changed, 246 insertions(+), 214 deletions(-) delete mode 100644 sasdata/data_util/slicing/delaunay_mesh.py delete mode 100644 sasdata/data_util/slicing/mesh.py create mode 100644 sasdata/data_util/slicing/meshes/__init__.py create mode 100644 sasdata/data_util/slicing/meshes/delaunay_mesh.py create mode 100644 sasdata/data_util/slicing/meshes/mesh.py rename sasdata/data_util/slicing/{ => meshes}/meshmerge.py (51%) create mode 100644 sasdata/data_util/slicing/meshes/util.py create mode 100644 sasdata/data_util/slicing/meshes/voronoi_mesh.py delete mode 100644 sasdata/data_util/slicing/voronoi_mesh.py diff --git a/sasdata/data_util/slicing/delaunay_mesh.py b/sasdata/data_util/slicing/delaunay_mesh.py deleted file mode 100644 index ef90e44df..000000000 --- a/sasdata/data_util/slicing/delaunay_mesh.py +++ /dev/null @@ -1,34 +0,0 @@ -import numpy as np - -from scipy.spatial import Delaunay - -from sasdata.data_util.slicing.mesh import Mesh - - -def delaunay_mesh(x, y) -> Mesh: - - input_data = np.array((x, y)).T - delaunay = Delaunay(input_data) - - edges = set() - - for simplex_index, simplex in enumerate(delaunay.simplices): - - wrapped = list(simplex) + [simplex[0]] - - for a, b in zip(wrapped[:-1], wrapped[1:]): - # make sure the representation is unique - if a > b: - edges.add((a, b)) - else: - edges.add((b, a)) - - edges = list(edges) - - return Mesh(points=input_data, edges=edges, cells=[]) - - -if __name__ == "__main__": - points = np.random.random((100, 2)) - mesh = delaunay_mesh(points[:,0], points[:,1]) - mesh.show() \ No newline at end of file diff --git a/sasdata/data_util/slicing/mesh.py b/sasdata/data_util/slicing/mesh.py deleted file mode 100644 index c27be6030..000000000 --- a/sasdata/data_util/slicing/mesh.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import Sequence - -import numpy as np - -import matplotlib.pyplot as plt -from matplotlib.collections import LineCollection - -class Mesh: - def __init__(self, points: np.ndarray, edges: Sequence[Sequence[int]], cells: Sequence[Sequence[int]]): - self.points = points - self.edges = edges - self.cells = cells - - self._cells_to_points = None - - - def show(self, actually_show=True, **kwargs): - - ax = plt.gca() - segments = [[self.points[edge[0]], self.points[edge[1]]] for edge in self.edges] - line_collection = LineCollection(segments=segments, **kwargs) - ax.add_collection(line_collection) - - if actually_show: - plt.show() - - def show_data(self, data: np.ndarray): - raise NotImplementedError("Show data not implemented") \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/__init__.py b/sasdata/data_util/slicing/meshes/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sasdata/data_util/slicing/meshes/delaunay_mesh.py b/sasdata/data_util/slicing/meshes/delaunay_mesh.py new file mode 100644 index 000000000..45e208786 --- /dev/null +++ b/sasdata/data_util/slicing/meshes/delaunay_mesh.py @@ -0,0 +1,32 @@ +import numpy as np +from scipy.spatial import Delaunay + +from sasdata.data_util.slicing.meshes.mesh import Mesh + +def delaunay_mesh(x, y) -> Mesh: + """ Create a triangulated mesh based on input points """ + + input_data = np.array((x, y)).T + delaunay = Delaunay(input_data) + + return Mesh(points=input_data, cells=delaunay.simplices) + + +if __name__ == "__main__": + import matplotlib.pyplot as plt + + points = np.random.random((100, 2)) + mesh = delaunay_mesh(points[:,0], points[:,1]) + mesh.show(actually_show=False) + + print(mesh.cells[50]) + + # pick random cell to show + for cell in mesh.cells_to_edges[10]: + a, b = mesh.edges[cell] + plt.plot( + [mesh.points[a][0], mesh.points[b][0]], + [mesh.points[a][1], mesh.points[b][1]], + color='r') + + plt.show() diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py new file mode 100644 index 000000000..b52b3e8a9 --- /dev/null +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -0,0 +1,96 @@ +from typing import Sequence + +import numpy as np + +import matplotlib.pyplot as plt +from matplotlib.collections import LineCollection + +from sasdata.data_util.slicing.meshes.util import closed_loop_edges + +class Mesh: + def __init__(self, + points: np.ndarray, + cells: Sequence[Sequence[int]]): + + """ + Object representing a mesh. + + Parameters are the values: + mesh points + map from edge to points + map from cells to edges + + it is done this way to ensure a non-redundant representation of cells and edges, + however there are no checks for the topology of the mesh, this is assumed to be done by + whatever creates it. There are also no checks for ordering of cells. + + :param points: points in 2D forming vertices of the mesh + :param cells: ordered lists of indices of points forming each cell (face) + + """ + + self.points = points + self.cells = cells + + # Get edges + + edges = set() + for cell_index, cell in enumerate(cells): + + for a, b in closed_loop_edges(cell): + # make sure the representation is unique + if a > b: + edges.add((a, b)) + else: + edges.add((b, a)) + + self.edges = list(edges) + + # Associate edges with faces + + edge_lookup = {edge: i for i, edge in enumerate(self.edges)} + self.cells_to_edges = [] + + for cell in cells: + + this_cell_data = [] + + for a, b in closed_loop_edges(cell): + # make sure the representation is unique + if a > b: + this_cell_data.append(edge_lookup[(a, b)]) + else: + this_cell_data.append(edge_lookup[(b, a)]) + + self.cells_to_edges.append(this_cell_data) + + # Counts for elements + self.n_points = self.points.shape[0] + self.n_edges = len(self.edges) + self.n_cells = len(self.cells) + + def show(self, actually_show=True, show_labels=False, **kwargs): + """ Show on a plot """ + ax = plt.gca() + segments = [[self.points[edge[0]], self.points[edge[1]]] for edge in self.edges] + line_collection = LineCollection(segments=segments, **kwargs) + ax.add_collection(line_collection) + + if show_labels: + text_color = kwargs["color"] if "color" in kwargs else 'k' + for i, cell in enumerate(self.cells): + xy = np.sum(self.points[cell, :], axis=0)/len(cell) + ax.text(xy[0], xy[1], str(i), horizontalalignment="center", verticalalignment="center", color=text_color) + + x_limits = [np.min(self.points[:,0]), np.max(self.points[:,0])] + y_limits = [np.min(self.points[:,1]), np.max(self.points[:,1])] + + plt.xlim(x_limits) + plt.ylim(y_limits) + + if actually_show: + plt.show() + + def show_data(self, data: np.ndarray, show_mesh=True): + """ Show with data """ + raise NotImplementedError("Show data not implemented") \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshmerge.py b/sasdata/data_util/slicing/meshes/meshmerge.py similarity index 51% rename from sasdata/data_util/slicing/meshmerge.py rename to sasdata/data_util/slicing/meshes/meshmerge.py index 32cd8e1f5..3ce52ba56 100644 --- a/sasdata/data_util/slicing/meshmerge.py +++ b/sasdata/data_util/slicing/meshes/meshmerge.py @@ -1,13 +1,9 @@ -from typing import Sequence -from scipy.spatial import Delaunay - import numpy as np -from dataclasses import dataclass - -from sasdata.data_util.slicing.mesh import Mesh +from sasdata.data_util.slicing.meshes.mesh import Mesh +from sasdata.data_util.slicing.meshes.delaunay_mesh import delaunay_mesh +from sasdata.data_util.slicing.meshes.util import closed_loop_edges -import matplotlib.pyplot as plt def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: """ Take two lists of polygons and find their intersections @@ -15,7 +11,8 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] Polygons in each of the input variables should not overlap i.e. a point in space should be assignable to at most one polygon in mesh_a and at most one polygon in mesh_b - Mesh topology should be sensible, otherwise bad things might happen + Mesh topology should be sensible, otherwise bad things might happen, also, the cells of the input meshes + must be in order (which is assumed by the mesh class constructor anyway). :returns: 1) A triangulated mesh based on both sets of polygons together @@ -95,17 +92,6 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] # Build list of all input points, in a way that we can check for coincident points - # plt.scatter(mesh_a.points[:,0], mesh_a.points[:,1]) - # plt.scatter(mesh_b.points[:,0], mesh_b.points[:,1]) - # plt.scatter(new_x, new_y) - # - # mesh_a.show(False) - # mesh_b.show(False, color=(.8, .5, 0)) - # - # plt.xlim([0,1]) - # plt.ylim([0,1]) - # - # plt.show() points = np.concatenate(( mesh_a.points, @@ -113,8 +99,6 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] np.array((new_x, new_y)).T )) - # plt.scatter(points[:,0], points[:,1]) - # plt.show() # Remove coincident points @@ -122,37 +106,75 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] # Triangulate based on these intersections + output_mesh = delaunay_mesh(points[:, 0], points[:, 1]) + # Find centroids of all output triangles, and find which source cells they belong to - ## Assign -1 to all cells - ## Find centroids - they're just the closed voronoi cells? - ## Check whether within bounding box - ## If in bounding box, check cell properly using winding number, if inside, assign + ## step 1) Assign -1 to all cells of original meshes + assignments_a = -np.ones(output_mesh.n_cells, dtype=int) + assignments_b = -np.ones(output_mesh.n_cells, dtype=int) + + ## step 2) Find centroids of triangulated mesh (just needs to be a point inside, but this is a good one) + centroids = [] + for cell in output_mesh.cells: + centroid = np.sum(output_mesh.points[cell, :]/3, axis=0) + centroids.append(centroid) + + ## step 3) Perform checks based on winding number method (see wikipedia Point in Polygon). + for mesh, assignments in [ + (mesh_a, assignments_a), + (mesh_b, assignments_b)]: + + for centroid_index, centroid in enumerate(centroids): + for cell_index, cell in enumerate(mesh.cells): + # Bounding box check + points = mesh.points[cell, :] + if np.any(centroid < np.min(points, axis=0)): # x or y less than any in polygon + continue -def simple_intersection(): - mesh_a = Mesh( - np.array([[0, 0.5],[1,0.5]], dtype=float), - [[0, 1]], []) + if np.any(centroid > np.max(points, axis=0)): # x or y greater than any in polygon + continue - mesh_b = Mesh( - np.array([[0.5, 0], [0.5, 1]], dtype=float), - [[0, 1]], []) + # Winding number check - count directional crossings of vertical half line from centroid + winding_number = 0 + for i1, i2 in closed_loop_edges(cell): + p1 = mesh.points[i1, :] + p2 = mesh.points[i2, :] - meshmerge(mesh_a, mesh_b) + # if the section xs do not straddle the x=centroid_x coordinate, then the + # edge cannot cross the half line. + # If it does, then remember which way it was + # * Careful about ends + # * Also, note that the p1[0] == p2[0] -> (no contribution) case is covered by the strict inequality + if p1[0] > centroid[0] >= p2[0]: + left_right = -1 + elif p2[0] > centroid[0] >= p1[0]: + left_right = 1 + else: + continue + # Find the y point that it crosses x=centroid at + # note: denominator cannot be zero because of strict inequality above + gradient = (p2[1] - p1[1]) / (p2[0] - p1[0]) + x_delta = centroid[0] - p1[0] + y = p1[1] + x_delta * gradient + if y > centroid[1]: + winding_number += left_right -def simple_intersection_2(): - mesh_a = Mesh( - np.array([[4,3],[1,3]], dtype=float), - [[0, 1]], []) - mesh_b = Mesh( - np.array([[3, 4], [3, 1]], dtype=float), - [[0, 1]], []) + if abs(winding_number) > 0: + # Do assignment of input cell to output triangle index + assignments[centroid_index] = cell_index + + # end cell loop + + # end centroid loop + + return output_mesh, assignments_a, assignments_b + - meshmerge(mesh_a, mesh_b) def main(): from voronoi_mesh import voronoi_mesh @@ -163,8 +185,10 @@ def main(): m2 = voronoi_mesh(np.random.random(n2), np.random.random(n2)) - meshmerge(m1, m2) + mesh, _, _ = meshmerge(m1, m2) + + mesh.show() + if __name__ == "__main__": main() - # simple_intersection() \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/util.py b/sasdata/data_util/slicing/meshes/util.py new file mode 100644 index 000000000..b78a9e076 --- /dev/null +++ b/sasdata/data_util/slicing/meshes/util.py @@ -0,0 +1,10 @@ +from typing import Sequence, TypeVar + +T = TypeVar("T") + +def closed_loop_edges(values: Sequence[T]) -> tuple[T, T]: + """ Generator for a closed loop of edge pairs """ + for pair in zip(values, values[1:]): + yield pair + + yield values[-1], values[0] \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/voronoi_mesh.py b/sasdata/data_util/slicing/meshes/voronoi_mesh.py new file mode 100644 index 000000000..77db2a687 --- /dev/null +++ b/sasdata/data_util/slicing/meshes/voronoi_mesh.py @@ -0,0 +1,20 @@ +import numpy as np +from scipy.spatial import Voronoi + + +from sasdata.data_util.slicing.meshes.mesh import Mesh + +def voronoi_mesh(x, y) -> Mesh: + + input_data = np.array((x.reshape(-1), y.reshape(-1))).T + voronoi = Voronoi(input_data) + + finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] + + return Mesh(points=voronoi.vertices, cells=finite_cells) + + +if __name__ == "__main__": + points = np.random.random((100, 2)) + mesh = voronoi_mesh(points[:,0], points[:,1]) + mesh.show() \ No newline at end of file diff --git a/sasdata/data_util/slicing/voronoi_mesh.py b/sasdata/data_util/slicing/voronoi_mesh.py deleted file mode 100644 index 34a8fd7d6..000000000 --- a/sasdata/data_util/slicing/voronoi_mesh.py +++ /dev/null @@ -1,37 +0,0 @@ -import numpy as np -from scipy.spatial import Voronoi - - -from sasdata.data_util.slicing.mesh import Mesh - -def voronoi_mesh(x, y) -> Mesh: - - input_data = np.array((x, y)).T - voronoi = Voronoi(input_data) - - edges = set() - - for point_index, points in enumerate(voronoi.points): - - region_index = voronoi.point_region[point_index] - region = voronoi.regions[region_index] - - wrapped = region + [region[0]] - for a, b in zip(wrapped[:-1], wrapped[1:]): - if not a == -1 and not b == -1: - - # make sure the representation is unique - if a > b: - edges.add((a, b)) - else: - edges.add((b, a)) - - edges = list(edges) - - return Mesh(points=voronoi.vertices, edges=edges, cells=[]) - - -if __name__ == "__main__": - points = np.random.random((100, 2)) - mesh = voronoi_mesh(points[:,0], points[:,1]) - mesh.show() \ No newline at end of file diff --git a/test/slicers/meshes_for_testing.py b/test/slicers/meshes_for_testing.py index fb346e79c..ff87dc8fc 100644 --- a/test/slicers/meshes_for_testing.py +++ b/test/slicers/meshes_for_testing.py @@ -4,9 +4,9 @@ import numpy as np -from sasdata.slicing.meshes.voronoi_mesh import voronoi_mesh -from sasdata.slicing.meshes.mesh import Mesh -from sasdata.slicing.meshes.meshmerge import meshmerge +from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh +from sasdata.data_util.slicing.meshes.mesh import Mesh +from sasdata.data_util.slicing.meshes.meshmerge import meshmerge coords = np.arange(-4, 5) grid_mesh = voronoi_mesh(*np.meshgrid(coords, coords)) @@ -32,61 +32,25 @@ # Subset of the mappings that meshmerge should include # This can be read off the plots generated below - - expected_shape_mappings = [ - (100, -1), - (152, -1), - (141, -1), - (172, -1), - (170, -1), - (0, -1), + (98, -1), + (99, -1), + (12, 0), (1, -1), - (8, 0), - (9, 0), - (37, 0), - (83, 0), - (190, 1), - (186, 1), - (189, 1), - (193, 1) -] + (148, 1), + (149, 1), + (110, 1), + (144, -1), + (123, -1)] + expected_grid_mappings = [ - (89, 0), - (90, 1), - (148, 16), - (175, 35), - (60, 47), - (44, 47), - (80, 60) + (89, 1), + (146, 29), + (66, 34), + (112, 45) ] -# -# Mesh location tests -# - -location_test_mesh_points = np.array([ - [0, 0], # 0 - [0, 1], # 1 - [0, 2], # 2 - [1, 0], # 3 - [1, 1], # 4 - [1, 2], # 5 - [2, 0], # 6 - [2, 1], # 7 - [2, 2]], dtype=float) - -location_test_mesh_cells = [ - [0, 1, 4, 3], - [1, 2, 5, 4], - [3, 4, 7, 6], - [4, 5, 8, 7]] - -location_test_mesh = Mesh(location_test_mesh_points, location_test_mesh_cells) - -test_coords = 0.25 + 0.5*np.arange(4) -location_test_points_x, location_test_points_y = np.meshgrid(test_coords, test_coords) if __name__ == "__main__": @@ -98,18 +62,14 @@ combined_mesh.show(actually_show=False, show_labels=True, color='k') grid_mesh.show(actually_show=False, show_labels=True, color='r') - plt.xlim([-5, 5]) - plt.ylim([-5, 5]) + plt.xlim([-4, 4]) + plt.ylim([-4, 4]) plt.figure() combined_mesh.show(actually_show=False, show_labels=True, color='k') shape_mesh.show(actually_show=False, show_labels=True, color='r') - plt.xlim([-5, 5]) - plt.ylim([-5, 5]) - - plt.figure() - location_test_mesh.show(actually_show=False, show_labels=True) - plt.scatter(location_test_points_x, location_test_points_y) + plt.xlim([-4, 4]) + plt.ylim([-4, 4]) plt.show() diff --git a/test/slicers/utest_meshmerge.py b/test/slicers/utest_meshmerge.py index d83892def..d1e16f2bf 100644 --- a/test/slicers/utest_meshmerge.py +++ b/test/slicers/utest_meshmerge.py @@ -4,23 +4,12 @@ It's pretty hard to test componentwise, but we can do some tests of the general behaviour """ -from sasdata.slicing.meshes.meshmerge import meshmerge +from sasdata.data_util.slicing.meshes.meshmerge import meshmerge from test.slicers.meshes_for_testing import ( grid_mesh, shape_mesh, expected_grid_mappings, expected_shape_mappings) def test_meshmerge_mappings(): - """ Test the output of meshmerge is correct - - IMPORTANT IF TESTS FAIL!!!... The docs for scipy.spatial.Voronoi and Delaunay - say that the ordering of faces might depend on machine precession. Thus, these - tests might not be reliable... we'll see how they play out - """ - - import sys - if sys.platform == "darwin": - # It does indeed rely on machine precision, only run on windows and linux - return combined_mesh, grid_mappings, shape_mappings = meshmerge(grid_mesh, shape_mesh) From 67039a86763ad908419ce0c7b2fe5a18c2b5c577 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 28 Sep 2023 02:56:00 +0100 Subject: [PATCH 618/675] Implementation of Rebinner base class --- sasdata/data_util/slicing/meshes/mesh.py | 28 +++++ sasdata/data_util/slicing/rebinning.py | 128 +++++++++++++++++++++++ test/slicers/utest_meshmerge.py | 7 ++ 3 files changed, 163 insertions(+) create mode 100644 sasdata/data_util/slicing/rebinning.py diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py index b52b3e8a9..0f12102ce 100644 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -69,6 +69,34 @@ def __init__(self, self.n_edges = len(self.edges) self.n_cells = len(self.cells) + # Areas + self._areas = None + + @property + def areas(self): + """ Areas of cells """ + + if self._areas is None: + # Calculate areas + areas = [] + for cell in self.cells: + # Use triangle shoelace formula, basically calculate the + # determinant based on of triangles with one point at 0,0 + a_times_2 = 0.0 + for i1, i2 in closed_loop_edges(cell): + p1 = self.points[i1, :] + p2 = self.points[i2, :] + a_times_2 += p1[0]*p2[1] - p1[1]*p2[0] + + areas.append(0.5*np.abs(a_times_2)) + + # Save in cache + self._areas = np.ndarray(areas) + + # Return cache + return self._areas + + def show(self, actually_show=True, show_labels=False, **kwargs): """ Show on a plot """ ax = plt.gca() diff --git a/sasdata/data_util/slicing/rebinning.py b/sasdata/data_util/slicing/rebinning.py new file mode 100644 index 000000000..c6ba6079b --- /dev/null +++ b/sasdata/data_util/slicing/rebinning.py @@ -0,0 +1,128 @@ +from abc import ABC, abstractmethod +from typing import Optional +from dataclasses import dataclass + +import numpy as np + +from sasdata.data_util.slicing.meshes.mesh import Mesh +from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh +from sasdata.data_util.slicing.meshes.meshmerge import meshmerge + + +@dataclass +class CacheData: + """ Data cached for repeated calculations with the same coordinates """ + input_coordinates: np.ndarray # Input data + input_coordinates_mesh: Mesh # Mesh of the input data + merged_mesh_data: tuple[Mesh, np.ndarray, np.ndarray] # mesh information about the merging + + +class Rebinner(): + + allowable_orders = [-1,0,1] + + def __init__(self, order): + """ Base class for rebinning methods""" + + self._order = order + self._bin_mesh_cache: Optional[Mesh] = None # cached version of the output bin mesh + + # Output dependent caching + self._input_cache: Optional[CacheData] = None + + if order not in Rebinner.allowable_orders: + raise ValueError(f"Expected order to be in {Rebinner.allowable_orders}, got {order}") + + @abstractmethod + def _bin_coordinates(self) -> np.ndarray: + """ Coordinates for the output bins """ + + @abstractmethod + def _bin_mesh(self) -> Mesh: + """ Get the meshes used for binning """ + + @property + def bin_mesh(self): + if self._bin_mesh_cache is None: + bin_mesh = self._bin_mesh() + self._data_mesh_cache = bin_mesh + else: + return self._bin_mesh_cache + + def _post_processing(self, coordinates, values) -> tuple[np.ndarray, np.ndarray]: + """ Perform post-processing on the mesh binned values """ + # Default is to do nothing, override if needed + return coordinates, values + + def _do_binning(self, data): + """ Main binning algorithm """ + + def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> np.ndarray: + """ Main calculation """ + + if self._order == -1: + # Construct the input output mapping just based on input points being the output cells, + # Equivalent to the original binning method + + pass + + else: + # Use a mapping based on meshes + + # Either create de-cache the appropriate mesh + # Why not use a hash? Hashing takes time, equality checks are pretty fast, need to check equality + # when there is a hit anyway in case of very rare chance of collision, hits are the most common case, + # we want it to work 100% of the time, not 99.9999% + if self._input_cache is not None and np.all(self._input_cache.input_coordinates == input_coordinates): + + input_coordinate_mesh = self._input_cache.input_coordinates_mesh + merge_data = self._input_cache.merged_mesh_data + + else: + # Calculate mesh data + input_coordinate_mesh = voronoi_mesh(input_coordinates[:,0], input_coordinates[:, 1]) + self._data_mesh_cahce = input_coordinate_mesh + + merge_data = meshmerge(self.bin_mesh, input_coordinate_mesh) + + # Cache mesh data + self._input_cache = CacheData( + input_coordinates=input_coordinates, + input_coordinates_mesh=input_coordinate_mesh, + merged_mesh_data=merge_data) + + merged_mesh, merged_to_input, merged_to_output = merge_data + + # Calculate values according to the order parameter + + if self._order == 0: + # Based on the overlap of cells only + + input_areas = input_coordinate_mesh.areas + output = np.zeros(self.bin_mesh.n_cells, dtype=float) + + for input_index, output_index, area in zip(merged_to_input, merged_to_output, merged_mesh.areas): + output[output_index] += input_data[input_index] * area / input_areas[input_data] + + return output + + elif self._order == 1: + raise NotImplementedError("1st order (linear) interpolation currently not implemented") + + else: + raise ValueError(f"Expected order to be in {Rebinner.allowable_orders}, got {self._order}") + + def sum(self, input_coordinates: np.ndarray, data: np.ndarray) -> np.ndarray: + """ Return the summed data in the output bins """ + return self._calculate(input_coordinates, data) + + def error_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: + raise NotImplementedError("Error propagation not implemented yet") + + def resolution_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: + raise NotImplementedError("Resolution propagation not implemented yet") + + def average(self, input_coordinates: np.ndarray, data: np.ndarray) -> np.ndarray: + """ Return the averaged data in the output bins """ + return self._calculate(input_coordinates, data) / self.bin_mesh.areas + diff --git a/test/slicers/utest_meshmerge.py b/test/slicers/utest_meshmerge.py index d1e16f2bf..f745d0250 100644 --- a/test/slicers/utest_meshmerge.py +++ b/test/slicers/utest_meshmerge.py @@ -10,6 +10,13 @@ def test_meshmerge_mappings(): + """ Test the output of meshmerge is correct + + IMPORTANT IF TESTS FAIL!!!... The docs for scipy.spatial.Voronoi and Delaunay + say that the ordering of faces might depend on machine precession. Thus, these + tests might not be reliable... we'll see how they play out + """ + combined_mesh, grid_mappings, shape_mappings = meshmerge(grid_mesh, shape_mesh) From 5eb11691fb6cbbcf171239857b919014a9ae53c0 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 28 Sep 2023 10:06:44 +0100 Subject: [PATCH 619/675] Work towards demo --- sasdata/data_util/slicing/meshes/mesh.py | 29 +++++++++++++-- .../data_util/slicing/meshes/voronoi_mesh.py | 11 ++++++ sasdata/data_util/slicing/rebinning.py | 21 ++++++++--- sasdata/data_util/slicing/slicer_demo.py | 19 ++++++++++ .../data_util/slicing/slicers/AnularSector.py | 35 +++++++++++++++++++ 5 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 sasdata/data_util/slicing/slicer_demo.py create mode 100644 sasdata/data_util/slicing/slicers/AnularSector.py diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py index 0f12102ce..cad7b5ffd 100644 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -3,6 +3,7 @@ import numpy as np import matplotlib.pyplot as plt +from matplotlib import cm from matplotlib.collections import LineCollection from sasdata.data_util.slicing.meshes.util import closed_loop_edges @@ -72,6 +73,12 @@ def __init__(self, # Areas self._areas = None + def find_locations(self, points): + """ Find indices of cells containing the input points """ + + + + @property def areas(self): """ Areas of cells """ @@ -119,6 +126,24 @@ def show(self, actually_show=True, show_labels=False, **kwargs): if actually_show: plt.show() - def show_data(self, data: np.ndarray, show_mesh=True): + def show_data(self, data: np.ndarray, cmap='winter', mesh_color='white', show_mesh=True, actually_show=True): """ Show with data """ - raise NotImplementedError("Show data not implemented") \ No newline at end of file + + colormap = cm.get_cmap(cmap, 256) + + cmin = np.min(data) + cmax = np.max(data) + + color_index_map = np.array(255 * (data - cmin) / (cmax - cmin), dtype=int) + + for cell, color_index in zip(self.cells, color_index_map): + + color = colormap(color_index) + + plt.fill(self.points[cell, 0], self.points[cell, 1], color=color) + + if show_mesh: + self.show(actually_show=False, color=mesh_color) + + if actually_show: + self.show() \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/voronoi_mesh.py b/sasdata/data_util/slicing/meshes/voronoi_mesh.py index 77db2a687..975488015 100644 --- a/sasdata/data_util/slicing/meshes/voronoi_mesh.py +++ b/sasdata/data_util/slicing/meshes/voronoi_mesh.py @@ -7,10 +7,21 @@ def voronoi_mesh(x, y) -> Mesh: input_data = np.array((x.reshape(-1), y.reshape(-1))).T + + # Need to make sure mesh covers a finite region, probably not important for + # much data stuff, but is important for plotting + # To do this first need to find an appropriate region + # Then we need to adjust the mesh to deal with these points + voronoi = Voronoi(input_data) + + + finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] + + return Mesh(points=voronoi.vertices, cells=finite_cells) diff --git a/sasdata/data_util/slicing/rebinning.py b/sasdata/data_util/slicing/rebinning.py index c6ba6079b..06ace87a9 100644 --- a/sasdata/data_util/slicing/rebinning.py +++ b/sasdata/data_util/slicing/rebinning.py @@ -19,7 +19,6 @@ class CacheData: class Rebinner(): - allowable_orders = [-1,0,1] def __init__(self, order): """ Base class for rebinning methods""" @@ -30,8 +29,9 @@ def __init__(self, order): # Output dependent caching self._input_cache: Optional[CacheData] = None - if order not in Rebinner.allowable_orders: - raise ValueError(f"Expected order to be in {Rebinner.allowable_orders}, got {order}") + if order not in self.allowable_orders: + raise ValueError(f"Expected order to be in {self.allowable_orders}, got {order}") + @abstractmethod def _bin_coordinates(self) -> np.ndarray: @@ -41,6 +41,10 @@ def _bin_coordinates(self) -> np.ndarray: def _bin_mesh(self) -> Mesh: """ Get the meshes used for binning """ + @property + def allowable_orders(self) -> list[int]: + return [-1, 0, 1] + @property def bin_mesh(self): if self._bin_mesh_cache is None: @@ -104,13 +108,22 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n for input_index, output_index, area in zip(merged_to_input, merged_to_output, merged_mesh.areas): output[output_index] += input_data[input_index] * area / input_areas[input_data] + return output elif self._order == 1: + # Linear interpolation requires the following relationship with the data, + # as the input data is the total over the whole input cell, the linear + # interpolation requires continuity at the vertices, and a constraint on the + # integral. + # + # We can take each of the input points, and the associated values, and solve a system + # of linear equations that gives a total value. + raise NotImplementedError("1st order (linear) interpolation currently not implemented") else: - raise ValueError(f"Expected order to be in {Rebinner.allowable_orders}, got {self._order}") + raise ValueError(f"Expected order to be in {self.allowable_orders}, got {self._order}") def sum(self, input_coordinates: np.ndarray, data: np.ndarray) -> np.ndarray: """ Return the summed data in the output bins """ diff --git a/sasdata/data_util/slicing/slicer_demo.py b/sasdata/data_util/slicing/slicer_demo.py new file mode 100644 index 000000000..775c1d9b7 --- /dev/null +++ b/sasdata/data_util/slicing/slicer_demo.py @@ -0,0 +1,19 @@ +""" Dev docs: """ + +import numpy as np + +from sasdata.data_util.slicing.slicers import AnularSector +from sasdata.data_util.slicing.meshes.mesh import Mesh +from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh + + + +if __name__ == "__main__": + + # Demo of sums, annular sector over some not very circular data + + q_range = 1.5 + + test_coordinates = (2*q_range)*(np.random.random((100, 2))-0.5) + + # Demo of averaging, annular sector over ring shaped data \ No newline at end of file diff --git a/sasdata/data_util/slicing/slicers/AnularSector.py b/sasdata/data_util/slicing/slicers/AnularSector.py new file mode 100644 index 000000000..bf3021d02 --- /dev/null +++ b/sasdata/data_util/slicing/slicers/AnularSector.py @@ -0,0 +1,35 @@ +import numpy as np + +from sasdata.data_util.slicing.rebinning import Rebinner +from sasdata.data_util.slicing.meshes.mesh import Mesh + +class AnularSector(Rebinner): + """ A single annular sector (wedge sum)""" + def __init__(self, q0: float, q1: float, phi0: float, phi1: float, order: int=1, points_per_degree: int=2): + super().__init__(order) + + self.q0 = q0 + self.q1 = q1 + self.phi0 = phi0 + self.phi1 = phi1 + + self.points_per_degree = points_per_degree + + def _bin_mesh(self) -> Mesh: + + n_points = 1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi + + angles = np.linspace(self.phi0, self.phi1, n_points) + + row1 = self.q0 * np.array([np.cos(angles), np.sin(angles)]) + row2 = self.q1 * np.array([np.cos(angles), np.sin(angles)])[:, ::-1] + + points = np.concatenate((row1, row2), axis=1) + + cells = [i for i in range(2*n_points)] + + return Mesh(points=points, cells=cells) + + def _bin_coordinates(self) -> np.ndarray: + return np.array([], dtype=float) + From b130f4affe4c15702c275c430d9c99c576312596 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 28 Sep 2023 12:59:02 +0100 Subject: [PATCH 620/675] Voronoi mesh edges and ordering --- sasdata/data_util/slicing/meshes/mesh.py | 16 +++- .../data_util/slicing/meshes/voronoi_mesh.py | 80 +++++++++++++++++-- test/slicers/meshes_for_testing.py | 46 +++++++---- 3 files changed, 114 insertions(+), 28 deletions(-) diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py index cad7b5ffd..05f4d33bc 100644 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -76,7 +76,7 @@ def __init__(self, def find_locations(self, points): """ Find indices of cells containing the input points """ - + @property @@ -98,7 +98,7 @@ def areas(self): areas.append(0.5*np.abs(a_times_2)) # Save in cache - self._areas = np.ndarray(areas) + self._areas = np.array(areas) # Return cache return self._areas @@ -126,11 +126,21 @@ def show(self, actually_show=True, show_labels=False, **kwargs): if actually_show: plt.show() - def show_data(self, data: np.ndarray, cmap='winter', mesh_color='white', show_mesh=True, actually_show=True): + def show_data(self, + data: np.ndarray, + cmap='winter', + mesh_color='white', + show_mesh=True, + actually_show=True, + density=False): + """ Show with data """ colormap = cm.get_cmap(cmap, 256) + if density: + data = data / self.areas + cmin = np.min(data) cmax = np.max(data) diff --git a/sasdata/data_util/slicing/meshes/voronoi_mesh.py b/sasdata/data_util/slicing/meshes/voronoi_mesh.py index 975488015..3497fbba7 100644 --- a/sasdata/data_util/slicing/meshes/voronoi_mesh.py +++ b/sasdata/data_util/slicing/meshes/voronoi_mesh.py @@ -4,28 +4,92 @@ from sasdata.data_util.slicing.meshes.mesh import Mesh -def voronoi_mesh(x, y) -> Mesh: +def voronoi_mesh(x, y, debug_plot=False) -> Mesh: + """ Create a mesh based on a voronoi diagram of points """ input_data = np.array((x.reshape(-1), y.reshape(-1))).T # Need to make sure mesh covers a finite region, probably not important for # much data stuff, but is important for plotting - # To do this first need to find an appropriate region - # Then we need to adjust the mesh to deal with these points + # + # * We want the cells at the edge of the mesh to have a reasonable size, definitely not infinite + # * The exact size doesn't matter that much + # * It should work well with a grid, but also + # * ...it should be robust so that if the data isn't on a grid, it doesn't cause any serious problems + # + # Plan: Create a square border of points that are totally around the points, this is + # at the distance it would be if it was an extra row of grid points + # to do this we'll need + # 1) an estimate of the grid spacing + # 2) the bounding box of the grid + # + # Use the median area of finite voronoi cells as an estimate voronoi = Voronoi(input_data) + finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] + premesh = Mesh(points=voronoi.vertices, cells=finite_cells) + area_spacing = np.median(premesh.areas) + gap = np.sqrt(area_spacing) + # Bounding box is easy + x_min, y_min = np.min(input_data, axis=0) + x_max, y_max = np.max(input_data, axis=0) + # Create a border + n_x = np.round((x_max - x_min)/gap).astype(int) + n_y = np.round((y_max - y_min)/gap).astype(int) - finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] + top_bottom_xs = np.linspace(x_min - gap, x_max + gap, n_x + 3) + left_right_ys = np.linspace(y_min, y_max, n_y + 1) + top = np.array([top_bottom_xs, (y_max + gap) * np.ones_like(top_bottom_xs)]) + bottom = np.array([top_bottom_xs, (y_min - gap) * np.ones_like(top_bottom_xs)]) + left = np.array([(x_min - gap) * np.ones_like(left_right_ys), left_right_ys]) + right = np.array([(x_max + gap) * np.ones_like(left_right_ys), left_right_ys]) + added_points = np.concatenate((top, bottom, left, right), axis=1).T - return Mesh(points=voronoi.vertices, cells=finite_cells) + if debug_plot: + import matplotlib.pyplot as plt + plt.scatter(x, y) + plt.scatter(added_points[:, 0], added_points[:, 1]) + plt.show() + new_points = np.concatenate((input_data, added_points), axis=0) + voronoi = Voronoi(new_points) -if __name__ == "__main__": + # Remove the cells that correspond to the added edge points, + # Because the points on the edge of the square are (weakly) convex, these + # regions be infinite + + # finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] + + # ... however, we can just use .region_points + input_regions = voronoi.point_region[:input_data.shape[0]] + cells = [voronoi.regions[region_index] for region_index in input_regions] + + return Mesh(points=voronoi.vertices, cells=cells) + + +def square_grid_check(): + values = np.linspace(-10, 10, 21) + x, y = np.meshgrid(values, values) + + mesh = voronoi_mesh(x, y) + + mesh.show(show_labels=True) + +def random_grid_check(): + import matplotlib.pyplot as plt points = np.random.random((100, 2)) - mesh = voronoi_mesh(points[:,0], points[:,1]) - mesh.show() \ No newline at end of file + mesh = voronoi_mesh(points[:, 0], points[:, 1], True) + mesh.show(actually_show=False) + plt.scatter(points[:, 0], points[:, 1]) + plt.show() + + +if __name__ == "__main__": + square_grid_check() + # random_grid_check() + diff --git a/test/slicers/meshes_for_testing.py b/test/slicers/meshes_for_testing.py index ff87dc8fc..c7426245b 100644 --- a/test/slicers/meshes_for_testing.py +++ b/test/slicers/meshes_for_testing.py @@ -32,26 +32,38 @@ # Subset of the mappings that meshmerge should include # This can be read off the plots generated below + + expected_shape_mappings = [ - (98, -1), - (99, -1), - (12, 0), + (100, -1), + (152, -1), + (141, -1), + (172, -1), + (170, -1), + (0, -1), (1, -1), - (148, 1), - (149, 1), - (110, 1), - (144, -1), - (123, -1)] - + (8, 0), + (9, 0), + (37, 0), + (83, 0), + (190, 1), + (186, 1), + (189, 1), + (193, 1) +] expected_grid_mappings = [ - (89, 1), - (146, 29), - (66, 34), - (112, 45) + (89, 0), + (90, 1), + (148, 16), + (175, 35), + (60, 47), + (44, 47), + (80, 60) ] + if __name__ == "__main__": import matplotlib.pyplot as plt @@ -62,14 +74,14 @@ combined_mesh.show(actually_show=False, show_labels=True, color='k') grid_mesh.show(actually_show=False, show_labels=True, color='r') - plt.xlim([-4, 4]) - plt.ylim([-4, 4]) + plt.xlim([-5, 5]) + plt.ylim([-5, 5]) plt.figure() combined_mesh.show(actually_show=False, show_labels=True, color='k') shape_mesh.show(actually_show=False, show_labels=True, color='r') - plt.xlim([-4, 4]) - plt.ylim([-4, 4]) + plt.xlim([-5, 5]) + plt.ylim([-5, 5]) plt.show() From c821709a7e10f34c93441f29250b1555c2d12531 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 28 Sep 2023 13:54:43 +0100 Subject: [PATCH 621/675] It works, needs benchmarking --- sasdata/data_util/slicing/meshes/mesh.py | 4 +- sasdata/data_util/slicing/rebinning.py | 28 ++++++++----- sasdata/data_util/slicing/slicer_demo.py | 42 +++++++++++++++++-- .../data_util/slicing/slicers/AnularSector.py | 14 +++++-- 4 files changed, 69 insertions(+), 19 deletions(-) diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py index 05f4d33bc..ba31c51ee 100644 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -130,7 +130,7 @@ def show_data(self, data: np.ndarray, cmap='winter', mesh_color='white', - show_mesh=True, + show_mesh=False, actually_show=True, density=False): @@ -150,7 +150,7 @@ def show_data(self, color = colormap(color_index) - plt.fill(self.points[cell, 0], self.points[cell, 1], color=color) + plt.fill(self.points[cell, 0], self.points[cell, 1], color=color, edgecolor=None) if show_mesh: self.show(actually_show=False, color=mesh_color) diff --git a/sasdata/data_util/slicing/rebinning.py b/sasdata/data_util/slicing/rebinning.py index 06ace87a9..86818f74f 100644 --- a/sasdata/data_util/slicing/rebinning.py +++ b/sasdata/data_util/slicing/rebinning.py @@ -46,12 +46,13 @@ def allowable_orders(self) -> list[int]: return [-1, 0, 1] @property - def bin_mesh(self): + def bin_mesh(self) -> Mesh: + if self._bin_mesh_cache is None: bin_mesh = self._bin_mesh() - self._data_mesh_cache = bin_mesh - else: - return self._bin_mesh_cache + self._bin_mesh_cache = bin_mesh + + return self._bin_mesh_cache def _post_processing(self, coordinates, values) -> tuple[np.ndarray, np.ndarray]: """ Perform post-processing on the mesh binned values """ @@ -95,7 +96,7 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n input_coordinates_mesh=input_coordinate_mesh, merged_mesh_data=merge_data) - merged_mesh, merged_to_input, merged_to_output = merge_data + merged_mesh, merged_to_output, merged_to_input = merge_data # Calculate values according to the order parameter @@ -105,8 +106,15 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n input_areas = input_coordinate_mesh.areas output = np.zeros(self.bin_mesh.n_cells, dtype=float) + print(np.max(merged_to_input)) + print(np.max(merged_to_output)) + for input_index, output_index, area in zip(merged_to_input, merged_to_output, merged_mesh.areas): - output[output_index] += input_data[input_index] * area / input_areas[input_data] + if input_index == -1 or output_index == -1: + # merged region does not correspond to anything of interest + continue + + output[output_index] += input_data[input_index] * area / input_areas[input_index] return output @@ -125,9 +133,9 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n else: raise ValueError(f"Expected order to be in {self.allowable_orders}, got {self._order}") - def sum(self, input_coordinates: np.ndarray, data: np.ndarray) -> np.ndarray: + def sum(self, x: np.ndarray, y: np.ndarray, data: np.ndarray) -> np.ndarray: """ Return the summed data in the output bins """ - return self._calculate(input_coordinates, data) + return self._calculate(np.array((x.reshape(-1), y.reshape(-1))).T, data) def error_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: raise NotImplementedError("Error propagation not implemented yet") @@ -135,7 +143,7 @@ def error_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, error def resolution_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: raise NotImplementedError("Resolution propagation not implemented yet") - def average(self, input_coordinates: np.ndarray, data: np.ndarray) -> np.ndarray: + def average(self, x: np.ndarray, y: np.ndarray, data: np.ndarray) -> np.ndarray: """ Return the averaged data in the output bins """ - return self._calculate(input_coordinates, data) / self.bin_mesh.areas + return self._calculate(np.array((x, y)).T, data) / self.bin_mesh.areas diff --git a/sasdata/data_util/slicing/slicer_demo.py b/sasdata/data_util/slicing/slicer_demo.py index 775c1d9b7..e76e1c4f4 100644 --- a/sasdata/data_util/slicing/slicer_demo.py +++ b/sasdata/data_util/slicing/slicer_demo.py @@ -1,19 +1,53 @@ -""" Dev docs: """ +""" Dev docs: Demo to show the behaviour of the re-binning methods """ import numpy as np -from sasdata.data_util.slicing.slicers import AnularSector +import matplotlib.pyplot as plt + +from sasdata.data_util.slicing.slicers.AnularSector import AnularSector from sasdata.data_util.slicing.meshes.mesh import Mesh from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh if __name__ == "__main__": + q_range = 1.5 + + + x = (2*q_range)*(np.random.random(400)-0.5) + y = (2*q_range)*(np.random.random(400)-0.5) + + display_mesh = voronoi_mesh(x, y) # Demo of sums, annular sector over some not very circular data - q_range = 1.5 - test_coordinates = (2*q_range)*(np.random.random((100, 2))-0.5) + def lobe_test_function(x, y): + return 1 + np.sin(x*np.pi/q_range)*np.sin(y*np.pi/q_range) + + + random_lobe_data = lobe_test_function(x, y) + + plt.figure("Input Dataset 1") + display_mesh.show_data(random_lobe_data, actually_show=False) + + data_order_0 = [] + + for index, size in enumerate(np.linspace(0.1, 1, 100)): + q0 = 0.75 - 0.6*size + q1 = 0.75 + 0.6*size + phi0 = np.pi/2 - size + phi1 = np.pi/2 + size + + rebinner = AnularSector(q0, q1, phi0, phi1, order=0) + + data_order_0.append(rebinner.sum(x, y, random_lobe_data)) + + if index % 10 == 0: + plt.figure("Regions") + rebinner.bin_mesh.show(actually_show=False) + + plt.show() + # Demo of averaging, annular sector over ring shaped data \ No newline at end of file diff --git a/sasdata/data_util/slicing/slicers/AnularSector.py b/sasdata/data_util/slicing/slicers/AnularSector.py index bf3021d02..e9f13774b 100644 --- a/sasdata/data_util/slicing/slicers/AnularSector.py +++ b/sasdata/data_util/slicing/slicers/AnularSector.py @@ -17,19 +17,27 @@ def __init__(self, q0: float, q1: float, phi0: float, phi1: float, order: int=1, def _bin_mesh(self) -> Mesh: - n_points = 1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi + n_points = int(1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi) angles = np.linspace(self.phi0, self.phi1, n_points) row1 = self.q0 * np.array([np.cos(angles), np.sin(angles)]) row2 = self.q1 * np.array([np.cos(angles), np.sin(angles)])[:, ::-1] - points = np.concatenate((row1, row2), axis=1) + points = np.concatenate((row1, row2), axis=1).T - cells = [i for i in range(2*n_points)] + cells = [[i for i in range(2*n_points)]] return Mesh(points=points, cells=cells) def _bin_coordinates(self) -> np.ndarray: return np.array([], dtype=float) + +def main(): + """ Just show a random example""" + AnularSector(1, 2, 1, 2).bin_mesh.show() + + +if __name__ == "__main__": + main() \ No newline at end of file From b0fecd7a8f2ab3cdda80b92c27787a5c11c478d5 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 29 Sep 2023 14:47:01 +0100 Subject: [PATCH 622/675] Much faster assignment/merge method --- sasdata/data_util/slicing/meshes/mesh.py | 93 ++++++++++++- sasdata/data_util/slicing/meshes/meshmerge.py | 124 +++++++++++------- sasdata/data_util/slicing/rebinning.py | 7 +- sasdata/data_util/slicing/slicer_demo.py | 8 +- test/slicers/meshes_for_testing.py | 30 ++++- 5 files changed, 201 insertions(+), 61 deletions(-) diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py index ba31c51ee..6b4df930c 100644 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -51,19 +51,24 @@ def __init__(self, edge_lookup = {edge: i for i, edge in enumerate(self.edges)} self.cells_to_edges = [] + self.cells_to_edges_signs = [] for cell in cells: this_cell_data = [] + this_sign_data = [] for a, b in closed_loop_edges(cell): # make sure the representation is unique if a > b: this_cell_data.append(edge_lookup[(a, b)]) + this_sign_data.append(1) else: this_cell_data.append(edge_lookup[(b, a)]) + this_sign_data.append(-1) self.cells_to_edges.append(this_cell_data) + self.cells_to_edges_signs.append(this_sign_data) # Counts for elements self.n_points = self.points.shape[0] @@ -73,11 +78,6 @@ def __init__(self, # Areas self._areas = None - def find_locations(self, points): - """ Find indices of cells containing the input points """ - - - @property def areas(self): @@ -126,6 +126,71 @@ def show(self, actually_show=True, show_labels=False, **kwargs): if actually_show: plt.show() + def locate_points(self, x: np.ndarray, y: np.ndarray): + """ Find the cells that contain the specified points""" + + x = x.reshape(-1) + y = y.reshape(-1) + + xy = np.concatenate(([x], [y]), axis=1) + + # The most simple implementation is not particularly fast, especially in python + # + # Less obvious, but hopefully faster strategy + # + # Ultimately, checking the inclusion of a point within a polygon + # requires checking the crossings of a half line with the polygon's + # edges. + # + # A fairly efficient thing to do is to check every edge for crossing + # the axis parallel lines x=point_x. + # Then these edges that cross can map back to the polygons they're in + # and a final check for inclusion can be done with the edge sign property + # and some explicit checking of the + # + # Basic idea is: + # 1) build a matrix for each point-edge pair + # True if the edge crosses the half-line above a point + # 2) for each cell get the winding number by evaluating the + # sum of the component edges, weighted 1/-1 according to direction + + + edges = np.array(self.edges) + + edge_xy_1 = self.points[edges[:, 0], :] + edge_xy_2 = self.points[edges[:, 1], :] + + edge_x_1 = edge_xy_1[:, 0] + edge_x_2 = edge_xy_2[:, 0] + + + + # Make an n_edges-by-n_inputs boolean matrix that indicates which of the + # edges cross x=points_x line + crossers = np.logical_xor( + edge_x_1.reshape(-1, 1) < x.reshape(1, -1), + edge_x_2.reshape(-1, 1) < x.reshape(1, -1)) + + # Calculate the gradients, some might be infs, but none that matter will be + # TODO: Disable warnings + gradients = (edge_xy_2[:, 1] - edge_xy_1[:, 1]) / (edge_xy_2[:, 0] - edge_xy_1[:, 0]) + + # Distance to crossing points edge 0 + delta_x = x.reshape(1, -1) - edge_x_1.reshape(-1, 1) + + # Signed distance from point to y (doesn't really matter which sign) + delta_y = gradients.reshape(-1, 1) * delta_x + edge_xy_1[:, 1:] - y.reshape(1, -1) + + score_matrix = np.logical_and(delta_y > 0, crossers) + + output = -np.ones(len(x), dtype=int) + for cell_index, (cell_edges, sign) in enumerate(zip(self.cells_to_edges, self.cells_to_edges_signs)): + cell_score = np.sum(score_matrix[cell_edges, :] * np.array(sign).reshape(-1, 1), axis=0) + points_in_cell = np.abs(cell_score) == 1 + output[points_in_cell] = cell_index + + return output + def show_data(self, data: np.ndarray, cmap='winter', @@ -156,4 +221,20 @@ def show_data(self, self.show(actually_show=False, color=mesh_color) if actually_show: - self.show() \ No newline at end of file + self.show() + + +if __name__ == "__main__": + from test.slicers.meshes_for_testing import location_test_mesh, location_test_points_x, location_test_points_y + + cell_indices = location_test_mesh.locate_points(location_test_points_x, location_test_points_y) + + print(cell_indices) + + for i in range(location_test_mesh.n_cells): + inds = cell_indices == i + plt.scatter( + location_test_points_x.reshape(-1)[inds], + location_test_points_y.reshape(-1)[inds]) + + location_test_mesh.show() \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/meshmerge.py b/sasdata/data_util/slicing/meshes/meshmerge.py index 3ce52ba56..2524c5169 100644 --- a/sasdata/data_util/slicing/meshes/meshmerge.py +++ b/sasdata/data_util/slicing/meshes/meshmerge.py @@ -5,6 +5,8 @@ from sasdata.data_util.slicing.meshes.util import closed_loop_edges +import time + def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: """ Take two lists of polygons and find their intersections @@ -21,6 +23,8 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] """ + t0 = time.time() + # Find intersections of all edges in mesh one with edges in mesh two new_x = [] @@ -89,6 +93,8 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] new_y.append(y) + t1 = time.time() + print("Edge intersections:", t1 - t0) # Build list of all input points, in a way that we can check for coincident points @@ -108,6 +114,11 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] output_mesh = delaunay_mesh(points[:, 0], points[:, 1]) + + t2 = time.time() + print("Delaunay:", t2 - t1) + + # Find centroids of all output triangles, and find which source cells they belong to ## step 1) Assign -1 to all cells of original meshes @@ -120,57 +131,72 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] centroid = np.sum(output_mesh.points[cell, :]/3, axis=0) centroids.append(centroid) - ## step 3) Perform checks based on winding number method (see wikipedia Point in Polygon). - for mesh, assignments in [ - (mesh_a, assignments_a), - (mesh_b, assignments_b)]: - - for centroid_index, centroid in enumerate(centroids): - for cell_index, cell in enumerate(mesh.cells): - - # Bounding box check - points = mesh.points[cell, :] - if np.any(centroid < np.min(points, axis=0)): # x or y less than any in polygon - continue + centroids = np.array(centroids) - if np.any(centroid > np.max(points, axis=0)): # x or y greater than any in polygon - continue + t3 = time.time() + print("Centroids:", t3 - t2) - # Winding number check - count directional crossings of vertical half line from centroid - winding_number = 0 - for i1, i2 in closed_loop_edges(cell): - p1 = mesh.points[i1, :] - p2 = mesh.points[i2, :] - # if the section xs do not straddle the x=centroid_x coordinate, then the - # edge cannot cross the half line. - # If it does, then remember which way it was - # * Careful about ends - # * Also, note that the p1[0] == p2[0] -> (no contribution) case is covered by the strict inequality - if p1[0] > centroid[0] >= p2[0]: - left_right = -1 - elif p2[0] > centroid[0] >= p1[0]: - left_right = 1 - else: - continue - - # Find the y point that it crosses x=centroid at - # note: denominator cannot be zero because of strict inequality above - gradient = (p2[1] - p1[1]) / (p2[0] - p1[0]) - x_delta = centroid[0] - p1[0] - y = p1[1] + x_delta * gradient - - if y > centroid[1]: - winding_number += left_right - - - if abs(winding_number) > 0: - # Do assignment of input cell to output triangle index - assignments[centroid_index] = cell_index - - # end cell loop - - # end centroid loop + ## step 3) Perform checks based on winding number method (see wikipedia Point in Polygon). + # + # # TODO: Brute force search is sllllloooooooowwwwww - keeping track of which points are where would be better + # for mesh, assignments in [ + # (mesh_a, assignments_a), + # (mesh_b, assignments_b)]: + # + # for centroid_index, centroid in enumerate(centroids): + # for cell_index, cell in enumerate(mesh.cells): + # + # # Bounding box check + # points = mesh.points[cell, :] + # if np.any(centroid < np.min(points, axis=0)): # x or y less than any in polygon + # continue + # + # if np.any(centroid > np.max(points, axis=0)): # x or y greater than any in polygon + # continue + # + # # Winding number check - count directional crossings of vertical half line from centroid + # winding_number = 0 + # for i1, i2 in closed_loop_edges(cell): + # p1 = mesh.points[i1, :] + # p2 = mesh.points[i2, :] + # + # # if the section xs do not straddle the x=centroid_x coordinate, then the + # # edge cannot cross the half line. + # # If it does, then remember which way it was + # # * Careful about ends + # # * Also, note that the p1[0] == p2[0] -> (no contribution) case is covered by the strict inequality + # if p1[0] > centroid[0] >= p2[0]: + # left_right = -1 + # elif p2[0] > centroid[0] >= p1[0]: + # left_right = 1 + # else: + # continue + # + # # Find the y point that it crosses x=centroid at + # # note: denominator cannot be zero because of strict inequality above + # gradient = (p2[1] - p1[1]) / (p2[0] - p1[0]) + # x_delta = centroid[0] - p1[0] + # y = p1[1] + x_delta * gradient + # + # if y > centroid[1]: + # winding_number += left_right + # + # + # if abs(winding_number) > 0: + # # Do assignment of input cell to output triangle index + # assignments[centroid_index] = cell_index + # break # point is assigned + # + # # end cell loop + # + # # end centroid loop + + assignments_a = mesh_a.locate_points(centroids[:, 0], centroids[:, 1]) + assignments_b = mesh_b.locate_points(centroids[:, 0], centroids[:, 1]) + + t4 = time.time() + print("Assignments:", t4 - t3) return output_mesh, assignments_a, assignments_b @@ -185,7 +211,7 @@ def main(): m2 = voronoi_mesh(np.random.random(n2), np.random.random(n2)) - mesh, _, _ = meshmerge(m1, m2) + mesh, assignement1, assignement2 = meshmerge(m1, m2) mesh.show() diff --git a/sasdata/data_util/slicing/rebinning.py b/sasdata/data_util/slicing/rebinning.py index 86818f74f..7b6eea938 100644 --- a/sasdata/data_util/slicing/rebinning.py +++ b/sasdata/data_util/slicing/rebinning.py @@ -8,6 +8,7 @@ from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh from sasdata.data_util.slicing.meshes.meshmerge import meshmerge +import time @dataclass class CacheData: @@ -99,16 +100,13 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n merged_mesh, merged_to_output, merged_to_input = merge_data # Calculate values according to the order parameter - + t0 = time.time() if self._order == 0: # Based on the overlap of cells only input_areas = input_coordinate_mesh.areas output = np.zeros(self.bin_mesh.n_cells, dtype=float) - print(np.max(merged_to_input)) - print(np.max(merged_to_output)) - for input_index, output_index, area in zip(merged_to_input, merged_to_output, merged_mesh.areas): if input_index == -1 or output_index == -1: # merged region does not correspond to anything of interest @@ -116,6 +114,7 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n output[output_index] += input_data[input_index] * area / input_areas[input_index] + print("Main calc:", time.time() - t0) return output diff --git a/sasdata/data_util/slicing/slicer_demo.py b/sasdata/data_util/slicing/slicer_demo.py index e76e1c4f4..d60c0acd2 100644 --- a/sasdata/data_util/slicing/slicer_demo.py +++ b/sasdata/data_util/slicing/slicer_demo.py @@ -33,7 +33,9 @@ def lobe_test_function(x, y): data_order_0 = [] - for index, size in enumerate(np.linspace(0.1, 1, 100)): + sizes = np.linspace(0.1, 1, 100) + + for index, size in enumerate(sizes): q0 = 0.75 - 0.6*size q1 = 0.75 + 0.6*size phi0 = np.pi/2 - size @@ -47,6 +49,10 @@ def lobe_test_function(x, y): plt.figure("Regions") rebinner.bin_mesh.show(actually_show=False) + plt.figure("Data") + + plt.plot(sizes, data_order_0) + plt.show() diff --git a/test/slicers/meshes_for_testing.py b/test/slicers/meshes_for_testing.py index c7426245b..7cb17b484 100644 --- a/test/slicers/meshes_for_testing.py +++ b/test/slicers/meshes_for_testing.py @@ -62,7 +62,31 @@ (80, 60) ] - +# +# Mesh location tests +# + +location_test_mesh_points = np.array([ + [0, 0], # 0 + [0, 1], # 1 + [0, 2], # 2 + [1, 0], # 3 + [1, 1], # 4 + [1, 2], # 5 + [2, 0], # 6 + [2, 1], # 7 + [2, 2]], dtype=float) + +location_test_mesh_cells = [ + [0, 1, 4, 3], + [1, 2, 5, 4], + [3, 4, 7, 6], + [4, 5, 8, 7]] + +location_test_mesh = Mesh(location_test_mesh_points, location_test_mesh_cells) + +test_coords = 0.25 + 0.5*np.arange(4) +location_test_points_x, location_test_points_y = np.meshgrid(test_coords, test_coords) if __name__ == "__main__": @@ -84,4 +108,8 @@ plt.xlim([-5, 5]) plt.ylim([-5, 5]) + plt.figure() + location_test_mesh.show(actually_show=False, show_labels=True) + plt.scatter(location_test_points_x, location_test_points_y) + plt.show() From bde98851ca217d57e48f282eafffaa0f0d152b5a Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 5 Oct 2023 12:49:13 +0100 Subject: [PATCH 623/675] Significantly faster edge crossing algorithm --- sasdata/data_util/slicing/meshes/meshmerge.py | 90 ++++++++----------- 1 file changed, 37 insertions(+), 53 deletions(-) diff --git a/sasdata/data_util/slicing/meshes/meshmerge.py b/sasdata/data_util/slicing/meshes/meshmerge.py index 2524c5169..c0235dcfe 100644 --- a/sasdata/data_util/slicing/meshes/meshmerge.py +++ b/sasdata/data_util/slicing/meshes/meshmerge.py @@ -26,72 +26,56 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] t0 = time.time() # Find intersections of all edges in mesh one with edges in mesh two + # TODO: Speed this up - new_x = [] - new_y = [] - for edge_a in mesh_a.edges: - for edge_b in mesh_b.edges: + # Fastest way might just be to calculate the intersections of all lines on edges, + # see whether we need filtering afterwards - p1 = mesh_a.points[edge_a[0]] - p2 = mesh_a.points[edge_a[1]] - p3 = mesh_b.points[edge_b[0]] - p4 = mesh_b.points[edge_b[1]] + edges_a = np.array(mesh_a.edges, dtype=int) + edges_b = np.array(mesh_b.edges, dtype=int) - # Bounding box check + edge_a_1 = mesh_a.points[edges_a[:, 0], :] + edge_a_2 = mesh_a.points[edges_a[:, 1], :] + edge_b_1 = mesh_b.points[edges_b[:, 0], :] + edge_b_2 = mesh_b.points[edges_b[:, 1], :] - # First edge entirely to left of other - if max((p1[0], p2[0])) < min((p3[0], p4[0])): - continue + a_grid, b_grid = np.mgrid[0:mesh_a.n_edges, 0:mesh_b.n_edges] + a_grid = a_grid.reshape(-1) + b_grid = b_grid.reshape(-1) - # First edge entirely below other - if max((p1[1], p2[1])) < min((p3[1], p4[1])): - continue - - # First edge entirely to right of other - if min((p1[0], p2[0])) > max((p3[0], p4[0])): - continue - - # First edge entirely above other - if min((p1[1], p2[1])) > max((p3[1], p4[1])): - continue - - # - # Parametric description of intersection in terms of position along lines - # - # Simultaneous eqns (to reflect current wiki notation) - # s(x2 - x1) - t(x4 - x3) = x3 - x1 - # s(y2 - y1) - t(y4 - y3) = y3 - y1 - # - # in matrix form: - # m.(s,t) = v - # + p1 = edge_a_1[a_grid, :] + p2 = edge_a_2[a_grid, :] + p3 = edge_b_1[b_grid, :] + p4 = edge_b_2[b_grid, :] + # + # Solve the equations + # + # z_a1 + s delta_z_a = z_b1 + t delta_z_b + # + # for z = (x, y) + # - m = np.array([ - [p2[0] - p1[0], p3[0] - p4[0]], - [p2[1] - p1[1], p3[1] - p4[1]]]) + start_point_diff = p1 - p3 - v = np.array([p3[0] - p1[0], p3[1] - p1[1]]) + delta1 = p2 - p1 + delta3 = p4 - p3 - if np.linalg.det(m) == 0: - # Lines don't intersect, or are colinear in a way that doesn't matter - continue + deltas = np.concatenate(([-delta1], [delta3]), axis=0) + deltas = np.moveaxis(deltas, 0, 2) - st = np.linalg.solve(m, v) + st = np.linalg.solve(deltas, start_point_diff) - # As the purpose of this is finding new points for the merged mesh, we don't - # want new points if they are right at the end of the lines, hence non-strict - # inequalities here - if np.any(st <= 0) or np.any(st >= 1): - # Exclude intection points, that are not on the *segments* - continue + # Find the points where s and t are in (0, 1) - x = p1[0] + (p2[0] - p1[0])*st[0] - y = p1[1] + (p2[1] - p1[1])*st[0] + intersection_inds = np.logical_and( + np.logical_and(0 < st[:, 0], st[:, 0] < 1), + np.logical_and(0 < st[:, 1], st[:, 1] < 1)) - new_x.append(x) - new_y.append(y) + start_points_for_intersections = p1[intersection_inds, :] + deltas_for_intersections = delta1[intersection_inds, :] + points_to_add = start_points_for_intersections + st[intersection_inds, 0].reshape(-1,1) * deltas_for_intersections t1 = time.time() print("Edge intersections:", t1 - t0) @@ -102,7 +86,7 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] points = np.concatenate(( mesh_a.points, mesh_b.points, - np.array((new_x, new_y)).T + points_to_add )) From 52b5edd7b309976398900c6c3d6e46a357064189 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 5 Oct 2023 13:57:08 +0100 Subject: [PATCH 624/675] Demo --- sasdata/data_util/slicing/meshes/mesh.py | 2 + sasdata/data_util/slicing/meshes/meshmerge.py | 68 ++--------- .../data_util/slicing/meshes/voronoi_mesh.py | 5 +- sasdata/data_util/slicing/rebinning.py | 41 +++---- sasdata/data_util/slicing/slicer_demo.py | 112 ++++++++++++++---- .../data_util/slicing/slicers/AnularSector.py | 6 +- 6 files changed, 126 insertions(+), 108 deletions(-) diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py index 6b4df930c..3ac23dafe 100644 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ b/sasdata/data_util/slicing/meshes/mesh.py @@ -203,6 +203,8 @@ def show_data(self, colormap = cm.get_cmap(cmap, 256) + data = data.reshape(-1) + if density: data = data / self.areas diff --git a/sasdata/data_util/slicing/meshes/meshmerge.py b/sasdata/data_util/slicing/meshes/meshmerge.py index c0235dcfe..161c1e5da 100644 --- a/sasdata/data_util/slicing/meshes/meshmerge.py +++ b/sasdata/data_util/slicing/meshes/meshmerge.py @@ -26,7 +26,6 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] t0 = time.time() # Find intersections of all edges in mesh one with edges in mesh two - # TODO: Speed this up # Fastest way might just be to calculate the intersections of all lines on edges, # see whether we need filtering afterwards @@ -48,6 +47,10 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] p3 = edge_b_1[b_grid, :] p4 = edge_b_2[b_grid, :] + # + # TODO: Investigate whether adding a bounding box check will help with speed, seems likely as most edges wont cross + # + # # Solve the equations # @@ -64,7 +67,9 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] deltas = np.concatenate(([-delta1], [delta3]), axis=0) deltas = np.moveaxis(deltas, 0, 2) - st = np.linalg.solve(deltas, start_point_diff) + non_singular = np.linalg.det(deltas) != 0 + + st = np.linalg.solve(deltas[non_singular], start_point_diff[non_singular]) # Find the points where s and t are in (0, 1) @@ -72,8 +77,8 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] np.logical_and(0 < st[:, 0], st[:, 0] < 1), np.logical_and(0 < st[:, 1], st[:, 1] < 1)) - start_points_for_intersections = p1[intersection_inds, :] - deltas_for_intersections = delta1[intersection_inds, :] + start_points_for_intersections = p1[non_singular][intersection_inds, :] + deltas_for_intersections = delta1[non_singular][intersection_inds, :] points_to_add = start_points_for_intersections + st[intersection_inds, 0].reshape(-1,1) * deltas_for_intersections @@ -121,60 +126,7 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] print("Centroids:", t3 - t2) - ## step 3) Perform checks based on winding number method (see wikipedia Point in Polygon). - # - # # TODO: Brute force search is sllllloooooooowwwwww - keeping track of which points are where would be better - # for mesh, assignments in [ - # (mesh_a, assignments_a), - # (mesh_b, assignments_b)]: - # - # for centroid_index, centroid in enumerate(centroids): - # for cell_index, cell in enumerate(mesh.cells): - # - # # Bounding box check - # points = mesh.points[cell, :] - # if np.any(centroid < np.min(points, axis=0)): # x or y less than any in polygon - # continue - # - # if np.any(centroid > np.max(points, axis=0)): # x or y greater than any in polygon - # continue - # - # # Winding number check - count directional crossings of vertical half line from centroid - # winding_number = 0 - # for i1, i2 in closed_loop_edges(cell): - # p1 = mesh.points[i1, :] - # p2 = mesh.points[i2, :] - # - # # if the section xs do not straddle the x=centroid_x coordinate, then the - # # edge cannot cross the half line. - # # If it does, then remember which way it was - # # * Careful about ends - # # * Also, note that the p1[0] == p2[0] -> (no contribution) case is covered by the strict inequality - # if p1[0] > centroid[0] >= p2[0]: - # left_right = -1 - # elif p2[0] > centroid[0] >= p1[0]: - # left_right = 1 - # else: - # continue - # - # # Find the y point that it crosses x=centroid at - # # note: denominator cannot be zero because of strict inequality above - # gradient = (p2[1] - p1[1]) / (p2[0] - p1[0]) - # x_delta = centroid[0] - p1[0] - # y = p1[1] + x_delta * gradient - # - # if y > centroid[1]: - # winding_number += left_right - # - # - # if abs(winding_number) > 0: - # # Do assignment of input cell to output triangle index - # assignments[centroid_index] = cell_index - # break # point is assigned - # - # # end cell loop - # - # # end centroid loop + ## step 3) Find where points belong based on Mesh classes point location algorithm assignments_a = mesh_a.locate_points(centroids[:, 0], centroids[:, 1]) assignments_b = mesh_b.locate_points(centroids[:, 0], centroids[:, 1]) diff --git a/sasdata/data_util/slicing/meshes/voronoi_mesh.py b/sasdata/data_util/slicing/meshes/voronoi_mesh.py index 3497fbba7..d3eb81d29 100644 --- a/sasdata/data_util/slicing/meshes/voronoi_mesh.py +++ b/sasdata/data_util/slicing/meshes/voronoi_mesh.py @@ -24,6 +24,7 @@ def voronoi_mesh(x, y, debug_plot=False) -> Mesh: # 2) the bounding box of the grid # + # Use the median area of finite voronoi cells as an estimate voronoi = Voronoi(input_data) finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] @@ -37,8 +38,8 @@ def voronoi_mesh(x, y, debug_plot=False) -> Mesh: x_max, y_max = np.max(input_data, axis=0) # Create a border - n_x = np.round((x_max - x_min)/gap).astype(int) - n_y = np.round((y_max - y_min)/gap).astype(int) + n_x = int(np.round((x_max - x_min)/gap)) + n_y = int(np.round((y_max - y_min)/gap)) top_bottom_xs = np.linspace(x_min - gap, x_max + gap, n_x + 3) left_right_ys = np.linspace(y_min, y_max, n_y + 1) diff --git a/sasdata/data_util/slicing/rebinning.py b/sasdata/data_util/slicing/rebinning.py index 7b6eea938..510535a9c 100644 --- a/sasdata/data_util/slicing/rebinning.py +++ b/sasdata/data_util/slicing/rebinning.py @@ -18,21 +18,17 @@ class CacheData: merged_mesh_data: tuple[Mesh, np.ndarray, np.ndarray] # mesh information about the merging -class Rebinner(): +class Rebinner(ABC): - def __init__(self, order): + def __init__(self): """ Base class for rebinning methods""" - self._order = order self._bin_mesh_cache: Optional[Mesh] = None # cached version of the output bin mesh # Output dependent caching self._input_cache: Optional[CacheData] = None - if order not in self.allowable_orders: - raise ValueError(f"Expected order to be in {self.allowable_orders}, got {order}") - @abstractmethod def _bin_coordinates(self) -> np.ndarray: @@ -60,17 +56,22 @@ def _post_processing(self, coordinates, values) -> tuple[np.ndarray, np.ndarray] # Default is to do nothing, override if needed return coordinates, values - def _do_binning(self, data): - """ Main binning algorithm """ - - def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> np.ndarray: + def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray, order: int) -> np.ndarray: """ Main calculation """ - if self._order == -1: + if order == -1: # Construct the input output mapping just based on input points being the output cells, # Equivalent to the original binning method - pass + mesh = self.bin_mesh + bin_identities = mesh.locate_points(input_coordinates[:,0], input_coordinates[:, 1]) + output_data = np.zeros(mesh.n_cells, dtype=float) + + for index, bin in enumerate(bin_identities): + if bin >= 0: + output_data[bin] += input_data[index] + + return output_data else: # Use a mapping based on meshes @@ -87,7 +88,7 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n else: # Calculate mesh data input_coordinate_mesh = voronoi_mesh(input_coordinates[:,0], input_coordinates[:, 1]) - self._data_mesh_cahce = input_coordinate_mesh + self._data_mesh_cache = input_coordinate_mesh merge_data = meshmerge(self.bin_mesh, input_coordinate_mesh) @@ -101,7 +102,7 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n # Calculate values according to the order parameter t0 = time.time() - if self._order == 0: + if order == 0: # Based on the overlap of cells only input_areas = input_coordinate_mesh.areas @@ -118,7 +119,7 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n return output - elif self._order == 1: + elif order == 1: # Linear interpolation requires the following relationship with the data, # as the input data is the total over the whole input cell, the linear # interpolation requires continuity at the vertices, and a constraint on the @@ -130,11 +131,11 @@ def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray) -> n raise NotImplementedError("1st order (linear) interpolation currently not implemented") else: - raise ValueError(f"Expected order to be in {self.allowable_orders}, got {self._order}") + raise ValueError(f"Expected order to be in {self.allowable_orders}, got {order}") - def sum(self, x: np.ndarray, y: np.ndarray, data: np.ndarray) -> np.ndarray: + def sum(self, x: np.ndarray, y: np.ndarray, data: np.ndarray, order: int = 0) -> np.ndarray: """ Return the summed data in the output bins """ - return self._calculate(np.array((x.reshape(-1), y.reshape(-1))).T, data) + return self._calculate(np.array((x.reshape(-1), y.reshape(-1))).T, data.reshape(-1), order) def error_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: raise NotImplementedError("Error propagation not implemented yet") @@ -142,7 +143,7 @@ def error_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, error def resolution_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: raise NotImplementedError("Resolution propagation not implemented yet") - def average(self, x: np.ndarray, y: np.ndarray, data: np.ndarray) -> np.ndarray: + def average(self, x: np.ndarray, y: np.ndarray, data: np.ndarray, order: int = 0) -> np.ndarray: """ Return the averaged data in the output bins """ - return self._calculate(np.array((x, y)).T, data) / self.bin_mesh.areas + return self._calculate(np.array((x, y)).T, data.reshape(-1), order) / self.bin_mesh.areas diff --git a/sasdata/data_util/slicing/slicer_demo.py b/sasdata/data_util/slicing/slicer_demo.py index d60c0acd2..6096ca905 100644 --- a/sasdata/data_util/slicing/slicer_demo.py +++ b/sasdata/data_util/slicing/slicer_demo.py @@ -12,48 +12,110 @@ if __name__ == "__main__": q_range = 1.5 + demo1 = True + demo2 = True + # Demo of sums, annular sector over some not very circular data - x = (2*q_range)*(np.random.random(400)-0.5) - y = (2*q_range)*(np.random.random(400)-0.5) + if demo1: - display_mesh = voronoi_mesh(x, y) + x = (2 * q_range) * (np.random.random(400) - 0.5) + y = (2 * q_range) * (np.random.random(400) - 0.5) - # Demo of sums, annular sector over some not very circular data + display_mesh = voronoi_mesh(x, y) - def lobe_test_function(x, y): - return 1 + np.sin(x*np.pi/q_range)*np.sin(y*np.pi/q_range) + def lobe_test_function(x, y): + return 1 + np.sin(x*np.pi/q_range)*np.sin(y*np.pi/q_range) - random_lobe_data = lobe_test_function(x, y) + random_lobe_data = lobe_test_function(x, y) - plt.figure("Input Dataset 1") - display_mesh.show_data(random_lobe_data, actually_show=False) + plt.figure("Input Dataset 1") + display_mesh.show_data(random_lobe_data, actually_show=False) - data_order_0 = [] + data_order_0 = [] + data_order_neg1 = [] - sizes = np.linspace(0.1, 1, 100) + sizes = np.linspace(0.1, 1, 100) - for index, size in enumerate(sizes): - q0 = 0.75 - 0.6*size - q1 = 0.75 + 0.6*size - phi0 = np.pi/2 - size - phi1 = np.pi/2 + size + for index, size in enumerate(sizes): + q0 = 0.75 - 0.6*size + q1 = 0.75 + 0.6*size + phi0 = np.pi/2 - size + phi1 = np.pi/2 + size - rebinner = AnularSector(q0, q1, phi0, phi1, order=0) + rebinner = AnularSector(q0, q1, phi0, phi1) - data_order_0.append(rebinner.sum(x, y, random_lobe_data)) + data_order_neg1.append(rebinner.sum(x, y, random_lobe_data, order=-1)) + data_order_0.append(rebinner.sum(x, y, random_lobe_data, order=0)) - if index % 10 == 0: - plt.figure("Regions") - rebinner.bin_mesh.show(actually_show=False) + if index % 10 == 0: + plt.figure("Regions 1") + rebinner.bin_mesh.show(actually_show=False) - plt.figure("Data") + plt.title("Regions") - plt.plot(sizes, data_order_0) + plt.figure("Sum of region, dataset 1") - plt.show() + plt.plot(sizes, data_order_neg1) + plt.plot(sizes, data_order_0) + + plt.legend(["Order -1", "Order 0"]) + plt.title("Sum over region") + + + # Demo of averaging, annular sector over ring shaped data + + if demo2: + + x, y = np.meshgrid(np.linspace(-q_range, q_range, 41), np.linspace(-q_range, q_range, 41)) + x = x.reshape(-1) + y = y.reshape(-1) + + display_mesh = voronoi_mesh(x, y) + + + def ring_test_function(x, y): + r = np.sqrt(x**2 + y**2) + return np.log(np.sinc(r*1.5)**2) + + + grid_ring_data = ring_test_function(x, y) + plt.figure("Input Dataset 2") + display_mesh.show_data(grid_ring_data, actually_show=False) + + data_order_0 = [] + data_order_neg1 = [] + + sizes = np.linspace(0.1, 1, 100) + + for index, size in enumerate(sizes): + q0 = 0.25 + q1 = 1.25 + + phi0 = np.pi/2 - size + phi1 = np.pi/2 + size + + rebinner = AnularSector(q0, q1, phi0, phi1) + + data_order_neg1.append(rebinner.average(x, y, grid_ring_data, order=-1)) + data_order_0.append(rebinner.average(x, y, grid_ring_data, order=0)) + + if index % 10 == 0: + plt.figure("Regions 2") + rebinner.bin_mesh.show(actually_show=False) + + plt.title("Regions") + + plt.figure("Average of region 2") + + plt.plot(sizes, data_order_neg1) + plt.plot(sizes, data_order_0) + + plt.legend(["Order -1", "Order 0"]) + plt.title("Sum over region") + + plt.show() - # Demo of averaging, annular sector over ring shaped data \ No newline at end of file diff --git a/sasdata/data_util/slicing/slicers/AnularSector.py b/sasdata/data_util/slicing/slicers/AnularSector.py index e9f13774b..6d034dad3 100644 --- a/sasdata/data_util/slicing/slicers/AnularSector.py +++ b/sasdata/data_util/slicing/slicers/AnularSector.py @@ -5,8 +5,8 @@ class AnularSector(Rebinner): """ A single annular sector (wedge sum)""" - def __init__(self, q0: float, q1: float, phi0: float, phi1: float, order: int=1, points_per_degree: int=2): - super().__init__(order) + def __init__(self, q0: float, q1: float, phi0: float, phi1: float, points_per_degree: int=2): + super().__init__() self.q0 = q0 self.q1 = q1 @@ -17,7 +17,7 @@ def __init__(self, q0: float, q1: float, phi0: float, phi1: float, order: int=1, def _bin_mesh(self) -> Mesh: - n_points = int(1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi) + n_points = np.max([int(1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi), 2]) angles = np.linspace(self.phi0, self.phi1, n_points) From 875818df28b38033cc4b2807934adf1229e5c929 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 15 Oct 2024 17:45:32 +0100 Subject: [PATCH 625/675] Notes --- sasdata/transforms/rebinning.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index 3335216ac..d5120b709 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -26,7 +26,11 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], order: InterpolationOptions = InterpolationOptions.LINEAR, is_density=False): + """ Calculate the matrix that converts values recorded at points specified by input_axis to + values recorded at points specified by output_axis""" + # We want the input values in terms of the output units, will implicitly check compatability + # TODO: incorporate mask working_units = output_axis.units @@ -136,6 +140,8 @@ def calculate_interpolation_matrix_2d_axis_axis(input_1: Quantity[ArrayLike], order: InterpolationOptions = InterpolationOptions.LINEAR, is_density: bool = False): + # If it wasn't for the mask, this would be the same as just two sets of 1D interpolation + match order: case InterpolationOptions.NEAREST_NEIGHBOUR: pass From ccf9337fa5539d054e3b3cfaa0fecd45ff288102 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 15 Oct 2024 17:46:18 +0100 Subject: [PATCH 626/675] No error --- sasdata/transforms/rebinning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index d5120b709..662a0b132 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -177,7 +177,7 @@ def calculate_interpolation_matrix(input_axes: list[Quantity[ArrayLike]], if len(output_axes) != len(input_axes): # Input or output axes might be 2D matrices - + pass From 5ff8041c530434992f4aeb0be1dfde690b03234f Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 22 Oct 2024 15:36:48 +0100 Subject: [PATCH 627/675] Interpolation stuff --- sasdata/manual_tests/interpolation.py | 4 ++-- sasdata/quantities/math.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sasdata/manual_tests/interpolation.py b/sasdata/manual_tests/interpolation.py index 9edee8a2e..c46078ba7 100644 --- a/sasdata/manual_tests/interpolation.py +++ b/sasdata/manual_tests/interpolation.py @@ -34,11 +34,11 @@ def linear_interpolation_check(): quantity_plot(new_x, new_y) - # print(new_y.history.summary()) + print(new_y.history.summary()) plt.show() -linear_interpolation_check() +linear_interpolation_check() \ No newline at end of file diff --git a/sasdata/quantities/math.py b/sasdata/quantities/math.py index d252ccc0d..6ef5b2983 100644 --- a/sasdata/quantities/math.py +++ b/sasdata/quantities/math.py @@ -2,4 +2,3 @@ # TODO Implementations for trig and exp # TODO Implementations for linear algebra stuff - From 5c2ffcb4075e5b8c5bd65b6592ff1d1170644b55 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 17 Oct 2024 14:21:59 +0100 Subject: [PATCH 628/675] Moving some things around --- sasdata/data_util/slicing/__init__.py | 0 sasdata/data_util/slicing/geometry.py | 0 sasdata/data_util/slicing/meshes/__init__.py | 0 .../data_util/slicing/meshes/delaunay_mesh.py | 32 --- sasdata/data_util/slicing/meshes/mesh.py | 242 ------------------ sasdata/data_util/slicing/meshes/meshmerge.py | 156 ----------- sasdata/data_util/slicing/meshes/util.py | 10 - .../data_util/slicing/meshes/voronoi_mesh.py | 96 ------- sasdata/data_util/slicing/rebinning.py | 149 ----------- sasdata/data_util/slicing/sample_polygons.py | 31 --- sasdata/data_util/slicing/slicer_demo.py | 121 --------- .../data_util/slicing/slicers/AnularSector.py | 43 ---- sasdata/data_util/slicing/transforms.py | 58 ----- sasdata/transforms/rebinning.py | 14 +- test/slicers/meshes_for_testing.py | 6 +- test/slicers/utest_meshmerge.py | 2 +- 16 files changed, 16 insertions(+), 944 deletions(-) delete mode 100644 sasdata/data_util/slicing/__init__.py delete mode 100644 sasdata/data_util/slicing/geometry.py delete mode 100644 sasdata/data_util/slicing/meshes/__init__.py delete mode 100644 sasdata/data_util/slicing/meshes/delaunay_mesh.py delete mode 100644 sasdata/data_util/slicing/meshes/mesh.py delete mode 100644 sasdata/data_util/slicing/meshes/meshmerge.py delete mode 100644 sasdata/data_util/slicing/meshes/util.py delete mode 100644 sasdata/data_util/slicing/meshes/voronoi_mesh.py delete mode 100644 sasdata/data_util/slicing/rebinning.py delete mode 100644 sasdata/data_util/slicing/sample_polygons.py delete mode 100644 sasdata/data_util/slicing/slicer_demo.py delete mode 100644 sasdata/data_util/slicing/slicers/AnularSector.py delete mode 100644 sasdata/data_util/slicing/transforms.py diff --git a/sasdata/data_util/slicing/__init__.py b/sasdata/data_util/slicing/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/sasdata/data_util/slicing/geometry.py b/sasdata/data_util/slicing/geometry.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/sasdata/data_util/slicing/meshes/__init__.py b/sasdata/data_util/slicing/meshes/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/sasdata/data_util/slicing/meshes/delaunay_mesh.py b/sasdata/data_util/slicing/meshes/delaunay_mesh.py deleted file mode 100644 index 45e208786..000000000 --- a/sasdata/data_util/slicing/meshes/delaunay_mesh.py +++ /dev/null @@ -1,32 +0,0 @@ -import numpy as np -from scipy.spatial import Delaunay - -from sasdata.data_util.slicing.meshes.mesh import Mesh - -def delaunay_mesh(x, y) -> Mesh: - """ Create a triangulated mesh based on input points """ - - input_data = np.array((x, y)).T - delaunay = Delaunay(input_data) - - return Mesh(points=input_data, cells=delaunay.simplices) - - -if __name__ == "__main__": - import matplotlib.pyplot as plt - - points = np.random.random((100, 2)) - mesh = delaunay_mesh(points[:,0], points[:,1]) - mesh.show(actually_show=False) - - print(mesh.cells[50]) - - # pick random cell to show - for cell in mesh.cells_to_edges[10]: - a, b = mesh.edges[cell] - plt.plot( - [mesh.points[a][0], mesh.points[b][0]], - [mesh.points[a][1], mesh.points[b][1]], - color='r') - - plt.show() diff --git a/sasdata/data_util/slicing/meshes/mesh.py b/sasdata/data_util/slicing/meshes/mesh.py deleted file mode 100644 index 3ac23dafe..000000000 --- a/sasdata/data_util/slicing/meshes/mesh.py +++ /dev/null @@ -1,242 +0,0 @@ -from typing import Sequence - -import numpy as np - -import matplotlib.pyplot as plt -from matplotlib import cm -from matplotlib.collections import LineCollection - -from sasdata.data_util.slicing.meshes.util import closed_loop_edges - -class Mesh: - def __init__(self, - points: np.ndarray, - cells: Sequence[Sequence[int]]): - - """ - Object representing a mesh. - - Parameters are the values: - mesh points - map from edge to points - map from cells to edges - - it is done this way to ensure a non-redundant representation of cells and edges, - however there are no checks for the topology of the mesh, this is assumed to be done by - whatever creates it. There are also no checks for ordering of cells. - - :param points: points in 2D forming vertices of the mesh - :param cells: ordered lists of indices of points forming each cell (face) - - """ - - self.points = points - self.cells = cells - - # Get edges - - edges = set() - for cell_index, cell in enumerate(cells): - - for a, b in closed_loop_edges(cell): - # make sure the representation is unique - if a > b: - edges.add((a, b)) - else: - edges.add((b, a)) - - self.edges = list(edges) - - # Associate edges with faces - - edge_lookup = {edge: i for i, edge in enumerate(self.edges)} - self.cells_to_edges = [] - self.cells_to_edges_signs = [] - - for cell in cells: - - this_cell_data = [] - this_sign_data = [] - - for a, b in closed_loop_edges(cell): - # make sure the representation is unique - if a > b: - this_cell_data.append(edge_lookup[(a, b)]) - this_sign_data.append(1) - else: - this_cell_data.append(edge_lookup[(b, a)]) - this_sign_data.append(-1) - - self.cells_to_edges.append(this_cell_data) - self.cells_to_edges_signs.append(this_sign_data) - - # Counts for elements - self.n_points = self.points.shape[0] - self.n_edges = len(self.edges) - self.n_cells = len(self.cells) - - # Areas - self._areas = None - - - @property - def areas(self): - """ Areas of cells """ - - if self._areas is None: - # Calculate areas - areas = [] - for cell in self.cells: - # Use triangle shoelace formula, basically calculate the - # determinant based on of triangles with one point at 0,0 - a_times_2 = 0.0 - for i1, i2 in closed_loop_edges(cell): - p1 = self.points[i1, :] - p2 = self.points[i2, :] - a_times_2 += p1[0]*p2[1] - p1[1]*p2[0] - - areas.append(0.5*np.abs(a_times_2)) - - # Save in cache - self._areas = np.array(areas) - - # Return cache - return self._areas - - - def show(self, actually_show=True, show_labels=False, **kwargs): - """ Show on a plot """ - ax = plt.gca() - segments = [[self.points[edge[0]], self.points[edge[1]]] for edge in self.edges] - line_collection = LineCollection(segments=segments, **kwargs) - ax.add_collection(line_collection) - - if show_labels: - text_color = kwargs["color"] if "color" in kwargs else 'k' - for i, cell in enumerate(self.cells): - xy = np.sum(self.points[cell, :], axis=0)/len(cell) - ax.text(xy[0], xy[1], str(i), horizontalalignment="center", verticalalignment="center", color=text_color) - - x_limits = [np.min(self.points[:,0]), np.max(self.points[:,0])] - y_limits = [np.min(self.points[:,1]), np.max(self.points[:,1])] - - plt.xlim(x_limits) - plt.ylim(y_limits) - - if actually_show: - plt.show() - - def locate_points(self, x: np.ndarray, y: np.ndarray): - """ Find the cells that contain the specified points""" - - x = x.reshape(-1) - y = y.reshape(-1) - - xy = np.concatenate(([x], [y]), axis=1) - - # The most simple implementation is not particularly fast, especially in python - # - # Less obvious, but hopefully faster strategy - # - # Ultimately, checking the inclusion of a point within a polygon - # requires checking the crossings of a half line with the polygon's - # edges. - # - # A fairly efficient thing to do is to check every edge for crossing - # the axis parallel lines x=point_x. - # Then these edges that cross can map back to the polygons they're in - # and a final check for inclusion can be done with the edge sign property - # and some explicit checking of the - # - # Basic idea is: - # 1) build a matrix for each point-edge pair - # True if the edge crosses the half-line above a point - # 2) for each cell get the winding number by evaluating the - # sum of the component edges, weighted 1/-1 according to direction - - - edges = np.array(self.edges) - - edge_xy_1 = self.points[edges[:, 0], :] - edge_xy_2 = self.points[edges[:, 1], :] - - edge_x_1 = edge_xy_1[:, 0] - edge_x_2 = edge_xy_2[:, 0] - - - - # Make an n_edges-by-n_inputs boolean matrix that indicates which of the - # edges cross x=points_x line - crossers = np.logical_xor( - edge_x_1.reshape(-1, 1) < x.reshape(1, -1), - edge_x_2.reshape(-1, 1) < x.reshape(1, -1)) - - # Calculate the gradients, some might be infs, but none that matter will be - # TODO: Disable warnings - gradients = (edge_xy_2[:, 1] - edge_xy_1[:, 1]) / (edge_xy_2[:, 0] - edge_xy_1[:, 0]) - - # Distance to crossing points edge 0 - delta_x = x.reshape(1, -1) - edge_x_1.reshape(-1, 1) - - # Signed distance from point to y (doesn't really matter which sign) - delta_y = gradients.reshape(-1, 1) * delta_x + edge_xy_1[:, 1:] - y.reshape(1, -1) - - score_matrix = np.logical_and(delta_y > 0, crossers) - - output = -np.ones(len(x), dtype=int) - for cell_index, (cell_edges, sign) in enumerate(zip(self.cells_to_edges, self.cells_to_edges_signs)): - cell_score = np.sum(score_matrix[cell_edges, :] * np.array(sign).reshape(-1, 1), axis=0) - points_in_cell = np.abs(cell_score) == 1 - output[points_in_cell] = cell_index - - return output - - def show_data(self, - data: np.ndarray, - cmap='winter', - mesh_color='white', - show_mesh=False, - actually_show=True, - density=False): - - """ Show with data """ - - colormap = cm.get_cmap(cmap, 256) - - data = data.reshape(-1) - - if density: - data = data / self.areas - - cmin = np.min(data) - cmax = np.max(data) - - color_index_map = np.array(255 * (data - cmin) / (cmax - cmin), dtype=int) - - for cell, color_index in zip(self.cells, color_index_map): - - color = colormap(color_index) - - plt.fill(self.points[cell, 0], self.points[cell, 1], color=color, edgecolor=None) - - if show_mesh: - self.show(actually_show=False, color=mesh_color) - - if actually_show: - self.show() - - -if __name__ == "__main__": - from test.slicers.meshes_for_testing import location_test_mesh, location_test_points_x, location_test_points_y - - cell_indices = location_test_mesh.locate_points(location_test_points_x, location_test_points_y) - - print(cell_indices) - - for i in range(location_test_mesh.n_cells): - inds = cell_indices == i - plt.scatter( - location_test_points_x.reshape(-1)[inds], - location_test_points_y.reshape(-1)[inds]) - - location_test_mesh.show() \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/meshmerge.py b/sasdata/data_util/slicing/meshes/meshmerge.py deleted file mode 100644 index 161c1e5da..000000000 --- a/sasdata/data_util/slicing/meshes/meshmerge.py +++ /dev/null @@ -1,156 +0,0 @@ -import numpy as np - -from sasdata.data_util.slicing.meshes.mesh import Mesh -from sasdata.data_util.slicing.meshes.delaunay_mesh import delaunay_mesh -from sasdata.data_util.slicing.meshes.util import closed_loop_edges - - -import time - -def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: - """ Take two lists of polygons and find their intersections - - Polygons in each of the input variables should not overlap i.e. a point in space should be assignable to - at most one polygon in mesh_a and at most one polygon in mesh_b - - Mesh topology should be sensible, otherwise bad things might happen, also, the cells of the input meshes - must be in order (which is assumed by the mesh class constructor anyway). - - :returns: - 1) A triangulated mesh based on both sets of polygons together - 2) The indices of the mesh_a polygon that corresponds to each triangle, -1 for nothing - 3) The indices of the mesh_b polygon that corresponds to each triangle, -1 for nothing - - """ - - t0 = time.time() - - # Find intersections of all edges in mesh one with edges in mesh two - - # Fastest way might just be to calculate the intersections of all lines on edges, - # see whether we need filtering afterwards - - edges_a = np.array(mesh_a.edges, dtype=int) - edges_b = np.array(mesh_b.edges, dtype=int) - - edge_a_1 = mesh_a.points[edges_a[:, 0], :] - edge_a_2 = mesh_a.points[edges_a[:, 1], :] - edge_b_1 = mesh_b.points[edges_b[:, 0], :] - edge_b_2 = mesh_b.points[edges_b[:, 1], :] - - a_grid, b_grid = np.mgrid[0:mesh_a.n_edges, 0:mesh_b.n_edges] - a_grid = a_grid.reshape(-1) - b_grid = b_grid.reshape(-1) - - p1 = edge_a_1[a_grid, :] - p2 = edge_a_2[a_grid, :] - p3 = edge_b_1[b_grid, :] - p4 = edge_b_2[b_grid, :] - - # - # TODO: Investigate whether adding a bounding box check will help with speed, seems likely as most edges wont cross - # - - # - # Solve the equations - # - # z_a1 + s delta_z_a = z_b1 + t delta_z_b - # - # for z = (x, y) - # - - start_point_diff = p1 - p3 - - delta1 = p2 - p1 - delta3 = p4 - p3 - - deltas = np.concatenate(([-delta1], [delta3]), axis=0) - deltas = np.moveaxis(deltas, 0, 2) - - non_singular = np.linalg.det(deltas) != 0 - - st = np.linalg.solve(deltas[non_singular], start_point_diff[non_singular]) - - # Find the points where s and t are in (0, 1) - - intersection_inds = np.logical_and( - np.logical_and(0 < st[:, 0], st[:, 0] < 1), - np.logical_and(0 < st[:, 1], st[:, 1] < 1)) - - start_points_for_intersections = p1[non_singular][intersection_inds, :] - deltas_for_intersections = delta1[non_singular][intersection_inds, :] - - points_to_add = start_points_for_intersections + st[intersection_inds, 0].reshape(-1,1) * deltas_for_intersections - - t1 = time.time() - print("Edge intersections:", t1 - t0) - - # Build list of all input points, in a way that we can check for coincident points - - - points = np.concatenate(( - mesh_a.points, - mesh_b.points, - points_to_add - )) - - - # Remove coincident points - - points = np.unique(points, axis=0) - - # Triangulate based on these intersections - - output_mesh = delaunay_mesh(points[:, 0], points[:, 1]) - - - t2 = time.time() - print("Delaunay:", t2 - t1) - - - # Find centroids of all output triangles, and find which source cells they belong to - - ## step 1) Assign -1 to all cells of original meshes - assignments_a = -np.ones(output_mesh.n_cells, dtype=int) - assignments_b = -np.ones(output_mesh.n_cells, dtype=int) - - ## step 2) Find centroids of triangulated mesh (just needs to be a point inside, but this is a good one) - centroids = [] - for cell in output_mesh.cells: - centroid = np.sum(output_mesh.points[cell, :]/3, axis=0) - centroids.append(centroid) - - centroids = np.array(centroids) - - t3 = time.time() - print("Centroids:", t3 - t2) - - - ## step 3) Find where points belong based on Mesh classes point location algorithm - - assignments_a = mesh_a.locate_points(centroids[:, 0], centroids[:, 1]) - assignments_b = mesh_b.locate_points(centroids[:, 0], centroids[:, 1]) - - t4 = time.time() - print("Assignments:", t4 - t3) - - return output_mesh, assignments_a, assignments_b - - -def main(): - from voronoi_mesh import voronoi_mesh - - n1 = 100 - n2 = 100 - - m1 = voronoi_mesh(np.random.random(n1), np.random.random(n1)) - m2 = voronoi_mesh(np.random.random(n2), np.random.random(n2)) - - - mesh, assignement1, assignement2 = meshmerge(m1, m2) - - mesh.show() - - -if __name__ == "__main__": - main() diff --git a/sasdata/data_util/slicing/meshes/util.py b/sasdata/data_util/slicing/meshes/util.py deleted file mode 100644 index b78a9e076..000000000 --- a/sasdata/data_util/slicing/meshes/util.py +++ /dev/null @@ -1,10 +0,0 @@ -from typing import Sequence, TypeVar - -T = TypeVar("T") - -def closed_loop_edges(values: Sequence[T]) -> tuple[T, T]: - """ Generator for a closed loop of edge pairs """ - for pair in zip(values, values[1:]): - yield pair - - yield values[-1], values[0] \ No newline at end of file diff --git a/sasdata/data_util/slicing/meshes/voronoi_mesh.py b/sasdata/data_util/slicing/meshes/voronoi_mesh.py deleted file mode 100644 index d3eb81d29..000000000 --- a/sasdata/data_util/slicing/meshes/voronoi_mesh.py +++ /dev/null @@ -1,96 +0,0 @@ -import numpy as np -from scipy.spatial import Voronoi - - -from sasdata.data_util.slicing.meshes.mesh import Mesh - -def voronoi_mesh(x, y, debug_plot=False) -> Mesh: - """ Create a mesh based on a voronoi diagram of points """ - - input_data = np.array((x.reshape(-1), y.reshape(-1))).T - - # Need to make sure mesh covers a finite region, probably not important for - # much data stuff, but is important for plotting - # - # * We want the cells at the edge of the mesh to have a reasonable size, definitely not infinite - # * The exact size doesn't matter that much - # * It should work well with a grid, but also - # * ...it should be robust so that if the data isn't on a grid, it doesn't cause any serious problems - # - # Plan: Create a square border of points that are totally around the points, this is - # at the distance it would be if it was an extra row of grid points - # to do this we'll need - # 1) an estimate of the grid spacing - # 2) the bounding box of the grid - # - - - # Use the median area of finite voronoi cells as an estimate - voronoi = Voronoi(input_data) - finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] - premesh = Mesh(points=voronoi.vertices, cells=finite_cells) - - area_spacing = np.median(premesh.areas) - gap = np.sqrt(area_spacing) - - # Bounding box is easy - x_min, y_min = np.min(input_data, axis=0) - x_max, y_max = np.max(input_data, axis=0) - - # Create a border - n_x = int(np.round((x_max - x_min)/gap)) - n_y = int(np.round((y_max - y_min)/gap)) - - top_bottom_xs = np.linspace(x_min - gap, x_max + gap, n_x + 3) - left_right_ys = np.linspace(y_min, y_max, n_y + 1) - - top = np.array([top_bottom_xs, (y_max + gap) * np.ones_like(top_bottom_xs)]) - bottom = np.array([top_bottom_xs, (y_min - gap) * np.ones_like(top_bottom_xs)]) - left = np.array([(x_min - gap) * np.ones_like(left_right_ys), left_right_ys]) - right = np.array([(x_max + gap) * np.ones_like(left_right_ys), left_right_ys]) - - added_points = np.concatenate((top, bottom, left, right), axis=1).T - - if debug_plot: - import matplotlib.pyplot as plt - plt.scatter(x, y) - plt.scatter(added_points[:, 0], added_points[:, 1]) - plt.show() - - new_points = np.concatenate((input_data, added_points), axis=0) - voronoi = Voronoi(new_points) - - # Remove the cells that correspond to the added edge points, - # Because the points on the edge of the square are (weakly) convex, these - # regions be infinite - - # finite_cells = [region for region in voronoi.regions if -1 not in region and len(region) > 0] - - # ... however, we can just use .region_points - input_regions = voronoi.point_region[:input_data.shape[0]] - cells = [voronoi.regions[region_index] for region_index in input_regions] - - return Mesh(points=voronoi.vertices, cells=cells) - - -def square_grid_check(): - values = np.linspace(-10, 10, 21) - x, y = np.meshgrid(values, values) - - mesh = voronoi_mesh(x, y) - - mesh.show(show_labels=True) - -def random_grid_check(): - import matplotlib.pyplot as plt - points = np.random.random((100, 2)) - mesh = voronoi_mesh(points[:, 0], points[:, 1], True) - mesh.show(actually_show=False) - plt.scatter(points[:, 0], points[:, 1]) - plt.show() - - -if __name__ == "__main__": - square_grid_check() - # random_grid_check() - diff --git a/sasdata/data_util/slicing/rebinning.py b/sasdata/data_util/slicing/rebinning.py deleted file mode 100644 index 510535a9c..000000000 --- a/sasdata/data_util/slicing/rebinning.py +++ /dev/null @@ -1,149 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Optional -from dataclasses import dataclass - -import numpy as np - -from sasdata.data_util.slicing.meshes.mesh import Mesh -from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh -from sasdata.data_util.slicing.meshes.meshmerge import meshmerge - -import time - -@dataclass -class CacheData: - """ Data cached for repeated calculations with the same coordinates """ - input_coordinates: np.ndarray # Input data - input_coordinates_mesh: Mesh # Mesh of the input data - merged_mesh_data: tuple[Mesh, np.ndarray, np.ndarray] # mesh information about the merging - - -class Rebinner(ABC): - - - def __init__(self): - """ Base class for rebinning methods""" - - self._bin_mesh_cache: Optional[Mesh] = None # cached version of the output bin mesh - - # Output dependent caching - self._input_cache: Optional[CacheData] = None - - - @abstractmethod - def _bin_coordinates(self) -> np.ndarray: - """ Coordinates for the output bins """ - - @abstractmethod - def _bin_mesh(self) -> Mesh: - """ Get the meshes used for binning """ - - @property - def allowable_orders(self) -> list[int]: - return [-1, 0, 1] - - @property - def bin_mesh(self) -> Mesh: - - if self._bin_mesh_cache is None: - bin_mesh = self._bin_mesh() - self._bin_mesh_cache = bin_mesh - - return self._bin_mesh_cache - - def _post_processing(self, coordinates, values) -> tuple[np.ndarray, np.ndarray]: - """ Perform post-processing on the mesh binned values """ - # Default is to do nothing, override if needed - return coordinates, values - - def _calculate(self, input_coordinates: np.ndarray, input_data: np.ndarray, order: int) -> np.ndarray: - """ Main calculation """ - - if order == -1: - # Construct the input output mapping just based on input points being the output cells, - # Equivalent to the original binning method - - mesh = self.bin_mesh - bin_identities = mesh.locate_points(input_coordinates[:,0], input_coordinates[:, 1]) - output_data = np.zeros(mesh.n_cells, dtype=float) - - for index, bin in enumerate(bin_identities): - if bin >= 0: - output_data[bin] += input_data[index] - - return output_data - - else: - # Use a mapping based on meshes - - # Either create de-cache the appropriate mesh - # Why not use a hash? Hashing takes time, equality checks are pretty fast, need to check equality - # when there is a hit anyway in case of very rare chance of collision, hits are the most common case, - # we want it to work 100% of the time, not 99.9999% - if self._input_cache is not None and np.all(self._input_cache.input_coordinates == input_coordinates): - - input_coordinate_mesh = self._input_cache.input_coordinates_mesh - merge_data = self._input_cache.merged_mesh_data - - else: - # Calculate mesh data - input_coordinate_mesh = voronoi_mesh(input_coordinates[:,0], input_coordinates[:, 1]) - self._data_mesh_cache = input_coordinate_mesh - - merge_data = meshmerge(self.bin_mesh, input_coordinate_mesh) - - # Cache mesh data - self._input_cache = CacheData( - input_coordinates=input_coordinates, - input_coordinates_mesh=input_coordinate_mesh, - merged_mesh_data=merge_data) - - merged_mesh, merged_to_output, merged_to_input = merge_data - - # Calculate values according to the order parameter - t0 = time.time() - if order == 0: - # Based on the overlap of cells only - - input_areas = input_coordinate_mesh.areas - output = np.zeros(self.bin_mesh.n_cells, dtype=float) - - for input_index, output_index, area in zip(merged_to_input, merged_to_output, merged_mesh.areas): - if input_index == -1 or output_index == -1: - # merged region does not correspond to anything of interest - continue - - output[output_index] += input_data[input_index] * area / input_areas[input_index] - - print("Main calc:", time.time() - t0) - - return output - - elif order == 1: - # Linear interpolation requires the following relationship with the data, - # as the input data is the total over the whole input cell, the linear - # interpolation requires continuity at the vertices, and a constraint on the - # integral. - # - # We can take each of the input points, and the associated values, and solve a system - # of linear equations that gives a total value. - - raise NotImplementedError("1st order (linear) interpolation currently not implemented") - - else: - raise ValueError(f"Expected order to be in {self.allowable_orders}, got {order}") - - def sum(self, x: np.ndarray, y: np.ndarray, data: np.ndarray, order: int = 0) -> np.ndarray: - """ Return the summed data in the output bins """ - return self._calculate(np.array((x.reshape(-1), y.reshape(-1))).T, data.reshape(-1), order) - - def error_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: - raise NotImplementedError("Error propagation not implemented yet") - - def resolution_propagate(self, input_coordinates: np.ndarray, data: np.ndarray, errors) -> np.ndarray: - raise NotImplementedError("Resolution propagation not implemented yet") - - def average(self, x: np.ndarray, y: np.ndarray, data: np.ndarray, order: int = 0) -> np.ndarray: - """ Return the averaged data in the output bins """ - return self._calculate(np.array((x, y)).T, data.reshape(-1), order) / self.bin_mesh.areas - diff --git a/sasdata/data_util/slicing/sample_polygons.py b/sasdata/data_util/slicing/sample_polygons.py deleted file mode 100644 index e12fb1e80..000000000 --- a/sasdata/data_util/slicing/sample_polygons.py +++ /dev/null @@ -1,31 +0,0 @@ -import numpy as np - -def wedge(q0, q1, theta0, theta1, clockwise=False, n_points_per_degree=2): - - # Traverse a rectangle in curvilinear coordinates (q0, theta0), (q0, theta1), (q1, theta1), (q1, theta0) - if clockwise: - if theta1 > theta0: - theta0 += 2*np.pi - - else: - if theta0 > theta1: - theta1 += 2*np.pi - - subtended_angle = np.abs(theta1 - theta0) - n_points = int(subtended_angle*180*n_points_per_degree/np.pi)+1 - - angles = np.linspace(theta0, theta1, n_points) - - xs = np.concatenate((q0*np.cos(angles), q1*np.cos(angles[::-1]))) - ys = np.concatenate((q0*np.sin(angles), q1*np.sin(angles[::-1]))) - - return np.array((xs, ys)).T - - -if __name__ == "__main__": - import matplotlib.pyplot as plt - xy = wedge(0.3, 0.6, 2, 3) - - plt.plot(xy[:,0], xy[:,1]) - plt.show() - diff --git a/sasdata/data_util/slicing/slicer_demo.py b/sasdata/data_util/slicing/slicer_demo.py deleted file mode 100644 index 6096ca905..000000000 --- a/sasdata/data_util/slicing/slicer_demo.py +++ /dev/null @@ -1,121 +0,0 @@ -""" Dev docs: Demo to show the behaviour of the re-binning methods """ - -import numpy as np - -import matplotlib.pyplot as plt - -from sasdata.data_util.slicing.slicers.AnularSector import AnularSector -from sasdata.data_util.slicing.meshes.mesh import Mesh -from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh - - - -if __name__ == "__main__": - q_range = 1.5 - demo1 = True - demo2 = True - - # Demo of sums, annular sector over some not very circular data - - if demo1: - - x = (2 * q_range) * (np.random.random(400) - 0.5) - y = (2 * q_range) * (np.random.random(400) - 0.5) - - display_mesh = voronoi_mesh(x, y) - - - def lobe_test_function(x, y): - return 1 + np.sin(x*np.pi/q_range)*np.sin(y*np.pi/q_range) - - - random_lobe_data = lobe_test_function(x, y) - - plt.figure("Input Dataset 1") - display_mesh.show_data(random_lobe_data, actually_show=False) - - data_order_0 = [] - data_order_neg1 = [] - - sizes = np.linspace(0.1, 1, 100) - - for index, size in enumerate(sizes): - q0 = 0.75 - 0.6*size - q1 = 0.75 + 0.6*size - phi0 = np.pi/2 - size - phi1 = np.pi/2 + size - - rebinner = AnularSector(q0, q1, phi0, phi1) - - data_order_neg1.append(rebinner.sum(x, y, random_lobe_data, order=-1)) - data_order_0.append(rebinner.sum(x, y, random_lobe_data, order=0)) - - if index % 10 == 0: - plt.figure("Regions 1") - rebinner.bin_mesh.show(actually_show=False) - - plt.title("Regions") - - plt.figure("Sum of region, dataset 1") - - plt.plot(sizes, data_order_neg1) - plt.plot(sizes, data_order_0) - - plt.legend(["Order -1", "Order 0"]) - plt.title("Sum over region") - - - # Demo of averaging, annular sector over ring shaped data - - if demo2: - - x, y = np.meshgrid(np.linspace(-q_range, q_range, 41), np.linspace(-q_range, q_range, 41)) - x = x.reshape(-1) - y = y.reshape(-1) - - display_mesh = voronoi_mesh(x, y) - - - def ring_test_function(x, y): - r = np.sqrt(x**2 + y**2) - return np.log(np.sinc(r*1.5)**2) - - - grid_ring_data = ring_test_function(x, y) - - plt.figure("Input Dataset 2") - display_mesh.show_data(grid_ring_data, actually_show=False) - - data_order_0 = [] - data_order_neg1 = [] - - sizes = np.linspace(0.1, 1, 100) - - for index, size in enumerate(sizes): - q0 = 0.25 - q1 = 1.25 - - phi0 = np.pi/2 - size - phi1 = np.pi/2 + size - - rebinner = AnularSector(q0, q1, phi0, phi1) - - data_order_neg1.append(rebinner.average(x, y, grid_ring_data, order=-1)) - data_order_0.append(rebinner.average(x, y, grid_ring_data, order=0)) - - if index % 10 == 0: - plt.figure("Regions 2") - rebinner.bin_mesh.show(actually_show=False) - - plt.title("Regions") - - plt.figure("Average of region 2") - - plt.plot(sizes, data_order_neg1) - plt.plot(sizes, data_order_0) - - plt.legend(["Order -1", "Order 0"]) - plt.title("Sum over region") - - plt.show() - diff --git a/sasdata/data_util/slicing/slicers/AnularSector.py b/sasdata/data_util/slicing/slicers/AnularSector.py deleted file mode 100644 index 6d034dad3..000000000 --- a/sasdata/data_util/slicing/slicers/AnularSector.py +++ /dev/null @@ -1,43 +0,0 @@ -import numpy as np - -from sasdata.data_util.slicing.rebinning import Rebinner -from sasdata.data_util.slicing.meshes.mesh import Mesh - -class AnularSector(Rebinner): - """ A single annular sector (wedge sum)""" - def __init__(self, q0: float, q1: float, phi0: float, phi1: float, points_per_degree: int=2): - super().__init__() - - self.q0 = q0 - self.q1 = q1 - self.phi0 = phi0 - self.phi1 = phi1 - - self.points_per_degree = points_per_degree - - def _bin_mesh(self) -> Mesh: - - n_points = np.max([int(1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi), 2]) - - angles = np.linspace(self.phi0, self.phi1, n_points) - - row1 = self.q0 * np.array([np.cos(angles), np.sin(angles)]) - row2 = self.q1 * np.array([np.cos(angles), np.sin(angles)])[:, ::-1] - - points = np.concatenate((row1, row2), axis=1).T - - cells = [[i for i in range(2*n_points)]] - - return Mesh(points=points, cells=cells) - - def _bin_coordinates(self) -> np.ndarray: - return np.array([], dtype=float) - - -def main(): - """ Just show a random example""" - AnularSector(1, 2, 1, 2).bin_mesh.show() - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/sasdata/data_util/slicing/transforms.py b/sasdata/data_util/slicing/transforms.py deleted file mode 100644 index d04742d3c..000000000 --- a/sasdata/data_util/slicing/transforms.py +++ /dev/null @@ -1,58 +0,0 @@ -import numpy as np -from scipy.spatial import Voronoi, Delaunay -import matplotlib.pyplot as plt -from matplotlib import cm - - -# Some test data - -qx_base_values = np.linspace(-10, 10, 21) -qy_base_values = np.linspace(-10, 10, 21) - -qx, qy = np.meshgrid(qx_base_values, qy_base_values) - -include = np.logical_not((np.abs(qx) < 2) & (np.abs(qy) < 2)) - -qx = qx[include] -qy = qy[include] - -r = np.sqrt(qx**2 + qy**2) - -data = np.log((1+np.cos(3*r))*np.exp(-r*r)) - -colormap = cm.get_cmap('winter', 256) - -def get_data_mesh(x, y, data): - - input_data = np.array((x, y)).T - voronoi = Voronoi(input_data) - - # plt.scatter(voronoi.vertices[:,0], voronoi.vertices[:,1]) - # plt.scatter(voronoi.points[:,0], voronoi.points[:,1]) - - cmin = np.min(data) - cmax = np.max(data) - - color_index_map = np.array(255 * (data - cmin) / (cmax - cmin), dtype=int) - - for point_index, points in enumerate(voronoi.points): - - region_index = voronoi.point_region[point_index] - region = voronoi.regions[region_index] - - if len(region) > 0: - - if -1 in region: - - pass - - else: - - color = colormap(color_index_map[point_index]) - - circly = region + [region[0]] - plt.fill(voronoi.vertices[circly, 0], voronoi.vertices[circly, 1], color=color, edgecolor="white") - - plt.show() - -get_data_mesh(qx.reshape(-1), qy.reshape(-1), data) \ No newline at end of file diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index 662a0b132..7bdc66209 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -130,7 +130,17 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], raise InterpolationError(f"Unsupported interpolation order: {order}") - return conversion_matrix + if mask is None: + return conversion_matrix, None + else: + # Create a new mask + + # Convert to numerical values + # Conservative masking: anything touched by the previous mask is now masked + new_mask = (np.array(mask, dtype=float) @ conversion_matrix) != 0.0 + + return conversion_matrix, new_mask + def calculate_interpolation_matrix_2d_axis_axis(input_1: Quantity[ArrayLike], input_2: Quantity[ArrayLike], @@ -140,7 +150,7 @@ def calculate_interpolation_matrix_2d_axis_axis(input_1: Quantity[ArrayLike], order: InterpolationOptions = InterpolationOptions.LINEAR, is_density: bool = False): - # If it wasn't for the mask, this would be the same as just two sets of 1D interpolation + # This is just the same 1D matrices things match order: case InterpolationOptions.NEAREST_NEIGHBOUR: diff --git a/test/slicers/meshes_for_testing.py b/test/slicers/meshes_for_testing.py index 7cb17b484..fb346e79c 100644 --- a/test/slicers/meshes_for_testing.py +++ b/test/slicers/meshes_for_testing.py @@ -4,9 +4,9 @@ import numpy as np -from sasdata.data_util.slicing.meshes.voronoi_mesh import voronoi_mesh -from sasdata.data_util.slicing.meshes.mesh import Mesh -from sasdata.data_util.slicing.meshes.meshmerge import meshmerge +from sasdata.slicing.meshes.voronoi_mesh import voronoi_mesh +from sasdata.slicing.meshes.mesh import Mesh +from sasdata.slicing.meshes.meshmerge import meshmerge coords = np.arange(-4, 5) grid_mesh = voronoi_mesh(*np.meshgrid(coords, coords)) diff --git a/test/slicers/utest_meshmerge.py b/test/slicers/utest_meshmerge.py index f745d0250..21071c049 100644 --- a/test/slicers/utest_meshmerge.py +++ b/test/slicers/utest_meshmerge.py @@ -4,7 +4,7 @@ It's pretty hard to test componentwise, but we can do some tests of the general behaviour """ -from sasdata.data_util.slicing.meshes.meshmerge import meshmerge +from sasdata.slicing.meshes import meshmerge from test.slicers.meshes_for_testing import ( grid_mesh, shape_mesh, expected_grid_mappings, expected_shape_mappings) From 59e6913f90519898fd3ce3cb2993c77514e1b6d2 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 21 Oct 2024 14:26:04 +0100 Subject: [PATCH 629/675] Move math and operations into quantity --- sasdata/model_requirements.py | 2 +- sasdata/quantities/math.py | 4 - sasdata/quantities/operations.py | 821 --------------------- sasdata/quantities/operations_examples.py | 4 +- sasdata/quantities/operations_test.py | 2 +- sasdata/quantities/quantity.py | 857 +++++++++++++++++++++- 6 files changed, 859 insertions(+), 831 deletions(-) delete mode 100644 sasdata/quantities/math.py delete mode 100644 sasdata/quantities/operations.py diff --git a/sasdata/model_requirements.py b/sasdata/model_requirements.py index 40b6ac728..12ad54565 100644 --- a/sasdata/model_requirements.py +++ b/sasdata/model_requirements.py @@ -3,7 +3,7 @@ import numpy as np from sasdata.metadata import Metadata -from sasdata.quantities.operations import Operation +from sasdata.quantities.quantity import Operation @dataclass diff --git a/sasdata/quantities/math.py b/sasdata/quantities/math.py deleted file mode 100644 index 6ef5b2983..000000000 --- a/sasdata/quantities/math.py +++ /dev/null @@ -1,4 +0,0 @@ -""" Math module extended to allow operations on quantities """ - -# TODO Implementations for trig and exp -# TODO Implementations for linear algebra stuff diff --git a/sasdata/quantities/operations.py b/sasdata/quantities/operations.py deleted file mode 100644 index 35f6fc7ef..000000000 --- a/sasdata/quantities/operations.py +++ /dev/null @@ -1,821 +0,0 @@ -from typing import Any, TypeVar, Union -import numpy as np - -import json - -T = TypeVar("T") - -def hash_and_name(hash_or_name: int | str): - """ Infer the name of a variable from a hash, or the hash from the name - - Note: hash_and_name(hash_and_name(number)[1]) is not the identity - however: hash_and_name(hash_and_name(number)) is - """ - - if isinstance(hash_or_name, str): - hash_value = hash(hash_or_name) - name = hash_or_name - - return hash_value, name - - elif isinstance(hash_or_name, int): - hash_value = hash_or_name - name = f"#{hash_or_name}" - - return hash_value, name - - elif isinstance(hash_or_name, tuple): - return hash_or_name - - else: - raise TypeError("Variable name_or_hash_value must be either str or int") - - -class Operation: - - serialisation_name = "unknown" - def summary(self, indent_amount: int = 0, indent: str=" "): - """ Summary of the operation tree""" - - s = f"{indent_amount*indent}{self._summary_open()}(\n" - - for chunk in self._summary_components(): - s += chunk.summary(indent_amount+1, indent) + "\n" - - s += f"{indent_amount*indent})" - - return s - def _summary_open(self): - """ First line of summary """ - - def _summary_components(self) -> list["Operation"]: - return [] - def evaluate(self, variables: dict[int, T]) -> T: - - """ Evaluate this operation """ - - def _derivative(self, hash_value: int) -> "Operation": - """ Get the derivative of this operation """ - - def _clean(self): - """ Clean up this operation - i.e. remove silly things like 1*x """ - return self - - def derivative(self, variable: Union[str, int, "Variable"], simplify=True): - if isinstance(variable, Variable): - hash_value = variable.hash_value - else: - hash_value, _ = hash_and_name(variable) - - derivative = self._derivative(hash_value) - - if not simplify: - return derivative - - derivative_string = derivative.serialise() - - # print("---------------") - # print("Base") - # print("---------------") - # print(derivative.summary()) - - # Inefficient way of doing repeated simplification, but it will work - for i in range(100): # set max iterations - - derivative = derivative._clean() - # - # print("-------------------") - # print("Iteration", i+1) - # print("-------------------") - # print(derivative.summary()) - # print("-------------------") - - new_derivative_string = derivative.serialise() - - if derivative_string == new_derivative_string: - break - - derivative_string = new_derivative_string - - return derivative - - @staticmethod - def deserialise(data: str) -> "Operation": - json_data = json.loads(data) - return Operation.deserialise_json(json_data) - - @staticmethod - def deserialise_json(json_data: dict) -> "Operation": - - operation = json_data["operation"] - parameters = json_data["parameters"] - cls = _serialisation_lookup[operation] - - try: - return cls._deserialise(parameters) - - except NotImplementedError: - raise NotImplementedError(f"No method to deserialise {operation} with {parameters} (cls={cls})") - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - raise NotImplementedError(f"Deserialise not implemented for this class") - - def serialise(self) -> str: - return json.dumps(self._serialise_json()) - - def _serialise_json(self) -> dict[str, Any]: - return {"operation": self.serialisation_name, - "parameters": self._serialise_parameters()} - - def _serialise_parameters(self) -> dict[str, Any]: - raise NotImplementedError("_serialise_parameters not implemented") - - def __eq__(self, other: "Operation"): - return NotImplemented - -class ConstantBase(Operation): - pass - -class AdditiveIdentity(ConstantBase): - - serialisation_name = "zero" - def evaluate(self, variables: dict[int, T]) -> T: - return 0 - - def _derivative(self, hash_value: int) -> Operation: - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return AdditiveIdentity() - - def _serialise_parameters(self) -> dict[str, Any]: - return {} - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}0 [Add.Id.]" - - def __eq__(self, other): - if isinstance(other, AdditiveIdentity): - return True - elif isinstance(other, Constant): - if other.value == 0: - return True - - return False - - - -class MultiplicativeIdentity(ConstantBase): - - serialisation_name = "one" - - def evaluate(self, variables: dict[int, T]) -> T: - return 1 - - def _derivative(self, hash_value: int): - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return MultiplicativeIdentity() - - - def _serialise_parameters(self) -> dict[str, Any]: - return {} - - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}1 [Mul.Id.]" - - def __eq__(self, other): - if isinstance(other, MultiplicativeIdentity): - return True - elif isinstance(other, Constant): - if other.value == 1: - return True - - return False - - -class Constant(ConstantBase): - - serialisation_name = "constant" - def __init__(self, value): - self.value = value - - def summary(self, indent_amount: int = 0, indent: str=" "): - pass - - def evaluate(self, variables: dict[int, T]) -> T: - return self.value - - def _derivative(self, hash_value: int): - return AdditiveIdentity() - - def _clean(self): - - if self.value == 0: - return AdditiveIdentity() - - elif self.value == 1: - return MultiplicativeIdentity() - - else: - return self - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - value = parameters["value"] - return Constant(value) - - - def _serialise_parameters(self) -> dict[str, Any]: - return {"value": self.value} - - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}{self.value}" - - def __eq__(self, other): - if isinstance(other, AdditiveIdentity): - return self.value == 0 - - elif isinstance(other, MultiplicativeIdentity): - return self.value == 1 - - elif isinstance(other, Constant): - if other.value == self.value: - return True - - return False - - -class Variable(Operation): - - serialisation_name = "variable" - def __init__(self, name_or_hash_value: int | str | tuple[int, str]): - self.hash_value, self.name = hash_and_name(name_or_hash_value) - - def evaluate(self, variables: dict[int, T]) -> T: - try: - return variables[self.hash_value] - except KeyError: - raise ValueError(f"Variable dictionary didn't have an entry for {self.name} (hash={self.hash_value})") - - def _derivative(self, hash_value: int) -> Operation: - if hash_value == self.hash_value: - return MultiplicativeIdentity() - else: - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - hash_value = parameters["hash_value"] - name = parameters["name"] - - return Variable((hash_value, name)) - - def _serialise_parameters(self) -> dict[str, Any]: - return {"hash_value": self.hash_value, - "name": self.name} - - def summary(self, indent_amount: int = 0, indent: str=" "): - return f"{indent_amount*indent}{self.name}" - - def __eq__(self, other): - if isinstance(other, Variable): - return self.hash_value == other.hash_value - - return False - -class UnaryOperation(Operation): - - def __init__(self, a: Operation): - self.a = a - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": self.a._serialise_json()} - - def _summary_components(self) -> list["Operation"]: - return [self.a] - - - - -class Neg(UnaryOperation): - - serialisation_name = "neg" - def evaluate(self, variables: dict[int, T]) -> T: - return -self.a.evaluate(variables) - - def _derivative(self, hash_value: int): - return Neg(self.a._derivative(hash_value)) - - def _clean(self): - - clean_a = self.a._clean() - - if isinstance(clean_a, Neg): - # Removes double negations - return clean_a.a - - elif isinstance(clean_a, Constant): - return Constant(-clean_a.value)._clean() - - else: - return Neg(clean_a) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Neg(Operation.deserialise_json(parameters["a"])) - - - def _summary_open(self): - return "Neg" - - def __eq__(self, other): - if isinstance(other, Neg): - return other.a == self.a - - -class Inv(UnaryOperation): - - serialisation_name = "reciprocal" - - def evaluate(self, variables: dict[int, T]) -> T: - return 1/self.a.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Neg(Div(self.a._derivative(hash_value), Mul(self.a, self.a))) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, Inv): - # Removes double negations - return clean_a.a - - elif isinstance(clean_a, Neg): - # cannonicalise 1/-a to -(1/a) - # over multiple iterations this should have the effect of ordering and gathering Neg and Inv - return Neg(Inv(clean_a.a)) - - elif isinstance(clean_a, Constant): - return Constant(1/clean_a.value)._clean() - - else: - return Inv(clean_a) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Inv(Operation.deserialise_json(parameters["a"])) - - def _summary_open(self): - return "Inv" - - - def __eq__(self, other): - if isinstance(other, Inv): - return other.a == self.a - -class BinaryOperation(Operation): - def __init__(self, a: Operation, b: Operation): - self.a = a - self.b = b - - def _clean(self): - return self._clean_ab(self.a._clean(), self.b._clean()) - - def _clean_ab(self, a, b): - raise NotImplementedError("_clean_ab not implemented") - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": self.a._serialise_json(), - "b": self.b._serialise_json()} - - @staticmethod - def _deserialise_ab(parameters) -> tuple[Operation, Operation]: - return (Operation.deserialise_json(parameters["a"]), - Operation.deserialise_json(parameters["b"])) - - - def _summary_components(self) -> list["Operation"]: - return [self.a, self.b] - - def _self_cls(self) -> type: - """ Own class""" - def __eq__(self, other): - if isinstance(other, self._self_cls()): - return other.a == self.a and self.b == other.b - -class Add(BinaryOperation): - - serialisation_name = "add" - - def _self_cls(self) -> type: - return Add - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) + self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add(self.a._derivative(hash_value), self.b._derivative(hash_value)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity): - # Convert 0 + b to b - return b - - elif isinstance(b, AdditiveIdentity): - # Convert a + 0 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"+"b" to "a+b" - return Constant(a.evaluate({}) + b.evaluate({}))._clean() - - elif isinstance(a, Neg): - if isinstance(b, Neg): - # Convert (-a)+(-b) to -(a+b) - return Neg(Add(a.a, b.a)) - else: - # Convert (-a) + b to b-a - return Sub(b, a.a) - - elif isinstance(b, Neg): - # Convert a+(-b) to a-b - return Sub(a, b.a) - - elif a == b: - return Mul(Constant(2), a) - - else: - return Add(a, b) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Add(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Add" - -class Sub(BinaryOperation): - - serialisation_name = "sub" - - - def _self_cls(self) -> type: - return Sub - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) - self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Sub(self.a._derivative(hash_value), self.b._derivative(hash_value)) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity): - # Convert 0 - b to -b - return Neg(b) - - elif isinstance(b, AdditiveIdentity): - # Convert a - 0 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant pair "a" - "b" to "a-b" - return Constant(a.evaluate({}) - b.evaluate({}))._clean() - - elif isinstance(a, Neg): - if isinstance(b, Neg): - # Convert (-a)-(-b) to b-a - return Sub(b.a, a.a) - else: - # Convert (-a)-b to -(a+b) - return Neg(Add(a.a, b)) - - elif isinstance(b, Neg): - # Convert a-(-b) to a+b - return Add(a, b.a) - - elif a == b: - return AdditiveIdentity() - - else: - return Sub(a, b) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Sub(*BinaryOperation._deserialise_ab(parameters)) - - - def _summary_open(self): - return "Sub" - -class Mul(BinaryOperation): - - serialisation_name = "mul" - - - def _self_cls(self) -> type: - return Mul - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) * self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add(Mul(self.a, self.b._derivative(hash_value)), Mul(self.a._derivative(hash_value), self.b)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): - # Convert 0*b or a*0 to 0 - return AdditiveIdentity() - - elif isinstance(a, MultiplicativeIdentity): - # Convert 1*b to b - return b - - elif isinstance(b, MultiplicativeIdentity): - # Convert a*1 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"*"b" to "a*b" - return Constant(a.evaluate({}) * b.evaluate({}))._clean() - - elif isinstance(a, Inv) and isinstance(b, Inv): - return Inv(Mul(a.a, b.a)) - - elif isinstance(a, Inv) and not isinstance(b, Inv): - return Div(b, a.a) - - elif not isinstance(a, Inv) and isinstance(b, Inv): - return Div(a, b.a) - - elif isinstance(a, Neg): - return Neg(Mul(a.a, b)) - - elif isinstance(b, Neg): - return Neg(Mul(a, b.a)) - - elif a == b: - return Pow(a, 2) - - elif isinstance(a, Pow) and a.a == b: - return Pow(b, a.power + 1) - - elif isinstance(b, Pow) and b.a == a: - return Pow(a, b.power + 1) - - elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: - return Pow(a.a, a.power + b.power) - - else: - return Mul(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Mul(*BinaryOperation._deserialise_ab(parameters)) - - - def _summary_open(self): - return "Mul" - -class Div(BinaryOperation): - - serialisation_name = "div" - - - def _self_cls(self) -> type: - return Div - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) / self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Sub(Div(self.a.derivative(hash_value), self.b), - Div(Mul(self.a, self.b.derivative(hash_value)), Mul(self.b, self.b))) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity): - # Convert 0/b to 0 - return AdditiveIdentity() - - elif isinstance(a, MultiplicativeIdentity): - # Convert 1/b to inverse of b - return Inv(b) - - elif isinstance(b, MultiplicativeIdentity): - # Convert a/1 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constants "a"/"b" to "a/b" - return Constant(self.a.evaluate({}) / self.b.evaluate({}))._clean() - - - elif isinstance(a, Inv) and isinstance(b, Inv): - return Div(b.a, a.a) - - elif isinstance(a, Inv) and not isinstance(b, Inv): - return Inv(Mul(a.a, b)) - - elif not isinstance(a, Inv) and isinstance(b, Inv): - return Mul(a, b.a) - - elif a == b: - return MultiplicativeIdentity() - - elif isinstance(a, Pow) and a.a == b: - return Pow(b, a.power - 1) - - elif isinstance(b, Pow) and b.a == a: - return Pow(a, 1 - b.power) - - elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: - return Pow(a.a, a.power - b.power) - - else: - return Div(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Div(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Div" - -class Pow(Operation): - - serialisation_name = "pow" - - def __init__(self, a: Operation, power: float): - self.a = a - self.power = power - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) ** self.power - - def _derivative(self, hash_value: int) -> Operation: - if self.power == 0: - return AdditiveIdentity() - - elif self.power == 1: - return self.a._derivative(hash_value) - - else: - return Mul(Constant(self.power), Mul(Pow(self.a, self.power-1), self.a._derivative(hash_value))) - - def _clean(self) -> Operation: - a = self.a._clean() - - if self.power == 1: - return a - - elif self.power == 0: - return MultiplicativeIdentity() - - elif self.power == -1: - return Inv(a) - - else: - return Pow(a, self.power) - - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": Operation._serialise_json(self.a), - "power": self.power} - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Pow(Operation.deserialise_json(parameters["a"]), parameters["power"]) - - def summary(self, indent_amount: int=0, indent=" "): - return (f"{indent_amount*indent}Pow\n" + - self.a.summary(indent_amount+1, indent) + "\n" + - f"{(indent_amount+1)*indent}{self.power}\n" + - f"{indent_amount*indent})") - - def __eq__(self, other): - if isinstance(other, Pow): - return self.a == other.a and self.power == other.power - - - -# -# Matrix operations -# - -class Transpose(UnaryOperation): - """ Transpose operation - as per numpy""" - - serialisation_name = "transpose" - - def evaluate(self, variables: dict[int, T]) -> T: - return np.transpose(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Transpose(self.a.derivative(hash_value)) # TODO: Check! - - def _clean(self): - clean_a = self.a._clean() - return Transpose(clean_a) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Transpose(Operation.deserialise_json(parameters["a"])) - - def _summary_open(self): - return "Transpose" - - def __eq__(self, other): - if isinstance(other, Transpose): - return other.a == self.a - - -class Dot(BinaryOperation): - """ Dot product - backed by numpy's dot method""" - - serialisation_name = "dot" - - def _self_cls(self) -> type: - return Dot - - def evaluate(self, variables: dict[int, T]) -> T: - return np.dot(self.a.evaluate(variables) + self.b.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Add( - Dot(self.a, - self.b._derivative(hash_value)), - Dot(self.a._derivative(hash_value), - self.b)) - - def _clean_ab(self, a, b): - return Dot(a, b) # Do nothing for now - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Dot(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Dot" - - -# TODO: Add to base operation class, and to quantities -class MatMul(BinaryOperation): - """ Matrix multiplication, using __matmul__ dunder""" - - serialisation_name = "matmul" - - def _self_cls(self) -> type: - return MatMul - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) @ self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add( - MatMul(self.a, - self.b._derivative(hash_value)), - MatMul(self.a._derivative(hash_value), - self.b)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): - # Convert 0*b or a*0 to 0 - return AdditiveIdentity() - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"@"b" to "a@b" - return Constant(a.evaluate({}) @ b.evaluate({}))._clean() - - elif isinstance(a, Neg): - return Neg(Mul(a.a, b)) - - elif isinstance(b, Neg): - return Neg(Mul(a, b.a)) - - return MatMul(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return MatMul(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "MatMul" - - - -_serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, - Variable, - Neg, Inv, - Add, Sub, Mul, Div, Pow, - Transpose, Dot, MatMul] - -_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} diff --git a/sasdata/quantities/operations_examples.py b/sasdata/quantities/operations_examples.py index 6c484eb36..e2e25666f 100644 --- a/sasdata/quantities/operations_examples.py +++ b/sasdata/quantities/operations_examples.py @@ -1,4 +1,4 @@ -from sasdata.quantities.operations import Variable, Mul +from sasdata.quantities.quantity import Variable, Mul x = Variable("x") y = Variable("y") @@ -8,4 +8,4 @@ dfdx = f.derivative(x).derivative(y).derivative(z) -print(dfdx.summary()) \ No newline at end of file +print(dfdx.summary()) diff --git a/sasdata/quantities/operations_test.py b/sasdata/quantities/operations_test.py index 0899eee7f..6767e32a0 100644 --- a/sasdata/quantities/operations_test.py +++ b/sasdata/quantities/operations_test.py @@ -1,6 +1,6 @@ import pytest -from sasdata.quantities.operations import Operation, \ +from sasdata.quantities.quantity import Operation, \ Neg, Inv, \ Add, Sub, Mul, Div, Pow, \ Variable, Constant, AdditiveIdentity, MultiplicativeIdentity diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 7bbd8359a..fd2336075 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -6,13 +6,866 @@ import numpy as np from numpy._typing import ArrayLike -from quantities.operations import Operation, Variable -from quantities import operations, units +from sasdata.quantities import units from sasdata.quantities.units import Unit, NamedUnit import hashlib +from typing import Any, TypeVar, Union +import numpy as np + +import json + +T = TypeVar("T") + + + + + +################### Quantity based operations, need to be here to avoid cyclic dependencies ##################### + + +def transpose(a: Union["Quantity[ArrayLike]", ArrayLike]): + if isinstance(a, Quantity): + return + + +def tensordot(a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, b: Union["Quantity[ArrayLike]", ArrayLike], a_index: int, b_index: int): + pass + + +################### Operation Definitions ####################################### + +def hash_and_name(hash_or_name: int | str): + """ Infer the name of a variable from a hash, or the hash from the name + + Note: hash_and_name(hash_and_name(number)[1]) is not the identity + however: hash_and_name(hash_and_name(number)) is + """ + + if isinstance(hash_or_name, str): + hash_value = hash(hash_or_name) + name = hash_or_name + + return hash_value, name + + elif isinstance(hash_or_name, int): + hash_value = hash_or_name + name = f"#{hash_or_name}" + + return hash_value, name + + elif isinstance(hash_or_name, tuple): + return hash_or_name + + else: + raise TypeError("Variable name_or_hash_value must be either str or int") + + +class Operation: + + serialisation_name = "unknown" + def summary(self, indent_amount: int = 0, indent: str=" "): + """ Summary of the operation tree""" + + s = f"{indent_amount*indent}{self._summary_open()}(\n" + + for chunk in self._summary_components(): + s += chunk.summary(indent_amount+1, indent) + "\n" + + s += f"{indent_amount*indent})" + + return s + def _summary_open(self): + """ First line of summary """ + + def _summary_components(self) -> list["Operation"]: + return [] + def evaluate(self, variables: dict[int, T]) -> T: + + """ Evaluate this operation """ + + def _derivative(self, hash_value: int) -> "Operation": + """ Get the derivative of this operation """ + + def _clean(self): + """ Clean up this operation - i.e. remove silly things like 1*x """ + return self + + def derivative(self, variable: Union[str, int, "Variable"], simplify=True): + if isinstance(variable, Variable): + hash_value = variable.hash_value + else: + hash_value, _ = hash_and_name(variable) + + derivative = self._derivative(hash_value) + + if not simplify: + return derivative + + derivative_string = derivative.serialise() + + # print("---------------") + # print("Base") + # print("---------------") + # print(derivative.summary()) + + # Inefficient way of doing repeated simplification, but it will work + for i in range(100): # set max iterations + + derivative = derivative._clean() + # + # print("-------------------") + # print("Iteration", i+1) + # print("-------------------") + # print(derivative.summary()) + # print("-------------------") + + new_derivative_string = derivative.serialise() + + if derivative_string == new_derivative_string: + break + + derivative_string = new_derivative_string + + return derivative + + @staticmethod + def deserialise(data: str) -> "Operation": + json_data = json.loads(data) + return Operation.deserialise_json(json_data) + + @staticmethod + def deserialise_json(json_data: dict) -> "Operation": + + operation = json_data["operation"] + parameters = json_data["parameters"] + cls = _serialisation_lookup[operation] + + try: + return cls._deserialise(parameters) + + except NotImplementedError: + raise NotImplementedError(f"No method to deserialise {operation} with {parameters} (cls={cls})") + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + raise NotImplementedError(f"Deserialise not implemented for this class") + + def serialise(self) -> str: + return json.dumps(self._serialise_json()) + + def _serialise_json(self) -> dict[str, Any]: + return {"operation": self.serialisation_name, + "parameters": self._serialise_parameters()} + + def _serialise_parameters(self) -> dict[str, Any]: + raise NotImplementedError("_serialise_parameters not implemented") + + def __eq__(self, other: "Operation"): + return NotImplemented + +class ConstantBase(Operation): + pass + +class AdditiveIdentity(ConstantBase): + + serialisation_name = "zero" + def evaluate(self, variables: dict[int, T]) -> T: + return 0 + + def _derivative(self, hash_value: int) -> Operation: + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return AdditiveIdentity() + + def _serialise_parameters(self) -> dict[str, Any]: + return {} + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}0 [Add.Id.]" + + def __eq__(self, other): + if isinstance(other, AdditiveIdentity): + return True + elif isinstance(other, Constant): + if other.value == 0: + return True + + return False + + + +class MultiplicativeIdentity(ConstantBase): + + serialisation_name = "one" + + def evaluate(self, variables: dict[int, T]) -> T: + return 1 + + def _derivative(self, hash_value: int): + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return MultiplicativeIdentity() + + + def _serialise_parameters(self) -> dict[str, Any]: + return {} + + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}1 [Mul.Id.]" + + def __eq__(self, other): + if isinstance(other, MultiplicativeIdentity): + return True + elif isinstance(other, Constant): + if other.value == 1: + return True + + return False + + +class Constant(ConstantBase): + + serialisation_name = "constant" + def __init__(self, value): + self.value = value + + def summary(self, indent_amount: int = 0, indent: str=" "): + pass + + def evaluate(self, variables: dict[int, T]) -> T: + return self.value + + def _derivative(self, hash_value: int): + return AdditiveIdentity() + + def _clean(self): + + if self.value == 0: + return AdditiveIdentity() + + elif self.value == 1: + return MultiplicativeIdentity() + + else: + return self + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + value = parameters["value"] + return Constant(value) + + + def _serialise_parameters(self) -> dict[str, Any]: + return {"value": self.value} + + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}{self.value}" + + def __eq__(self, other): + if isinstance(other, AdditiveIdentity): + return self.value == 0 + + elif isinstance(other, MultiplicativeIdentity): + return self.value == 1 + + elif isinstance(other, Constant): + if other.value == self.value: + return True + + return False + + +class Variable(Operation): + + serialisation_name = "variable" + def __init__(self, name_or_hash_value: int | str | tuple[int, str]): + self.hash_value, self.name = hash_and_name(name_or_hash_value) + + def evaluate(self, variables: dict[int, T]) -> T: + try: + return variables[self.hash_value] + except KeyError: + raise ValueError(f"Variable dictionary didn't have an entry for {self.name} (hash={self.hash_value})") + + def _derivative(self, hash_value: int) -> Operation: + if hash_value == self.hash_value: + return MultiplicativeIdentity() + else: + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + hash_value = parameters["hash_value"] + name = parameters["name"] + + return Variable((hash_value, name)) + + def _serialise_parameters(self) -> dict[str, Any]: + return {"hash_value": self.hash_value, + "name": self.name} + + def summary(self, indent_amount: int = 0, indent: str=" "): + return f"{indent_amount*indent}{self.name}" + + def __eq__(self, other): + if isinstance(other, Variable): + return self.hash_value == other.hash_value + + return False + +class UnaryOperation(Operation): + + def __init__(self, a: Operation): + self.a = a + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": self.a._serialise_json()} + + def _summary_components(self) -> list["Operation"]: + return [self.a] + + + + +class Neg(UnaryOperation): + + serialisation_name = "neg" + def evaluate(self, variables: dict[int, T]) -> T: + return -self.a.evaluate(variables) + + def _derivative(self, hash_value: int): + return Neg(self.a._derivative(hash_value)) + + def _clean(self): + + clean_a = self.a._clean() + + if isinstance(clean_a, Neg): + # Removes double negations + return clean_a.a + + elif isinstance(clean_a, Constant): + return Constant(-clean_a.value)._clean() + + else: + return Neg(clean_a) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Neg(Operation.deserialise_json(parameters["a"])) + + + def _summary_open(self): + return "Neg" + + def __eq__(self, other): + if isinstance(other, Neg): + return other.a == self.a + + +class Inv(UnaryOperation): + + serialisation_name = "reciprocal" + + def evaluate(self, variables: dict[int, T]) -> T: + return 1/self.a.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Neg(Div(self.a._derivative(hash_value), Mul(self.a, self.a))) + + def _clean(self): + clean_a = self.a._clean() + + if isinstance(clean_a, Inv): + # Removes double negations + return clean_a.a + + elif isinstance(clean_a, Neg): + # cannonicalise 1/-a to -(1/a) + # over multiple iterations this should have the effect of ordering and gathering Neg and Inv + return Neg(Inv(clean_a.a)) + + elif isinstance(clean_a, Constant): + return Constant(1/clean_a.value)._clean() + + else: + return Inv(clean_a) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Inv(Operation.deserialise_json(parameters["a"])) + + def _summary_open(self): + return "Inv" + + + def __eq__(self, other): + if isinstance(other, Inv): + return other.a == self.a + +class BinaryOperation(Operation): + def __init__(self, a: Operation, b: Operation): + self.a = a + self.b = b + + def _clean(self): + return self._clean_ab(self.a._clean(), self.b._clean()) + + def _clean_ab(self, a, b): + raise NotImplementedError("_clean_ab not implemented") + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": self.a._serialise_json(), + "b": self.b._serialise_json()} + + @staticmethod + def _deserialise_ab(parameters) -> tuple[Operation, Operation]: + return (Operation.deserialise_json(parameters["a"]), + Operation.deserialise_json(parameters["b"])) + + + def _summary_components(self) -> list["Operation"]: + return [self.a, self.b] + + def _self_cls(self) -> type: + """ Own class""" + def __eq__(self, other): + if isinstance(other, self._self_cls()): + return other.a == self.a and self.b == other.b + +class Add(BinaryOperation): + + serialisation_name = "add" + + def _self_cls(self) -> type: + return Add + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) + self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add(self.a._derivative(hash_value), self.b._derivative(hash_value)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity): + # Convert 0 + b to b + return b + + elif isinstance(b, AdditiveIdentity): + # Convert a + 0 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"+"b" to "a+b" + return Constant(a.evaluate({}) + b.evaluate({}))._clean() + + elif isinstance(a, Neg): + if isinstance(b, Neg): + # Convert (-a)+(-b) to -(a+b) + return Neg(Add(a.a, b.a)) + else: + # Convert (-a) + b to b-a + return Sub(b, a.a) + + elif isinstance(b, Neg): + # Convert a+(-b) to a-b + return Sub(a, b.a) + + elif a == b: + return Mul(Constant(2), a) + + else: + return Add(a, b) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Add(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Add" + +class Sub(BinaryOperation): + + serialisation_name = "sub" + + + def _self_cls(self) -> type: + return Sub + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) - self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Sub(self.a._derivative(hash_value), self.b._derivative(hash_value)) + + def _clean_ab(self, a, b): + if isinstance(a, AdditiveIdentity): + # Convert 0 - b to -b + return Neg(b) + + elif isinstance(b, AdditiveIdentity): + # Convert a - 0 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant pair "a" - "b" to "a-b" + return Constant(a.evaluate({}) - b.evaluate({}))._clean() + + elif isinstance(a, Neg): + if isinstance(b, Neg): + # Convert (-a)-(-b) to b-a + return Sub(b.a, a.a) + else: + # Convert (-a)-b to -(a+b) + return Neg(Add(a.a, b)) + + elif isinstance(b, Neg): + # Convert a-(-b) to a+b + return Add(a, b.a) + + elif a == b: + return AdditiveIdentity() + + else: + return Sub(a, b) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Sub(*BinaryOperation._deserialise_ab(parameters)) + + + def _summary_open(self): + return "Sub" + +class Mul(BinaryOperation): + + serialisation_name = "mul" + + + def _self_cls(self) -> type: + return Mul + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) * self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add(Mul(self.a, self.b._derivative(hash_value)), Mul(self.a._derivative(hash_value), self.b)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): + # Convert 0*b or a*0 to 0 + return AdditiveIdentity() + + elif isinstance(a, MultiplicativeIdentity): + # Convert 1*b to b + return b + + elif isinstance(b, MultiplicativeIdentity): + # Convert a*1 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"*"b" to "a*b" + return Constant(a.evaluate({}) * b.evaluate({}))._clean() + + elif isinstance(a, Inv) and isinstance(b, Inv): + return Inv(Mul(a.a, b.a)) + + elif isinstance(a, Inv) and not isinstance(b, Inv): + return Div(b, a.a) + + elif not isinstance(a, Inv) and isinstance(b, Inv): + return Div(a, b.a) + + elif isinstance(a, Neg): + return Neg(Mul(a.a, b)) + + elif isinstance(b, Neg): + return Neg(Mul(a, b.a)) + + elif a == b: + return Pow(a, 2) + + elif isinstance(a, Pow) and a.a == b: + return Pow(b, a.power + 1) + + elif isinstance(b, Pow) and b.a == a: + return Pow(a, b.power + 1) + + elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: + return Pow(a.a, a.power + b.power) + + else: + return Mul(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Mul(*BinaryOperation._deserialise_ab(parameters)) + + + def _summary_open(self): + return "Mul" + +class Div(BinaryOperation): + + serialisation_name = "div" + + + def _self_cls(self) -> type: + return Div + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) / self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Sub(Div(self.a.derivative(hash_value), self.b), + Div(Mul(self.a, self.b.derivative(hash_value)), Mul(self.b, self.b))) + + def _clean_ab(self, a, b): + if isinstance(a, AdditiveIdentity): + # Convert 0/b to 0 + return AdditiveIdentity() + + elif isinstance(a, MultiplicativeIdentity): + # Convert 1/b to inverse of b + return Inv(b) + + elif isinstance(b, MultiplicativeIdentity): + # Convert a/1 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constants "a"/"b" to "a/b" + return Constant(self.a.evaluate({}) / self.b.evaluate({}))._clean() + + + elif isinstance(a, Inv) and isinstance(b, Inv): + return Div(b.a, a.a) + + elif isinstance(a, Inv) and not isinstance(b, Inv): + return Inv(Mul(a.a, b)) + + elif not isinstance(a, Inv) and isinstance(b, Inv): + return Mul(a, b.a) + + elif a == b: + return MultiplicativeIdentity() + + elif isinstance(a, Pow) and a.a == b: + return Pow(b, a.power - 1) + + elif isinstance(b, Pow) and b.a == a: + return Pow(a, 1 - b.power) + + elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: + return Pow(a.a, a.power - b.power) + + else: + return Div(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Div(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Div" + +class Pow(Operation): + + serialisation_name = "pow" + + def __init__(self, a: Operation, power: float): + self.a = a + self.power = power + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) ** self.power + + def _derivative(self, hash_value: int) -> Operation: + if self.power == 0: + return AdditiveIdentity() + + elif self.power == 1: + return self.a._derivative(hash_value) + + else: + return Mul(Constant(self.power), Mul(Pow(self.a, self.power-1), self.a._derivative(hash_value))) + + def _clean(self) -> Operation: + a = self.a._clean() + + if self.power == 1: + return a + + elif self.power == 0: + return MultiplicativeIdentity() + + elif self.power == -1: + return Inv(a) + + else: + return Pow(a, self.power) + + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": Operation._serialise_json(self.a), + "power": self.power} + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Pow(Operation.deserialise_json(parameters["a"]), parameters["power"]) + + def summary(self, indent_amount: int=0, indent=" "): + return (f"{indent_amount*indent}Pow\n" + + self.a.summary(indent_amount+1, indent) + "\n" + + f"{(indent_amount+1)*indent}{self.power}\n" + + f"{indent_amount*indent})") + + def __eq__(self, other): + if isinstance(other, Pow): + return self.a == other.a and self.power == other.power + + + +# +# Matrix operations +# + +class Transpose(UnaryOperation): + """ Transpose operation - as per numpy""" + + serialisation_name = "transpose" + + def evaluate(self, variables: dict[int, T]) -> T: + return np.transpose(self.a.evaluate(variables)) + + def _derivative(self, hash_value: int) -> Operation: + return Transpose(self.a.derivative(hash_value)) # TODO: Check! + + def _clean(self): + clean_a = self.a._clean() + return Transpose(clean_a) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Transpose(Operation.deserialise_json(parameters["a"])) + + def _summary_open(self): + return "Transpose" + + def __eq__(self, other): + if isinstance(other, Transpose): + return other.a == self.a + + +class Dot(BinaryOperation): + """ Dot product - backed by numpy's dot method""" + + serialisation_name = "dot" + + def evaluate(self, variables: dict[int, T]) -> T: + return np.dot(self.a.evaluate(variables) + self.b.evaluate(variables)) + + def _derivative(self, hash_value: int) -> Operation: + return Add( + Dot(self.a, + self.b._derivative(hash_value)), + Dot(self.a._derivative(hash_value), + self.b)) + + def _clean_ab(self, a, b): + return Dot(a, b) # Do nothing for now + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Dot(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Dot" + + +# TODO: Add to base operation class, and to quantities +class MatMul(BinaryOperation): + """ Matrix multiplication, using __matmul__ dunder""" + + serialisation_name = "matmul" + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) @ self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add( + MatMul(self.a, + self.b._derivative(hash_value)), + MatMul(self.a._derivative(hash_value), + self.b)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): + # Convert 0*b or a*0 to 0 + return AdditiveIdentity() + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"@"b" to "a@b" + return Constant(a.evaluate({}) @ b.evaluate({}))._clean() + + elif isinstance(a, Neg): + return Neg(Mul(a.a, b)) + + elif isinstance(b, Neg): + return Neg(Mul(a, b.a)) + + return MatMul(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return MatMul(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "MatMul" + +class TensorProduct(Operation): + serialisation_name = "tensor_product" + + def __init__(self, a: Operation, b: Operation, a_index: int, b_index: int): + self.a = a + self.b = b + self.a_index = a_index + self.b_index = b_index + + def evaluate(self, variables: dict[int, T]) -> T: + return np.tensordot(self.a, self.b, axes=(self.a_index, self.b_index)) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + pass + + def _summary_open(self): + return "TensorProduct" + + +_serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, + Variable, + Neg, Inv, + Add, Sub, Mul, Div, Pow, + Transpose, Dot, MatMul] + +_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} + + class UnitError(Exception): """Errors caused by unit specification not being correct""" From f536d4ed40f3bd782302f6a04c2aa073b5319657 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 21 Oct 2024 14:27:53 +0100 Subject: [PATCH 630/675] Fixes from move --- sasdata/quantities/quantity.py | 42 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index fd2336075..355f1af20 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1070,14 +1070,14 @@ def __mul__(self: Self, other: ArrayLike | Self ) -> Self: return DerivedQuantity( self.value * other.value, self.units * other.units, - history=QuantityHistory.apply_operation(operations.Mul, self.history, other.history)) + history=QuantityHistory.apply_operation(Mul, self.history, other.history)) else: return DerivedQuantity(self.value * other, self.units, QuantityHistory( - operations.Mul( + Mul( self.history.operation_tree, - operations.Constant(other)), + Constant(other)), self.history.references)) def __rmul__(self: Self, other: ArrayLike | Self): @@ -1086,15 +1086,15 @@ def __rmul__(self: Self, other: ArrayLike | Self): other.value * self.value, other.units * self.units, history=QuantityHistory.apply_operation( - operations.Mul, + Mul, other.history, self.history)) else: return DerivedQuantity(other * self.value, self.units, QuantityHistory( - operations.Mul( - operations.Constant(other), + Mul( + Constant(other), self.history.operation_tree), self.history.references)) @@ -1105,7 +1105,7 @@ def __matmul__(self, other: ArrayLike | Self): self.value @ other.value, self.units * other.units, history=QuantityHistory.apply_operation( - operations.MatMul, + MatMul, self.history, other.history)) else: @@ -1113,9 +1113,9 @@ def __matmul__(self, other: ArrayLike | Self): self.value @ other, self.units, QuantityHistory( - operations.MatMul( + MatMul( self.history.operation_tree, - operations.Constant(other)), + Constant(other)), self.history.references)) def __rmatmul__(self, other: ArrayLike | Self): @@ -1124,15 +1124,15 @@ def __rmatmul__(self, other: ArrayLike | Self): other.value @ self.value, other.units * self.units, history=QuantityHistory.apply_operation( - operations.MatMul, + MatMul, other.history, self.history)) else: return DerivedQuantity(other @ self.value, self.units, QuantityHistory( - operations.MatMul( - operations.Constant(other), + MatMul( + Constant(other), self.history.operation_tree), self.history.references)) @@ -1143,15 +1143,15 @@ def __truediv__(self: Self, other: float | Self) -> Self: self.value / other.value, self.units / other.units, history=QuantityHistory.apply_operation( - operations.Div, + Div, self.history, other.history)) else: return DerivedQuantity(self.value / other, self.units, QuantityHistory( - operations.Div( - operations.Constant(other), + Div( + Constant(other), self.history.operation_tree), self.history.references)) @@ -1161,7 +1161,7 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: other.value / self.value, other.units / self.units, history=QuantityHistory.apply_operation( - operations.Div, + Div, other.history, self.history )) @@ -1171,8 +1171,8 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: other / self.value, self.units ** -1, QuantityHistory( - operations.Div( - operations.Constant(other), + Div( + Constant(other), self.history.operation_tree), self.history.references)) @@ -1183,7 +1183,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: self.value + (other.value * other.units.scale) / self.units.scale, self.units, QuantityHistory.apply_operation( - operations.Add, + Add, self.history, other.history)) else: @@ -1197,7 +1197,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: def __neg__(self): return DerivedQuantity(-self.value, self.units, QuantityHistory.apply_operation( - operations.Neg, + Neg, self.history )) @@ -1211,7 +1211,7 @@ def __pow__(self: Self, other: int | float): return DerivedQuantity(self.value ** other, self.units ** other, QuantityHistory( - operations.Pow( + Pow( self.history.operation_tree, other), self.history.references)) From a688dd6836c75cfe16283fe68a638696eef057be Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 21 Oct 2024 17:07:42 +0100 Subject: [PATCH 631/675] Tensor product implementation --- sasdata/quantities/quantity.py | 102 +++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 12 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 355f1af20..b8cadbc41 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -27,12 +27,63 @@ def transpose(a: Union["Quantity[ArrayLike]", ArrayLike]): + """ Transpose an array or an array based quantity """ if isinstance(a, Quantity): - return + return DerivedQuantity(value=np.transpose(a.value), + units=a.units, + history=QuantityHistory.apply_operation(Transpose, a.history)) + else: + return np.transpose(a) + +def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike]", ArrayLike]): + """ Dot product of two arrays or two array based quantities """ + a_is_quantity = isinstance(a, Quantity) + b_is_quantity = isinstance(b, Quantity) + + if a_is_quantity or b_is_quantity: + + # If its only one of them that is a quantity, convert the other one + + if not a_is_quantity: + a = Quantity(a, units.dimensionless) + + if not b_is_quantity: + b = Quantity(b, units.dimensionless) + + return DerivedQuantity( + value=np.dot(a.value, b.value), + units=a.units * b.units, + history=QuantityHistory.apply_operation(Dot, a.history, b.history)) + else: + return np.dot(a, b) def tensordot(a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, b: Union["Quantity[ArrayLike]", ArrayLike], a_index: int, b_index: int): - pass + a_is_quantity = isinstance(a, Quantity) + b_is_quantity = isinstance(b, Quantity) + + if a_is_quantity or b_is_quantity: + + # If its only one of them that is a quantity, convert the other one + + if not a_is_quantity: + a = Quantity(a, units.dimensionless) + + if not b_is_quantity: + b = Quantity(b, units.dimensionless) + + return DerivedQuantity( + value=np.tensordot(a.value, b.value, axes=(a_index, b_index)), + units=a.units * b.units, + history=QuantityHistory.apply_operation( + TensorDot, + a.history, + b.history, + a_index=a_index, + b_index=b_index)) + + else: + return np.tensordot(a, b, axes=(a_index, b_index)) ################### Operation Definitions ####################################### @@ -775,7 +826,7 @@ class Dot(BinaryOperation): serialisation_name = "dot" def evaluate(self, variables: dict[int, T]) -> T: - return np.dot(self.a.evaluate(variables) + self.b.evaluate(variables)) + return dot(self.a.evaluate(variables), self.b.evaluate(variables)) def _derivative(self, hash_value: int) -> Operation: return Add( @@ -787,6 +838,7 @@ def _derivative(self, hash_value: int) -> Operation: def _clean_ab(self, a, b): return Dot(a, b) # Do nothing for now + @staticmethod def _deserialise(parameters: dict) -> "Operation": return Dot(*BinaryOperation._deserialise_ab(parameters)) @@ -837,7 +889,7 @@ def _deserialise(parameters: dict) -> "Operation": def _summary_open(self): return "MatMul" -class TensorProduct(Operation): +class TensorDot(Operation): serialisation_name = "tensor_product" def __init__(self, a: Operation, b: Operation, a_index: int, b_index: int): @@ -847,21 +899,32 @@ def __init__(self, a: Operation, b: Operation, a_index: int, b_index: int): self.b_index = b_index def evaluate(self, variables: dict[int, T]) -> T: - return np.tensordot(self.a, self.b, axes=(self.a_index, self.b_index)) + return tensordot(self.a, self.b, self.a_index, self.b_index) + + + def _serialise_parameters(self) -> dict[str, Any]: + return { + "a": self.a._serialise_json(), + "b": self.b._serialise_json(), + "a_index": self.a_index, + "b_index": self.b_index } @staticmethod def _deserialise(parameters: dict) -> "Operation": - pass + return TensorDot(a = Operation.deserialise_json(parameters["a"]), + b = Operation.deserialise_json(parameters["b"]), + a_index=int(parameters["a_index"]), + b_index=int(parameters["b_index"])) def _summary_open(self): return "TensorProduct" _serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, - Variable, - Neg, Inv, - Add, Sub, Mul, Div, Pow, - Transpose, Dot, MatMul] + Variable, + Neg, Inv, + Add, Sub, Mul, Div, Pow, + Transpose, Dot, MatMul, TensorDot] _serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} @@ -881,10 +944,25 @@ def hash_data_via_numpy(*data: ArrayLike): return int(md5_hash.hexdigest(), 16) + +##################################### +# # +# # +# # +# Quantities begin here # +# # +# # +# # +##################################### + + + QuantityType = TypeVar("QuantityType") class QuantityHistory: + """ Class that holds the information for keeping track of operations done on quantities """ + def __init__(self, operation_tree: Operation, references: dict[int, "Quantity"]): self.operation_tree = operation_tree self.references = references @@ -938,7 +1016,7 @@ def variable(quantity: "Quantity"): return QuantityHistory(Variable(quantity.hash_value), {quantity.hash_value: quantity}) @staticmethod - def apply_operation(operation: type[Operation], *histories: "QuantityHistory") -> "QuantityHistory": + def apply_operation(operation: type[Operation], *histories: "QuantityHistory", **extra_parameters) -> "QuantityHistory": """ Apply an operation to the history This is slightly unsafe as it is possible to attempt to apply an n-ary operation to a number of trees other @@ -955,7 +1033,7 @@ def apply_operation(operation: type[Operation], *histories: "QuantityHistory") - references.update(history.references) return QuantityHistory( - operation(*[history.operation_tree for history in histories]), + operation(*[history.operation_tree for history in histories], **extra_parameters), references) def has_variance(self): From 0c3071df9ba563b70521aae8f3b8498aa5cbf593 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Mon, 21 Oct 2024 19:39:47 +0100 Subject: [PATCH 632/675] Extended transpose, and tensor tests --- sasdata/quantities/quantity.py | 80 +++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index b8cadbc41..fd4b12074 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -25,15 +25,23 @@ ################### Quantity based operations, need to be here to avoid cyclic dependencies ##################### - -def transpose(a: Union["Quantity[ArrayLike]", ArrayLike]): - """ Transpose an array or an array based quantity """ +def transpose(a: Union["Quantity[ArrayLike]", ArrayLike], axes: tuple | None = None): + """ Transpose an array or an array based quantity, can also do reordering of axes""" if isinstance(a, Quantity): - return DerivedQuantity(value=np.transpose(a.value), - units=a.units, - history=QuantityHistory.apply_operation(Transpose, a.history)) + + if axes is None: + return DerivedQuantity(value=np.transpose(a.value, axes=axes), + units=a.units, + history=QuantityHistory.apply_operation(Transpose, a.history)) + + else: + return DerivedQuantity(value=np.transpose(a.value, axes=axes), + units=a.units, + history=QuantityHistory.apply_operation(Transpose, a.history, axes=axes)) + else: - return np.transpose(a) + return np.transpose(a, axes=axes) + def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike]", ArrayLike]): """ Dot product of two arrays or two array based quantities """ @@ -45,10 +53,10 @@ def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike # If its only one of them that is a quantity, convert the other one if not a_is_quantity: - a = Quantity(a, units.dimensionless) + a = Quantity(a, units.none) if not b_is_quantity: - b = Quantity(b, units.dimensionless) + b = Quantity(b, units.none) return DerivedQuantity( value=np.dot(a.value, b.value), @@ -59,6 +67,18 @@ def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike return np.dot(a, b) def tensordot(a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, b: Union["Quantity[ArrayLike]", ArrayLike], a_index: int, b_index: int): + """ Tensor dot product - equivalent to contracting two tensors, such as + + A_{i0, i1, i2, i3...} and B_{j0, j1, j2...} + + e.g. if a_index is 1 and b_index is zero, it will be the sum + + C_{i0, i2, i3 ..., j1, j2 ...} = sum_k A_{i0, k, i2, i3 ...} B_{k, j1, j2 ...} + + (I think, have to check what happens with indices TODO!) + + """ + a_is_quantity = isinstance(a, Quantity) b_is_quantity = isinstance(b, Quantity) @@ -67,10 +87,10 @@ def tensordot(a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, b: Union[" # If its only one of them that is a quantity, convert the other one if not a_is_quantity: - a = Quantity(a, units.dimensionless) + a = Quantity(a, units.none) if not b_is_quantity: - b = Quantity(b, units.dimensionless) + b = Quantity(b, units.none) return DerivedQuantity( value=np.tensordot(a.value, b.value, axes=(a_index, b_index)), @@ -793,11 +813,15 @@ def __eq__(self, other): # Matrix operations # -class Transpose(UnaryOperation): +class Transpose(Operation): """ Transpose operation - as per numpy""" serialisation_name = "transpose" + def __init__(self, a: Operation, axes: tuple[int] | None = None): + self.a = a + self.axes = axes + def evaluate(self, variables: dict[int, T]) -> T: return np.transpose(self.a.evaluate(variables)) @@ -808,9 +832,27 @@ def _clean(self): clean_a = self.a._clean() return Transpose(clean_a) + + def _serialise_parameters(self) -> dict[str, Any]: + if self.axes is None: + return { "a": self.a._serialise_json() } + else: + return { + "a": self.a._serialise_json(), + "axes": list(self.axes) + } + + @staticmethod def _deserialise(parameters: dict) -> "Operation": - return Transpose(Operation.deserialise_json(parameters["a"])) + if "axes" in parameters: + return Transpose( + a=Operation.deserialise_json(parameters["a"]), + axes=tuple(parameters["axes"])) + else: + return Transpose( + a=Operation.deserialise_json(parameters["a"])) + def _summary_open(self): return "Transpose" @@ -976,6 +1018,10 @@ def jacobian(self) -> list[Operation]: # Use the hash value to specify the variable of differentiation return [self.operation_tree.derivative(key) for key in self.reference_key_list] + def _recalculate(self): + """ Recalculate the value of this object - primary use case is for testing """ + return self.operation_tree.evaluate(self.references) + def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, int]: "Quantity"] = {}): """ Do standard error propagation to calculate the uncertainties associated with this quantity @@ -987,14 +1033,6 @@ def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, raise NotImplementedError("User specified covariances not currently implemented") jacobian = self.jacobian() - # jacobian_units = [quantity_units / self.references[key].units for key in self.reference_key_list] - # - # # Evaluate the jacobian - # # TODO: should we use quantities here, does that work automatically? - # evaluated_jacobian = [Quantity( - # value=entry.evaluate(self.si_reference_values), - # units=unit.si_equivalent()) - # for entry, unit in zip(jacobian, jacobian_units)] evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] From cdc99fe4e24b68f643ecfa6eba7616b246c51a96 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 22 Oct 2024 19:03:30 +0100 Subject: [PATCH 633/675] Encodings for numerical values --- sasdata/quantities/numerical_encoding.py | 38 ++----------------- sasdata/quantities/quantity.py | 6 ++- sasdata/quantities/test_numerical_encoding.py | 14 ------- 3 files changed, 7 insertions(+), 51 deletions(-) diff --git a/sasdata/quantities/numerical_encoding.py b/sasdata/quantities/numerical_encoding.py index 66b24b8ff..63a888d5d 100644 --- a/sasdata/quantities/numerical_encoding.py +++ b/sasdata/quantities/numerical_encoding.py @@ -1,13 +1,12 @@ import numpy as np -from scipy.sparse import coo_matrix, csr_matrix, csc_matrix, coo_array, csr_array, csc_array import base64 import struct -def numerical_encode(obj: int | float | np.ndarray | coo_matrix | coo_array | csr_matrix | csr_array | csc_matrix | csc_array): +def numerical_encode(obj: int | float | np.ndarray): if isinstance(obj, int): return {"type": "int", @@ -25,38 +24,11 @@ def numerical_encode(obj: int | float | np.ndarray | coo_matrix | coo_array | cs "shape": list(obj.shape) } - elif isinstance(obj, (coo_matrix, coo_array, csr_matrix, csr_array, csc_matrix, csc_array)): - - output = { - "type": obj.__class__.__name__, # not robust to name changes, but more concise - "dtype": obj.dtype.str, - "shape": list(obj.shape) - } - - if isinstance(obj, (coo_array, coo_matrix)): - - output["data"] = numerical_encode(obj.data) - output["coords"] = [numerical_encode(coord) for coord in obj.coords] - - - elif isinstance(obj, (csr_array, csr_matrix)): - pass - - - elif isinstance(obj, (csc_array, csc_matrix)): - - pass - - - return output - else: raise TypeError(f"Cannot serialise object of type: {type(obj)}") -def numerical_decode(data: dict[str, str | int | list[int]]) -> int | float | np.ndarray | coo_matrix | coo_array | csr_matrix | csr_array | csc_matrix | csc_array: - obj_type = data["type"] - - match obj_type: +def numerical_decode(data: dict[str, str | int | list[int]]) -> int | float | np.ndarray: + match data["type"]: case "int": return int(data["value"]) @@ -68,7 +40,3 @@ def numerical_decode(data: dict[str, str | int | list[int]]) -> int | float | np dtype = np.dtype(data["dtype"]) shape = tuple(data["shape"]) return np.frombuffer(value, dtype=dtype).reshape(*shape) - - case _: - raise ValueError(f"Cannot decode objects of type '{obj_type}'") - diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index fd4b12074..0bd2e9b89 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,16 +1,19 @@ +from encodings.base64_codec import base64_decode from typing import Collection, Sequence, TypeVar, Generic, Self from dataclasses import dataclass import numpy as np +from lxml.etree import SerialisationError from numpy._typing import ArrayLike from sasdata.quantities import units from sasdata.quantities.units import Unit, NamedUnit import hashlib - +import base64 +import struct from typing import Any, TypeVar, Union import numpy as np @@ -133,7 +136,6 @@ def hash_and_name(hash_or_name: int | str): else: raise TypeError("Variable name_or_hash_value must be either str or int") - class Operation: serialisation_name = "unknown" diff --git a/sasdata/quantities/test_numerical_encoding.py b/sasdata/quantities/test_numerical_encoding.py index 93642a3f6..4b170584e 100644 --- a/sasdata/quantities/test_numerical_encoding.py +++ b/sasdata/quantities/test_numerical_encoding.py @@ -1,5 +1,3 @@ -""" Tests for the encoding and decoding of numerical data""" - import numpy as np import pytest @@ -54,15 +52,3 @@ def test_numpy_dtypes_encode_decode(dtype): decoded = numerical_decode(encoded) assert decoded.dtype == test_matrix.dtype - -@pytest.mark.parametrize("dtype", [int, float, complex]) -@pytest.mark.parametrize("shape, n, m", [ - ((8, 8), (1,3,5),(2,5,7)), - ((6, 8), (1,0,5),(0,5,0)), - ((6, 1), (1, 0, 5), (0, 0, 0)), -]) -def test_coo_matrix_encode_decode(shape, n, m, dtype): - - #i_indices = - - values = np.arange(10) \ No newline at end of file From e58e6f89d2f4b42d53b622b13365a78e7fa16d5a Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Tue, 22 Oct 2024 19:08:50 +0100 Subject: [PATCH 634/675] Tidying up --- sasdata/quantities/quantity.py | 17 ++++++----------- sasdata/quantities/test_numerical_encoding.py | 2 ++ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 0bd2e9b89..0a91d107a 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,22 +1,18 @@ -from encodings.base64_codec import base64_decode -from typing import Collection, Sequence, TypeVar, Generic, Self -from dataclasses import dataclass + +from typing import Self import numpy as np -from lxml.etree import SerialisationError from numpy._typing import ArrayLike from sasdata.quantities import units +from sasdata.quantities.numerical_encoding import numerical_decode, numerical_encode from sasdata.quantities.units import Unit, NamedUnit import hashlib -import base64 -import struct from typing import Any, TypeVar, Union -import numpy as np import json @@ -311,7 +307,7 @@ def __init__(self, value): self.value = value def summary(self, indent_amount: int = 0, indent: str=" "): - pass + return repr(self.value) def evaluate(self, variables: dict[int, T]) -> T: return self.value @@ -332,13 +328,12 @@ def _clean(self): @staticmethod def _deserialise(parameters: dict) -> "Operation": - value = parameters["value"] + value = numerical_decode(parameters["value"]) return Constant(value) def _serialise_parameters(self) -> dict[str, Any]: - return {"value": self.value} - + return {"value": numerical_encode(self.value)} def summary(self, indent_amount: int=0, indent=" "): return f"{indent_amount*indent}{self.value}" diff --git a/sasdata/quantities/test_numerical_encoding.py b/sasdata/quantities/test_numerical_encoding.py index 4b170584e..e1166eed6 100644 --- a/sasdata/quantities/test_numerical_encoding.py +++ b/sasdata/quantities/test_numerical_encoding.py @@ -1,3 +1,5 @@ +""" Tests for the encoding and decoding of numerical data""" + import numpy as np import pytest From a702e67ef1f8a6e1d499c7278c78108ff23ba082 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 23 Oct 2024 08:05:42 +0100 Subject: [PATCH 635/675] Work on sparse matrix serialisation --- sasdata/quantities/numerical_encoding.py | 38 +++++++++++++++++-- sasdata/quantities/test_numerical_encoding.py | 9 +++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/sasdata/quantities/numerical_encoding.py b/sasdata/quantities/numerical_encoding.py index 63a888d5d..66b24b8ff 100644 --- a/sasdata/quantities/numerical_encoding.py +++ b/sasdata/quantities/numerical_encoding.py @@ -1,12 +1,13 @@ import numpy as np +from scipy.sparse import coo_matrix, csr_matrix, csc_matrix, coo_array, csr_array, csc_array import base64 import struct -def numerical_encode(obj: int | float | np.ndarray): +def numerical_encode(obj: int | float | np.ndarray | coo_matrix | coo_array | csr_matrix | csr_array | csc_matrix | csc_array): if isinstance(obj, int): return {"type": "int", @@ -24,11 +25,38 @@ def numerical_encode(obj: int | float | np.ndarray): "shape": list(obj.shape) } + elif isinstance(obj, (coo_matrix, coo_array, csr_matrix, csr_array, csc_matrix, csc_array)): + + output = { + "type": obj.__class__.__name__, # not robust to name changes, but more concise + "dtype": obj.dtype.str, + "shape": list(obj.shape) + } + + if isinstance(obj, (coo_array, coo_matrix)): + + output["data"] = numerical_encode(obj.data) + output["coords"] = [numerical_encode(coord) for coord in obj.coords] + + + elif isinstance(obj, (csr_array, csr_matrix)): + pass + + + elif isinstance(obj, (csc_array, csc_matrix)): + + pass + + + return output + else: raise TypeError(f"Cannot serialise object of type: {type(obj)}") -def numerical_decode(data: dict[str, str | int | list[int]]) -> int | float | np.ndarray: - match data["type"]: +def numerical_decode(data: dict[str, str | int | list[int]]) -> int | float | np.ndarray | coo_matrix | coo_array | csr_matrix | csr_array | csc_matrix | csc_array: + obj_type = data["type"] + + match obj_type: case "int": return int(data["value"]) @@ -40,3 +68,7 @@ def numerical_decode(data: dict[str, str | int | list[int]]) -> int | float | np dtype = np.dtype(data["dtype"]) shape = tuple(data["shape"]) return np.frombuffer(value, dtype=dtype).reshape(*shape) + + case _: + raise ValueError(f"Cannot decode objects of type '{obj_type}'") + diff --git a/sasdata/quantities/test_numerical_encoding.py b/sasdata/quantities/test_numerical_encoding.py index e1166eed6..83fa5fe2a 100644 --- a/sasdata/quantities/test_numerical_encoding.py +++ b/sasdata/quantities/test_numerical_encoding.py @@ -54,3 +54,12 @@ def test_numpy_dtypes_encode_decode(dtype): decoded = numerical_decode(encoded) assert decoded.dtype == test_matrix.dtype + +@pytest.mark.parametrize("dtype", [int, float, complex]) +@pytest.mark.parametrize("shape, n, m", [ + ((8, 8), (1,3,5),(2,5,7)), + ((6, 8), (1,0,5),(0,5,0)), + ((6, 1), (1, 0, 5), (0, 0, 0)), +]) +def test_coo_matrix_encode_decode(shape, n, m, dtype): + test_matrix = np.arange() From 25db0554a533d3ec5b243ecf6e7b4dc2a07fed35 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 9 Oct 2024 17:44:40 +0100 Subject: [PATCH 636/675] Is anyone capable of putting sensible things in HDF5 files? Corrections for ineptitudes. --- sasdata/metadata.py | 50 ++++++++++++++++++++-------- sasdata/quantities/_accessor_base.py | 35 ++++++++++++++----- sasdata/quantities/accessors.py | 35 ++++++++++++++----- sasdata/temp_hdf5_reader.py | 22 ++++++++---- 4 files changed, 106 insertions(+), 36 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index c42b89ac0..9b5a2b51f 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -337,10 +337,10 @@ def summary(self) -> str: class Instrument: def __init__(self, target: AccessorTarget): - self.aperture = Aperture(target.with_path_prefix("sasaperture")) - self.collimation = Collimation(target.with_path_prefix("sascollimation")) - self.detector = Detector(target.with_path_prefix("sasdetector")) - self.source = Source(target.with_path_prefix("sassource")) + self.aperture = Aperture(target.with_path_prefix("sasaperture|aperture")) + self.collimation = Collimation(target.with_path_prefix("sascollimation|collimation")) + self.detector = Detector(target.with_path_prefix("sasdetector|detector")) + self.source = Source(target.with_path_prefix("sassource|source")) def summary(self): return ( self.aperture.summary() + @@ -348,27 +348,51 @@ def summary(self): self.detector.summary() + self.source.summary()) +def decode_string(data): + """ This is some crazy stuff""" + + if isinstance(data, str): + return data + + elif isinstance(data, np.ndarray): + + if data.dtype == object: + + data = data.reshape(-1) + data = data[0] + + if isinstance(data, bytes): + return data.decode("utf-8") + + return str(data) + + else: + return data.tobytes().decode("utf-8") + + else: + return str(data) class Metadata: def __init__(self, target: AccessorTarget): self._target = target - self.instrument = Instrument(target.with_path_prefix("sasinstrument")) - self.process = Process(target.with_path_prefix("sasprocess")) - self.sample = Sample(target.with_path_prefix("sassample")) - self.transmission_spectrum = TransmissionSpectrum(target.with_path_prefix("sastransmission_spectrum")) + self.instrument = Instrument(target.with_path_prefix("sasinstrument|instrument")) + self.process = Process(target.with_path_prefix("sasprocess|process")) + self.sample = Sample(target.with_path_prefix("sassample|sample")) + self.transmission_spectrum = TransmissionSpectrum(target.with_path_prefix("sastransmission_spectrum|transmission_spectrum")) self._title = StringAccessor(target, "title") self._run = StringAccessor(target, "run") - self._definitiion = StringAccessor(target, "definition") + self._definition = StringAccessor(target, "definition") - self.title: str = self._title.value - self.run: str = self._run.value - self.definitiion: str = self._definitiion.value + self.title: str = decode_string(self._title.value) + self.run: str = decode_string(self._run.value) + self.definition: str = decode_string(self._definition.value) def summary(self): return ( - f" {self.title}, Run: {self.run}\n" + " " + "="*len(self.title) + + f" {self.title}, Run: {self.run}\n" + + " " + "="*len(self.title) + "=======" + "="*len(self.run) + "\n\n" + f"Definition: {self.title}\n" + diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index f750623bc..b56ecce68 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -47,21 +47,40 @@ def get_value(self, path: str): current_tree_position: Group | Dataset = self._data for token in tokens: + + options = token.split("|") + if isinstance(current_tree_position, Group): - if token in current_tree_position.children: - current_tree_position = current_tree_position.children[token] - else: + + found = False + for option in options: + if option in current_tree_position.children: + current_tree_position = current_tree_position.children[option] + found = True + + if self.verbose: + logger.info(f"Found option: {option}") + + if not found: if self.verbose: - logger.info(f"Failed at token {token} on group {current_tree_position.name}. Options: " + + logger.info(f"Failed to find any of {options} on group {current_tree_position.name}. Options: " + ",".join([key for key in current_tree_position.children])) return None elif isinstance(current_tree_position, Dataset): - if token in current_tree_position.attributes: - current_tree_position = current_tree_position.attributes[token] - else: + + found = False + for option in options: + if option in current_tree_position.attributes: + current_tree_position = current_tree_position.attributes[option] + found = True + + if self.verbose: + logger.info(f"Found option: {option}") + + if not found: if self.verbose: - logger.info(f"Failed at token {token} on attribute {current_tree_position.name}. Options: " + + logger.info(f"Failed to find any of {options} on attribute {current_tree_position.name}. Options: " + ",".join([key for key in current_tree_position.attributes])) return None diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 71f6b0da3..2f1f2514a 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -127,21 +127,40 @@ def get_value(self, path: str): current_tree_position: Group | Dataset = self._data for token in tokens: + + options = token.split("|") + if isinstance(current_tree_position, Group): - if token in current_tree_position.children: - current_tree_position = current_tree_position.children[token] - else: + + found = False + for option in options: + if option in current_tree_position.children: + current_tree_position = current_tree_position.children[option] + found = True + + if self.verbose: + logger.info(f"Found option: {option}") + + if not found: if self.verbose: - logger.info(f"Failed at token {token} on group {current_tree_position.name}. Options: " + + logger.info(f"Failed to find any of {options} on group {current_tree_position.name}. Options: " + ",".join([key for key in current_tree_position.children])) return None elif isinstance(current_tree_position, Dataset): - if token in current_tree_position.attributes: - current_tree_position = current_tree_position.attributes[token] - else: + + found = False + for option in options: + if option in current_tree_position.attributes: + current_tree_position = current_tree_position.attributes[option] + found = True + + if self.verbose: + logger.info(f"Found option: {option}") + + if not found: if self.verbose: - logger.info(f"Failed at token {token} on attribute {current_tree_position.name}. Options: " + + logger.info(f"Failed to find any of {options} on attribute {current_tree_position.name}. Options: " + ",".join([key for key in current_tree_position.attributes])) return None diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index b0bf6e423..f96b2a4cb 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -19,7 +19,10 @@ from sasdata.quantities.unit_parser import parse # test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" -test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" +# test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS.h5" +# test_file = "./example_data/2d_data/BAM_2D.h5" +test_file = "./example_data/2d_data/14250_2D_NoDetInfo_NXcanSAS_v3.h5" +# test_file = "./example_data/2d_data/33837rear_2D_1.75_16.5_NXcanSAS_v3.h5" logger = logging.getLogger(__name__) @@ -76,8 +79,12 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: value=child.data, units=units) - if "uncertainty" in child.attributes: - uncertainty_name = child.attributes["uncertainty"] + # Turns out people can't be trusted to use the same keys here + if "uncertainty" in child.attributes or "uncertainties" in child.attributes: + try: + uncertainty_name = child.attributes["uncertainty"] + except: + uncertainty_name = child.attributes["uncertainties"] uncertainty_map[name] = uncertainty_name uncertainties.add(uncertainty_name) @@ -111,12 +118,13 @@ def load_data(filename) -> list[SasData]: entry_keys = [key for key in entry.keys()] - if "sasdata" not in entry_keys: - logger.warning("No sasdata key") + if "sasdata" not in entry_keys and "data" not in entry_keys: + logger.warning("No sasdata or data key") for key in entry_keys: component = entry[key] - if key.lower() == "sasdata": + lower_key = key.lower() + if lower_key == "sasdata" or lower_key == "data": datum = recurse_hdf5(component) # TODO: Use named identifier data_contents = connected_data(datum, "FILE_ID_HERE") @@ -140,4 +148,4 @@ def load_data(filename) -> list[SasData]: data = load_data(test_file) for dataset in data: - print(dataset.summary(include_raw=True)) \ No newline at end of file + print(dataset.summary(include_raw=False)) \ No newline at end of file From 06910b3634ba370068879abacc9e29d2227d2122 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 23 Oct 2024 09:54:20 +0100 Subject: [PATCH 637/675] Line endings :) --- sasdata/model_requirements.py | 2 +- sasdata/quantities/operations_examples.py | 2 +- sasdata/quantities/operations_test.py | 2 +- sasdata/quantities/quantity.py | 1084 +-------------------- sasdata/quantities/units_tests.py | 2 +- 5 files changed, 35 insertions(+), 1057 deletions(-) diff --git a/sasdata/model_requirements.py b/sasdata/model_requirements.py index 12ad54565..5d68ad1b4 100644 --- a/sasdata/model_requirements.py +++ b/sasdata/model_requirements.py @@ -3,7 +3,7 @@ import numpy as np from sasdata.metadata import Metadata -from sasdata.quantities.quantity import Operation +from transforms.operation import Operation @dataclass diff --git a/sasdata/quantities/operations_examples.py b/sasdata/quantities/operations_examples.py index e2e25666f..4509a86ac 100644 --- a/sasdata/quantities/operations_examples.py +++ b/sasdata/quantities/operations_examples.py @@ -1,4 +1,4 @@ -from sasdata.quantities.quantity import Variable, Mul +from sasdata.quantities.operations import Variable, Mul x = Variable("x") y = Variable("y") diff --git a/sasdata/quantities/operations_test.py b/sasdata/quantities/operations_test.py index 6767e32a0..0899eee7f 100644 --- a/sasdata/quantities/operations_test.py +++ b/sasdata/quantities/operations_test.py @@ -1,6 +1,6 @@ import pytest -from sasdata.quantities.quantity import Operation, \ +from sasdata.quantities.operations import Operation, \ Neg, Inv, \ Add, Sub, Mul, Div, Pow, \ Variable, Constant, AdditiveIdentity, MultiplicativeIdentity diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 0a91d107a..fec182a69 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,972 +1,18 @@ -from typing import Self +from typing import Collection, Sequence, TypeVar, Generic, Self +from dataclasses import dataclass import numpy as np from numpy._typing import ArrayLike -from sasdata.quantities import units -from sasdata.quantities.numerical_encoding import numerical_decode, numerical_encode +from sasdata.quantities.operations import Operation, Variable +from sasdata.quantities import operations, units from sasdata.quantities.units import Unit, NamedUnit import hashlib -from typing import Any, TypeVar, Union - -import json - -T = TypeVar("T") - - - - - -################### Quantity based operations, need to be here to avoid cyclic dependencies ##################### - -def transpose(a: Union["Quantity[ArrayLike]", ArrayLike], axes: tuple | None = None): - """ Transpose an array or an array based quantity, can also do reordering of axes""" - if isinstance(a, Quantity): - - if axes is None: - return DerivedQuantity(value=np.transpose(a.value, axes=axes), - units=a.units, - history=QuantityHistory.apply_operation(Transpose, a.history)) - - else: - return DerivedQuantity(value=np.transpose(a.value, axes=axes), - units=a.units, - history=QuantityHistory.apply_operation(Transpose, a.history, axes=axes)) - - else: - return np.transpose(a, axes=axes) - - -def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike]", ArrayLike]): - """ Dot product of two arrays or two array based quantities """ - a_is_quantity = isinstance(a, Quantity) - b_is_quantity = isinstance(b, Quantity) - - if a_is_quantity or b_is_quantity: - - # If its only one of them that is a quantity, convert the other one - - if not a_is_quantity: - a = Quantity(a, units.none) - - if not b_is_quantity: - b = Quantity(b, units.none) - - return DerivedQuantity( - value=np.dot(a.value, b.value), - units=a.units * b.units, - history=QuantityHistory.apply_operation(Dot, a.history, b.history)) - - else: - return np.dot(a, b) - -def tensordot(a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, b: Union["Quantity[ArrayLike]", ArrayLike], a_index: int, b_index: int): - """ Tensor dot product - equivalent to contracting two tensors, such as - - A_{i0, i1, i2, i3...} and B_{j0, j1, j2...} - - e.g. if a_index is 1 and b_index is zero, it will be the sum - - C_{i0, i2, i3 ..., j1, j2 ...} = sum_k A_{i0, k, i2, i3 ...} B_{k, j1, j2 ...} - - (I think, have to check what happens with indices TODO!) - - """ - - a_is_quantity = isinstance(a, Quantity) - b_is_quantity = isinstance(b, Quantity) - - if a_is_quantity or b_is_quantity: - - # If its only one of them that is a quantity, convert the other one - - if not a_is_quantity: - a = Quantity(a, units.none) - - if not b_is_quantity: - b = Quantity(b, units.none) - - return DerivedQuantity( - value=np.tensordot(a.value, b.value, axes=(a_index, b_index)), - units=a.units * b.units, - history=QuantityHistory.apply_operation( - TensorDot, - a.history, - b.history, - a_index=a_index, - b_index=b_index)) - - else: - return np.tensordot(a, b, axes=(a_index, b_index)) - - -################### Operation Definitions ####################################### - -def hash_and_name(hash_or_name: int | str): - """ Infer the name of a variable from a hash, or the hash from the name - - Note: hash_and_name(hash_and_name(number)[1]) is not the identity - however: hash_and_name(hash_and_name(number)) is - """ - - if isinstance(hash_or_name, str): - hash_value = hash(hash_or_name) - name = hash_or_name - - return hash_value, name - - elif isinstance(hash_or_name, int): - hash_value = hash_or_name - name = f"#{hash_or_name}" - - return hash_value, name - - elif isinstance(hash_or_name, tuple): - return hash_or_name - - else: - raise TypeError("Variable name_or_hash_value must be either str or int") - -class Operation: - - serialisation_name = "unknown" - def summary(self, indent_amount: int = 0, indent: str=" "): - """ Summary of the operation tree""" - - s = f"{indent_amount*indent}{self._summary_open()}(\n" - - for chunk in self._summary_components(): - s += chunk.summary(indent_amount+1, indent) + "\n" - - s += f"{indent_amount*indent})" - - return s - def _summary_open(self): - """ First line of summary """ - - def _summary_components(self) -> list["Operation"]: - return [] - def evaluate(self, variables: dict[int, T]) -> T: - - """ Evaluate this operation """ - - def _derivative(self, hash_value: int) -> "Operation": - """ Get the derivative of this operation """ - - def _clean(self): - """ Clean up this operation - i.e. remove silly things like 1*x """ - return self - - def derivative(self, variable: Union[str, int, "Variable"], simplify=True): - if isinstance(variable, Variable): - hash_value = variable.hash_value - else: - hash_value, _ = hash_and_name(variable) - - derivative = self._derivative(hash_value) - - if not simplify: - return derivative - - derivative_string = derivative.serialise() - - # print("---------------") - # print("Base") - # print("---------------") - # print(derivative.summary()) - - # Inefficient way of doing repeated simplification, but it will work - for i in range(100): # set max iterations - - derivative = derivative._clean() - # - # print("-------------------") - # print("Iteration", i+1) - # print("-------------------") - # print(derivative.summary()) - # print("-------------------") - - new_derivative_string = derivative.serialise() - - if derivative_string == new_derivative_string: - break - - derivative_string = new_derivative_string - - return derivative - - @staticmethod - def deserialise(data: str) -> "Operation": - json_data = json.loads(data) - return Operation.deserialise_json(json_data) - - @staticmethod - def deserialise_json(json_data: dict) -> "Operation": - - operation = json_data["operation"] - parameters = json_data["parameters"] - cls = _serialisation_lookup[operation] - - try: - return cls._deserialise(parameters) - - except NotImplementedError: - raise NotImplementedError(f"No method to deserialise {operation} with {parameters} (cls={cls})") - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - raise NotImplementedError(f"Deserialise not implemented for this class") - - def serialise(self) -> str: - return json.dumps(self._serialise_json()) - - def _serialise_json(self) -> dict[str, Any]: - return {"operation": self.serialisation_name, - "parameters": self._serialise_parameters()} - - def _serialise_parameters(self) -> dict[str, Any]: - raise NotImplementedError("_serialise_parameters not implemented") - - def __eq__(self, other: "Operation"): - return NotImplemented - -class ConstantBase(Operation): - pass - -class AdditiveIdentity(ConstantBase): - - serialisation_name = "zero" - def evaluate(self, variables: dict[int, T]) -> T: - return 0 - - def _derivative(self, hash_value: int) -> Operation: - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return AdditiveIdentity() - - def _serialise_parameters(self) -> dict[str, Any]: - return {} - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}0 [Add.Id.]" - - def __eq__(self, other): - if isinstance(other, AdditiveIdentity): - return True - elif isinstance(other, Constant): - if other.value == 0: - return True - - return False - - - -class MultiplicativeIdentity(ConstantBase): - - serialisation_name = "one" - - def evaluate(self, variables: dict[int, T]) -> T: - return 1 - - def _derivative(self, hash_value: int): - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return MultiplicativeIdentity() - - - def _serialise_parameters(self) -> dict[str, Any]: - return {} - - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}1 [Mul.Id.]" - - def __eq__(self, other): - if isinstance(other, MultiplicativeIdentity): - return True - elif isinstance(other, Constant): - if other.value == 1: - return True - - return False - - -class Constant(ConstantBase): - - serialisation_name = "constant" - def __init__(self, value): - self.value = value - - def summary(self, indent_amount: int = 0, indent: str=" "): - return repr(self.value) - - def evaluate(self, variables: dict[int, T]) -> T: - return self.value - - def _derivative(self, hash_value: int): - return AdditiveIdentity() - - def _clean(self): - - if self.value == 0: - return AdditiveIdentity() - - elif self.value == 1: - return MultiplicativeIdentity() - - else: - return self - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - value = numerical_decode(parameters["value"]) - return Constant(value) - - - def _serialise_parameters(self) -> dict[str, Any]: - return {"value": numerical_encode(self.value)} - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}{self.value}" - - def __eq__(self, other): - if isinstance(other, AdditiveIdentity): - return self.value == 0 - - elif isinstance(other, MultiplicativeIdentity): - return self.value == 1 - - elif isinstance(other, Constant): - if other.value == self.value: - return True - - return False - - -class Variable(Operation): - - serialisation_name = "variable" - def __init__(self, name_or_hash_value: int | str | tuple[int, str]): - self.hash_value, self.name = hash_and_name(name_or_hash_value) - - def evaluate(self, variables: dict[int, T]) -> T: - try: - return variables[self.hash_value] - except KeyError: - raise ValueError(f"Variable dictionary didn't have an entry for {self.name} (hash={self.hash_value})") - - def _derivative(self, hash_value: int) -> Operation: - if hash_value == self.hash_value: - return MultiplicativeIdentity() - else: - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - hash_value = parameters["hash_value"] - name = parameters["name"] - - return Variable((hash_value, name)) - - def _serialise_parameters(self) -> dict[str, Any]: - return {"hash_value": self.hash_value, - "name": self.name} - - def summary(self, indent_amount: int = 0, indent: str=" "): - return f"{indent_amount*indent}{self.name}" - - def __eq__(self, other): - if isinstance(other, Variable): - return self.hash_value == other.hash_value - - return False - -class UnaryOperation(Operation): - - def __init__(self, a: Operation): - self.a = a - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": self.a._serialise_json()} - - def _summary_components(self) -> list["Operation"]: - return [self.a] - - - - -class Neg(UnaryOperation): - - serialisation_name = "neg" - def evaluate(self, variables: dict[int, T]) -> T: - return -self.a.evaluate(variables) - - def _derivative(self, hash_value: int): - return Neg(self.a._derivative(hash_value)) - - def _clean(self): - - clean_a = self.a._clean() - - if isinstance(clean_a, Neg): - # Removes double negations - return clean_a.a - - elif isinstance(clean_a, Constant): - return Constant(-clean_a.value)._clean() - - else: - return Neg(clean_a) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Neg(Operation.deserialise_json(parameters["a"])) - - - def _summary_open(self): - return "Neg" - - def __eq__(self, other): - if isinstance(other, Neg): - return other.a == self.a - - -class Inv(UnaryOperation): - - serialisation_name = "reciprocal" - - def evaluate(self, variables: dict[int, T]) -> T: - return 1/self.a.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Neg(Div(self.a._derivative(hash_value), Mul(self.a, self.a))) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, Inv): - # Removes double negations - return clean_a.a - - elif isinstance(clean_a, Neg): - # cannonicalise 1/-a to -(1/a) - # over multiple iterations this should have the effect of ordering and gathering Neg and Inv - return Neg(Inv(clean_a.a)) - - elif isinstance(clean_a, Constant): - return Constant(1/clean_a.value)._clean() - - else: - return Inv(clean_a) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Inv(Operation.deserialise_json(parameters["a"])) - - def _summary_open(self): - return "Inv" - - - def __eq__(self, other): - if isinstance(other, Inv): - return other.a == self.a - -class BinaryOperation(Operation): - def __init__(self, a: Operation, b: Operation): - self.a = a - self.b = b - - def _clean(self): - return self._clean_ab(self.a._clean(), self.b._clean()) - - def _clean_ab(self, a, b): - raise NotImplementedError("_clean_ab not implemented") - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": self.a._serialise_json(), - "b": self.b._serialise_json()} - - @staticmethod - def _deserialise_ab(parameters) -> tuple[Operation, Operation]: - return (Operation.deserialise_json(parameters["a"]), - Operation.deserialise_json(parameters["b"])) - - - def _summary_components(self) -> list["Operation"]: - return [self.a, self.b] - - def _self_cls(self) -> type: - """ Own class""" - def __eq__(self, other): - if isinstance(other, self._self_cls()): - return other.a == self.a and self.b == other.b - -class Add(BinaryOperation): - - serialisation_name = "add" - - def _self_cls(self) -> type: - return Add - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) + self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add(self.a._derivative(hash_value), self.b._derivative(hash_value)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity): - # Convert 0 + b to b - return b - - elif isinstance(b, AdditiveIdentity): - # Convert a + 0 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"+"b" to "a+b" - return Constant(a.evaluate({}) + b.evaluate({}))._clean() - - elif isinstance(a, Neg): - if isinstance(b, Neg): - # Convert (-a)+(-b) to -(a+b) - return Neg(Add(a.a, b.a)) - else: - # Convert (-a) + b to b-a - return Sub(b, a.a) - - elif isinstance(b, Neg): - # Convert a+(-b) to a-b - return Sub(a, b.a) - - elif a == b: - return Mul(Constant(2), a) - - else: - return Add(a, b) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Add(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Add" - -class Sub(BinaryOperation): - - serialisation_name = "sub" - - - def _self_cls(self) -> type: - return Sub - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) - self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Sub(self.a._derivative(hash_value), self.b._derivative(hash_value)) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity): - # Convert 0 - b to -b - return Neg(b) - - elif isinstance(b, AdditiveIdentity): - # Convert a - 0 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant pair "a" - "b" to "a-b" - return Constant(a.evaluate({}) - b.evaluate({}))._clean() - - elif isinstance(a, Neg): - if isinstance(b, Neg): - # Convert (-a)-(-b) to b-a - return Sub(b.a, a.a) - else: - # Convert (-a)-b to -(a+b) - return Neg(Add(a.a, b)) - - elif isinstance(b, Neg): - # Convert a-(-b) to a+b - return Add(a, b.a) - - elif a == b: - return AdditiveIdentity() - - else: - return Sub(a, b) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Sub(*BinaryOperation._deserialise_ab(parameters)) - - - def _summary_open(self): - return "Sub" - -class Mul(BinaryOperation): - - serialisation_name = "mul" - - - def _self_cls(self) -> type: - return Mul - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) * self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add(Mul(self.a, self.b._derivative(hash_value)), Mul(self.a._derivative(hash_value), self.b)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): - # Convert 0*b or a*0 to 0 - return AdditiveIdentity() - - elif isinstance(a, MultiplicativeIdentity): - # Convert 1*b to b - return b - - elif isinstance(b, MultiplicativeIdentity): - # Convert a*1 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"*"b" to "a*b" - return Constant(a.evaluate({}) * b.evaluate({}))._clean() - - elif isinstance(a, Inv) and isinstance(b, Inv): - return Inv(Mul(a.a, b.a)) - - elif isinstance(a, Inv) and not isinstance(b, Inv): - return Div(b, a.a) - - elif not isinstance(a, Inv) and isinstance(b, Inv): - return Div(a, b.a) - - elif isinstance(a, Neg): - return Neg(Mul(a.a, b)) - - elif isinstance(b, Neg): - return Neg(Mul(a, b.a)) - - elif a == b: - return Pow(a, 2) - - elif isinstance(a, Pow) and a.a == b: - return Pow(b, a.power + 1) - - elif isinstance(b, Pow) and b.a == a: - return Pow(a, b.power + 1) - - elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: - return Pow(a.a, a.power + b.power) - - else: - return Mul(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Mul(*BinaryOperation._deserialise_ab(parameters)) - - - def _summary_open(self): - return "Mul" - -class Div(BinaryOperation): - - serialisation_name = "div" - - - def _self_cls(self) -> type: - return Div - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) / self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Sub(Div(self.a.derivative(hash_value), self.b), - Div(Mul(self.a, self.b.derivative(hash_value)), Mul(self.b, self.b))) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity): - # Convert 0/b to 0 - return AdditiveIdentity() - - elif isinstance(a, MultiplicativeIdentity): - # Convert 1/b to inverse of b - return Inv(b) - - elif isinstance(b, MultiplicativeIdentity): - # Convert a/1 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constants "a"/"b" to "a/b" - return Constant(self.a.evaluate({}) / self.b.evaluate({}))._clean() - - - elif isinstance(a, Inv) and isinstance(b, Inv): - return Div(b.a, a.a) - - elif isinstance(a, Inv) and not isinstance(b, Inv): - return Inv(Mul(a.a, b)) - - elif not isinstance(a, Inv) and isinstance(b, Inv): - return Mul(a, b.a) - - elif a == b: - return MultiplicativeIdentity() - - elif isinstance(a, Pow) and a.a == b: - return Pow(b, a.power - 1) - - elif isinstance(b, Pow) and b.a == a: - return Pow(a, 1 - b.power) - - elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: - return Pow(a.a, a.power - b.power) - - else: - return Div(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Div(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Div" - -class Pow(Operation): - - serialisation_name = "pow" - - def __init__(self, a: Operation, power: float): - self.a = a - self.power = power - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) ** self.power - - def _derivative(self, hash_value: int) -> Operation: - if self.power == 0: - return AdditiveIdentity() - - elif self.power == 1: - return self.a._derivative(hash_value) - - else: - return Mul(Constant(self.power), Mul(Pow(self.a, self.power-1), self.a._derivative(hash_value))) - - def _clean(self) -> Operation: - a = self.a._clean() - - if self.power == 1: - return a - - elif self.power == 0: - return MultiplicativeIdentity() - - elif self.power == -1: - return Inv(a) - - else: - return Pow(a, self.power) - - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": Operation._serialise_json(self.a), - "power": self.power} - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Pow(Operation.deserialise_json(parameters["a"]), parameters["power"]) - - def summary(self, indent_amount: int=0, indent=" "): - return (f"{indent_amount*indent}Pow\n" + - self.a.summary(indent_amount+1, indent) + "\n" + - f"{(indent_amount+1)*indent}{self.power}\n" + - f"{indent_amount*indent})") - - def __eq__(self, other): - if isinstance(other, Pow): - return self.a == other.a and self.power == other.power - - - -# -# Matrix operations -# - -class Transpose(Operation): - """ Transpose operation - as per numpy""" - - serialisation_name = "transpose" - - def __init__(self, a: Operation, axes: tuple[int] | None = None): - self.a = a - self.axes = axes - - def evaluate(self, variables: dict[int, T]) -> T: - return np.transpose(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Transpose(self.a.derivative(hash_value)) # TODO: Check! - - def _clean(self): - clean_a = self.a._clean() - return Transpose(clean_a) - - - def _serialise_parameters(self) -> dict[str, Any]: - if self.axes is None: - return { "a": self.a._serialise_json() } - else: - return { - "a": self.a._serialise_json(), - "axes": list(self.axes) - } - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - if "axes" in parameters: - return Transpose( - a=Operation.deserialise_json(parameters["a"]), - axes=tuple(parameters["axes"])) - else: - return Transpose( - a=Operation.deserialise_json(parameters["a"])) - - - def _summary_open(self): - return "Transpose" - - def __eq__(self, other): - if isinstance(other, Transpose): - return other.a == self.a - - -class Dot(BinaryOperation): - """ Dot product - backed by numpy's dot method""" - - serialisation_name = "dot" - - def evaluate(self, variables: dict[int, T]) -> T: - return dot(self.a.evaluate(variables), self.b.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Add( - Dot(self.a, - self.b._derivative(hash_value)), - Dot(self.a._derivative(hash_value), - self.b)) - - def _clean_ab(self, a, b): - return Dot(a, b) # Do nothing for now - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Dot(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Dot" - - -# TODO: Add to base operation class, and to quantities -class MatMul(BinaryOperation): - """ Matrix multiplication, using __matmul__ dunder""" - - serialisation_name = "matmul" - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) @ self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add( - MatMul(self.a, - self.b._derivative(hash_value)), - MatMul(self.a._derivative(hash_value), - self.b)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): - # Convert 0*b or a*0 to 0 - return AdditiveIdentity() - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"@"b" to "a@b" - return Constant(a.evaluate({}) @ b.evaluate({}))._clean() - - elif isinstance(a, Neg): - return Neg(Mul(a.a, b)) - - elif isinstance(b, Neg): - return Neg(Mul(a, b.a)) - - return MatMul(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return MatMul(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "MatMul" - -class TensorDot(Operation): - serialisation_name = "tensor_product" - - def __init__(self, a: Operation, b: Operation, a_index: int, b_index: int): - self.a = a - self.b = b - self.a_index = a_index - self.b_index = b_index - - def evaluate(self, variables: dict[int, T]) -> T: - return tensordot(self.a, self.b, self.a_index, self.b_index) - - - def _serialise_parameters(self) -> dict[str, Any]: - return { - "a": self.a._serialise_json(), - "b": self.b._serialise_json(), - "a_index": self.a_index, - "b_index": self.b_index } - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return TensorDot(a = Operation.deserialise_json(parameters["a"]), - b = Operation.deserialise_json(parameters["b"]), - a_index=int(parameters["a_index"]), - b_index=int(parameters["b_index"])) - - def _summary_open(self): - return "TensorProduct" - - -_serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, - Variable, - Neg, Inv, - Add, Sub, Mul, Div, Pow, - Transpose, Dot, MatMul, TensorDot] - -_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} - class UnitError(Exception): """Errors caused by unit specification not being correct""" @@ -983,25 +29,10 @@ def hash_data_via_numpy(*data: ArrayLike): return int(md5_hash.hexdigest(), 16) - -##################################### -# # -# # -# # -# Quantities begin here # -# # -# # -# # -##################################### - - - QuantityType = TypeVar("QuantityType") class QuantityHistory: - """ Class that holds the information for keeping track of operations done on quantities """ - def __init__(self, operation_tree: Operation, references: dict[int, "Quantity"]): self.operation_tree = operation_tree self.references = references @@ -1015,10 +46,6 @@ def jacobian(self) -> list[Operation]: # Use the hash value to specify the variable of differentiation return [self.operation_tree.derivative(key) for key in self.reference_key_list] - def _recalculate(self): - """ Recalculate the value of this object - primary use case is for testing """ - return self.operation_tree.evaluate(self.references) - def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, int]: "Quantity"] = {}): """ Do standard error propagation to calculate the uncertainties associated with this quantity @@ -1030,6 +57,14 @@ def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, raise NotImplementedError("User specified covariances not currently implemented") jacobian = self.jacobian() + # jacobian_units = [quantity_units / self.references[key].units for key in self.reference_key_list] + # + # # Evaluate the jacobian + # # TODO: should we use quantities here, does that work automatically? + # evaluated_jacobian = [Quantity( + # value=entry.evaluate(self.si_reference_values), + # units=unit.si_equivalent()) + # for entry, unit in zip(jacobian, jacobian_units)] evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] @@ -1051,7 +86,7 @@ def variable(quantity: "Quantity"): return QuantityHistory(Variable(quantity.hash_value), {quantity.hash_value: quantity}) @staticmethod - def apply_operation(operation: type[Operation], *histories: "QuantityHistory", **extra_parameters) -> "QuantityHistory": + def apply_operation(operation: type[Operation], *histories: "QuantityHistory") -> "QuantityHistory": """ Apply an operation to the history This is slightly unsafe as it is possible to attempt to apply an n-ary operation to a number of trees other @@ -1068,7 +103,7 @@ def apply_operation(operation: type[Operation], *histories: "QuantityHistory", * references.update(history.references) return QuantityHistory( - operation(*[history.operation_tree for history in histories], **extra_parameters), + operation(*[history.operation_tree for history in histories]), references) def has_variance(self): @@ -1078,16 +113,6 @@ def has_variance(self): return False - def summary(self): - - variable_strings = [self.references[key].string_repr for key in self.references] - - s = "Variables: "+",".join(variable_strings) - s += "\n" - s += self.operation_tree.summary() - - return s - class Quantity[QuantityType]: @@ -1110,10 +135,10 @@ def __init__(self, self.hash_value = -1 """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ - self._variance = None - """ Contains the variance if it is data driven """ + """ Contains the variance if it is data driven, else it is """ if standard_error is None: + self._variance = None self.hash_value = hash_data_via_numpy(hash_seed, value) else: self._variance = standard_error ** 2 @@ -1183,14 +208,14 @@ def __mul__(self: Self, other: ArrayLike | Self ) -> Self: return DerivedQuantity( self.value * other.value, self.units * other.units, - history=QuantityHistory.apply_operation(Mul, self.history, other.history)) + history=QuantityHistory.apply_operation(operations.Mul, self.history, other.history)) else: return DerivedQuantity(self.value * other, self.units, QuantityHistory( - Mul( + operations.Mul( self.history.operation_tree, - Constant(other)), + operations.Constant(other)), self.history.references)) def __rmul__(self: Self, other: ArrayLike | Self): @@ -1199,72 +224,33 @@ def __rmul__(self: Self, other: ArrayLike | Self): other.value * self.value, other.units * self.units, history=QuantityHistory.apply_operation( - Mul, + operations.Mul, other.history, self.history)) else: return DerivedQuantity(other * self.value, self.units, QuantityHistory( - Mul( - Constant(other), - self.history.operation_tree), - self.history.references)) - - - def __matmul__(self, other: ArrayLike | Self): - if isinstance(other, Quantity): - return DerivedQuantity( - self.value @ other.value, - self.units * other.units, - history=QuantityHistory.apply_operation( - MatMul, - self.history, - other.history)) - else: - return DerivedQuantity( - self.value @ other, - self.units, - QuantityHistory( - MatMul( - self.history.operation_tree, - Constant(other)), - self.history.references)) - - def __rmatmul__(self, other: ArrayLike | Self): - if isinstance(other, Quantity): - return DerivedQuantity( - other.value @ self.value, - other.units * self.units, - history=QuantityHistory.apply_operation( - MatMul, - other.history, - self.history)) - - else: - return DerivedQuantity(other @ self.value, self.units, - QuantityHistory( - MatMul( - Constant(other), + operations.Mul( + operations.Constant(other), self.history.operation_tree), self.history.references)) - def __truediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): return DerivedQuantity( self.value / other.value, self.units / other.units, history=QuantityHistory.apply_operation( - Div, + operations.Div, self.history, other.history)) else: return DerivedQuantity(self.value / other, self.units, QuantityHistory( - Div( - Constant(other), + operations.Div( + operations.Constant(other), self.history.operation_tree), self.history.references)) @@ -1274,7 +260,7 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: other.value / self.value, other.units / self.units, history=QuantityHistory.apply_operation( - Div, + operations.Div, other.history, self.history )) @@ -1284,8 +270,8 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: other / self.value, self.units ** -1, QuantityHistory( - Div( - Constant(other), + operations.Div( + operations.Constant(other), self.history.operation_tree), self.history.references)) @@ -1296,7 +282,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: self.value + (other.value * other.units.scale) / self.units.scale, self.units, QuantityHistory.apply_operation( - Add, + operations.Add, self.history, other.history)) else: @@ -1310,7 +296,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: def __neg__(self): return DerivedQuantity(-self.value, self.units, QuantityHistory.apply_operation( - Neg, + operations.Neg, self.history )) @@ -1324,7 +310,7 @@ def __pow__(self: Self, other: int | float): return DerivedQuantity(self.value ** other, self.units ** other, QuantityHistory( - Pow( + operations.Pow( self.history.operation_tree, other), self.history.references)) @@ -1376,10 +362,6 @@ def __repr__(self): def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass - @property - def string_repr(self): - return str(self.hash_value) - class NamedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, @@ -1414,10 +396,6 @@ def with_standard_error(self, standard_error: Quantity): f"are not compatible with value units ({self.units})") - @property - def string_repr(self): - return self.name - class DerivedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): super().__init__(value, units, standard_error=None) diff --git a/sasdata/quantities/units_tests.py b/sasdata/quantities/units_tests.py index d0d090934..9fea2a6b8 100644 --- a/sasdata/quantities/units_tests.py +++ b/sasdata/quantities/units_tests.py @@ -43,4 +43,4 @@ def run_test(self): for test in tests: print(test.test_name) - test.run_test() \ No newline at end of file + test.run_test() From 26a6f9c7363c28c04bcf37f29e8cbecac48c8364 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 23 Oct 2024 10:02:47 +0100 Subject: [PATCH 638/675] Updated final line endings --- sasdata/quantities/quantity.py | 1084 +++++++++++++++++++++++++++++++- 1 file changed, 1053 insertions(+), 31 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index fec182a69..0a91d107a 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,18 +1,972 @@ -from typing import Collection, Sequence, TypeVar, Generic, Self -from dataclasses import dataclass +from typing import Self import numpy as np from numpy._typing import ArrayLike -from sasdata.quantities.operations import Operation, Variable -from sasdata.quantities import operations, units +from sasdata.quantities import units +from sasdata.quantities.numerical_encoding import numerical_decode, numerical_encode from sasdata.quantities.units import Unit, NamedUnit import hashlib +from typing import Any, TypeVar, Union + +import json + +T = TypeVar("T") + + + + + +################### Quantity based operations, need to be here to avoid cyclic dependencies ##################### + +def transpose(a: Union["Quantity[ArrayLike]", ArrayLike], axes: tuple | None = None): + """ Transpose an array or an array based quantity, can also do reordering of axes""" + if isinstance(a, Quantity): + + if axes is None: + return DerivedQuantity(value=np.transpose(a.value, axes=axes), + units=a.units, + history=QuantityHistory.apply_operation(Transpose, a.history)) + + else: + return DerivedQuantity(value=np.transpose(a.value, axes=axes), + units=a.units, + history=QuantityHistory.apply_operation(Transpose, a.history, axes=axes)) + + else: + return np.transpose(a, axes=axes) + + +def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike]", ArrayLike]): + """ Dot product of two arrays or two array based quantities """ + a_is_quantity = isinstance(a, Quantity) + b_is_quantity = isinstance(b, Quantity) + + if a_is_quantity or b_is_quantity: + + # If its only one of them that is a quantity, convert the other one + + if not a_is_quantity: + a = Quantity(a, units.none) + + if not b_is_quantity: + b = Quantity(b, units.none) + + return DerivedQuantity( + value=np.dot(a.value, b.value), + units=a.units * b.units, + history=QuantityHistory.apply_operation(Dot, a.history, b.history)) + + else: + return np.dot(a, b) + +def tensordot(a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, b: Union["Quantity[ArrayLike]", ArrayLike], a_index: int, b_index: int): + """ Tensor dot product - equivalent to contracting two tensors, such as + + A_{i0, i1, i2, i3...} and B_{j0, j1, j2...} + + e.g. if a_index is 1 and b_index is zero, it will be the sum + + C_{i0, i2, i3 ..., j1, j2 ...} = sum_k A_{i0, k, i2, i3 ...} B_{k, j1, j2 ...} + + (I think, have to check what happens with indices TODO!) + + """ + + a_is_quantity = isinstance(a, Quantity) + b_is_quantity = isinstance(b, Quantity) + + if a_is_quantity or b_is_quantity: + + # If its only one of them that is a quantity, convert the other one + + if not a_is_quantity: + a = Quantity(a, units.none) + + if not b_is_quantity: + b = Quantity(b, units.none) + + return DerivedQuantity( + value=np.tensordot(a.value, b.value, axes=(a_index, b_index)), + units=a.units * b.units, + history=QuantityHistory.apply_operation( + TensorDot, + a.history, + b.history, + a_index=a_index, + b_index=b_index)) + + else: + return np.tensordot(a, b, axes=(a_index, b_index)) + + +################### Operation Definitions ####################################### + +def hash_and_name(hash_or_name: int | str): + """ Infer the name of a variable from a hash, or the hash from the name + + Note: hash_and_name(hash_and_name(number)[1]) is not the identity + however: hash_and_name(hash_and_name(number)) is + """ + + if isinstance(hash_or_name, str): + hash_value = hash(hash_or_name) + name = hash_or_name + + return hash_value, name + + elif isinstance(hash_or_name, int): + hash_value = hash_or_name + name = f"#{hash_or_name}" + + return hash_value, name + + elif isinstance(hash_or_name, tuple): + return hash_or_name + + else: + raise TypeError("Variable name_or_hash_value must be either str or int") + +class Operation: + + serialisation_name = "unknown" + def summary(self, indent_amount: int = 0, indent: str=" "): + """ Summary of the operation tree""" + + s = f"{indent_amount*indent}{self._summary_open()}(\n" + + for chunk in self._summary_components(): + s += chunk.summary(indent_amount+1, indent) + "\n" + + s += f"{indent_amount*indent})" + + return s + def _summary_open(self): + """ First line of summary """ + + def _summary_components(self) -> list["Operation"]: + return [] + def evaluate(self, variables: dict[int, T]) -> T: + + """ Evaluate this operation """ + + def _derivative(self, hash_value: int) -> "Operation": + """ Get the derivative of this operation """ + + def _clean(self): + """ Clean up this operation - i.e. remove silly things like 1*x """ + return self + + def derivative(self, variable: Union[str, int, "Variable"], simplify=True): + if isinstance(variable, Variable): + hash_value = variable.hash_value + else: + hash_value, _ = hash_and_name(variable) + + derivative = self._derivative(hash_value) + + if not simplify: + return derivative + + derivative_string = derivative.serialise() + + # print("---------------") + # print("Base") + # print("---------------") + # print(derivative.summary()) + + # Inefficient way of doing repeated simplification, but it will work + for i in range(100): # set max iterations + + derivative = derivative._clean() + # + # print("-------------------") + # print("Iteration", i+1) + # print("-------------------") + # print(derivative.summary()) + # print("-------------------") + + new_derivative_string = derivative.serialise() + + if derivative_string == new_derivative_string: + break + + derivative_string = new_derivative_string + + return derivative + + @staticmethod + def deserialise(data: str) -> "Operation": + json_data = json.loads(data) + return Operation.deserialise_json(json_data) + + @staticmethod + def deserialise_json(json_data: dict) -> "Operation": + + operation = json_data["operation"] + parameters = json_data["parameters"] + cls = _serialisation_lookup[operation] + + try: + return cls._deserialise(parameters) + + except NotImplementedError: + raise NotImplementedError(f"No method to deserialise {operation} with {parameters} (cls={cls})") + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + raise NotImplementedError(f"Deserialise not implemented for this class") + + def serialise(self) -> str: + return json.dumps(self._serialise_json()) + + def _serialise_json(self) -> dict[str, Any]: + return {"operation": self.serialisation_name, + "parameters": self._serialise_parameters()} + + def _serialise_parameters(self) -> dict[str, Any]: + raise NotImplementedError("_serialise_parameters not implemented") + + def __eq__(self, other: "Operation"): + return NotImplemented + +class ConstantBase(Operation): + pass + +class AdditiveIdentity(ConstantBase): + + serialisation_name = "zero" + def evaluate(self, variables: dict[int, T]) -> T: + return 0 + + def _derivative(self, hash_value: int) -> Operation: + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return AdditiveIdentity() + + def _serialise_parameters(self) -> dict[str, Any]: + return {} + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}0 [Add.Id.]" + + def __eq__(self, other): + if isinstance(other, AdditiveIdentity): + return True + elif isinstance(other, Constant): + if other.value == 0: + return True + + return False + + + +class MultiplicativeIdentity(ConstantBase): + + serialisation_name = "one" + + def evaluate(self, variables: dict[int, T]) -> T: + return 1 + + def _derivative(self, hash_value: int): + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return MultiplicativeIdentity() + + + def _serialise_parameters(self) -> dict[str, Any]: + return {} + + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}1 [Mul.Id.]" + + def __eq__(self, other): + if isinstance(other, MultiplicativeIdentity): + return True + elif isinstance(other, Constant): + if other.value == 1: + return True + + return False + + +class Constant(ConstantBase): + + serialisation_name = "constant" + def __init__(self, value): + self.value = value + + def summary(self, indent_amount: int = 0, indent: str=" "): + return repr(self.value) + + def evaluate(self, variables: dict[int, T]) -> T: + return self.value + + def _derivative(self, hash_value: int): + return AdditiveIdentity() + + def _clean(self): + + if self.value == 0: + return AdditiveIdentity() + + elif self.value == 1: + return MultiplicativeIdentity() + + else: + return self + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + value = numerical_decode(parameters["value"]) + return Constant(value) + + + def _serialise_parameters(self) -> dict[str, Any]: + return {"value": numerical_encode(self.value)} + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}{self.value}" + + def __eq__(self, other): + if isinstance(other, AdditiveIdentity): + return self.value == 0 + + elif isinstance(other, MultiplicativeIdentity): + return self.value == 1 + + elif isinstance(other, Constant): + if other.value == self.value: + return True + + return False + + +class Variable(Operation): + + serialisation_name = "variable" + def __init__(self, name_or_hash_value: int | str | tuple[int, str]): + self.hash_value, self.name = hash_and_name(name_or_hash_value) + + def evaluate(self, variables: dict[int, T]) -> T: + try: + return variables[self.hash_value] + except KeyError: + raise ValueError(f"Variable dictionary didn't have an entry for {self.name} (hash={self.hash_value})") + + def _derivative(self, hash_value: int) -> Operation: + if hash_value == self.hash_value: + return MultiplicativeIdentity() + else: + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + hash_value = parameters["hash_value"] + name = parameters["name"] + + return Variable((hash_value, name)) + + def _serialise_parameters(self) -> dict[str, Any]: + return {"hash_value": self.hash_value, + "name": self.name} + + def summary(self, indent_amount: int = 0, indent: str=" "): + return f"{indent_amount*indent}{self.name}" + + def __eq__(self, other): + if isinstance(other, Variable): + return self.hash_value == other.hash_value + + return False + +class UnaryOperation(Operation): + + def __init__(self, a: Operation): + self.a = a + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": self.a._serialise_json()} + + def _summary_components(self) -> list["Operation"]: + return [self.a] + + + + +class Neg(UnaryOperation): + + serialisation_name = "neg" + def evaluate(self, variables: dict[int, T]) -> T: + return -self.a.evaluate(variables) + + def _derivative(self, hash_value: int): + return Neg(self.a._derivative(hash_value)) + + def _clean(self): + + clean_a = self.a._clean() + + if isinstance(clean_a, Neg): + # Removes double negations + return clean_a.a + + elif isinstance(clean_a, Constant): + return Constant(-clean_a.value)._clean() + + else: + return Neg(clean_a) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Neg(Operation.deserialise_json(parameters["a"])) + + + def _summary_open(self): + return "Neg" + + def __eq__(self, other): + if isinstance(other, Neg): + return other.a == self.a + + +class Inv(UnaryOperation): + + serialisation_name = "reciprocal" + + def evaluate(self, variables: dict[int, T]) -> T: + return 1/self.a.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Neg(Div(self.a._derivative(hash_value), Mul(self.a, self.a))) + + def _clean(self): + clean_a = self.a._clean() + + if isinstance(clean_a, Inv): + # Removes double negations + return clean_a.a + + elif isinstance(clean_a, Neg): + # cannonicalise 1/-a to -(1/a) + # over multiple iterations this should have the effect of ordering and gathering Neg and Inv + return Neg(Inv(clean_a.a)) + + elif isinstance(clean_a, Constant): + return Constant(1/clean_a.value)._clean() + + else: + return Inv(clean_a) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Inv(Operation.deserialise_json(parameters["a"])) + + def _summary_open(self): + return "Inv" + + + def __eq__(self, other): + if isinstance(other, Inv): + return other.a == self.a + +class BinaryOperation(Operation): + def __init__(self, a: Operation, b: Operation): + self.a = a + self.b = b + + def _clean(self): + return self._clean_ab(self.a._clean(), self.b._clean()) + + def _clean_ab(self, a, b): + raise NotImplementedError("_clean_ab not implemented") + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": self.a._serialise_json(), + "b": self.b._serialise_json()} + + @staticmethod + def _deserialise_ab(parameters) -> tuple[Operation, Operation]: + return (Operation.deserialise_json(parameters["a"]), + Operation.deserialise_json(parameters["b"])) + + + def _summary_components(self) -> list["Operation"]: + return [self.a, self.b] + + def _self_cls(self) -> type: + """ Own class""" + def __eq__(self, other): + if isinstance(other, self._self_cls()): + return other.a == self.a and self.b == other.b + +class Add(BinaryOperation): + + serialisation_name = "add" + + def _self_cls(self) -> type: + return Add + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) + self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add(self.a._derivative(hash_value), self.b._derivative(hash_value)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity): + # Convert 0 + b to b + return b + + elif isinstance(b, AdditiveIdentity): + # Convert a + 0 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"+"b" to "a+b" + return Constant(a.evaluate({}) + b.evaluate({}))._clean() + + elif isinstance(a, Neg): + if isinstance(b, Neg): + # Convert (-a)+(-b) to -(a+b) + return Neg(Add(a.a, b.a)) + else: + # Convert (-a) + b to b-a + return Sub(b, a.a) + + elif isinstance(b, Neg): + # Convert a+(-b) to a-b + return Sub(a, b.a) + + elif a == b: + return Mul(Constant(2), a) + + else: + return Add(a, b) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Add(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Add" + +class Sub(BinaryOperation): + + serialisation_name = "sub" + + + def _self_cls(self) -> type: + return Sub + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) - self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Sub(self.a._derivative(hash_value), self.b._derivative(hash_value)) + + def _clean_ab(self, a, b): + if isinstance(a, AdditiveIdentity): + # Convert 0 - b to -b + return Neg(b) + + elif isinstance(b, AdditiveIdentity): + # Convert a - 0 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant pair "a" - "b" to "a-b" + return Constant(a.evaluate({}) - b.evaluate({}))._clean() + + elif isinstance(a, Neg): + if isinstance(b, Neg): + # Convert (-a)-(-b) to b-a + return Sub(b.a, a.a) + else: + # Convert (-a)-b to -(a+b) + return Neg(Add(a.a, b)) + + elif isinstance(b, Neg): + # Convert a-(-b) to a+b + return Add(a, b.a) + + elif a == b: + return AdditiveIdentity() + + else: + return Sub(a, b) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Sub(*BinaryOperation._deserialise_ab(parameters)) + + + def _summary_open(self): + return "Sub" + +class Mul(BinaryOperation): + + serialisation_name = "mul" + + + def _self_cls(self) -> type: + return Mul + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) * self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add(Mul(self.a, self.b._derivative(hash_value)), Mul(self.a._derivative(hash_value), self.b)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): + # Convert 0*b or a*0 to 0 + return AdditiveIdentity() + + elif isinstance(a, MultiplicativeIdentity): + # Convert 1*b to b + return b + + elif isinstance(b, MultiplicativeIdentity): + # Convert a*1 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"*"b" to "a*b" + return Constant(a.evaluate({}) * b.evaluate({}))._clean() + + elif isinstance(a, Inv) and isinstance(b, Inv): + return Inv(Mul(a.a, b.a)) + + elif isinstance(a, Inv) and not isinstance(b, Inv): + return Div(b, a.a) + + elif not isinstance(a, Inv) and isinstance(b, Inv): + return Div(a, b.a) + + elif isinstance(a, Neg): + return Neg(Mul(a.a, b)) + + elif isinstance(b, Neg): + return Neg(Mul(a, b.a)) + + elif a == b: + return Pow(a, 2) + + elif isinstance(a, Pow) and a.a == b: + return Pow(b, a.power + 1) + + elif isinstance(b, Pow) and b.a == a: + return Pow(a, b.power + 1) + + elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: + return Pow(a.a, a.power + b.power) + + else: + return Mul(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Mul(*BinaryOperation._deserialise_ab(parameters)) + + + def _summary_open(self): + return "Mul" + +class Div(BinaryOperation): + + serialisation_name = "div" + + + def _self_cls(self) -> type: + return Div + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) / self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Sub(Div(self.a.derivative(hash_value), self.b), + Div(Mul(self.a, self.b.derivative(hash_value)), Mul(self.b, self.b))) + + def _clean_ab(self, a, b): + if isinstance(a, AdditiveIdentity): + # Convert 0/b to 0 + return AdditiveIdentity() + + elif isinstance(a, MultiplicativeIdentity): + # Convert 1/b to inverse of b + return Inv(b) + + elif isinstance(b, MultiplicativeIdentity): + # Convert a/1 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constants "a"/"b" to "a/b" + return Constant(self.a.evaluate({}) / self.b.evaluate({}))._clean() + + + elif isinstance(a, Inv) and isinstance(b, Inv): + return Div(b.a, a.a) + + elif isinstance(a, Inv) and not isinstance(b, Inv): + return Inv(Mul(a.a, b)) + + elif not isinstance(a, Inv) and isinstance(b, Inv): + return Mul(a, b.a) + + elif a == b: + return MultiplicativeIdentity() + + elif isinstance(a, Pow) and a.a == b: + return Pow(b, a.power - 1) + + elif isinstance(b, Pow) and b.a == a: + return Pow(a, 1 - b.power) + + elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: + return Pow(a.a, a.power - b.power) + + else: + return Div(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Div(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Div" + +class Pow(Operation): + + serialisation_name = "pow" + + def __init__(self, a: Operation, power: float): + self.a = a + self.power = power + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) ** self.power + + def _derivative(self, hash_value: int) -> Operation: + if self.power == 0: + return AdditiveIdentity() + + elif self.power == 1: + return self.a._derivative(hash_value) + + else: + return Mul(Constant(self.power), Mul(Pow(self.a, self.power-1), self.a._derivative(hash_value))) + + def _clean(self) -> Operation: + a = self.a._clean() + + if self.power == 1: + return a + + elif self.power == 0: + return MultiplicativeIdentity() + + elif self.power == -1: + return Inv(a) + + else: + return Pow(a, self.power) + + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": Operation._serialise_json(self.a), + "power": self.power} + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Pow(Operation.deserialise_json(parameters["a"]), parameters["power"]) + + def summary(self, indent_amount: int=0, indent=" "): + return (f"{indent_amount*indent}Pow\n" + + self.a.summary(indent_amount+1, indent) + "\n" + + f"{(indent_amount+1)*indent}{self.power}\n" + + f"{indent_amount*indent})") + + def __eq__(self, other): + if isinstance(other, Pow): + return self.a == other.a and self.power == other.power + + + +# +# Matrix operations +# + +class Transpose(Operation): + """ Transpose operation - as per numpy""" + + serialisation_name = "transpose" + + def __init__(self, a: Operation, axes: tuple[int] | None = None): + self.a = a + self.axes = axes + + def evaluate(self, variables: dict[int, T]) -> T: + return np.transpose(self.a.evaluate(variables)) + + def _derivative(self, hash_value: int) -> Operation: + return Transpose(self.a.derivative(hash_value)) # TODO: Check! + + def _clean(self): + clean_a = self.a._clean() + return Transpose(clean_a) + + + def _serialise_parameters(self) -> dict[str, Any]: + if self.axes is None: + return { "a": self.a._serialise_json() } + else: + return { + "a": self.a._serialise_json(), + "axes": list(self.axes) + } + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + if "axes" in parameters: + return Transpose( + a=Operation.deserialise_json(parameters["a"]), + axes=tuple(parameters["axes"])) + else: + return Transpose( + a=Operation.deserialise_json(parameters["a"])) + + + def _summary_open(self): + return "Transpose" + + def __eq__(self, other): + if isinstance(other, Transpose): + return other.a == self.a + + +class Dot(BinaryOperation): + """ Dot product - backed by numpy's dot method""" + + serialisation_name = "dot" + + def evaluate(self, variables: dict[int, T]) -> T: + return dot(self.a.evaluate(variables), self.b.evaluate(variables)) + + def _derivative(self, hash_value: int) -> Operation: + return Add( + Dot(self.a, + self.b._derivative(hash_value)), + Dot(self.a._derivative(hash_value), + self.b)) + + def _clean_ab(self, a, b): + return Dot(a, b) # Do nothing for now + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Dot(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Dot" + + +# TODO: Add to base operation class, and to quantities +class MatMul(BinaryOperation): + """ Matrix multiplication, using __matmul__ dunder""" + + serialisation_name = "matmul" + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) @ self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add( + MatMul(self.a, + self.b._derivative(hash_value)), + MatMul(self.a._derivative(hash_value), + self.b)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): + # Convert 0*b or a*0 to 0 + return AdditiveIdentity() + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"@"b" to "a@b" + return Constant(a.evaluate({}) @ b.evaluate({}))._clean() + + elif isinstance(a, Neg): + return Neg(Mul(a.a, b)) + + elif isinstance(b, Neg): + return Neg(Mul(a, b.a)) + + return MatMul(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return MatMul(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "MatMul" + +class TensorDot(Operation): + serialisation_name = "tensor_product" + + def __init__(self, a: Operation, b: Operation, a_index: int, b_index: int): + self.a = a + self.b = b + self.a_index = a_index + self.b_index = b_index + + def evaluate(self, variables: dict[int, T]) -> T: + return tensordot(self.a, self.b, self.a_index, self.b_index) + + + def _serialise_parameters(self) -> dict[str, Any]: + return { + "a": self.a._serialise_json(), + "b": self.b._serialise_json(), + "a_index": self.a_index, + "b_index": self.b_index } + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return TensorDot(a = Operation.deserialise_json(parameters["a"]), + b = Operation.deserialise_json(parameters["b"]), + a_index=int(parameters["a_index"]), + b_index=int(parameters["b_index"])) + + def _summary_open(self): + return "TensorProduct" + + +_serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, + Variable, + Neg, Inv, + Add, Sub, Mul, Div, Pow, + Transpose, Dot, MatMul, TensorDot] + +_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} + class UnitError(Exception): """Errors caused by unit specification not being correct""" @@ -29,10 +983,25 @@ def hash_data_via_numpy(*data: ArrayLike): return int(md5_hash.hexdigest(), 16) + +##################################### +# # +# # +# # +# Quantities begin here # +# # +# # +# # +##################################### + + + QuantityType = TypeVar("QuantityType") class QuantityHistory: + """ Class that holds the information for keeping track of operations done on quantities """ + def __init__(self, operation_tree: Operation, references: dict[int, "Quantity"]): self.operation_tree = operation_tree self.references = references @@ -46,6 +1015,10 @@ def jacobian(self) -> list[Operation]: # Use the hash value to specify the variable of differentiation return [self.operation_tree.derivative(key) for key in self.reference_key_list] + def _recalculate(self): + """ Recalculate the value of this object - primary use case is for testing """ + return self.operation_tree.evaluate(self.references) + def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, int]: "Quantity"] = {}): """ Do standard error propagation to calculate the uncertainties associated with this quantity @@ -57,14 +1030,6 @@ def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, raise NotImplementedError("User specified covariances not currently implemented") jacobian = self.jacobian() - # jacobian_units = [quantity_units / self.references[key].units for key in self.reference_key_list] - # - # # Evaluate the jacobian - # # TODO: should we use quantities here, does that work automatically? - # evaluated_jacobian = [Quantity( - # value=entry.evaluate(self.si_reference_values), - # units=unit.si_equivalent()) - # for entry, unit in zip(jacobian, jacobian_units)] evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] @@ -86,7 +1051,7 @@ def variable(quantity: "Quantity"): return QuantityHistory(Variable(quantity.hash_value), {quantity.hash_value: quantity}) @staticmethod - def apply_operation(operation: type[Operation], *histories: "QuantityHistory") -> "QuantityHistory": + def apply_operation(operation: type[Operation], *histories: "QuantityHistory", **extra_parameters) -> "QuantityHistory": """ Apply an operation to the history This is slightly unsafe as it is possible to attempt to apply an n-ary operation to a number of trees other @@ -103,7 +1068,7 @@ def apply_operation(operation: type[Operation], *histories: "QuantityHistory") - references.update(history.references) return QuantityHistory( - operation(*[history.operation_tree for history in histories]), + operation(*[history.operation_tree for history in histories], **extra_parameters), references) def has_variance(self): @@ -113,6 +1078,16 @@ def has_variance(self): return False + def summary(self): + + variable_strings = [self.references[key].string_repr for key in self.references] + + s = "Variables: "+",".join(variable_strings) + s += "\n" + s += self.operation_tree.summary() + + return s + class Quantity[QuantityType]: @@ -135,10 +1110,10 @@ def __init__(self, self.hash_value = -1 """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ - """ Contains the variance if it is data driven, else it is """ + self._variance = None + """ Contains the variance if it is data driven """ if standard_error is None: - self._variance = None self.hash_value = hash_data_via_numpy(hash_seed, value) else: self._variance = standard_error ** 2 @@ -208,14 +1183,14 @@ def __mul__(self: Self, other: ArrayLike | Self ) -> Self: return DerivedQuantity( self.value * other.value, self.units * other.units, - history=QuantityHistory.apply_operation(operations.Mul, self.history, other.history)) + history=QuantityHistory.apply_operation(Mul, self.history, other.history)) else: return DerivedQuantity(self.value * other, self.units, QuantityHistory( - operations.Mul( + Mul( self.history.operation_tree, - operations.Constant(other)), + Constant(other)), self.history.references)) def __rmul__(self: Self, other: ArrayLike | Self): @@ -224,33 +1199,72 @@ def __rmul__(self: Self, other: ArrayLike | Self): other.value * self.value, other.units * self.units, history=QuantityHistory.apply_operation( - operations.Mul, + Mul, other.history, self.history)) else: return DerivedQuantity(other * self.value, self.units, QuantityHistory( - operations.Mul( - operations.Constant(other), + Mul( + Constant(other), + self.history.operation_tree), + self.history.references)) + + + def __matmul__(self, other: ArrayLike | Self): + if isinstance(other, Quantity): + return DerivedQuantity( + self.value @ other.value, + self.units * other.units, + history=QuantityHistory.apply_operation( + MatMul, + self.history, + other.history)) + else: + return DerivedQuantity( + self.value @ other, + self.units, + QuantityHistory( + MatMul( + self.history.operation_tree, + Constant(other)), + self.history.references)) + + def __rmatmul__(self, other: ArrayLike | Self): + if isinstance(other, Quantity): + return DerivedQuantity( + other.value @ self.value, + other.units * self.units, + history=QuantityHistory.apply_operation( + MatMul, + other.history, + self.history)) + + else: + return DerivedQuantity(other @ self.value, self.units, + QuantityHistory( + MatMul( + Constant(other), self.history.operation_tree), self.history.references)) + def __truediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): return DerivedQuantity( self.value / other.value, self.units / other.units, history=QuantityHistory.apply_operation( - operations.Div, + Div, self.history, other.history)) else: return DerivedQuantity(self.value / other, self.units, QuantityHistory( - operations.Div( - operations.Constant(other), + Div( + Constant(other), self.history.operation_tree), self.history.references)) @@ -260,7 +1274,7 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: other.value / self.value, other.units / self.units, history=QuantityHistory.apply_operation( - operations.Div, + Div, other.history, self.history )) @@ -270,8 +1284,8 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: other / self.value, self.units ** -1, QuantityHistory( - operations.Div( - operations.Constant(other), + Div( + Constant(other), self.history.operation_tree), self.history.references)) @@ -282,7 +1296,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: self.value + (other.value * other.units.scale) / self.units.scale, self.units, QuantityHistory.apply_operation( - operations.Add, + Add, self.history, other.history)) else: @@ -296,7 +1310,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: def __neg__(self): return DerivedQuantity(-self.value, self.units, QuantityHistory.apply_operation( - operations.Neg, + Neg, self.history )) @@ -310,7 +1324,7 @@ def __pow__(self: Self, other: int | float): return DerivedQuantity(self.value ** other, self.units ** other, QuantityHistory( - operations.Pow( + Pow( self.history.operation_tree, other), self.history.references)) @@ -362,6 +1376,10 @@ def __repr__(self): def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass + @property + def string_repr(self): + return str(self.hash_value) + class NamedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, @@ -396,6 +1414,10 @@ def with_standard_error(self, standard_error: Quantity): f"are not compatible with value units ({self.units})") + @property + def string_repr(self): + return self.name + class DerivedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): super().__init__(value, units, standard_error=None) From 1df8506c6d5183485733482a91e70f27b413ac41 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Wed, 23 Oct 2024 10:10:03 +0100 Subject: [PATCH 639/675] Fix test import --- test/slicers/utest_meshmerge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/slicers/utest_meshmerge.py b/test/slicers/utest_meshmerge.py index 21071c049..4e4ee83f1 100644 --- a/test/slicers/utest_meshmerge.py +++ b/test/slicers/utest_meshmerge.py @@ -4,7 +4,7 @@ It's pretty hard to test componentwise, but we can do some tests of the general behaviour """ -from sasdata.slicing.meshes import meshmerge +from sasdata.slicing.meshes.meshmerge import meshmerge from test.slicers.meshes_for_testing import ( grid_mesh, shape_mesh, expected_grid_mappings, expected_shape_mappings) From bfca9f527a0b3348e67d80c6f3a620a70871022b Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Fri, 25 Oct 2024 10:46:07 +0100 Subject: [PATCH 640/675] Work on tests for sparse matrix encoding --- sasdata/quantities/test_numerical_encoding.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/test_numerical_encoding.py b/sasdata/quantities/test_numerical_encoding.py index 83fa5fe2a..80cfbad9a 100644 --- a/sasdata/quantities/test_numerical_encoding.py +++ b/sasdata/quantities/test_numerical_encoding.py @@ -62,4 +62,7 @@ def test_numpy_dtypes_encode_decode(dtype): ((6, 1), (1, 0, 5), (0, 0, 0)), ]) def test_coo_matrix_encode_decode(shape, n, m, dtype): - test_matrix = np.arange() + + i_indices = + + values = np.arange(10) \ No newline at end of file From 5ea206c6acee996b75c4f241a14597697b565fc6 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 9 Jan 2025 09:57:04 +0100 Subject: [PATCH 641/675] Fix tests --- sasdata/transforms/rebinning.py | 1 + sasdata/transforms/test_interpolation.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index 7bdc66209..fe96bcb72 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -132,6 +132,7 @@ def calculate_interpolation_matrix_1d(input_axis: Quantity[ArrayLike], if mask is None: return conversion_matrix, None + else: # Create a new mask diff --git a/sasdata/transforms/test_interpolation.py b/sasdata/transforms/test_interpolation.py index 688da65fd..97b4f7911 100644 --- a/sasdata/transforms/test_interpolation.py +++ b/sasdata/transforms/test_interpolation.py @@ -23,7 +23,7 @@ def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Q test_points = NamedQuantity("x_test", np.linspace(-5, 5, 11), units.meters) - mapping = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) + mapping, _ = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) y_original = fun(original_points) y_test = y_original @ mapping @@ -53,7 +53,7 @@ def test_linear_interpolate_different_units(fun: Callable[[Quantity[ArrayLike]], original_points = NamedQuantity("x_base", np.linspace(-10,10, 107), units.meters) test_points = NamedQuantity("x_test", np.linspace(-5000, 5000, 11), units.millimeters) - mapping = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) + mapping, _ = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) y_original = fun(original_points) y_test = y_original @ mapping @@ -83,7 +83,7 @@ def test_linearity_linear(): x_and_y = NamedQuantity("x_base", np.linspace(-10, 10, 2), units.meters) new_x = NamedQuantity("x_test", np.linspace(-5000, 5000, 101), units.millimeters) - mapping = calculate_interpolation_matrix_1d(x_and_y, new_x, order=InterpolationOptions.LINEAR) + mapping, _ = calculate_interpolation_matrix_1d(x_and_y, new_x, order=InterpolationOptions.LINEAR) linear_points = x_and_y @ mapping From 7b5956a7c3b92ab54687f97079d5cdacf15154c9 Mon Sep 17 00:00:00 2001 From: lucas-wilkins Date: Thu, 9 Jan 2025 10:02:11 +0100 Subject: [PATCH 642/675] Disable machine specific mesh test for MacOS --- test/slicers/utest_meshmerge.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/slicers/utest_meshmerge.py b/test/slicers/utest_meshmerge.py index 4e4ee83f1..d83892def 100644 --- a/test/slicers/utest_meshmerge.py +++ b/test/slicers/utest_meshmerge.py @@ -17,6 +17,10 @@ def test_meshmerge_mappings(): tests might not be reliable... we'll see how they play out """ + import sys + if sys.platform == "darwin": + # It does indeed rely on machine precision, only run on windows and linux + return combined_mesh, grid_mappings, shape_mappings = meshmerge(grid_mesh, shape_mesh) From 72247b0de4e5a15670b6acd0b84493c0abae3681 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 28 Jan 2025 08:38:02 +0000 Subject: [PATCH 643/675] Make import paths absolute. --- sasdata/data.py | 4 ++-- sasdata/metadata.py | 2 +- sasdata/quantities/absolute_temperature.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 544ba27d3..4c94a5be9 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -4,7 +4,7 @@ import numpy as np -from quantities.quantity import NamedQuantity +from sasdata.quantities.quantity import NamedQuantity from sasdata.metadata import Metadata from sasdata.quantities.accessors import AccessorTarget from sasdata.data_backing import Group, key_tree @@ -42,4 +42,4 @@ def summary(self, indent = " ", include_raw=False): if include_raw: s += key_tree(self._raw_metadata) - return s \ No newline at end of file + return s diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 9b5a2b51f..dc11b1636 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -4,7 +4,7 @@ from numpy.typing import ArrayLike import sasdata.quantities.units as units -from quantities.absolute_temperature import AbsoluteTemperatureAccessor +from sasdata.quantities.absolute_temperature import AbsoluteTemperatureAccessor from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor, \ DimensionlessAccessor, FloatAccessor, TemperatureAccessor, AccessorTarget diff --git a/sasdata/quantities/absolute_temperature.py b/sasdata/quantities/absolute_temperature.py index 95c8982fb..ecfd0e6d9 100644 --- a/sasdata/quantities/absolute_temperature.py +++ b/sasdata/quantities/absolute_temperature.py @@ -1,6 +1,6 @@ from typing import TypeVar -from quantities.quantity import Quantity +from sasdata.quantities.quantity import Quantity from sasdata.quantities.accessors import TemperatureAccessor From bdafc0b372bf1e3190ce84ead660a7d0059eaa8f Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 17 Feb 2025 12:34:53 +0000 Subject: [PATCH 644/675] Fix import of _units_base by _build_tables.py --- sasdata/quantities/_build_tables.py | 9 ++++++++- sasdata/quantities/_units_base.py | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index c302802d6..2e3a13de9 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -131,7 +131,14 @@ def format_name(name: str): with open("_units_base.py", 'r') as base: for line in base: - fid.write(line) + # unicode_superscript is a local module when called from + # _unit_tables.py but a submodule of sasdata.quantities + # when called from units.py. This condition patches the + # line when the copy is made. + if line.startswith("from unicode_superscript"): + fid.write(line.replace("from unicode_superscript", "from sasdata.quantities.unicode_superscript")) + else: + fid.write(line) # Write in unit definitions fid.write("\n\n" diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 5a990ea27..17a5b6f1e 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -4,7 +4,7 @@ import numpy as np -from sasdata.quantities.unicode_superscript import int_as_unicode_superscript +from unicode_superscript import int_as_unicode_superscript class DimensionError(Exception): pass From f6e4e9bc47bda5b9d2fcbe16f1827ce941a73a68 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 17 Feb 2025 13:19:26 +0000 Subject: [PATCH 645/675] Add latex symbols to units which need them --- sasdata/quantities/_build_tables.py | 185 +++++++++++++++------------- sasdata/quantities/_units_base.py | 2 + sasdata/quantities/units.py | 70 ++++++----- 3 files changed, 134 insertions(+), 123 deletions(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 2e3a13de9..546720d29 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -3,85 +3,89 @@ """ import numpy as np -from collections import defaultdict +from collections import defaultdict, namedtuple from _units_base import Dimensions, Unit from _autogen_warning import warning_text -bigger_magnitudes = [ - ("E", None, "exa", 1e18), - ("P", None, "peta", 1e15), - ("T", None, "tera", 1e12), - ("G", None, "giga", 1e9), - ("M", None, "mega", 1e6), - ("k", None, "kilo", 1e3) ] - -smaller_magnitudes = [ - ("m", None, "milli", 1e-3), - ("u", "µ", "micro", 1e-6), - ("n", None, "nano", 1e-9), - ("p", None, "pico", 1e-12), - ("f", None, "femto", 1e-15), - ("a", None, "atto", 1e-18)] - -unusual_magnitudes = [ - ("d", None, "deci", 1e-1), - ("c", None, "centi", 1e-2) +Magnitude = namedtuple("Magnitude", ["symbol", "special_symbol", "latex_symbol", "name", "scale"]) + +bigger_magnitudes: list[Magnitude] = [ + Magnitude("E", None, None, "exa", 1e18), + Magnitude("P", None, None, "peta", 1e15), + Magnitude("T", None, None, "tera", 1e12), + Magnitude("G", None, None, "giga", 1e9), + Magnitude("M", None, None, "mega", 1e6), + Magnitude("k", None, None, "kilo", 1e3) ] + +smaller_magnitudes: list[Magnitude] = [ + Magnitude("m", None, None, "milli", 1e-3), + Magnitude("u", "µ", r"\mu", "micro", 1e-6), + Magnitude("n", None, None, "nano", 1e-9), + Magnitude("p", None, None, "pico", 1e-12), + Magnitude("f", None, None, "femto", 1e-15), + Magnitude("a", None, None, "atto", 1e-18)] + +unusual_magnitudes: list[Magnitude] = [ + Magnitude("d", None, None, "deci", 1e-1), + Magnitude("c", None, None, "centi", 1e-2) ] all_magnitudes = bigger_magnitudes + smaller_magnitudes +UnitData = namedtuple("UnitData", ["symbol", "special_symbol", "latex_symbol", "singular", "plural", "scale", "length", "time", "mass", "current", "temperature", "moles_hint", "angle_hint", "magnitudes"]) + # Length, time, mass, current, temperature base_si_units = [ - ("m", None, "meter", "meters", 1, 1, 0, 0, 0, 0, 0, 0, all_magnitudes + unusual_magnitudes), - ("s", None, "second", "seconds", 1, 0, 1, 0, 0, 0, 0, 0, smaller_magnitudes), - ("g", None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, 0, 0, all_magnitudes), - ("A", None, "ampere", "amperes", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), - ("K", None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, 0, 0, all_magnitudes) ] + UnitData("m", None, None, "meter", "meters", 1, 1, 0, 0, 0, 0, 0, 0, all_magnitudes + unusual_magnitudes), + UnitData("s", None, None, "second", "seconds", 1, 0, 1, 0, 0, 0, 0, 0, smaller_magnitudes), + UnitData("g", None, None, "gram", "grams", 1e-3, 0, 0, 1, 0, 0, 0, 0, all_magnitudes), + UnitData("A", None, None, "ampere", "amperes", 1, 0, 0, 0, 1, 0, 0, 0, all_magnitudes), + UnitData("K", None, None, "kelvin", "kelvin", 1, 0, 0, 0, 0, 1, 0, 0, all_magnitudes) ] derived_si_units = [ - ("Hz", None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, 0, 0, all_magnitudes), - ("N", None, "newton", "newtons", 1, 1, -2, 1, 0, 0, 0, 0, all_magnitudes), - ("Pa", None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, 0, 0, all_magnitudes), - ("J", None, "joule", "joules", 1, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), - ("W", None, "watt", "watts", 1, 2, -3, 1, 0, 0, 0, 0, all_magnitudes), - ("C", None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, 0, 0, all_magnitudes), - ("V", None, "volts", "volts", 1, 2, -3, 1, -1, 0, 0, 0, all_magnitudes), - ("Ohm", "Ω", "ohm", "ohms", 1, 2, -3, 1, -2, 0, 0, 0, all_magnitudes), - ("F", None, "farad", "farads", 1, -2, 4, -1, 2, 0, 0, 0, all_magnitudes), - ("S", None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, 0, 0, all_magnitudes), - ("Wb", None, "weber", "webers", 1, 2, -2, 1, -1, 0, 0, 0, all_magnitudes), - ("T", None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, 0, 0, all_magnitudes), - ("H", None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), + UnitData("Hz", None, None, "hertz", "hertz", 1, 0, -1, 0, 0, 0, 0, 0, all_magnitudes), + UnitData("N", None, None, "newton", "newtons", 1, 1, -2, 1, 0, 0, 0, 0, all_magnitudes), + UnitData("Pa", None, None, "pascal", "pascals", 1, -1, -2, 1, 0, 0, 0, 0, all_magnitudes), + UnitData("J", None, None, "joule", "joules", 1, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), + UnitData("W", None, None, "watt", "watts", 1, 2, -3, 1, 0, 0, 0, 0, all_magnitudes), + UnitData("C", None, None, "coulomb", "coulombs", 1, 0, 1, 0, 1, 0, 0, 0, all_magnitudes), + UnitData("V", None, None, "volts", "volts", 1, 2, -3, 1, -1, 0, 0, 0, all_magnitudes), + UnitData("Ohm", "Ω", r"\Omega", "ohm", "ohms", 1, 2, -3, 1, -2, 0, 0, 0, all_magnitudes), + UnitData("F", None, None, "farad", "farads", 1, -2, 4, -1, 2, 0, 0, 0, all_magnitudes), + UnitData("S", None, None, "siemens", "siemens", 1, -2, 3, -1, 2, 0, 0, 0, all_magnitudes), + UnitData("Wb", None, None, "weber", "webers", 1, 2, -2, 1, -1, 0, 0, 0, all_magnitudes), + UnitData("T", None, None, "tesla", "tesla", 1, 0, -2, 1, -1, 0, 0, 0, all_magnitudes), + UnitData("H", None, None, "henry", "henry", 1, 2, -2, 1, -2, 0, 0, 0, all_magnitudes), ] non_si_dimensioned_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ - ("Ang", "Å", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, 0, 0, []), - ("min", None, "minute", "minutes", 60, 0, 1, 0, 0, 0, 0, 0, []), - ("h", None, "hour", "hours", 360, 0, 1, 0, 0, 0, 0, 0, []), - ("d", None, "day", "days", 360*24, 0, 1, 0, 0, 0, 0, 0, []), - ("y", None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, 0, 0, []), - ("deg", None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, 0, 1, []), - ("rad", None, "radian", "radians", 1, 0, 0, 0, 0, 0, 0, 1, []), - ("sr", None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, 0, 2, []), - ("l", None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), - ("eV", None, "electronvolt", "electronvolts", 1.602176634e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), - ("au", None, "atomic mass unit", "atomic mass units", 1.660538921e-27, 0, 0, 1, 0, 0, 0, 0, []), - ("mol", None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes), - ("kgForce", None, "kg force", "kg force", 9.80665, 1, -2, 1, 0, 0, 0, 0, []), - ("C", None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []), - ("miles", None, "mile", "miles", 1760*3*0.3048, 1, 0, 0, 0, 0, 0, 0, []), - ("yrd", None, "yard", "yards", 3*0.3048, 1, 0, 0, 0, 0, 0, 0, []), - ("ft", None, "foot", "feet", 0.3048, 1, 0, 0, 0, 0, 0, 0, []), - ("in", None, "inch", "inches", 0.0254, 1, 0, 0, 0, 0, 0, 0, []), - ("lb", None, "pound", "pounds", 0.45359237, 0, 0, 1, 0, 0, 0, 0, []), - ("lbf", None, "pound force", "pounds force", 4.448222, 1, -2, 1, 0, 0, 0, 0, []), - ("oz", None, "ounce", "ounces", 0.45359237/16, 0, 0, 1, 0, 0, 0, 0, []), - ("psi", None, "pound force per square inch", "pounds force per square inch", 4.448222/(0.0254**2), -1, -2, 1, 0, 0, 0, 0, []), + UnitData("Ang", "Å", r"\AA", "angstrom", "angstroms", 1e-10, 1, 0, 0, 0, 0, 0, 0, []), + UnitData("min", None, None, "minute", "minutes", 60, 0, 1, 0, 0, 0, 0, 0, []), + UnitData("h", None, None, "hour", "hours", 360, 0, 1, 0, 0, 0, 0, 0, []), + UnitData("d", None, None, "day", "days", 360*24, 0, 1, 0, 0, 0, 0, 0, []), + UnitData("y", None, None, "year", "years", 360*24*365.2425, 0, 1, 0, 0, 0, 0, 0, []), + UnitData("deg", None, None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, 0, 1, []), + UnitData("rad", None, None, "radian", "radians", 1, 0, 0, 0, 0, 0, 0, 1, []), + UnitData("sr", None, None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, 0, 2, []), + UnitData("l", None, None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), + UnitData("eV", None, None, "electronvolt", "electronvolts", 1.602176634e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), + UnitData("au", None, None, "atomic mass unit", "atomic mass units", 1.660538921e-27, 0, 0, 1, 0, 0, 0, 0, []), + UnitData("mol", None, None, "mole", "moles", 6.02214076e23, 0, 0, 0, 0, 0, 1, 0, smaller_magnitudes), + UnitData("kgForce", None, None, "kg force", "kg force", 9.80665, 1, -2, 1, 0, 0, 0, 0, []), + UnitData("C", None, None, "degree Celsius", "degrees Celsius", 1, 0, 0, 0, 0, 1, 0, 0, []), + UnitData("miles", None, None, "mile", "miles", 1760*3*0.3048, 1, 0, 0, 0, 0, 0, 0, []), + UnitData("yrd", None, None, "yard", "yards", 3*0.3048, 1, 0, 0, 0, 0, 0, 0, []), + UnitData("ft", None, None, "foot", "feet", 0.3048, 1, 0, 0, 0, 0, 0, 0, []), + UnitData("in", None, None, "inch", "inches", 0.0254, 1, 0, 0, 0, 0, 0, 0, []), + UnitData("lb", None, None, "pound", "pounds", 0.45359237, 0, 0, 1, 0, 0, 0, 0, []), + UnitData("lbf", None, None, "pound force", "pounds force", 4.448222, 1, -2, 1, 0, 0, 0, 0, []), + UnitData("oz", None, None, "ounce", "ounces", 0.45359237/16, 0, 0, 1, 0, 0, 0, 0, []), + UnitData("psi", None, None, "pound force per square inch", "pounds force per square inch", 4.448222/(0.0254**2), -1, -2, 1, 0, 0, 0, 0, []), ] non_si_dimensionless_units: list[tuple[str, str | None, str, str, float, int, int, int, int, int, int, int, list]] = [ - ("none", None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), - ("percent", "%", "percent", "percent", 0.01, 0, 0, 0, 0, 0, 0, 0, []) + UnitData("none", None, None, "none", "none", 1, 0, 0, 0, 0, 0, 0, 0, []), + UnitData("percent", "%", r"\%", "percent", "percent", 0.01, 0, 0, 0, 0, 0, 0, 0, []) ] non_si_units = non_si_dimensioned_units + non_si_dimensionless_units @@ -152,51 +156,54 @@ def format_name(name: str): for unit_def in all_units: - try: - symbol, special_symbol, singular, plural, scale, length, time, \ - mass, current, temperature, moles_hint, angle_hint, magnitudes = unit_def - except Exception as e: - print(unit_def) - raise e - - formatted_plural = format_name(plural) - formatted_singular = format_name(singular) + formatted_plural = format_name(unit_def.plural) + formatted_singular = format_name(unit_def.singular) - dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) - fid.write(f"{formatted_plural} = NamedUnit({scale}, Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," + dimensions = Dimensions(unit_def.length, unit_def.time, unit_def.mass, unit_def.current, unit_def.temperature, unit_def.moles_hint, unit_def.angle_hint) + fid.write(f"{formatted_plural} = NamedUnit({unit_def.scale}, Dimensions({unit_def.length}, {unit_def.time}, {unit_def.mass}, {unit_def.current}, {unit_def.temperature}, {unit_def.moles_hint}, {unit_def.angle_hint})," f"name='{formatted_plural}'," - f"ascii_symbol='{symbol}'," - f"symbol='{symbol if special_symbol is None else special_symbol}')\n") + f"ascii_symbol='{unit_def.symbol}'," + f"{'' if unit_def.latex_symbol is None else f"""latex_symbol=r'{unit_def.latex_symbol}',""" }" + f"symbol='{unit_def.symbol if unit_def.special_symbol is None else unit_def.special_symbol}')\n") - symbol_lookup[symbol] = formatted_plural - if special_symbol is not None: - symbol_lookup[special_symbol] = formatted_plural + symbol_lookup[unit_def.symbol] = formatted_plural + if unit_def.special_symbol is not None: + symbol_lookup[unit_def.special_symbol] = formatted_plural unit_types_temp[hash(dimensions)].append( - (symbol, special_symbol, formatted_singular, formatted_plural, scale, dimensions)) + (unit_def.symbol, unit_def.special_symbol, formatted_singular, formatted_plural, unit_def.scale, dimensions)) unit_types[hash(dimensions)].append(formatted_plural) - for mag_symbol, mag_special_symbol, name, mag_scale in magnitudes: + for mag in unit_def.magnitudes: # Work out the combined symbol, accounts for unicode or not - combined_special_symbol = (mag_symbol if mag_special_symbol is None else mag_special_symbol) + \ - (symbol if special_symbol is None else special_symbol) + combined_special_symbol = (mag.symbol if mag.special_symbol is None else mag.special_symbol) + \ + (unit_def.symbol if unit_def.special_symbol is None else unit_def.special_symbol) - combined_symbol = mag_symbol + symbol + combined_symbol = mag.symbol + unit_def.symbol # Combined unit name - combined_name_singular = f"{name}{formatted_singular}" - combined_name_plural = f"{name}{formatted_plural}" + combined_name_singular = f"{mag.name}{formatted_singular}" + combined_name_plural = f"{mag.name}{formatted_plural}" + + combined_scale = unit_def.scale * mag.scale - combined_scale = scale * mag_scale + latex_symbol = None + if unit_def.latex_symbol is not None and mag.latex_symbol is not None: + latex_symbol = f"{{{mag.latex_symbol}}}{unit_def.latex_symbol}" + elif unit_def.latex_symbol is not None: + latex_symbol = f"{mag.symbol}{unit_def.latex_symbol}" + elif mag.latex_symbol is not None: + latex_symbol = f"{{{mag.latex_symbol}}}{unit_def.symbol}" # Units - dimensions = Dimensions(length, time, mass, current, temperature, moles_hint, angle_hint) + dimensions = Dimensions(unit_def.length, unit_def.time, unit_def.mass, unit_def.current, unit_def.temperature, unit_def.moles_hint, unit_def.angle_hint) fid.write(f"{combined_name_plural} = NamedUnit({combined_scale}, " - f"Dimensions({length}, {time}, {mass}, {current}, {temperature}, {moles_hint}, {angle_hint})," + f"Dimensions({unit_def.length}, {unit_def.time}, {unit_def.mass}, {unit_def.current}, {unit_def.temperature}, {unit_def.moles_hint}, {unit_def.angle_hint})," f"name='{combined_name_plural}'," f"ascii_symbol='{combined_symbol}'," + f"{'' if latex_symbol is None else f"""latex_symbol=r'{latex_symbol}',""" }" f"symbol='{combined_special_symbol}')\n") symbol_lookup[combined_symbol] = combined_name_plural @@ -425,7 +432,7 @@ def format_name(name: str): with open("si.py", 'w') as fid: fid.write('"""'+(warning_text%"_build_tables.py")+'"""\n\n') - si_unit_names = [values[3] for values in base_si_units + derived_si_units if values[3] != "grams"] + ["kilograms"] + si_unit_names = [values.plural for values in base_si_units + derived_si_units if values.plural != "grams"] + ["kilograms"] for name in si_unit_names: diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 17a5b6f1e..518c3d4e3 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -279,12 +279,14 @@ def __init__(self, dimensions: Dimensions, name: str | None = None, ascii_symbol: str | None = None, + latex_symbol: str | None = None, symbol: str | None = None): super().__init__(si_scaling_factor, dimensions) self.name = name self.ascii_symbol = ascii_symbol self.symbol = symbol + self.latex_symbol = latex_symbol if latex_symbol is not None else ascii_symbol def __repr__(self): return self.name diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 834bb2436..bd35dc36e 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -363,12 +363,14 @@ def __init__(self, dimensions: Dimensions, name: str | None = None, ascii_symbol: str | None = None, + latex_symbol: str | None = None, symbol: str | None = None): super().__init__(si_scaling_factor, dimensions) self.name = name self.ascii_symbol = ascii_symbol self.symbol = symbol + self.latex_symbol = latex_symbol if latex_symbol is not None else ascii_symbol def __repr__(self): return self.name @@ -437,7 +439,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megameters = NamedUnit(1000000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='megameters',ascii_symbol='Mm',symbol='Mm') kilometers = NamedUnit(1000.0, Dimensions(1, 0, 0, 0, 0, 0, 0),name='kilometers',ascii_symbol='km',symbol='km') millimeters = NamedUnit(0.001, Dimensions(1, 0, 0, 0, 0, 0, 0),name='millimeters',ascii_symbol='mm',symbol='mm') -micrometers = NamedUnit(1e-06, Dimensions(1, 0, 0, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',symbol='µm') +micrometers = NamedUnit(1e-06, Dimensions(1, 0, 0, 0, 0, 0, 0),name='micrometers',ascii_symbol='um',latex_symbol=r'{\mu}m',symbol='µm') nanometers = NamedUnit(1e-09, Dimensions(1, 0, 0, 0, 0, 0, 0),name='nanometers',ascii_symbol='nm',symbol='nm') picometers = NamedUnit(1e-12, Dimensions(1, 0, 0, 0, 0, 0, 0),name='picometers',ascii_symbol='pm',symbol='pm') femtometers = NamedUnit(1e-15, Dimensions(1, 0, 0, 0, 0, 0, 0),name='femtometers',ascii_symbol='fm',symbol='fm') @@ -446,7 +448,7 @@ def __init__(self, name: str, units: list[NamedUnit]): centimeters = NamedUnit(0.01, Dimensions(1, 0, 0, 0, 0, 0, 0),name='centimeters',ascii_symbol='cm',symbol='cm') seconds = NamedUnit(1, Dimensions(0, 1, 0, 0, 0, 0, 0),name='seconds',ascii_symbol='s',symbol='s') milliseconds = NamedUnit(0.001, Dimensions(0, 1, 0, 0, 0, 0, 0),name='milliseconds',ascii_symbol='ms',symbol='ms') -microseconds = NamedUnit(1e-06, Dimensions(0, 1, 0, 0, 0, 0, 0),name='microseconds',ascii_symbol='us',symbol='µs') +microseconds = NamedUnit(1e-06, Dimensions(0, 1, 0, 0, 0, 0, 0),name='microseconds',ascii_symbol='us',latex_symbol=r'{\mu}s',symbol='µs') nanoseconds = NamedUnit(1e-09, Dimensions(0, 1, 0, 0, 0, 0, 0),name='nanoseconds',ascii_symbol='ns',symbol='ns') picoseconds = NamedUnit(1e-12, Dimensions(0, 1, 0, 0, 0, 0, 0),name='picoseconds',ascii_symbol='ps',symbol='ps') femtoseconds = NamedUnit(1e-15, Dimensions(0, 1, 0, 0, 0, 0, 0),name='femtoseconds',ascii_symbol='fs',symbol='fs') @@ -459,7 +461,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megagrams = NamedUnit(1000.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='megagrams',ascii_symbol='Mg',symbol='Mg') kilograms = NamedUnit(1.0, Dimensions(0, 0, 1, 0, 0, 0, 0),name='kilograms',ascii_symbol='kg',symbol='kg') milligrams = NamedUnit(1e-06, Dimensions(0, 0, 1, 0, 0, 0, 0),name='milligrams',ascii_symbol='mg',symbol='mg') -micrograms = NamedUnit(1e-09, Dimensions(0, 0, 1, 0, 0, 0, 0),name='micrograms',ascii_symbol='ug',symbol='µg') +micrograms = NamedUnit(1e-09, Dimensions(0, 0, 1, 0, 0, 0, 0),name='micrograms',ascii_symbol='ug',latex_symbol=r'{\mu}g',symbol='µg') nanograms = NamedUnit(1.0000000000000002e-12, Dimensions(0, 0, 1, 0, 0, 0, 0),name='nanograms',ascii_symbol='ng',symbol='ng') picograms = NamedUnit(1e-15, Dimensions(0, 0, 1, 0, 0, 0, 0),name='picograms',ascii_symbol='pg',symbol='pg') femtograms = NamedUnit(1e-18, Dimensions(0, 0, 1, 0, 0, 0, 0),name='femtograms',ascii_symbol='fg',symbol='fg') @@ -472,7 +474,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megaamperes = NamedUnit(1000000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='megaamperes',ascii_symbol='MA',symbol='MA') kiloamperes = NamedUnit(1000.0, Dimensions(0, 0, 0, 1, 0, 0, 0),name='kiloamperes',ascii_symbol='kA',symbol='kA') milliamperes = NamedUnit(0.001, Dimensions(0, 0, 0, 1, 0, 0, 0),name='milliamperes',ascii_symbol='mA',symbol='mA') -microamperes = NamedUnit(1e-06, Dimensions(0, 0, 0, 1, 0, 0, 0),name='microamperes',ascii_symbol='uA',symbol='µA') +microamperes = NamedUnit(1e-06, Dimensions(0, 0, 0, 1, 0, 0, 0),name='microamperes',ascii_symbol='uA',latex_symbol=r'{\mu}A',symbol='µA') nanoamperes = NamedUnit(1e-09, Dimensions(0, 0, 0, 1, 0, 0, 0),name='nanoamperes',ascii_symbol='nA',symbol='nA') picoamperes = NamedUnit(1e-12, Dimensions(0, 0, 0, 1, 0, 0, 0),name='picoamperes',ascii_symbol='pA',symbol='pA') femtoamperes = NamedUnit(1e-15, Dimensions(0, 0, 0, 1, 0, 0, 0),name='femtoamperes',ascii_symbol='fA',symbol='fA') @@ -485,7 +487,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megakelvin = NamedUnit(1000000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='megakelvin',ascii_symbol='MK',symbol='MK') kilokelvin = NamedUnit(1000.0, Dimensions(0, 0, 0, 0, 1, 0, 0),name='kilokelvin',ascii_symbol='kK',symbol='kK') millikelvin = NamedUnit(0.001, Dimensions(0, 0, 0, 0, 1, 0, 0),name='millikelvin',ascii_symbol='mK',symbol='mK') -microkelvin = NamedUnit(1e-06, Dimensions(0, 0, 0, 0, 1, 0, 0),name='microkelvin',ascii_symbol='uK',symbol='µK') +microkelvin = NamedUnit(1e-06, Dimensions(0, 0, 0, 0, 1, 0, 0),name='microkelvin',ascii_symbol='uK',latex_symbol=r'{\mu}K',symbol='µK') nanokelvin = NamedUnit(1e-09, Dimensions(0, 0, 0, 0, 1, 0, 0),name='nanokelvin',ascii_symbol='nK',symbol='nK') picokelvin = NamedUnit(1e-12, Dimensions(0, 0, 0, 0, 1, 0, 0),name='picokelvin',ascii_symbol='pK',symbol='pK') femtokelvin = NamedUnit(1e-15, Dimensions(0, 0, 0, 0, 1, 0, 0),name='femtokelvin',ascii_symbol='fK',symbol='fK') @@ -498,7 +500,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megahertz = NamedUnit(1000000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='megahertz',ascii_symbol='MHz',symbol='MHz') kilohertz = NamedUnit(1000.0, Dimensions(0, -1, 0, 0, 0, 0, 0),name='kilohertz',ascii_symbol='kHz',symbol='kHz') millihertz = NamedUnit(0.001, Dimensions(0, -1, 0, 0, 0, 0, 0),name='millihertz',ascii_symbol='mHz',symbol='mHz') -microhertz = NamedUnit(1e-06, Dimensions(0, -1, 0, 0, 0, 0, 0),name='microhertz',ascii_symbol='uHz',symbol='µHz') +microhertz = NamedUnit(1e-06, Dimensions(0, -1, 0, 0, 0, 0, 0),name='microhertz',ascii_symbol='uHz',latex_symbol=r'{\mu}Hz',symbol='µHz') nanohertz = NamedUnit(1e-09, Dimensions(0, -1, 0, 0, 0, 0, 0),name='nanohertz',ascii_symbol='nHz',symbol='nHz') picohertz = NamedUnit(1e-12, Dimensions(0, -1, 0, 0, 0, 0, 0),name='picohertz',ascii_symbol='pHz',symbol='pHz') femtohertz = NamedUnit(1e-15, Dimensions(0, -1, 0, 0, 0, 0, 0),name='femtohertz',ascii_symbol='fHz',symbol='fHz') @@ -511,7 +513,7 @@ def __init__(self, name: str, units: list[NamedUnit]): meganewtons = NamedUnit(1000000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='meganewtons',ascii_symbol='MN',symbol='MN') kilonewtons = NamedUnit(1000.0, Dimensions(1, -2, 1, 0, 0, 0, 0),name='kilonewtons',ascii_symbol='kN',symbol='kN') millinewtons = NamedUnit(0.001, Dimensions(1, -2, 1, 0, 0, 0, 0),name='millinewtons',ascii_symbol='mN',symbol='mN') -micronewtons = NamedUnit(1e-06, Dimensions(1, -2, 1, 0, 0, 0, 0),name='micronewtons',ascii_symbol='uN',symbol='µN') +micronewtons = NamedUnit(1e-06, Dimensions(1, -2, 1, 0, 0, 0, 0),name='micronewtons',ascii_symbol='uN',latex_symbol=r'{\mu}N',symbol='µN') nanonewtons = NamedUnit(1e-09, Dimensions(1, -2, 1, 0, 0, 0, 0),name='nanonewtons',ascii_symbol='nN',symbol='nN') piconewtons = NamedUnit(1e-12, Dimensions(1, -2, 1, 0, 0, 0, 0),name='piconewtons',ascii_symbol='pN',symbol='pN') femtonewtons = NamedUnit(1e-15, Dimensions(1, -2, 1, 0, 0, 0, 0),name='femtonewtons',ascii_symbol='fN',symbol='fN') @@ -524,7 +526,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megapascals = NamedUnit(1000000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='megapascals',ascii_symbol='MPa',symbol='MPa') kilopascals = NamedUnit(1000.0, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='kilopascals',ascii_symbol='kPa',symbol='kPa') millipascals = NamedUnit(0.001, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='millipascals',ascii_symbol='mPa',symbol='mPa') -micropascals = NamedUnit(1e-06, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='micropascals',ascii_symbol='uPa',symbol='µPa') +micropascals = NamedUnit(1e-06, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='micropascals',ascii_symbol='uPa',latex_symbol=r'{\mu}Pa',symbol='µPa') nanopascals = NamedUnit(1e-09, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='nanopascals',ascii_symbol='nPa',symbol='nPa') picopascals = NamedUnit(1e-12, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='picopascals',ascii_symbol='pPa',symbol='pPa') femtopascals = NamedUnit(1e-15, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='femtopascals',ascii_symbol='fPa',symbol='fPa') @@ -537,7 +539,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megajoules = NamedUnit(1000000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='megajoules',ascii_symbol='MJ',symbol='MJ') kilojoules = NamedUnit(1000.0, Dimensions(2, -2, 1, 0, 0, 0, 0),name='kilojoules',ascii_symbol='kJ',symbol='kJ') millijoules = NamedUnit(0.001, Dimensions(2, -2, 1, 0, 0, 0, 0),name='millijoules',ascii_symbol='mJ',symbol='mJ') -microjoules = NamedUnit(1e-06, Dimensions(2, -2, 1, 0, 0, 0, 0),name='microjoules',ascii_symbol='uJ',symbol='µJ') +microjoules = NamedUnit(1e-06, Dimensions(2, -2, 1, 0, 0, 0, 0),name='microjoules',ascii_symbol='uJ',latex_symbol=r'{\mu}J',symbol='µJ') nanojoules = NamedUnit(1e-09, Dimensions(2, -2, 1, 0, 0, 0, 0),name='nanojoules',ascii_symbol='nJ',symbol='nJ') picojoules = NamedUnit(1e-12, Dimensions(2, -2, 1, 0, 0, 0, 0),name='picojoules',ascii_symbol='pJ',symbol='pJ') femtojoules = NamedUnit(1e-15, Dimensions(2, -2, 1, 0, 0, 0, 0),name='femtojoules',ascii_symbol='fJ',symbol='fJ') @@ -550,7 +552,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megawatts = NamedUnit(1000000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='megawatts',ascii_symbol='MW',symbol='MW') kilowatts = NamedUnit(1000.0, Dimensions(2, -3, 1, 0, 0, 0, 0),name='kilowatts',ascii_symbol='kW',symbol='kW') milliwatts = NamedUnit(0.001, Dimensions(2, -3, 1, 0, 0, 0, 0),name='milliwatts',ascii_symbol='mW',symbol='mW') -microwatts = NamedUnit(1e-06, Dimensions(2, -3, 1, 0, 0, 0, 0),name='microwatts',ascii_symbol='uW',symbol='µW') +microwatts = NamedUnit(1e-06, Dimensions(2, -3, 1, 0, 0, 0, 0),name='microwatts',ascii_symbol='uW',latex_symbol=r'{\mu}W',symbol='µW') nanowatts = NamedUnit(1e-09, Dimensions(2, -3, 1, 0, 0, 0, 0),name='nanowatts',ascii_symbol='nW',symbol='nW') picowatts = NamedUnit(1e-12, Dimensions(2, -3, 1, 0, 0, 0, 0),name='picowatts',ascii_symbol='pW',symbol='pW') femtowatts = NamedUnit(1e-15, Dimensions(2, -3, 1, 0, 0, 0, 0),name='femtowatts',ascii_symbol='fW',symbol='fW') @@ -563,7 +565,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megacoulombs = NamedUnit(1000000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='megacoulombs',ascii_symbol='MC',symbol='MC') kilocoulombs = NamedUnit(1000.0, Dimensions(0, 1, 0, 1, 0, 0, 0),name='kilocoulombs',ascii_symbol='kC',symbol='kC') millicoulombs = NamedUnit(0.001, Dimensions(0, 1, 0, 1, 0, 0, 0),name='millicoulombs',ascii_symbol='mC',symbol='mC') -microcoulombs = NamedUnit(1e-06, Dimensions(0, 1, 0, 1, 0, 0, 0),name='microcoulombs',ascii_symbol='uC',symbol='µC') +microcoulombs = NamedUnit(1e-06, Dimensions(0, 1, 0, 1, 0, 0, 0),name='microcoulombs',ascii_symbol='uC',latex_symbol=r'{\mu}C',symbol='µC') nanocoulombs = NamedUnit(1e-09, Dimensions(0, 1, 0, 1, 0, 0, 0),name='nanocoulombs',ascii_symbol='nC',symbol='nC') picocoulombs = NamedUnit(1e-12, Dimensions(0, 1, 0, 1, 0, 0, 0),name='picocoulombs',ascii_symbol='pC',symbol='pC') femtocoulombs = NamedUnit(1e-15, Dimensions(0, 1, 0, 1, 0, 0, 0),name='femtocoulombs',ascii_symbol='fC',symbol='fC') @@ -576,24 +578,24 @@ def __init__(self, name: str, units: list[NamedUnit]): megavolts = NamedUnit(1000000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='megavolts',ascii_symbol='MV',symbol='MV') kilovolts = NamedUnit(1000.0, Dimensions(2, -3, 1, -1, 0, 0, 0),name='kilovolts',ascii_symbol='kV',symbol='kV') millivolts = NamedUnit(0.001, Dimensions(2, -3, 1, -1, 0, 0, 0),name='millivolts',ascii_symbol='mV',symbol='mV') -microvolts = NamedUnit(1e-06, Dimensions(2, -3, 1, -1, 0, 0, 0),name='microvolts',ascii_symbol='uV',symbol='µV') +microvolts = NamedUnit(1e-06, Dimensions(2, -3, 1, -1, 0, 0, 0),name='microvolts',ascii_symbol='uV',latex_symbol=r'{\mu}V',symbol='µV') nanovolts = NamedUnit(1e-09, Dimensions(2, -3, 1, -1, 0, 0, 0),name='nanovolts',ascii_symbol='nV',symbol='nV') picovolts = NamedUnit(1e-12, Dimensions(2, -3, 1, -1, 0, 0, 0),name='picovolts',ascii_symbol='pV',symbol='pV') femtovolts = NamedUnit(1e-15, Dimensions(2, -3, 1, -1, 0, 0, 0),name='femtovolts',ascii_symbol='fV',symbol='fV') attovolts = NamedUnit(1e-18, Dimensions(2, -3, 1, -1, 0, 0, 0),name='attovolts',ascii_symbol='aV',symbol='aV') -ohms = NamedUnit(1, Dimensions(2, -3, 1, -2, 0, 0, 0),name='ohms',ascii_symbol='Ohm',symbol='Ω') -exaohms = NamedUnit(1e+18, Dimensions(2, -3, 1, -2, 0, 0, 0),name='exaohms',ascii_symbol='EOhm',symbol='EΩ') -petaohms = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='petaohms',ascii_symbol='POhm',symbol='PΩ') -teraohms = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='teraohms',ascii_symbol='TOhm',symbol='TΩ') -gigaohms = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='gigaohms',ascii_symbol='GOhm',symbol='GΩ') -megaohms = NamedUnit(1000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='megaohms',ascii_symbol='MOhm',symbol='MΩ') -kiloohms = NamedUnit(1000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='kiloohms',ascii_symbol='kOhm',symbol='kΩ') -milliohms = NamedUnit(0.001, Dimensions(2, -3, 1, -2, 0, 0, 0),name='milliohms',ascii_symbol='mOhm',symbol='mΩ') -microohms = NamedUnit(1e-06, Dimensions(2, -3, 1, -2, 0, 0, 0),name='microohms',ascii_symbol='uOhm',symbol='µΩ') -nanoohms = NamedUnit(1e-09, Dimensions(2, -3, 1, -2, 0, 0, 0),name='nanoohms',ascii_symbol='nOhm',symbol='nΩ') -picoohms = NamedUnit(1e-12, Dimensions(2, -3, 1, -2, 0, 0, 0),name='picoohms',ascii_symbol='pOhm',symbol='pΩ') -femtoohms = NamedUnit(1e-15, Dimensions(2, -3, 1, -2, 0, 0, 0),name='femtoohms',ascii_symbol='fOhm',symbol='fΩ') -attoohms = NamedUnit(1e-18, Dimensions(2, -3, 1, -2, 0, 0, 0),name='attoohms',ascii_symbol='aOhm',symbol='aΩ') +ohms = NamedUnit(1, Dimensions(2, -3, 1, -2, 0, 0, 0),name='ohms',ascii_symbol='Ohm',latex_symbol=r'\Omega',symbol='Ω') +exaohms = NamedUnit(1e+18, Dimensions(2, -3, 1, -2, 0, 0, 0),name='exaohms',ascii_symbol='EOhm',latex_symbol=r'E\Omega',symbol='EΩ') +petaohms = NamedUnit(1000000000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='petaohms',ascii_symbol='POhm',latex_symbol=r'P\Omega',symbol='PΩ') +teraohms = NamedUnit(1000000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='teraohms',ascii_symbol='TOhm',latex_symbol=r'T\Omega',symbol='TΩ') +gigaohms = NamedUnit(1000000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='gigaohms',ascii_symbol='GOhm',latex_symbol=r'G\Omega',symbol='GΩ') +megaohms = NamedUnit(1000000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='megaohms',ascii_symbol='MOhm',latex_symbol=r'M\Omega',symbol='MΩ') +kiloohms = NamedUnit(1000.0, Dimensions(2, -3, 1, -2, 0, 0, 0),name='kiloohms',ascii_symbol='kOhm',latex_symbol=r'k\Omega',symbol='kΩ') +milliohms = NamedUnit(0.001, Dimensions(2, -3, 1, -2, 0, 0, 0),name='milliohms',ascii_symbol='mOhm',latex_symbol=r'm\Omega',symbol='mΩ') +microohms = NamedUnit(1e-06, Dimensions(2, -3, 1, -2, 0, 0, 0),name='microohms',ascii_symbol='uOhm',latex_symbol=r'{\mu}\Omega',symbol='µΩ') +nanoohms = NamedUnit(1e-09, Dimensions(2, -3, 1, -2, 0, 0, 0),name='nanoohms',ascii_symbol='nOhm',latex_symbol=r'n\Omega',symbol='nΩ') +picoohms = NamedUnit(1e-12, Dimensions(2, -3, 1, -2, 0, 0, 0),name='picoohms',ascii_symbol='pOhm',latex_symbol=r'p\Omega',symbol='pΩ') +femtoohms = NamedUnit(1e-15, Dimensions(2, -3, 1, -2, 0, 0, 0),name='femtoohms',ascii_symbol='fOhm',latex_symbol=r'f\Omega',symbol='fΩ') +attoohms = NamedUnit(1e-18, Dimensions(2, -3, 1, -2, 0, 0, 0),name='attoohms',ascii_symbol='aOhm',latex_symbol=r'a\Omega',symbol='aΩ') farads = NamedUnit(1, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='farads',ascii_symbol='F',symbol='F') exafarads = NamedUnit(1e+18, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='exafarads',ascii_symbol='EF',symbol='EF') petafarads = NamedUnit(1000000000000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='petafarads',ascii_symbol='PF',symbol='PF') @@ -602,7 +604,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megafarads = NamedUnit(1000000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='megafarads',ascii_symbol='MF',symbol='MF') kilofarads = NamedUnit(1000.0, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='kilofarads',ascii_symbol='kF',symbol='kF') millifarads = NamedUnit(0.001, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='millifarads',ascii_symbol='mF',symbol='mF') -microfarads = NamedUnit(1e-06, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='microfarads',ascii_symbol='uF',symbol='µF') +microfarads = NamedUnit(1e-06, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='microfarads',ascii_symbol='uF',latex_symbol=r'{\mu}F',symbol='µF') nanofarads = NamedUnit(1e-09, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='nanofarads',ascii_symbol='nF',symbol='nF') picofarads = NamedUnit(1e-12, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='picofarads',ascii_symbol='pF',symbol='pF') femtofarads = NamedUnit(1e-15, Dimensions(-2, 4, -1, 2, 0, 0, 0),name='femtofarads',ascii_symbol='fF',symbol='fF') @@ -615,7 +617,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megasiemens = NamedUnit(1000000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='megasiemens',ascii_symbol='MS',symbol='MS') kilosiemens = NamedUnit(1000.0, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='kilosiemens',ascii_symbol='kS',symbol='kS') millisiemens = NamedUnit(0.001, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='millisiemens',ascii_symbol='mS',symbol='mS') -microsiemens = NamedUnit(1e-06, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='microsiemens',ascii_symbol='uS',symbol='µS') +microsiemens = NamedUnit(1e-06, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='microsiemens',ascii_symbol='uS',latex_symbol=r'{\mu}S',symbol='µS') nanosiemens = NamedUnit(1e-09, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='nanosiemens',ascii_symbol='nS',symbol='nS') picosiemens = NamedUnit(1e-12, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='picosiemens',ascii_symbol='pS',symbol='pS') femtosiemens = NamedUnit(1e-15, Dimensions(-2, 3, -1, 2, 0, 0, 0),name='femtosiemens',ascii_symbol='fS',symbol='fS') @@ -628,7 +630,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megawebers = NamedUnit(1000000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='megawebers',ascii_symbol='MWb',symbol='MWb') kilowebers = NamedUnit(1000.0, Dimensions(2, -2, 1, -1, 0, 0, 0),name='kilowebers',ascii_symbol='kWb',symbol='kWb') milliwebers = NamedUnit(0.001, Dimensions(2, -2, 1, -1, 0, 0, 0),name='milliwebers',ascii_symbol='mWb',symbol='mWb') -microwebers = NamedUnit(1e-06, Dimensions(2, -2, 1, -1, 0, 0, 0),name='microwebers',ascii_symbol='uWb',symbol='µWb') +microwebers = NamedUnit(1e-06, Dimensions(2, -2, 1, -1, 0, 0, 0),name='microwebers',ascii_symbol='uWb',latex_symbol=r'{\mu}Wb',symbol='µWb') nanowebers = NamedUnit(1e-09, Dimensions(2, -2, 1, -1, 0, 0, 0),name='nanowebers',ascii_symbol='nWb',symbol='nWb') picowebers = NamedUnit(1e-12, Dimensions(2, -2, 1, -1, 0, 0, 0),name='picowebers',ascii_symbol='pWb',symbol='pWb') femtowebers = NamedUnit(1e-15, Dimensions(2, -2, 1, -1, 0, 0, 0),name='femtowebers',ascii_symbol='fWb',symbol='fWb') @@ -641,7 +643,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megatesla = NamedUnit(1000000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='megatesla',ascii_symbol='MT',symbol='MT') kilotesla = NamedUnit(1000.0, Dimensions(0, -2, 1, -1, 0, 0, 0),name='kilotesla',ascii_symbol='kT',symbol='kT') millitesla = NamedUnit(0.001, Dimensions(0, -2, 1, -1, 0, 0, 0),name='millitesla',ascii_symbol='mT',symbol='mT') -microtesla = NamedUnit(1e-06, Dimensions(0, -2, 1, -1, 0, 0, 0),name='microtesla',ascii_symbol='uT',symbol='µT') +microtesla = NamedUnit(1e-06, Dimensions(0, -2, 1, -1, 0, 0, 0),name='microtesla',ascii_symbol='uT',latex_symbol=r'{\mu}T',symbol='µT') nanotesla = NamedUnit(1e-09, Dimensions(0, -2, 1, -1, 0, 0, 0),name='nanotesla',ascii_symbol='nT',symbol='nT') picotesla = NamedUnit(1e-12, Dimensions(0, -2, 1, -1, 0, 0, 0),name='picotesla',ascii_symbol='pT',symbol='pT') femtotesla = NamedUnit(1e-15, Dimensions(0, -2, 1, -1, 0, 0, 0),name='femtotesla',ascii_symbol='fT',symbol='fT') @@ -654,12 +656,12 @@ def __init__(self, name: str, units: list[NamedUnit]): megahenry = NamedUnit(1000000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='megahenry',ascii_symbol='MH',symbol='MH') kilohenry = NamedUnit(1000.0, Dimensions(2, -2, 1, -2, 0, 0, 0),name='kilohenry',ascii_symbol='kH',symbol='kH') millihenry = NamedUnit(0.001, Dimensions(2, -2, 1, -2, 0, 0, 0),name='millihenry',ascii_symbol='mH',symbol='mH') -microhenry = NamedUnit(1e-06, Dimensions(2, -2, 1, -2, 0, 0, 0),name='microhenry',ascii_symbol='uH',symbol='µH') +microhenry = NamedUnit(1e-06, Dimensions(2, -2, 1, -2, 0, 0, 0),name='microhenry',ascii_symbol='uH',latex_symbol=r'{\mu}H',symbol='µH') nanohenry = NamedUnit(1e-09, Dimensions(2, -2, 1, -2, 0, 0, 0),name='nanohenry',ascii_symbol='nH',symbol='nH') picohenry = NamedUnit(1e-12, Dimensions(2, -2, 1, -2, 0, 0, 0),name='picohenry',ascii_symbol='pH',symbol='pH') femtohenry = NamedUnit(1e-15, Dimensions(2, -2, 1, -2, 0, 0, 0),name='femtohenry',ascii_symbol='fH',symbol='fH') attohenry = NamedUnit(1e-18, Dimensions(2, -2, 1, -2, 0, 0, 0),name='attohenry',ascii_symbol='aH',symbol='aH') -angstroms = NamedUnit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',symbol='Å') +angstroms = NamedUnit(1e-10, Dimensions(1, 0, 0, 0, 0, 0, 0),name='angstroms',ascii_symbol='Ang',latex_symbol=r'\AA',symbol='Å') minutes = NamedUnit(60, Dimensions(0, 1, 0, 0, 0, 0, 0),name='minutes',ascii_symbol='min',symbol='min') hours = NamedUnit(360, Dimensions(0, 1, 0, 0, 0, 0, 0),name='hours',ascii_symbol='h',symbol='h') days = NamedUnit(8640, Dimensions(0, 1, 0, 0, 0, 0, 0),name='days',ascii_symbol='d',symbol='d') @@ -676,7 +678,7 @@ def __init__(self, name: str, units: list[NamedUnit]): megaelectronvolts = NamedUnit(1.6021766339999998e-13, Dimensions(2, -2, 1, 0, 0, 0, 0),name='megaelectronvolts',ascii_symbol='MeV',symbol='MeV') kiloelectronvolts = NamedUnit(1.602176634e-16, Dimensions(2, -2, 1, 0, 0, 0, 0),name='kiloelectronvolts',ascii_symbol='keV',symbol='keV') millielectronvolts = NamedUnit(1.6021766339999998e-22, Dimensions(2, -2, 1, 0, 0, 0, 0),name='millielectronvolts',ascii_symbol='meV',symbol='meV') -microelectronvolts = NamedUnit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0, 0, 0),name='microelectronvolts',ascii_symbol='ueV',symbol='µeV') +microelectronvolts = NamedUnit(1.602176634e-25, Dimensions(2, -2, 1, 0, 0, 0, 0),name='microelectronvolts',ascii_symbol='ueV',latex_symbol=r'{\mu}eV',symbol='µeV') nanoelectronvolts = NamedUnit(1.602176634e-28, Dimensions(2, -2, 1, 0, 0, 0, 0),name='nanoelectronvolts',ascii_symbol='neV',symbol='neV') picoelectronvolts = NamedUnit(1.6021766339999998e-31, Dimensions(2, -2, 1, 0, 0, 0, 0),name='picoelectronvolts',ascii_symbol='peV',symbol='peV') femtoelectronvolts = NamedUnit(1.602176634e-34, Dimensions(2, -2, 1, 0, 0, 0, 0),name='femtoelectronvolts',ascii_symbol='feV',symbol='feV') @@ -684,7 +686,7 @@ def __init__(self, name: str, units: list[NamedUnit]): atomic_mass_units = NamedUnit(1.660538921e-27, Dimensions(0, 0, 1, 0, 0, 0, 0),name='atomic_mass_units',ascii_symbol='au',symbol='au') moles = NamedUnit(6.02214076e+23, Dimensions(0, 0, 0, 0, 0, 1, 0),name='moles',ascii_symbol='mol',symbol='mol') millimoles = NamedUnit(6.02214076e+20, Dimensions(0, 0, 0, 0, 0, 1, 0),name='millimoles',ascii_symbol='mmol',symbol='mmol') -micromoles = NamedUnit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0, 1, 0),name='micromoles',ascii_symbol='umol',symbol='µmol') +micromoles = NamedUnit(6.02214076e+17, Dimensions(0, 0, 0, 0, 0, 1, 0),name='micromoles',ascii_symbol='umol',latex_symbol=r'{\mu}mol',symbol='µmol') nanomoles = NamedUnit(602214076000000.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='nanomoles',ascii_symbol='nmol',symbol='nmol') picomoles = NamedUnit(602214076000.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='picomoles',ascii_symbol='pmol',symbol='pmol') femtomoles = NamedUnit(602214076.0, Dimensions(0, 0, 0, 0, 0, 1, 0),name='femtomoles',ascii_symbol='fmol',symbol='fmol') @@ -700,7 +702,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ounces = NamedUnit(0.028349523125, Dimensions(0, 0, 1, 0, 0, 0, 0),name='ounces',ascii_symbol='oz',symbol='oz') pounds_force_per_square_inch = NamedUnit(6894.757889515779, Dimensions(-1, -2, 1, 0, 0, 0, 0),name='pounds_force_per_square_inch',ascii_symbol='psi',symbol='psi') none = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 0),name='none',ascii_symbol='none',symbol='none') -percent = NamedUnit(0.01, Dimensions(0, 0, 0, 0, 0, 0, 0),name='percent',ascii_symbol='percent',symbol='%') +percent = NamedUnit(0.01, Dimensions(0, 0, 0, 0, 0, 0, 0),name='percent',ascii_symbol='percent',latex_symbol=r'\%',symbol='%') square_meters = NamedUnit(1, Dimensions(length=2), name='square_meters', ascii_symbol='m^2', symbol='m²') cubic_meters = NamedUnit(1, Dimensions(length=3), name='cubic_meters', ascii_symbol='m^3', symbol='m³') per_meter = NamedUnit(1.0, Dimensions(length=-1), name='per_meter', ascii_symbol='m^-1', symbol='m⁻¹') From 16d5dd99e3e57e7978e5ba2cc166a8ca83ddecd5 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 18 Oct 2024 11:56:04 +0100 Subject: [PATCH 646/675] Started with the basic ascii reader func signature --- sasdata/temp_ascii_reader.py | 208 +---------------------------------- 1 file changed, 4 insertions(+), 204 deletions(-) diff --git a/sasdata/temp_ascii_reader.py b/sasdata/temp_ascii_reader.py index 53e86adf8..73d486460 100644 --- a/sasdata/temp_ascii_reader.py +++ b/sasdata/temp_ascii_reader.py @@ -1,42 +1,8 @@ -from sasdata.ascii_reader_metadata import AsciiMetadataCategory, AsciiReaderMetadata, pairings, bidirectional_pairings -from sasdata.data import SasData -from sasdata.dataset_types import DatasetType, one_dim -from sasdata.dataset_types import DatasetType -from sasdata.guess import guess_column_count, guess_columns, guess_starting_position -from sasdata.quantities.units import NamedUnit -from sasdata.quantities.quantity import NamedQuantity, Quantity -from sasdata.quantities.accessors import AccessorTarget, Group -from sasdata.metadata import Metadata -from sasdata.data_backing import Dataset, Group -from enum import Enum -from dataclasses import dataclass, field -import numpy as np -import re -from dataclasses import dataclass, field, replace -from enum import Enum -from os import path +#!/usr/bin/env python -import numpy as np - -from sasdata.ascii_reader_metadata import ( - AsciiMetadataCategory, - AsciiReaderMetadata, - bidirectional_pairings, - pairings, -) from sasdata.data import SasData -from sasdata.dataset_types import DatasetType, one_dim, unit_kinds -from sasdata.default_units import get_default_unit -from sasdata.guess import ( - guess_column_count, - guess_columns, - guess_dataset_type, - guess_starting_position, -) -from sasdata.metadata import Metadata, MetaNode -from sasdata.quantities.quantity import NamedQuantity, Quantity from sasdata.quantities.units import NamedUnit - +from enum import Enum class AsciiSeparator(Enum): Comma = (0,) @@ -44,171 +10,5 @@ class AsciiSeparator(Enum): Tab = 2 -# TODO: Turn them all of for now so the caller can turn one of them on. But is this the desired behaviour? -def initialise_separator_dict(initial_value: bool = False) -> dict[str, bool]: - return {"Whitespace": initial_value, "Comma": initial_value, "Tab": initial_value} - - -@dataclass -class AsciiReaderParams: - """This object contains the parameters that are used to load a series of - ASCII files. These parameters can be generated by the ASCII Reader Dialog - when using SasView.""" - - # These will be the FULL file path. Will need to convert to basenames for some functions. - filenames: list[str] - # The unit object for the column should only be None if the column is ! - columns: list[tuple[str, NamedUnit | None]] - metadata: AsciiReaderMetadata = field(default_factory=AsciiReaderMetadata) - starting_line: int = 0 - excluded_lines: set[int] = field(default_factory=set) - separator_dict: dict[str, bool] = field(default_factory=initialise_separator_dict) - # Take a copy in case its mutated (which it shouldn't be) - dataset_type: DatasetType = field(default_factory=lambda: replace(one_dim)) - - def __post_init__(self): - self.initialise_metadata() - - def initialise_metadata(self): - for filename in self.filenames: - basename = path.basename(filename) - if basename not in self.metadata.filename_separator: - self.metadata.filename_separator[basename] = "_" - self.metadata.filename_specific_metadata[basename] = {} - -# TODO: Should I make this work on a list of filenames as well? -def guess_params_from_filename(filename: str, dataset_type: DatasetType) -> AsciiReaderParams: - # Lets assume that all the separators are to be enabled. - # Lets just assume we want all of the seaprators on. This seems to work for most files. - separator_dict = initialise_separator_dict(True) - with open(filename) as file: - lines = file.readlines() - lines_split = [split_line(separator_dict, line) for line in lines] - startpos = guess_starting_position(lines_split) - colcount = guess_column_count(lines_split, startpos) - columns = guess_columns(colcount, dataset_type) - params = AsciiReaderParams([filename], columns, starting_line=startpos, separator_dict=separator_dict) - - -def split_line(separator_dict: dict[str, bool], line: str) -> list[str]: - """Split a line in a CSV file based on which seperators the user has - selected on the widget. - - """ - expr = "" - for seperator, isenabled in separator_dict.items(): - if isenabled: - if expr != r"": - expr += r"|" - match seperator: - case "Comma": - seperator_text = r"," - case "Whitespace": - seperator_text = r"\s+" - case "Tab": - seperator_text = r"\t" - expr += seperator_text - - return re.split(expr, line.strip()) - - -# TODO: Implement error handling. -def load_quantities(params: AsciiReaderParams, filename: str, metadata: Metadata) -> dict[str, Quantity]: - """Load a list of quantities from the filename based on the params.""" - with open(filename) as ascii_file: - lines = ascii_file.readlines() - arrays: list[np.ndarray] = [] - for _ in params.columns_included: - arrays.append(np.zeros(len(lines) - params.starting_line)) - for i, current_line in enumerate(lines): - if i < params.starting_line or current_line in params.excluded_lines: - continue - line_split = split_line(params.separator_dict, current_line) - try: - for j, token in enumerate(line_split): - # Sometimes in the split, there might be an extra column that doesn't need to be there (e.g. an empty - # string.) This won't convert to a float so we need to ignore it. - if j >= len(params.columns_included): - continue - # TODO: Data might not be floats. Maybe don't hard code this. - arrays[j][i - params.starting_line] = float(token) - except ValueError: - # If any of the lines contain non-numerical data, then this line can't be read in as a quantity so it - # should be ignored entirely. - print(f"Line {i + 1} skipped.") - continue - file_quantities = { - name: NamedQuantity(name, arrays[i], unit, id_header=metadata.id_header) - for i, (name, unit) in enumerate(params.columns_included) - } - return file_quantities - - -def import_metadata(metadata: dict[str, AsciiMetadataCategory[str]]) -> MetaNode: - root_contents = [] - for top_level_key, top_level_item in metadata.items(): - children = [] - for metadatum_name, metadatum in top_level_item.values.items(): - children.append(MetaNode(name=metadatum_name, attrs={}, contents=metadatum)) - if top_level_key == "other": - root_contents.extend(children) - else: - group = MetaNode(name=top_level_key, attrs={}, contents=children) - root_contents.append(group) - return MetaNode(name="root", attrs={}, contents=root_contents) - - -def merge_uncertainties(quantities: dict[str, Quantity]) -> dict[str, Quantity]: - """Data in the ASCII files will have the uncertainties in a separate column. - This function will merge columns of data with the columns containing their - uncertainties so that both are in one Quantity object.""" - new_quantities: dict[str, Quantity] = {} - error_quantity_names = pairings.values() - for name, quantity in quantities.items(): - if name in error_quantity_names: - continue - pairing = bidirectional_pairings.get(name, "") - error_quantity = None - for other_name, other_quantity in quantities.items(): - if other_name == pairing: - error_quantity = other_quantity - if error_quantity is not None: - to_add = quantity.with_standard_error(error_quantity) - else: - to_add = quantity - new_quantities[name] = to_add - return new_quantities - - -def load_data(params: AsciiReaderParams) -> list[SasData]: - """This loads a series of SasData objects based on the params. The amount of - SasData objects loaded will depend on how many filenames are present in the - list contained in the params.""" - loaded_data: list[SasData] = [] - for filename in params.filenames: - raw_metadata = import_metadata( - params.metadata.all_file_metadata(path.basename(filename)) - ) - metadata = Metadata( - title=None, - run=[], - definition=None, - sample=None, - instrument=None, - process=None, - raw=raw_metadata, - ) - quantities = load_quantities(params, filename, metadata) - data = SasData( - path.basename(filename), - merge_uncertainties(quantities), - params.dataset_type, - metadata, - ) - loaded_data.append(data) - return loaded_data - - -def load_data_default_params(filename: str) -> list[SasData]: - params = guess_params_from_filename(filename, guess_dataset_type(filename)) - return load_data(params) +def load_data(filename: str, starting_line: int, columns: list[tuple[str, NamedUnit]], separators: list[AsciiSeparator]) -> list[SasData]: + raise NotImplementedError() From 9763bd8585a7255c70554de59a095030d99a8d19 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Mon, 21 Oct 2024 10:46:48 +0100 Subject: [PATCH 647/675] Fixed import errors. I can't get the ASCII dialog to run when using relative imports in these files so I've converted them to absolute imports. --- sasdata/data.py | 2 - sasdata/metadata.py | 50 ++++++---------------- sasdata/quantities/absolute_temperature.py | 30 ++++++------- 3 files changed, 28 insertions(+), 54 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 4c94a5be9..9b15cb3b2 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -2,8 +2,6 @@ from typing import TypeVar, Any, Self from dataclasses import dataclass -import numpy as np - from sasdata.quantities.quantity import NamedQuantity from sasdata.metadata import Metadata from sasdata.quantities.accessors import AccessorTarget diff --git a/sasdata/metadata.py b/sasdata/metadata.py index dc11b1636..6c21ed7e4 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -337,10 +337,10 @@ def summary(self) -> str: class Instrument: def __init__(self, target: AccessorTarget): - self.aperture = Aperture(target.with_path_prefix("sasaperture|aperture")) - self.collimation = Collimation(target.with_path_prefix("sascollimation|collimation")) - self.detector = Detector(target.with_path_prefix("sasdetector|detector")) - self.source = Source(target.with_path_prefix("sassource|source")) + self.aperture = Aperture(target.with_path_prefix("sasaperture")) + self.collimation = Collimation(target.with_path_prefix("sascollimation")) + self.detector = Detector(target.with_path_prefix("sasdetector")) + self.source = Source(target.with_path_prefix("sassource")) def summary(self): return ( self.aperture.summary() + @@ -348,51 +348,27 @@ def summary(self): self.detector.summary() + self.source.summary()) -def decode_string(data): - """ This is some crazy stuff""" - - if isinstance(data, str): - return data - - elif isinstance(data, np.ndarray): - - if data.dtype == object: - - data = data.reshape(-1) - data = data[0] - - if isinstance(data, bytes): - return data.decode("utf-8") - - return str(data) - - else: - return data.tobytes().decode("utf-8") - - else: - return str(data) class Metadata: def __init__(self, target: AccessorTarget): self._target = target - self.instrument = Instrument(target.with_path_prefix("sasinstrument|instrument")) - self.process = Process(target.with_path_prefix("sasprocess|process")) - self.sample = Sample(target.with_path_prefix("sassample|sample")) - self.transmission_spectrum = TransmissionSpectrum(target.with_path_prefix("sastransmission_spectrum|transmission_spectrum")) + self.instrument = Instrument(target.with_path_prefix("sasinstrument")) + self.process = Process(target.with_path_prefix("sasprocess")) + self.sample = Sample(target.with_path_prefix("sassample")) + self.transmission_spectrum = TransmissionSpectrum(target.with_path_prefix("sastransmission_spectrum")) self._title = StringAccessor(target, "title") self._run = StringAccessor(target, "run") - self._definition = StringAccessor(target, "definition") + self._definitiion = StringAccessor(target, "definition") - self.title: str = decode_string(self._title.value) - self.run: str = decode_string(self._run.value) - self.definition: str = decode_string(self._definition.value) + self.title: str = self._title.value + self.run: str = self._run.value + self.definitiion: str = self._definitiion.value def summary(self): return ( - f" {self.title}, Run: {self.run}\n" + - " " + "="*len(self.title) + + f" {self.title}, Run: {self.run}\n" + " " + "="*len(self.title) + "=======" + "="*len(self.run) + "\n\n" + f"Definition: {self.title}\n" + diff --git a/sasdata/quantities/absolute_temperature.py b/sasdata/quantities/absolute_temperature.py index ecfd0e6d9..71f75fb32 100644 --- a/sasdata/quantities/absolute_temperature.py +++ b/sasdata/quantities/absolute_temperature.py @@ -1,15 +1,15 @@ -from typing import TypeVar - -from sasdata.quantities.quantity import Quantity -from sasdata.quantities.accessors import TemperatureAccessor - - -DataType = TypeVar("DataType") -class AbsoluteTemperatureAccessor(TemperatureAccessor[DataType]): - """ Parsing for absolute temperatures """ - @property - def value(self) -> Quantity[DataType] | None: - if self._numerical_part() is None: - return None - else: - return Quantity.parse(self._numerical_part(), self._unit_part(), absolute_temperature=True) +from typing import TypeVar + +from sasdata.quantities.quantity import Quantity +from sasdata.quantities.accessors import TemperatureAccessor + + +DataType = TypeVar("DataType") +class AbsoluteTemperatureAccessor(TemperatureAccessor[DataType]): + """ Parsing for absolute temperatures """ + @property + def value(self) -> Quantity[DataType] | None: + if self._numerical_part() is None: + return None + else: + return Quantity.parse(self._numerical_part(), self._unit_part(), absolute_temperature=True) From 8759b9887da81917b2985124896849f1d6c623e0 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Tue, 3 Dec 2024 11:43:48 +0000 Subject: [PATCH 648/675] Moving internal metadata to sasdata. This is going to need to live here now in order to avoid circular dependencies. --- sasdata/ascii_reader_metadata.py | 85 ++------------------------------ 1 file changed, 4 insertions(+), 81 deletions(-) diff --git a/sasdata/ascii_reader_metadata.py b/sasdata/ascii_reader_metadata.py index 850492a84..ca7a577c6 100644 --- a/sasdata/ascii_reader_metadata.py +++ b/sasdata/ascii_reader_metadata.py @@ -1,6 +1,7 @@ import re from dataclasses import dataclass, field from typing import TypeVar +from re import split as re_split initial_metadata = { 'source': ['name', 'radiation', 'type', 'probe_particle', 'beam_size_name', 'beam_size', 'beam_shape', 'wavelength', 'wavelength_min', 'wavelength_max', 'wavelength_spread'], @@ -10,27 +11,11 @@ 'process': ['name', 'date', 'description', 'term', 'notes'], 'sample': ['name', 'sample_id', 'thickness', 'transmission', 'temperature', 'position', 'orientation', 'details'], 'transmission_spectrum': ['name', 'timestamp', 'transmission', 'transmission_deviation'], - 'magnetic': ['demagnetizing_field', 'saturation_magnetization', 'applied_magnetic_field', 'counting_index'], 'other': ['title', 'run', 'definition'] } -CASING_REGEX = r'[A-Z][a-z]*' - -# First item has the highest precedence. -SEPARATOR_PRECEDENCE = [ - '_', - '-', -] -# If none of these characters exist in that string, use casing. See init_separator - T = TypeVar('T') -# TODO: There may be a better place for this. -pairings = {'I': 'dI', 'Q': 'dQ', 'Qx': 'dQx', 'Qy': 'dQy', 'Qz': 'dQz'} -pairing_error = {value: key for key, value in pairings.items()} -# Allows this to be bidirectional. -bidirectional_pairings = pairings | pairing_error - @dataclass class AsciiMetadataCategory[T]: values: dict[str, T] = field(default_factory=dict) @@ -42,68 +27,13 @@ def default_categories() -> dict[str, AsciiMetadataCategory[str | int]]: class AsciiReaderMetadata: # Key is the filename. filename_specific_metadata: dict[str, dict[str, AsciiMetadataCategory[str]]] = field(default_factory=dict) - # True instead of str means use the casing to separate the filename. - filename_separator: dict[str, str | bool] = field(default_factory=dict) + filename_separator: dict[str, str] = field(default_factory=dict) master_metadata: dict[str, AsciiMetadataCategory[int]] = field(default_factory=default_categories) - def init_separator(self, filename: str): - separator = next(filter(lambda c: c in SEPARATOR_PRECEDENCE, filename), True) - self.filename_separator[filename] = separator - - def filename_components(self, filename: str, cut_off_extension: bool = True, capture: bool = False) -> list[str]: - """Split the filename into several components based on the current separator for that file.""" - separator = self.filename_separator[filename] - # FIXME: This sort of string construction may be an issue. Might need an alternative. - base_str = '({})' if capture else '{}' - if isinstance(separator, str): - splitted = re.split(base_str.replace('{}', separator), filename) - else: - splitted = re.findall(base_str.replace('{}', CASING_REGEX), filename) - # If the last component has a file extensions, remove it. - last_component = splitted[-1] - if cut_off_extension and '.' in last_component: - pos = last_component.index('.') - last_component = last_component[:pos] - splitted[-1] = last_component - return splitted - - def purge_unreachable(self, filename: str): - """This is used when the separator has changed. If lets say we now have 2 components when there were 5 but the - 3rd component was selected, this will now produce an index out of range exception. Thus we'll need to purge this - to stop exceptions from happening.""" - components = self.filename_components(filename) - component_length = len(components) - # Converting to list as this mutates the dictionary as it goes through it. - for category_name, category in list(self.master_metadata.items()): - for key, value in list(category.values.items()): - if value >= component_length: - del self.master_metadata[category_name].values[key] + def filename_components(self, filename: str) -> list[str]: + return re_split(self.filename_separator[filename], filename) - def all_file_metadata(self, filename: str) -> dict[str, AsciiMetadataCategory[str]]: - """Return all of the metadata for known for the specified filename. This - will combin the master metadata specified for all files with the - metadata specific to that filename.""" - file_metadata = self.filename_specific_metadata[filename] - components = self.filename_components(filename) - # The ordering here is important. If there are conflicts, the second dictionary will override the first one. - # Conflicts shouldn't really be happening anyway but if they do, we're gonna go with the master metadata taking - # precedence for now. - return_metadata: dict[str, AsciiMetadataCategory[str]] = {} - for category_name, category in (file_metadata | self.master_metadata).items(): - combined_category_dict = category.values | self.master_metadata[category_name].values - new_category_dict: dict[str, str] = {} - for key, value in combined_category_dict.items(): - if isinstance(value, str): - new_category_dict[key] = value - elif isinstance(value, int): - new_category_dict[key] = components[value] - else: - raise TypeError(f'Invalid value for {key} in {category_name}') - new_category = AsciiMetadataCategory(new_category_dict) - return_metadata[category_name] = new_category - return return_metadata def get_metadata(self, category: str, value: str, filename: str, error_on_not_found=False) -> str | None: - """Get a particular piece of metadata for the filename.""" components = self.filename_components(filename) # We prioritise the master metadata. @@ -122,10 +52,6 @@ def get_metadata(self, category: str, value: str, filename: str, error_on_not_fo return None def update_metadata(self, category: str, key: str, filename: str, new_value: str | int): - """Update the metadata for a filename. If the new_value is a string, - then this new metadata will be specific to that file. Otherwise, if - new_value is an integer, then this will represent the component of the - filename that this metadata applies to all.""" if isinstance(new_value, str): self.filename_specific_metadata[filename][category].values[key] = new_value # TODO: What about the master metadata? Until that's gone, that still takes precedence. @@ -135,7 +61,6 @@ def update_metadata(self, category: str, key: str, filename: str, new_value: str raise TypeError('Invalid type for new_value') def clear_metadata(self, category: str, key: str, filename: str): - """Remove any metadata recorded for a certain filename.""" category_obj = self.filename_specific_metadata[filename][category] if key in category_obj.values: del category_obj.values[key] @@ -143,7 +68,5 @@ def clear_metadata(self, category: str, key: str, filename: str): del self.master_metadata[category].values[key] def add_file(self, new_filename: str): - """Add a filename to the metadata, filling it with some default - categories.""" # TODO: Fix typing here. Pyright is showing errors. self.filename_specific_metadata[new_filename] = default_categories() From cd6850aef2abe0d25395da8bd1234ac8d96a882d Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Dec 2024 11:17:41 +0000 Subject: [PATCH 649/675] A very basic skeleton of the trend object. --- sasdata/trend.py | 80 ++++++------------------------------------------ 1 file changed, 9 insertions(+), 71 deletions(-) diff --git a/sasdata/trend.py b/sasdata/trend.py index 9b1a371a4..40c0e8a90 100644 --- a/sasdata/trend.py +++ b/sasdata/trend.py @@ -1,89 +1,27 @@ +#!/usr/bin/env python + from dataclasses import dataclass import numpy as np from sasdata.data import SasData -from sasdata.data_backing import Dataset, Group -from sasdata.quantities.quantity import Quantity -from sasdata.transforms.rebinning import calculate_interpolation_matrix_1d # Axis strs refer to the name of their associated NamedQuantity. -# TODO: This probably shouldn't be here but will keep it here for now. -# TODO: Not sure how to type hint the return. -def get_metadatum_from_path(data: SasData, metadata_path: list[str]): - current_group = data._raw_metadata - for path_item in metadata_path: - current_item = current_group.children.get(path_item, None) - if current_item is None or (isinstance(current_item, Dataset) and path_item != metadata_path[-1]): - raise ValueError('Path does not lead to valid a metadatum.') - elif isinstance(current_item, Group): - current_group = current_item - else: - return current_item.data - raise ValueError('End of path without finding a dataset.') - - @dataclass class Trend: data: list[SasData] - # This is going to be a path to a specific metadatum. - # - # TODO: But what if the trend axis will be a particular NamedQuantity? Will probably need to think on this. - trend_axis: list[str] + trend_axis: str # Designed to take in a particular value of the trend axis, and return the SasData object that matches it. # TODO: Not exaclty sure what item's type will be. It could depend on where it is pointing to. def __getitem__(self, item) -> SasData: - for datum in self.data: - metadatum = get_metadatum_from_path(datum, self.trend_axis) - if metadatum == item: - return datum - raise KeyError() - @property - def trend_axes(self) -> list[float]: - return [get_metadatum_from_path(datum, self.trend_axis) for datum in self.data] + raise NotImplementedError() - # TODO: Assumes there are at least 2 items in data. Is this reasonable to assume? Should there be error handling for - # situations where this may not be the case? def all_axis_match(self, axis: str) -> bool: - reference_data = self.data[0] - data_axis = reference_data[axis] - for datum in self.data[1::]: - axis_datum = datum[axis] - # FIXME: Linter is complaining about typing. - if not np.all(np.isclose(axis_datum.value, data_axis.value)): - return False - return True - - # TODO: For now, return a new trend, but decide later. Shouldn't be too hard to change. - def interpolate(self, axis: str) -> "Trend": - new_data: list[SasData] = [] - reference_data = self.data[0] - # TODO: I don't like the repetition here. Can probably abstract a function for this ot make it clearer. - data_axis = reference_data[axis] - for i, datum in enumerate(self.data): - if i == 0: - # This is already the reference axis; no need to interpolate it. - continue - # TODO: Again, repetition - axis_datum = datum[axis] - # TODO: There are other options which may need to be filled (or become new params to this method) - mat, _ = calculate_interpolation_matrix_1d(axis_datum, data_axis) - new_quantities: dict[str, Quantity] = {} - for name, quantity in datum._data_contents.items(): - if name == axis: - new_quantities[name] = data_axis - continue - new_quantities[name] = quantity @ mat + raise NotImplementedError() - new_datum = SasData( - name=datum.name, - data_contents=new_quantities, - dataset_type=datum.dataset_type, - metadata=datum.metadata, - ) - new_data.append(new_datum) - new_trend = Trend(new_data, - self.trend_axis) - return new_trend + # TODO: Not sure if this should return a new trend, or just mutate the existing trend + # TODO: May be some details on the method as well. + def interpolate(self, axis: str) -> Self: + raise NotImplementedError() From 0b757ec37a6782cf6d24253dc31b4574bb2a4830 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Dec 2024 11:43:30 +0000 Subject: [PATCH 650/675] Implemented equality based on the hash. --- sasdata/quantities/quantity.py | 1087 +------------------------------- 1 file changed, 34 insertions(+), 1053 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 0a91d107a..6b391d2db 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,972 +1,18 @@ -from typing import Self +from typing import Collection, Sequence, TypeVar, Generic, Self +from dataclasses import dataclass import numpy as np from numpy._typing import ArrayLike -from sasdata.quantities import units -from sasdata.quantities.numerical_encoding import numerical_decode, numerical_encode +from sasdata.quantities.operations import Operation, Variable +from sasdata.quantities import operations, units from sasdata.quantities.units import Unit, NamedUnit import hashlib -from typing import Any, TypeVar, Union - -import json - -T = TypeVar("T") - - - - - -################### Quantity based operations, need to be here to avoid cyclic dependencies ##################### - -def transpose(a: Union["Quantity[ArrayLike]", ArrayLike], axes: tuple | None = None): - """ Transpose an array or an array based quantity, can also do reordering of axes""" - if isinstance(a, Quantity): - - if axes is None: - return DerivedQuantity(value=np.transpose(a.value, axes=axes), - units=a.units, - history=QuantityHistory.apply_operation(Transpose, a.history)) - - else: - return DerivedQuantity(value=np.transpose(a.value, axes=axes), - units=a.units, - history=QuantityHistory.apply_operation(Transpose, a.history, axes=axes)) - - else: - return np.transpose(a, axes=axes) - - -def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike]", ArrayLike]): - """ Dot product of two arrays or two array based quantities """ - a_is_quantity = isinstance(a, Quantity) - b_is_quantity = isinstance(b, Quantity) - - if a_is_quantity or b_is_quantity: - - # If its only one of them that is a quantity, convert the other one - - if not a_is_quantity: - a = Quantity(a, units.none) - - if not b_is_quantity: - b = Quantity(b, units.none) - - return DerivedQuantity( - value=np.dot(a.value, b.value), - units=a.units * b.units, - history=QuantityHistory.apply_operation(Dot, a.history, b.history)) - - else: - return np.dot(a, b) - -def tensordot(a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, b: Union["Quantity[ArrayLike]", ArrayLike], a_index: int, b_index: int): - """ Tensor dot product - equivalent to contracting two tensors, such as - - A_{i0, i1, i2, i3...} and B_{j0, j1, j2...} - - e.g. if a_index is 1 and b_index is zero, it will be the sum - - C_{i0, i2, i3 ..., j1, j2 ...} = sum_k A_{i0, k, i2, i3 ...} B_{k, j1, j2 ...} - - (I think, have to check what happens with indices TODO!) - - """ - - a_is_quantity = isinstance(a, Quantity) - b_is_quantity = isinstance(b, Quantity) - - if a_is_quantity or b_is_quantity: - - # If its only one of them that is a quantity, convert the other one - - if not a_is_quantity: - a = Quantity(a, units.none) - - if not b_is_quantity: - b = Quantity(b, units.none) - - return DerivedQuantity( - value=np.tensordot(a.value, b.value, axes=(a_index, b_index)), - units=a.units * b.units, - history=QuantityHistory.apply_operation( - TensorDot, - a.history, - b.history, - a_index=a_index, - b_index=b_index)) - - else: - return np.tensordot(a, b, axes=(a_index, b_index)) - - -################### Operation Definitions ####################################### - -def hash_and_name(hash_or_name: int | str): - """ Infer the name of a variable from a hash, or the hash from the name - - Note: hash_and_name(hash_and_name(number)[1]) is not the identity - however: hash_and_name(hash_and_name(number)) is - """ - - if isinstance(hash_or_name, str): - hash_value = hash(hash_or_name) - name = hash_or_name - - return hash_value, name - - elif isinstance(hash_or_name, int): - hash_value = hash_or_name - name = f"#{hash_or_name}" - - return hash_value, name - - elif isinstance(hash_or_name, tuple): - return hash_or_name - - else: - raise TypeError("Variable name_or_hash_value must be either str or int") - -class Operation: - - serialisation_name = "unknown" - def summary(self, indent_amount: int = 0, indent: str=" "): - """ Summary of the operation tree""" - - s = f"{indent_amount*indent}{self._summary_open()}(\n" - - for chunk in self._summary_components(): - s += chunk.summary(indent_amount+1, indent) + "\n" - - s += f"{indent_amount*indent})" - - return s - def _summary_open(self): - """ First line of summary """ - - def _summary_components(self) -> list["Operation"]: - return [] - def evaluate(self, variables: dict[int, T]) -> T: - - """ Evaluate this operation """ - - def _derivative(self, hash_value: int) -> "Operation": - """ Get the derivative of this operation """ - - def _clean(self): - """ Clean up this operation - i.e. remove silly things like 1*x """ - return self - - def derivative(self, variable: Union[str, int, "Variable"], simplify=True): - if isinstance(variable, Variable): - hash_value = variable.hash_value - else: - hash_value, _ = hash_and_name(variable) - - derivative = self._derivative(hash_value) - - if not simplify: - return derivative - - derivative_string = derivative.serialise() - - # print("---------------") - # print("Base") - # print("---------------") - # print(derivative.summary()) - - # Inefficient way of doing repeated simplification, but it will work - for i in range(100): # set max iterations - - derivative = derivative._clean() - # - # print("-------------------") - # print("Iteration", i+1) - # print("-------------------") - # print(derivative.summary()) - # print("-------------------") - - new_derivative_string = derivative.serialise() - - if derivative_string == new_derivative_string: - break - - derivative_string = new_derivative_string - - return derivative - - @staticmethod - def deserialise(data: str) -> "Operation": - json_data = json.loads(data) - return Operation.deserialise_json(json_data) - - @staticmethod - def deserialise_json(json_data: dict) -> "Operation": - - operation = json_data["operation"] - parameters = json_data["parameters"] - cls = _serialisation_lookup[operation] - - try: - return cls._deserialise(parameters) - - except NotImplementedError: - raise NotImplementedError(f"No method to deserialise {operation} with {parameters} (cls={cls})") - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - raise NotImplementedError(f"Deserialise not implemented for this class") - - def serialise(self) -> str: - return json.dumps(self._serialise_json()) - - def _serialise_json(self) -> dict[str, Any]: - return {"operation": self.serialisation_name, - "parameters": self._serialise_parameters()} - - def _serialise_parameters(self) -> dict[str, Any]: - raise NotImplementedError("_serialise_parameters not implemented") - - def __eq__(self, other: "Operation"): - return NotImplemented - -class ConstantBase(Operation): - pass - -class AdditiveIdentity(ConstantBase): - - serialisation_name = "zero" - def evaluate(self, variables: dict[int, T]) -> T: - return 0 - - def _derivative(self, hash_value: int) -> Operation: - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return AdditiveIdentity() - - def _serialise_parameters(self) -> dict[str, Any]: - return {} - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}0 [Add.Id.]" - - def __eq__(self, other): - if isinstance(other, AdditiveIdentity): - return True - elif isinstance(other, Constant): - if other.value == 0: - return True - - return False - - - -class MultiplicativeIdentity(ConstantBase): - - serialisation_name = "one" - - def evaluate(self, variables: dict[int, T]) -> T: - return 1 - - def _derivative(self, hash_value: int): - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return MultiplicativeIdentity() - - - def _serialise_parameters(self) -> dict[str, Any]: - return {} - - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}1 [Mul.Id.]" - - def __eq__(self, other): - if isinstance(other, MultiplicativeIdentity): - return True - elif isinstance(other, Constant): - if other.value == 1: - return True - - return False - - -class Constant(ConstantBase): - - serialisation_name = "constant" - def __init__(self, value): - self.value = value - - def summary(self, indent_amount: int = 0, indent: str=" "): - return repr(self.value) - - def evaluate(self, variables: dict[int, T]) -> T: - return self.value - - def _derivative(self, hash_value: int): - return AdditiveIdentity() - - def _clean(self): - - if self.value == 0: - return AdditiveIdentity() - - elif self.value == 1: - return MultiplicativeIdentity() - - else: - return self - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - value = numerical_decode(parameters["value"]) - return Constant(value) - - - def _serialise_parameters(self) -> dict[str, Any]: - return {"value": numerical_encode(self.value)} - - def summary(self, indent_amount: int=0, indent=" "): - return f"{indent_amount*indent}{self.value}" - - def __eq__(self, other): - if isinstance(other, AdditiveIdentity): - return self.value == 0 - - elif isinstance(other, MultiplicativeIdentity): - return self.value == 1 - - elif isinstance(other, Constant): - if other.value == self.value: - return True - - return False - - -class Variable(Operation): - - serialisation_name = "variable" - def __init__(self, name_or_hash_value: int | str | tuple[int, str]): - self.hash_value, self.name = hash_and_name(name_or_hash_value) - - def evaluate(self, variables: dict[int, T]) -> T: - try: - return variables[self.hash_value] - except KeyError: - raise ValueError(f"Variable dictionary didn't have an entry for {self.name} (hash={self.hash_value})") - - def _derivative(self, hash_value: int) -> Operation: - if hash_value == self.hash_value: - return MultiplicativeIdentity() - else: - return AdditiveIdentity() - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - hash_value = parameters["hash_value"] - name = parameters["name"] - - return Variable((hash_value, name)) - - def _serialise_parameters(self) -> dict[str, Any]: - return {"hash_value": self.hash_value, - "name": self.name} - - def summary(self, indent_amount: int = 0, indent: str=" "): - return f"{indent_amount*indent}{self.name}" - - def __eq__(self, other): - if isinstance(other, Variable): - return self.hash_value == other.hash_value - - return False - -class UnaryOperation(Operation): - - def __init__(self, a: Operation): - self.a = a - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": self.a._serialise_json()} - - def _summary_components(self) -> list["Operation"]: - return [self.a] - - - - -class Neg(UnaryOperation): - - serialisation_name = "neg" - def evaluate(self, variables: dict[int, T]) -> T: - return -self.a.evaluate(variables) - - def _derivative(self, hash_value: int): - return Neg(self.a._derivative(hash_value)) - - def _clean(self): - - clean_a = self.a._clean() - - if isinstance(clean_a, Neg): - # Removes double negations - return clean_a.a - - elif isinstance(clean_a, Constant): - return Constant(-clean_a.value)._clean() - - else: - return Neg(clean_a) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Neg(Operation.deserialise_json(parameters["a"])) - - - def _summary_open(self): - return "Neg" - - def __eq__(self, other): - if isinstance(other, Neg): - return other.a == self.a - - -class Inv(UnaryOperation): - - serialisation_name = "reciprocal" - - def evaluate(self, variables: dict[int, T]) -> T: - return 1/self.a.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Neg(Div(self.a._derivative(hash_value), Mul(self.a, self.a))) - - def _clean(self): - clean_a = self.a._clean() - - if isinstance(clean_a, Inv): - # Removes double negations - return clean_a.a - - elif isinstance(clean_a, Neg): - # cannonicalise 1/-a to -(1/a) - # over multiple iterations this should have the effect of ordering and gathering Neg and Inv - return Neg(Inv(clean_a.a)) - - elif isinstance(clean_a, Constant): - return Constant(1/clean_a.value)._clean() - - else: - return Inv(clean_a) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Inv(Operation.deserialise_json(parameters["a"])) - - def _summary_open(self): - return "Inv" - - - def __eq__(self, other): - if isinstance(other, Inv): - return other.a == self.a - -class BinaryOperation(Operation): - def __init__(self, a: Operation, b: Operation): - self.a = a - self.b = b - - def _clean(self): - return self._clean_ab(self.a._clean(), self.b._clean()) - - def _clean_ab(self, a, b): - raise NotImplementedError("_clean_ab not implemented") - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": self.a._serialise_json(), - "b": self.b._serialise_json()} - - @staticmethod - def _deserialise_ab(parameters) -> tuple[Operation, Operation]: - return (Operation.deserialise_json(parameters["a"]), - Operation.deserialise_json(parameters["b"])) - - - def _summary_components(self) -> list["Operation"]: - return [self.a, self.b] - - def _self_cls(self) -> type: - """ Own class""" - def __eq__(self, other): - if isinstance(other, self._self_cls()): - return other.a == self.a and self.b == other.b - -class Add(BinaryOperation): - - serialisation_name = "add" - - def _self_cls(self) -> type: - return Add - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) + self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add(self.a._derivative(hash_value), self.b._derivative(hash_value)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity): - # Convert 0 + b to b - return b - - elif isinstance(b, AdditiveIdentity): - # Convert a + 0 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"+"b" to "a+b" - return Constant(a.evaluate({}) + b.evaluate({}))._clean() - - elif isinstance(a, Neg): - if isinstance(b, Neg): - # Convert (-a)+(-b) to -(a+b) - return Neg(Add(a.a, b.a)) - else: - # Convert (-a) + b to b-a - return Sub(b, a.a) - - elif isinstance(b, Neg): - # Convert a+(-b) to a-b - return Sub(a, b.a) - - elif a == b: - return Mul(Constant(2), a) - - else: - return Add(a, b) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Add(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Add" - -class Sub(BinaryOperation): - - serialisation_name = "sub" - - - def _self_cls(self) -> type: - return Sub - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) - self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Sub(self.a._derivative(hash_value), self.b._derivative(hash_value)) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity): - # Convert 0 - b to -b - return Neg(b) - - elif isinstance(b, AdditiveIdentity): - # Convert a - 0 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant pair "a" - "b" to "a-b" - return Constant(a.evaluate({}) - b.evaluate({}))._clean() - - elif isinstance(a, Neg): - if isinstance(b, Neg): - # Convert (-a)-(-b) to b-a - return Sub(b.a, a.a) - else: - # Convert (-a)-b to -(a+b) - return Neg(Add(a.a, b)) - - elif isinstance(b, Neg): - # Convert a-(-b) to a+b - return Add(a, b.a) - - elif a == b: - return AdditiveIdentity() - - else: - return Sub(a, b) - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Sub(*BinaryOperation._deserialise_ab(parameters)) - - - def _summary_open(self): - return "Sub" - -class Mul(BinaryOperation): - - serialisation_name = "mul" - - - def _self_cls(self) -> type: - return Mul - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) * self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add(Mul(self.a, self.b._derivative(hash_value)), Mul(self.a._derivative(hash_value), self.b)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): - # Convert 0*b or a*0 to 0 - return AdditiveIdentity() - - elif isinstance(a, MultiplicativeIdentity): - # Convert 1*b to b - return b - - elif isinstance(b, MultiplicativeIdentity): - # Convert a*1 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"*"b" to "a*b" - return Constant(a.evaluate({}) * b.evaluate({}))._clean() - - elif isinstance(a, Inv) and isinstance(b, Inv): - return Inv(Mul(a.a, b.a)) - - elif isinstance(a, Inv) and not isinstance(b, Inv): - return Div(b, a.a) - - elif not isinstance(a, Inv) and isinstance(b, Inv): - return Div(a, b.a) - - elif isinstance(a, Neg): - return Neg(Mul(a.a, b)) - - elif isinstance(b, Neg): - return Neg(Mul(a, b.a)) - - elif a == b: - return Pow(a, 2) - - elif isinstance(a, Pow) and a.a == b: - return Pow(b, a.power + 1) - - elif isinstance(b, Pow) and b.a == a: - return Pow(a, b.power + 1) - - elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: - return Pow(a.a, a.power + b.power) - - else: - return Mul(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Mul(*BinaryOperation._deserialise_ab(parameters)) - - - def _summary_open(self): - return "Mul" - -class Div(BinaryOperation): - - serialisation_name = "div" - - - def _self_cls(self) -> type: - return Div - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) / self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Sub(Div(self.a.derivative(hash_value), self.b), - Div(Mul(self.a, self.b.derivative(hash_value)), Mul(self.b, self.b))) - - def _clean_ab(self, a, b): - if isinstance(a, AdditiveIdentity): - # Convert 0/b to 0 - return AdditiveIdentity() - - elif isinstance(a, MultiplicativeIdentity): - # Convert 1/b to inverse of b - return Inv(b) - - elif isinstance(b, MultiplicativeIdentity): - # Convert a/1 to a - return a - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constants "a"/"b" to "a/b" - return Constant(self.a.evaluate({}) / self.b.evaluate({}))._clean() - - - elif isinstance(a, Inv) and isinstance(b, Inv): - return Div(b.a, a.a) - - elif isinstance(a, Inv) and not isinstance(b, Inv): - return Inv(Mul(a.a, b)) - - elif not isinstance(a, Inv) and isinstance(b, Inv): - return Mul(a, b.a) - - elif a == b: - return MultiplicativeIdentity() - - elif isinstance(a, Pow) and a.a == b: - return Pow(b, a.power - 1) - - elif isinstance(b, Pow) and b.a == a: - return Pow(a, 1 - b.power) - - elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: - return Pow(a.a, a.power - b.power) - - else: - return Div(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Div(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Div" - -class Pow(Operation): - - serialisation_name = "pow" - - def __init__(self, a: Operation, power: float): - self.a = a - self.power = power - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) ** self.power - - def _derivative(self, hash_value: int) -> Operation: - if self.power == 0: - return AdditiveIdentity() - - elif self.power == 1: - return self.a._derivative(hash_value) - - else: - return Mul(Constant(self.power), Mul(Pow(self.a, self.power-1), self.a._derivative(hash_value))) - - def _clean(self) -> Operation: - a = self.a._clean() - - if self.power == 1: - return a - - elif self.power == 0: - return MultiplicativeIdentity() - - elif self.power == -1: - return Inv(a) - - else: - return Pow(a, self.power) - - - def _serialise_parameters(self) -> dict[str, Any]: - return {"a": Operation._serialise_json(self.a), - "power": self.power} - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Pow(Operation.deserialise_json(parameters["a"]), parameters["power"]) - - def summary(self, indent_amount: int=0, indent=" "): - return (f"{indent_amount*indent}Pow\n" + - self.a.summary(indent_amount+1, indent) + "\n" + - f"{(indent_amount+1)*indent}{self.power}\n" + - f"{indent_amount*indent})") - - def __eq__(self, other): - if isinstance(other, Pow): - return self.a == other.a and self.power == other.power - - - -# -# Matrix operations -# - -class Transpose(Operation): - """ Transpose operation - as per numpy""" - - serialisation_name = "transpose" - - def __init__(self, a: Operation, axes: tuple[int] | None = None): - self.a = a - self.axes = axes - - def evaluate(self, variables: dict[int, T]) -> T: - return np.transpose(self.a.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Transpose(self.a.derivative(hash_value)) # TODO: Check! - - def _clean(self): - clean_a = self.a._clean() - return Transpose(clean_a) - - - def _serialise_parameters(self) -> dict[str, Any]: - if self.axes is None: - return { "a": self.a._serialise_json() } - else: - return { - "a": self.a._serialise_json(), - "axes": list(self.axes) - } - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - if "axes" in parameters: - return Transpose( - a=Operation.deserialise_json(parameters["a"]), - axes=tuple(parameters["axes"])) - else: - return Transpose( - a=Operation.deserialise_json(parameters["a"])) - - - def _summary_open(self): - return "Transpose" - - def __eq__(self, other): - if isinstance(other, Transpose): - return other.a == self.a - - -class Dot(BinaryOperation): - """ Dot product - backed by numpy's dot method""" - - serialisation_name = "dot" - - def evaluate(self, variables: dict[int, T]) -> T: - return dot(self.a.evaluate(variables), self.b.evaluate(variables)) - - def _derivative(self, hash_value: int) -> Operation: - return Add( - Dot(self.a, - self.b._derivative(hash_value)), - Dot(self.a._derivative(hash_value), - self.b)) - - def _clean_ab(self, a, b): - return Dot(a, b) # Do nothing for now - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return Dot(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "Dot" - - -# TODO: Add to base operation class, and to quantities -class MatMul(BinaryOperation): - """ Matrix multiplication, using __matmul__ dunder""" - - serialisation_name = "matmul" - - def evaluate(self, variables: dict[int, T]) -> T: - return self.a.evaluate(variables) @ self.b.evaluate(variables) - - def _derivative(self, hash_value: int) -> Operation: - return Add( - MatMul(self.a, - self.b._derivative(hash_value)), - MatMul(self.a._derivative(hash_value), - self.b)) - - def _clean_ab(self, a, b): - - if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): - # Convert 0*b or a*0 to 0 - return AdditiveIdentity() - - elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): - # Convert constant "a"@"b" to "a@b" - return Constant(a.evaluate({}) @ b.evaluate({}))._clean() - - elif isinstance(a, Neg): - return Neg(Mul(a.a, b)) - - elif isinstance(b, Neg): - return Neg(Mul(a, b.a)) - - return MatMul(a, b) - - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return MatMul(*BinaryOperation._deserialise_ab(parameters)) - - def _summary_open(self): - return "MatMul" - -class TensorDot(Operation): - serialisation_name = "tensor_product" - - def __init__(self, a: Operation, b: Operation, a_index: int, b_index: int): - self.a = a - self.b = b - self.a_index = a_index - self.b_index = b_index - - def evaluate(self, variables: dict[int, T]) -> T: - return tensordot(self.a, self.b, self.a_index, self.b_index) - - - def _serialise_parameters(self) -> dict[str, Any]: - return { - "a": self.a._serialise_json(), - "b": self.b._serialise_json(), - "a_index": self.a_index, - "b_index": self.b_index } - - @staticmethod - def _deserialise(parameters: dict) -> "Operation": - return TensorDot(a = Operation.deserialise_json(parameters["a"]), - b = Operation.deserialise_json(parameters["b"]), - a_index=int(parameters["a_index"]), - b_index=int(parameters["b_index"])) - - def _summary_open(self): - return "TensorProduct" - - -_serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, - Variable, - Neg, Inv, - Add, Sub, Mul, Div, Pow, - Transpose, Dot, MatMul, TensorDot] - -_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} - class UnitError(Exception): """Errors caused by unit specification not being correct""" @@ -983,25 +29,10 @@ def hash_data_via_numpy(*data: ArrayLike): return int(md5_hash.hexdigest(), 16) - -##################################### -# # -# # -# # -# Quantities begin here # -# # -# # -# # -##################################### - - - QuantityType = TypeVar("QuantityType") class QuantityHistory: - """ Class that holds the information for keeping track of operations done on quantities """ - def __init__(self, operation_tree: Operation, references: dict[int, "Quantity"]): self.operation_tree = operation_tree self.references = references @@ -1015,10 +46,6 @@ def jacobian(self) -> list[Operation]: # Use the hash value to specify the variable of differentiation return [self.operation_tree.derivative(key) for key in self.reference_key_list] - def _recalculate(self): - """ Recalculate the value of this object - primary use case is for testing """ - return self.operation_tree.evaluate(self.references) - def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, int]: "Quantity"] = {}): """ Do standard error propagation to calculate the uncertainties associated with this quantity @@ -1030,6 +57,14 @@ def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, raise NotImplementedError("User specified covariances not currently implemented") jacobian = self.jacobian() + # jacobian_units = [quantity_units / self.references[key].units for key in self.reference_key_list] + # + # # Evaluate the jacobian + # # TODO: should we use quantities here, does that work automatically? + # evaluated_jacobian = [Quantity( + # value=entry.evaluate(self.si_reference_values), + # units=unit.si_equivalent()) + # for entry, unit in zip(jacobian, jacobian_units)] evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] @@ -1051,7 +86,7 @@ def variable(quantity: "Quantity"): return QuantityHistory(Variable(quantity.hash_value), {quantity.hash_value: quantity}) @staticmethod - def apply_operation(operation: type[Operation], *histories: "QuantityHistory", **extra_parameters) -> "QuantityHistory": + def apply_operation(operation: type[Operation], *histories: "QuantityHistory") -> "QuantityHistory": """ Apply an operation to the history This is slightly unsafe as it is possible to attempt to apply an n-ary operation to a number of trees other @@ -1068,7 +103,7 @@ def apply_operation(operation: type[Operation], *histories: "QuantityHistory", * references.update(history.references) return QuantityHistory( - operation(*[history.operation_tree for history in histories], **extra_parameters), + operation(*[history.operation_tree for history in histories]), references) def has_variance(self): @@ -1078,16 +113,6 @@ def has_variance(self): return False - def summary(self): - - variable_strings = [self.references[key].string_repr for key in self.references] - - s = "Variables: "+",".join(variable_strings) - s += "\n" - s += self.operation_tree.summary() - - return s - class Quantity[QuantityType]: @@ -1110,10 +135,10 @@ def __init__(self, self.hash_value = -1 """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ - self._variance = None - """ Contains the variance if it is data driven """ + """ Contains the variance if it is data driven, else it is """ if standard_error is None: + self._variance = None self.hash_value = hash_data_via_numpy(hash_seed, value) else: self._variance = standard_error ** 2 @@ -1183,14 +208,14 @@ def __mul__(self: Self, other: ArrayLike | Self ) -> Self: return DerivedQuantity( self.value * other.value, self.units * other.units, - history=QuantityHistory.apply_operation(Mul, self.history, other.history)) + history=QuantityHistory.apply_operation(operations.Mul, self.history, other.history)) else: return DerivedQuantity(self.value * other, self.units, QuantityHistory( - Mul( + operations.Mul( self.history.operation_tree, - Constant(other)), + operations.Constant(other)), self.history.references)) def __rmul__(self: Self, other: ArrayLike | Self): @@ -1199,72 +224,33 @@ def __rmul__(self: Self, other: ArrayLike | Self): other.value * self.value, other.units * self.units, history=QuantityHistory.apply_operation( - Mul, + operations.Mul, other.history, self.history)) else: return DerivedQuantity(other * self.value, self.units, QuantityHistory( - Mul( - Constant(other), + operations.Mul( + operations.Constant(other), self.history.operation_tree), self.history.references)) - - def __matmul__(self, other: ArrayLike | Self): - if isinstance(other, Quantity): - return DerivedQuantity( - self.value @ other.value, - self.units * other.units, - history=QuantityHistory.apply_operation( - MatMul, - self.history, - other.history)) - else: - return DerivedQuantity( - self.value @ other, - self.units, - QuantityHistory( - MatMul( - self.history.operation_tree, - Constant(other)), - self.history.references)) - - def __rmatmul__(self, other: ArrayLike | Self): - if isinstance(other, Quantity): - return DerivedQuantity( - other.value @ self.value, - other.units * self.units, - history=QuantityHistory.apply_operation( - MatMul, - other.history, - self.history)) - - else: - return DerivedQuantity(other @ self.value, self.units, - QuantityHistory( - MatMul( - Constant(other), - self.history.operation_tree), - self.history.references)) - - def __truediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): return DerivedQuantity( self.value / other.value, self.units / other.units, history=QuantityHistory.apply_operation( - Div, + operations.Div, self.history, other.history)) else: return DerivedQuantity(self.value / other, self.units, QuantityHistory( - Div( - Constant(other), + operations.Div( + operations.Constant(other), self.history.operation_tree), self.history.references)) @@ -1274,7 +260,7 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: other.value / self.value, other.units / self.units, history=QuantityHistory.apply_operation( - Div, + operations.Div, other.history, self.history )) @@ -1284,8 +270,8 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: other / self.value, self.units ** -1, QuantityHistory( - Div( - Constant(other), + operations.Div( + operations.Constant(other), self.history.operation_tree), self.history.references)) @@ -1296,7 +282,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: self.value + (other.value * other.units.scale) / self.units.scale, self.units, QuantityHistory.apply_operation( - Add, + operations.Add, self.history, other.history)) else: @@ -1310,7 +296,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: def __neg__(self): return DerivedQuantity(-self.value, self.units, QuantityHistory.apply_operation( - Neg, + operations.Neg, self.history )) @@ -1324,11 +310,14 @@ def __pow__(self: Self, other: int | float): return DerivedQuantity(self.value ** other, self.units ** other, QuantityHistory( - Pow( + operations.Pow( self.history.operation_tree, other), self.history.references)) + def __eq__(self, other: object) -> bool: + return isinstance(other, Quantity) and self.hash_value == other.hash_value + @staticmethod def _array_repr_format(arr: np.ndarray): """ Format the array """ @@ -1376,10 +365,6 @@ def __repr__(self): def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass - @property - def string_repr(self): - return str(self.hash_value) - class NamedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, @@ -1414,10 +399,6 @@ def with_standard_error(self, standard_error: Quantity): f"are not compatible with value units ({self.units})") - @property - def string_repr(self): - return self.name - class DerivedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): super().__init__(value, units, standard_error=None) From 1baa90061151546858d4d7f124b41219b75398ab Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 20 Dec 2024 15:52:15 +0000 Subject: [PATCH 651/675] Wrote a test for the trend. --- test/utest_trend.py | 56 +++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 38 deletions(-) diff --git a/test/utest_trend.py b/test/utest_trend.py index b079bf53c..e86c480f1 100644 --- a/test/utest_trend.py +++ b/test/utest_trend.py @@ -1,37 +1,30 @@ from os import listdir, path import pytest - +from os import path, listdir +from sasdata.ascii_reader_metadata import AsciiMetadataCategory, AsciiReaderMetadata +from sasdata.quantities.units import per_nanometer +from sasdata.temp_ascii_reader import AsciiReaderParams import sasdata.temp_ascii_reader as ascii_reader from sasdata.ascii_reader_metadata import AsciiMetadataCategory from sasdata.quantities.units import per_angstrom, per_nanometer from sasdata.temp_ascii_reader import AsciiReaderParams from sasdata.trend import Trend -mumag_test_directories = [ +test_directories = [ 'FeNiB_perpendicular_Bersweiler_et_al', 'Nanoperm_perpendicular_Honecker_et_al', 'NdFeB_parallel_Bick_et_al' ] -custom_test_directory = 'custom_test' - -def get_files_to_load(directory_name: str) -> list[str]: - load_from = path.join(path.dirname(__file__), 'trend_test_data', directory_name) - base_filenames_to_load = listdir(load_from) - files_to_load = [path.join(load_from, basename) for basename in base_filenames_to_load] - return files_to_load +@pytest.mark.parametrize('directory_name', test_directories) +def test_trend_build(directory_name: str): + """Try to build a trend object on the MuMag datasets, and see if all the Q items match (as they should).""" + load_from = path.join(path.dirname(__file__), directory_name) + files_to_load = listdir(load_from) -@pytest.mark.parametrize('directory_name', mumag_test_directories) -def test_trend_build_interpolate(directory_name: str): - """Try to build a trend object on the MuMag datasets""" - files_to_load = get_files_to_load(directory_name) - params = AsciiReaderParams( - filenames=files_to_load, - columns=[('Q', per_nanometer), ('I', per_nanometer), ('dI', per_nanometer)], - ) - params.separator_dict['Whitespace'] = True - params.metadata.master_metadata['magnetic'] = AsciiMetadataCategory( + metadata = AsciiReaderMetadata() + metadata.master_metadata['magnetic'] = AsciiMetadataCategory( values={ 'counting_index': 0, 'applied_magnetic_field': 1, @@ -39,31 +32,18 @@ def test_trend_build_interpolate(directory_name: str): 'demagnetizing_field': 3 } ) - data = ascii_reader.load_data(params) - trend = Trend( - data=data, - trend_axis=['magnetic', 'applied_magnetic_field'] - ) - # Initially, the q axes in this date don't exactly match - to_interpolate_on = 'Q' - assert not trend.all_axis_match(to_interpolate_on) - interpolated_trend = trend.interpolate(to_interpolate_on) - assert interpolated_trend.all_axis_match(to_interpolate_on) -def test_trend_q_axis_match(): - files_to_load = get_files_to_load(custom_test_directory) params = AsciiReaderParams( filenames=files_to_load, - columns=[('Q', per_angstrom), ('I', per_angstrom)] - ) - params.metadata.master_metadata['magnetic'] = AsciiMetadataCategory( - values={ - 'counting_index': 0, - } + starting_line=0, + columns=[('Q', per_nanometer), ('I', per_nanometer), ('dI', per_nanometer)], + excluded_lines=set(), + separator_dict={'Whitespace': True, 'Comma': False, 'Tab': False}, + metadata=metadata, ) data = ascii_reader.load_data(params) trend = Trend( data=data, - trend_axis=['magnetic', 'counting_index'] + trend_axis=['magnetic', 'applied_magnetic_field'] ) assert trend.all_axis_match('Q') From fa771df39bcc9003888e1e3c6819b28bc6e19b98 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 15 Jan 2025 13:38:30 +0000 Subject: [PATCH 652/675] Use full function name here. --- sasdata/metadata.py | 52 +++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 6c21ed7e4..fb90777f5 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -337,10 +337,10 @@ def summary(self) -> str: class Instrument: def __init__(self, target: AccessorTarget): - self.aperture = Aperture(target.with_path_prefix("sasaperture")) - self.collimation = Collimation(target.with_path_prefix("sascollimation")) - self.detector = Detector(target.with_path_prefix("sasdetector")) - self.source = Source(target.with_path_prefix("sassource")) + self.aperture = Aperture(target.with_path_prefix("sasaperture|aperture")) + self.collimation = Collimation(target.with_path_prefix("sascollimation|collimation")) + self.detector = Detector(target.with_path_prefix("sasdetector|detector")) + self.source = Source(target.with_path_prefix("sassource|source")) def summary(self): return ( self.aperture.summary() + @@ -348,31 +348,55 @@ def summary(self): self.detector.summary() + self.source.summary()) +def decode_string(data): + """ This is some crazy stuff""" + + if isinstance(data, str): + return data + + elif isinstance(data, np.ndarray): + + if data.dtype == object: + + data = data.reshape(-1) + data = data[0] + + if isinstance(data, bytes): + return data.decode("utf-8") + + return str(data) + + else: + return data.tobytes().decode("utf-8") + + else: + return str(data) class Metadata: def __init__(self, target: AccessorTarget): self._target = target - self.instrument = Instrument(target.with_path_prefix("sasinstrument")) - self.process = Process(target.with_path_prefix("sasprocess")) - self.sample = Sample(target.with_path_prefix("sassample")) - self.transmission_spectrum = TransmissionSpectrum(target.with_path_prefix("sastransmission_spectrum")) + self.instrument = Instrument(target.with_path_prefix("sasinstrument|instrument")) + self.process = Process(target.with_path_prefix("sasprocess|process")) + self.sample = Sample(target.with_path_prefix("sassample|sample")) + self.transmission_spectrum = TransmissionSpectrum(target.with_path_prefix("sastransmission_spectrum|transmission_spectrum")) self._title = StringAccessor(target, "title") self._run = StringAccessor(target, "run") - self._definitiion = StringAccessor(target, "definition") + self._definition = StringAccessor(target, "definition") - self.title: str = self._title.value - self.run: str = self._run.value - self.definitiion: str = self._definitiion.value + self.title: str = decode_string(self._title.value) + self.run: str = decode_string(self._run.value) + self.definition: str = decode_string(self._definition.value) def summary(self): return ( - f" {self.title}, Run: {self.run}\n" + " " + "="*len(self.title) + + f" {self.title}, Run: {self.run}\n" + + " " + "="*len(self.title) + "=======" + "="*len(self.run) + "\n\n" + f"Definition: {self.title}\n" + self.process.summary() + self.sample.summary() + self.instrument.summary() + - self.transmission_spectrum.summary()) \ No newline at end of file + self.transmission_spectrum.summary()) From 6a6971ef16c432da5317dc269914b07ab0037f88 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Fri, 17 Jan 2025 07:52:44 +0000 Subject: [PATCH 653/675] Add with standard error for quantities. --- sasdata/quantities/quantity.py | 1099 +++++++++++++++++++++++++++++++- 1 file changed, 1065 insertions(+), 34 deletions(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 6b391d2db..57cde09c8 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,18 +1,972 @@ -from typing import Collection, Sequence, TypeVar, Generic, Self -from dataclasses import dataclass +from typing import Self import numpy as np from numpy._typing import ArrayLike -from sasdata.quantities.operations import Operation, Variable -from sasdata.quantities import operations, units +from sasdata.quantities import units +from sasdata.quantities.numerical_encoding import numerical_decode, numerical_encode from sasdata.quantities.units import Unit, NamedUnit import hashlib +from typing import Any, TypeVar, Union + +import json + +T = TypeVar("T") + + + + + +################### Quantity based operations, need to be here to avoid cyclic dependencies ##################### + +def transpose(a: Union["Quantity[ArrayLike]", ArrayLike], axes: tuple | None = None): + """ Transpose an array or an array based quantity, can also do reordering of axes""" + if isinstance(a, Quantity): + + if axes is None: + return DerivedQuantity(value=np.transpose(a.value, axes=axes), + units=a.units, + history=QuantityHistory.apply_operation(Transpose, a.history)) + + else: + return DerivedQuantity(value=np.transpose(a.value, axes=axes), + units=a.units, + history=QuantityHistory.apply_operation(Transpose, a.history, axes=axes)) + + else: + return np.transpose(a, axes=axes) + + +def dot(a: Union["Quantity[ArrayLike]", ArrayLike], b: Union["Quantity[ArrayLike]", ArrayLike]): + """ Dot product of two arrays or two array based quantities """ + a_is_quantity = isinstance(a, Quantity) + b_is_quantity = isinstance(b, Quantity) + + if a_is_quantity or b_is_quantity: + + # If its only one of them that is a quantity, convert the other one + + if not a_is_quantity: + a = Quantity(a, units.none) + + if not b_is_quantity: + b = Quantity(b, units.none) + + return DerivedQuantity( + value=np.dot(a.value, b.value), + units=a.units * b.units, + history=QuantityHistory.apply_operation(Dot, a.history, b.history)) + + else: + return np.dot(a, b) + +def tensordot(a: Union["Quantity[ArrayLike]", ArrayLike] | ArrayLike, b: Union["Quantity[ArrayLike]", ArrayLike], a_index: int, b_index: int): + """ Tensor dot product - equivalent to contracting two tensors, such as + + A_{i0, i1, i2, i3...} and B_{j0, j1, j2...} + + e.g. if a_index is 1 and b_index is zero, it will be the sum + + C_{i0, i2, i3 ..., j1, j2 ...} = sum_k A_{i0, k, i2, i3 ...} B_{k, j1, j2 ...} + + (I think, have to check what happens with indices TODO!) + + """ + + a_is_quantity = isinstance(a, Quantity) + b_is_quantity = isinstance(b, Quantity) + + if a_is_quantity or b_is_quantity: + + # If its only one of them that is a quantity, convert the other one + + if not a_is_quantity: + a = Quantity(a, units.none) + + if not b_is_quantity: + b = Quantity(b, units.none) + + return DerivedQuantity( + value=np.tensordot(a.value, b.value, axes=(a_index, b_index)), + units=a.units * b.units, + history=QuantityHistory.apply_operation( + TensorDot, + a.history, + b.history, + a_index=a_index, + b_index=b_index)) + + else: + return np.tensordot(a, b, axes=(a_index, b_index)) + + +################### Operation Definitions ####################################### + +def hash_and_name(hash_or_name: int | str): + """ Infer the name of a variable from a hash, or the hash from the name + + Note: hash_and_name(hash_and_name(number)[1]) is not the identity + however: hash_and_name(hash_and_name(number)) is + """ + + if isinstance(hash_or_name, str): + hash_value = hash(hash_or_name) + name = hash_or_name + + return hash_value, name + + elif isinstance(hash_or_name, int): + hash_value = hash_or_name + name = f"#{hash_or_name}" + + return hash_value, name + + elif isinstance(hash_or_name, tuple): + return hash_or_name + + else: + raise TypeError("Variable name_or_hash_value must be either str or int") + +class Operation: + + serialisation_name = "unknown" + def summary(self, indent_amount: int = 0, indent: str=" "): + """ Summary of the operation tree""" + + s = f"{indent_amount*indent}{self._summary_open()}(\n" + + for chunk in self._summary_components(): + s += chunk.summary(indent_amount+1, indent) + "\n" + + s += f"{indent_amount*indent})" + + return s + def _summary_open(self): + """ First line of summary """ + + def _summary_components(self) -> list["Operation"]: + return [] + def evaluate(self, variables: dict[int, T]) -> T: + + """ Evaluate this operation """ + + def _derivative(self, hash_value: int) -> "Operation": + """ Get the derivative of this operation """ + + def _clean(self): + """ Clean up this operation - i.e. remove silly things like 1*x """ + return self + + def derivative(self, variable: Union[str, int, "Variable"], simplify=True): + if isinstance(variable, Variable): + hash_value = variable.hash_value + else: + hash_value, _ = hash_and_name(variable) + + derivative = self._derivative(hash_value) + + if not simplify: + return derivative + + derivative_string = derivative.serialise() + + # print("---------------") + # print("Base") + # print("---------------") + # print(derivative.summary()) + + # Inefficient way of doing repeated simplification, but it will work + for i in range(100): # set max iterations + + derivative = derivative._clean() + # + # print("-------------------") + # print("Iteration", i+1) + # print("-------------------") + # print(derivative.summary()) + # print("-------------------") + + new_derivative_string = derivative.serialise() + + if derivative_string == new_derivative_string: + break + + derivative_string = new_derivative_string + + return derivative + + @staticmethod + def deserialise(data: str) -> "Operation": + json_data = json.loads(data) + return Operation.deserialise_json(json_data) + + @staticmethod + def deserialise_json(json_data: dict) -> "Operation": + + operation = json_data["operation"] + parameters = json_data["parameters"] + cls = _serialisation_lookup[operation] + + try: + return cls._deserialise(parameters) + + except NotImplementedError: + raise NotImplementedError(f"No method to deserialise {operation} with {parameters} (cls={cls})") + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + raise NotImplementedError(f"Deserialise not implemented for this class") + + def serialise(self) -> str: + return json.dumps(self._serialise_json()) + + def _serialise_json(self) -> dict[str, Any]: + return {"operation": self.serialisation_name, + "parameters": self._serialise_parameters()} + + def _serialise_parameters(self) -> dict[str, Any]: + raise NotImplementedError("_serialise_parameters not implemented") + + def __eq__(self, other: "Operation"): + return NotImplemented + +class ConstantBase(Operation): + pass + +class AdditiveIdentity(ConstantBase): + + serialisation_name = "zero" + def evaluate(self, variables: dict[int, T]) -> T: + return 0 + + def _derivative(self, hash_value: int) -> Operation: + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return AdditiveIdentity() + + def _serialise_parameters(self) -> dict[str, Any]: + return {} + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}0 [Add.Id.]" + + def __eq__(self, other): + if isinstance(other, AdditiveIdentity): + return True + elif isinstance(other, Constant): + if other.value == 0: + return True + + return False + + + +class MultiplicativeIdentity(ConstantBase): + + serialisation_name = "one" + + def evaluate(self, variables: dict[int, T]) -> T: + return 1 + + def _derivative(self, hash_value: int): + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return MultiplicativeIdentity() + + + def _serialise_parameters(self) -> dict[str, Any]: + return {} + + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}1 [Mul.Id.]" + + def __eq__(self, other): + if isinstance(other, MultiplicativeIdentity): + return True + elif isinstance(other, Constant): + if other.value == 1: + return True + + return False + + +class Constant(ConstantBase): + + serialisation_name = "constant" + def __init__(self, value): + self.value = value + + def summary(self, indent_amount: int = 0, indent: str=" "): + return repr(self.value) + + def evaluate(self, variables: dict[int, T]) -> T: + return self.value + + def _derivative(self, hash_value: int): + return AdditiveIdentity() + + def _clean(self): + + if self.value == 0: + return AdditiveIdentity() + + elif self.value == 1: + return MultiplicativeIdentity() + + else: + return self + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + value = numerical_decode(parameters["value"]) + return Constant(value) + + + def _serialise_parameters(self) -> dict[str, Any]: + return {"value": numerical_encode(self.value)} + + def summary(self, indent_amount: int=0, indent=" "): + return f"{indent_amount*indent}{self.value}" + + def __eq__(self, other): + if isinstance(other, AdditiveIdentity): + return self.value == 0 + + elif isinstance(other, MultiplicativeIdentity): + return self.value == 1 + + elif isinstance(other, Constant): + if other.value == self.value: + return True + + return False + + +class Variable(Operation): + + serialisation_name = "variable" + def __init__(self, name_or_hash_value: int | str | tuple[int, str]): + self.hash_value, self.name = hash_and_name(name_or_hash_value) + + def evaluate(self, variables: dict[int, T]) -> T: + try: + return variables[self.hash_value] + except KeyError: + raise ValueError(f"Variable dictionary didn't have an entry for {self.name} (hash={self.hash_value})") + + def _derivative(self, hash_value: int) -> Operation: + if hash_value == self.hash_value: + return MultiplicativeIdentity() + else: + return AdditiveIdentity() + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + hash_value = parameters["hash_value"] + name = parameters["name"] + + return Variable((hash_value, name)) + + def _serialise_parameters(self) -> dict[str, Any]: + return {"hash_value": self.hash_value, + "name": self.name} + + def summary(self, indent_amount: int = 0, indent: str=" "): + return f"{indent_amount*indent}{self.name}" + + def __eq__(self, other): + if isinstance(other, Variable): + return self.hash_value == other.hash_value + + return False + +class UnaryOperation(Operation): + + def __init__(self, a: Operation): + self.a = a + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": self.a._serialise_json()} + + def _summary_components(self) -> list["Operation"]: + return [self.a] + + + + +class Neg(UnaryOperation): + + serialisation_name = "neg" + def evaluate(self, variables: dict[int, T]) -> T: + return -self.a.evaluate(variables) + + def _derivative(self, hash_value: int): + return Neg(self.a._derivative(hash_value)) + + def _clean(self): + + clean_a = self.a._clean() + + if isinstance(clean_a, Neg): + # Removes double negations + return clean_a.a + + elif isinstance(clean_a, Constant): + return Constant(-clean_a.value)._clean() + + else: + return Neg(clean_a) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Neg(Operation.deserialise_json(parameters["a"])) + + + def _summary_open(self): + return "Neg" + + def __eq__(self, other): + if isinstance(other, Neg): + return other.a == self.a + + +class Inv(UnaryOperation): + + serialisation_name = "reciprocal" + + def evaluate(self, variables: dict[int, T]) -> T: + return 1/self.a.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Neg(Div(self.a._derivative(hash_value), Mul(self.a, self.a))) + + def _clean(self): + clean_a = self.a._clean() + + if isinstance(clean_a, Inv): + # Removes double negations + return clean_a.a + + elif isinstance(clean_a, Neg): + # cannonicalise 1/-a to -(1/a) + # over multiple iterations this should have the effect of ordering and gathering Neg and Inv + return Neg(Inv(clean_a.a)) + + elif isinstance(clean_a, Constant): + return Constant(1/clean_a.value)._clean() + + else: + return Inv(clean_a) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Inv(Operation.deserialise_json(parameters["a"])) + + def _summary_open(self): + return "Inv" + + + def __eq__(self, other): + if isinstance(other, Inv): + return other.a == self.a + +class BinaryOperation(Operation): + def __init__(self, a: Operation, b: Operation): + self.a = a + self.b = b + + def _clean(self): + return self._clean_ab(self.a._clean(), self.b._clean()) + + def _clean_ab(self, a, b): + raise NotImplementedError("_clean_ab not implemented") + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": self.a._serialise_json(), + "b": self.b._serialise_json()} + + @staticmethod + def _deserialise_ab(parameters) -> tuple[Operation, Operation]: + return (Operation.deserialise_json(parameters["a"]), + Operation.deserialise_json(parameters["b"])) + + + def _summary_components(self) -> list["Operation"]: + return [self.a, self.b] + + def _self_cls(self) -> type: + """ Own class""" + def __eq__(self, other): + if isinstance(other, self._self_cls()): + return other.a == self.a and self.b == other.b + +class Add(BinaryOperation): + + serialisation_name = "add" + + def _self_cls(self) -> type: + return Add + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) + self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add(self.a._derivative(hash_value), self.b._derivative(hash_value)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity): + # Convert 0 + b to b + return b + + elif isinstance(b, AdditiveIdentity): + # Convert a + 0 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"+"b" to "a+b" + return Constant(a.evaluate({}) + b.evaluate({}))._clean() + + elif isinstance(a, Neg): + if isinstance(b, Neg): + # Convert (-a)+(-b) to -(a+b) + return Neg(Add(a.a, b.a)) + else: + # Convert (-a) + b to b-a + return Sub(b, a.a) + + elif isinstance(b, Neg): + # Convert a+(-b) to a-b + return Sub(a, b.a) + + elif a == b: + return Mul(Constant(2), a) + + else: + return Add(a, b) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Add(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Add" + +class Sub(BinaryOperation): + + serialisation_name = "sub" + + + def _self_cls(self) -> type: + return Sub + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) - self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Sub(self.a._derivative(hash_value), self.b._derivative(hash_value)) + + def _clean_ab(self, a, b): + if isinstance(a, AdditiveIdentity): + # Convert 0 - b to -b + return Neg(b) + + elif isinstance(b, AdditiveIdentity): + # Convert a - 0 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant pair "a" - "b" to "a-b" + return Constant(a.evaluate({}) - b.evaluate({}))._clean() + + elif isinstance(a, Neg): + if isinstance(b, Neg): + # Convert (-a)-(-b) to b-a + return Sub(b.a, a.a) + else: + # Convert (-a)-b to -(a+b) + return Neg(Add(a.a, b)) + + elif isinstance(b, Neg): + # Convert a-(-b) to a+b + return Add(a, b.a) + + elif a == b: + return AdditiveIdentity() + + else: + return Sub(a, b) + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Sub(*BinaryOperation._deserialise_ab(parameters)) + + + def _summary_open(self): + return "Sub" + +class Mul(BinaryOperation): + + serialisation_name = "mul" + + + def _self_cls(self) -> type: + return Mul + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) * self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add(Mul(self.a, self.b._derivative(hash_value)), Mul(self.a._derivative(hash_value), self.b)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): + # Convert 0*b or a*0 to 0 + return AdditiveIdentity() + + elif isinstance(a, MultiplicativeIdentity): + # Convert 1*b to b + return b + + elif isinstance(b, MultiplicativeIdentity): + # Convert a*1 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"*"b" to "a*b" + return Constant(a.evaluate({}) * b.evaluate({}))._clean() + + elif isinstance(a, Inv) and isinstance(b, Inv): + return Inv(Mul(a.a, b.a)) + + elif isinstance(a, Inv) and not isinstance(b, Inv): + return Div(b, a.a) + + elif not isinstance(a, Inv) and isinstance(b, Inv): + return Div(a, b.a) + + elif isinstance(a, Neg): + return Neg(Mul(a.a, b)) + + elif isinstance(b, Neg): + return Neg(Mul(a, b.a)) + + elif a == b: + return Pow(a, 2) + + elif isinstance(a, Pow) and a.a == b: + return Pow(b, a.power + 1) + + elif isinstance(b, Pow) and b.a == a: + return Pow(a, b.power + 1) + + elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: + return Pow(a.a, a.power + b.power) + + else: + return Mul(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Mul(*BinaryOperation._deserialise_ab(parameters)) + + + def _summary_open(self): + return "Mul" + +class Div(BinaryOperation): + + serialisation_name = "div" + + + def _self_cls(self) -> type: + return Div + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) / self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Sub(Div(self.a.derivative(hash_value), self.b), + Div(Mul(self.a, self.b.derivative(hash_value)), Mul(self.b, self.b))) + + def _clean_ab(self, a, b): + if isinstance(a, AdditiveIdentity): + # Convert 0/b to 0 + return AdditiveIdentity() + + elif isinstance(a, MultiplicativeIdentity): + # Convert 1/b to inverse of b + return Inv(b) + + elif isinstance(b, MultiplicativeIdentity): + # Convert a/1 to a + return a + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constants "a"/"b" to "a/b" + return Constant(self.a.evaluate({}) / self.b.evaluate({}))._clean() + + + elif isinstance(a, Inv) and isinstance(b, Inv): + return Div(b.a, a.a) + + elif isinstance(a, Inv) and not isinstance(b, Inv): + return Inv(Mul(a.a, b)) + + elif not isinstance(a, Inv) and isinstance(b, Inv): + return Mul(a, b.a) + + elif a == b: + return MultiplicativeIdentity() + + elif isinstance(a, Pow) and a.a == b: + return Pow(b, a.power - 1) + + elif isinstance(b, Pow) and b.a == a: + return Pow(a, 1 - b.power) + + elif isinstance(a, Pow) and isinstance(b, Pow) and a.a == b.a: + return Pow(a.a, a.power - b.power) + + else: + return Div(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Div(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Div" + +class Pow(Operation): + + serialisation_name = "pow" + + def __init__(self, a: Operation, power: float): + self.a = a + self.power = power + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) ** self.power + + def _derivative(self, hash_value: int) -> Operation: + if self.power == 0: + return AdditiveIdentity() + + elif self.power == 1: + return self.a._derivative(hash_value) + + else: + return Mul(Constant(self.power), Mul(Pow(self.a, self.power-1), self.a._derivative(hash_value))) + + def _clean(self) -> Operation: + a = self.a._clean() + + if self.power == 1: + return a + + elif self.power == 0: + return MultiplicativeIdentity() + + elif self.power == -1: + return Inv(a) + + else: + return Pow(a, self.power) + + + def _serialise_parameters(self) -> dict[str, Any]: + return {"a": Operation._serialise_json(self.a), + "power": self.power} + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Pow(Operation.deserialise_json(parameters["a"]), parameters["power"]) + + def summary(self, indent_amount: int=0, indent=" "): + return (f"{indent_amount*indent}Pow\n" + + self.a.summary(indent_amount+1, indent) + "\n" + + f"{(indent_amount+1)*indent}{self.power}\n" + + f"{indent_amount*indent})") + + def __eq__(self, other): + if isinstance(other, Pow): + return self.a == other.a and self.power == other.power + + + +# +# Matrix operations +# + +class Transpose(Operation): + """ Transpose operation - as per numpy""" + + serialisation_name = "transpose" + + def __init__(self, a: Operation, axes: tuple[int] | None = None): + self.a = a + self.axes = axes + + def evaluate(self, variables: dict[int, T]) -> T: + return np.transpose(self.a.evaluate(variables)) + + def _derivative(self, hash_value: int) -> Operation: + return Transpose(self.a.derivative(hash_value)) # TODO: Check! + + def _clean(self): + clean_a = self.a._clean() + return Transpose(clean_a) + + + def _serialise_parameters(self) -> dict[str, Any]: + if self.axes is None: + return { "a": self.a._serialise_json() } + else: + return { + "a": self.a._serialise_json(), + "axes": list(self.axes) + } + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + if "axes" in parameters: + return Transpose( + a=Operation.deserialise_json(parameters["a"]), + axes=tuple(parameters["axes"])) + else: + return Transpose( + a=Operation.deserialise_json(parameters["a"])) + + + def _summary_open(self): + return "Transpose" + + def __eq__(self, other): + if isinstance(other, Transpose): + return other.a == self.a + + +class Dot(BinaryOperation): + """ Dot product - backed by numpy's dot method""" + + serialisation_name = "dot" + + def evaluate(self, variables: dict[int, T]) -> T: + return dot(self.a.evaluate(variables), self.b.evaluate(variables)) + + def _derivative(self, hash_value: int) -> Operation: + return Add( + Dot(self.a, + self.b._derivative(hash_value)), + Dot(self.a._derivative(hash_value), + self.b)) + + def _clean_ab(self, a, b): + return Dot(a, b) # Do nothing for now + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return Dot(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "Dot" + + +# TODO: Add to base operation class, and to quantities +class MatMul(BinaryOperation): + """ Matrix multiplication, using __matmul__ dunder""" + + serialisation_name = "matmul" + + def evaluate(self, variables: dict[int, T]) -> T: + return self.a.evaluate(variables) @ self.b.evaluate(variables) + + def _derivative(self, hash_value: int) -> Operation: + return Add( + MatMul(self.a, + self.b._derivative(hash_value)), + MatMul(self.a._derivative(hash_value), + self.b)) + + def _clean_ab(self, a, b): + + if isinstance(a, AdditiveIdentity) or isinstance(b, AdditiveIdentity): + # Convert 0*b or a*0 to 0 + return AdditiveIdentity() + + elif isinstance(a, ConstantBase) and isinstance(b, ConstantBase): + # Convert constant "a"@"b" to "a@b" + return Constant(a.evaluate({}) @ b.evaluate({}))._clean() + + elif isinstance(a, Neg): + return Neg(Mul(a.a, b)) + + elif isinstance(b, Neg): + return Neg(Mul(a, b.a)) + + return MatMul(a, b) + + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return MatMul(*BinaryOperation._deserialise_ab(parameters)) + + def _summary_open(self): + return "MatMul" + +class TensorDot(Operation): + serialisation_name = "tensor_product" + + def __init__(self, a: Operation, b: Operation, a_index: int, b_index: int): + self.a = a + self.b = b + self.a_index = a_index + self.b_index = b_index + + def evaluate(self, variables: dict[int, T]) -> T: + return tensordot(self.a, self.b, self.a_index, self.b_index) + + + def _serialise_parameters(self) -> dict[str, Any]: + return { + "a": self.a._serialise_json(), + "b": self.b._serialise_json(), + "a_index": self.a_index, + "b_index": self.b_index } + + @staticmethod + def _deserialise(parameters: dict) -> "Operation": + return TensorDot(a = Operation.deserialise_json(parameters["a"]), + b = Operation.deserialise_json(parameters["b"]), + a_index=int(parameters["a_index"]), + b_index=int(parameters["b_index"])) + + def _summary_open(self): + return "TensorProduct" + + +_serialisable_classes = [AdditiveIdentity, MultiplicativeIdentity, Constant, + Variable, + Neg, Inv, + Add, Sub, Mul, Div, Pow, + Transpose, Dot, MatMul, TensorDot] + +_serialisation_lookup = {cls.serialisation_name: cls for cls in _serialisable_classes} + class UnitError(Exception): """Errors caused by unit specification not being correct""" @@ -29,10 +983,25 @@ def hash_data_via_numpy(*data: ArrayLike): return int(md5_hash.hexdigest(), 16) + +##################################### +# # +# # +# # +# Quantities begin here # +# # +# # +# # +##################################### + + + QuantityType = TypeVar("QuantityType") class QuantityHistory: + """ Class that holds the information for keeping track of operations done on quantities """ + def __init__(self, operation_tree: Operation, references: dict[int, "Quantity"]): self.operation_tree = operation_tree self.references = references @@ -46,6 +1015,10 @@ def jacobian(self) -> list[Operation]: # Use the hash value to specify the variable of differentiation return [self.operation_tree.derivative(key) for key in self.reference_key_list] + def _recalculate(self): + """ Recalculate the value of this object - primary use case is for testing """ + return self.operation_tree.evaluate(self.references) + def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, int]: "Quantity"] = {}): """ Do standard error propagation to calculate the uncertainties associated with this quantity @@ -57,14 +1030,6 @@ def variance_propagate(self, quantity_units: Unit, covariances: dict[tuple[int, raise NotImplementedError("User specified covariances not currently implemented") jacobian = self.jacobian() - # jacobian_units = [quantity_units / self.references[key].units for key in self.reference_key_list] - # - # # Evaluate the jacobian - # # TODO: should we use quantities here, does that work automatically? - # evaluated_jacobian = [Quantity( - # value=entry.evaluate(self.si_reference_values), - # units=unit.si_equivalent()) - # for entry, unit in zip(jacobian, jacobian_units)] evaluated_jacobian = [entry.evaluate(self.references) for entry in jacobian] @@ -86,7 +1051,7 @@ def variable(quantity: "Quantity"): return QuantityHistory(Variable(quantity.hash_value), {quantity.hash_value: quantity}) @staticmethod - def apply_operation(operation: type[Operation], *histories: "QuantityHistory") -> "QuantityHistory": + def apply_operation(operation: type[Operation], *histories: "QuantityHistory", **extra_parameters) -> "QuantityHistory": """ Apply an operation to the history This is slightly unsafe as it is possible to attempt to apply an n-ary operation to a number of trees other @@ -103,7 +1068,7 @@ def apply_operation(operation: type[Operation], *histories: "QuantityHistory") - references.update(history.references) return QuantityHistory( - operation(*[history.operation_tree for history in histories]), + operation(*[history.operation_tree for history in histories], **extra_parameters), references) def has_variance(self): @@ -113,6 +1078,16 @@ def has_variance(self): return False + def summary(self): + + variable_strings = [self.references[key].string_repr for key in self.references] + + s = "Variables: "+",".join(variable_strings) + s += "\n" + s += self.operation_tree.summary() + + return s + class Quantity[QuantityType]: @@ -135,10 +1110,10 @@ def __init__(self, self.hash_value = -1 """ Hash based on value and uncertainty for data, -1 if it is a derived hash value """ - """ Contains the variance if it is data driven, else it is """ + self._variance = None + """ Contains the variance if it is data driven """ if standard_error is None: - self._variance = None self.hash_value = hash_data_via_numpy(hash_seed, value) else: self._variance = standard_error ** 2 @@ -146,6 +1121,18 @@ def __init__(self, self.history = QuantityHistory.variable(self) + # TODO: Adding this method as a temporary measure but we need a single + # method that does this. + def with_standard_error(self, standard_error: Quantity): + if standard_error.units.equivalent(self.units): + return NamedQuantity( + value=self.value, + units=self.units, + standard_error=standard_error.in_units_of(self.units),) + else: + raise UnitError(f"Standard error units ({standard_error.units}) " + f"are not compatible with value units ({self.units})") + @property def has_variance(self): return self._variance is not None @@ -208,14 +1195,14 @@ def __mul__(self: Self, other: ArrayLike | Self ) -> Self: return DerivedQuantity( self.value * other.value, self.units * other.units, - history=QuantityHistory.apply_operation(operations.Mul, self.history, other.history)) + history=QuantityHistory.apply_operation(Mul, self.history, other.history)) else: return DerivedQuantity(self.value * other, self.units, QuantityHistory( - operations.Mul( + Mul( self.history.operation_tree, - operations.Constant(other)), + Constant(other)), self.history.references)) def __rmul__(self: Self, other: ArrayLike | Self): @@ -224,33 +1211,72 @@ def __rmul__(self: Self, other: ArrayLike | Self): other.value * self.value, other.units * self.units, history=QuantityHistory.apply_operation( - operations.Mul, + Mul, other.history, self.history)) else: return DerivedQuantity(other * self.value, self.units, QuantityHistory( - operations.Mul( - operations.Constant(other), + Mul( + Constant(other), self.history.operation_tree), self.history.references)) + + def __matmul__(self, other: ArrayLike | Self): + if isinstance(other, Quantity): + return DerivedQuantity( + self.value @ other.value, + self.units * other.units, + history=QuantityHistory.apply_operation( + MatMul, + self.history, + other.history)) + else: + return DerivedQuantity( + self.value @ other, + self.units, + QuantityHistory( + MatMul( + self.history.operation_tree, + Constant(other)), + self.history.references)) + + def __rmatmul__(self, other: ArrayLike | Self): + if isinstance(other, Quantity): + return DerivedQuantity( + other.value @ self.value, + other.units * self.units, + history=QuantityHistory.apply_operation( + MatMul, + other.history, + self.history)) + + else: + return DerivedQuantity(other @ self.value, self.units, + QuantityHistory( + MatMul( + Constant(other), + self.history.operation_tree), + self.history.references)) + + def __truediv__(self: Self, other: float | Self) -> Self: if isinstance(other, Quantity): return DerivedQuantity( self.value / other.value, self.units / other.units, history=QuantityHistory.apply_operation( - operations.Div, + Div, self.history, other.history)) else: return DerivedQuantity(self.value / other, self.units, QuantityHistory( - operations.Div( - operations.Constant(other), + Div( + Constant(other), self.history.operation_tree), self.history.references)) @@ -260,7 +1286,7 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: other.value / self.value, other.units / self.units, history=QuantityHistory.apply_operation( - operations.Div, + Div, other.history, self.history )) @@ -270,8 +1296,8 @@ def __rtruediv__(self: Self, other: float | Self) -> Self: other / self.value, self.units ** -1, QuantityHistory( - operations.Div( - operations.Constant(other), + Div( + Constant(other), self.history.operation_tree), self.history.references)) @@ -282,7 +1308,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: self.value + (other.value * other.units.scale) / self.units.scale, self.units, QuantityHistory.apply_operation( - operations.Add, + Add, self.history, other.history)) else: @@ -296,7 +1322,7 @@ def __add__(self: Self, other: Self | ArrayLike) -> Self: def __neg__(self): return DerivedQuantity(-self.value, self.units, QuantityHistory.apply_operation( - operations.Neg, + Neg, self.history )) @@ -310,14 +1336,11 @@ def __pow__(self: Self, other: int | float): return DerivedQuantity(self.value ** other, self.units ** other, QuantityHistory( - operations.Pow( + Pow( self.history.operation_tree, other), self.history.references)) - def __eq__(self, other: object) -> bool: - return isinstance(other, Quantity) and self.hash_value == other.hash_value - @staticmethod def _array_repr_format(arr: np.ndarray): """ Format the array """ @@ -365,6 +1388,10 @@ def __repr__(self): def parse(number_or_string: str | ArrayLike, unit: str, absolute_temperature: False): pass + @property + def string_repr(self): + return str(self.hash_value) + class NamedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, @@ -399,6 +1426,10 @@ def with_standard_error(self, standard_error: Quantity): f"are not compatible with value units ({self.units})") + @property + def string_repr(self): + return self.name + class DerivedQuantity[QuantityType](Quantity[QuantityType]): def __init__(self, value: QuantityType, units: Unit, history: QuantityHistory): super().__init__(value, units, standard_error=None) From 4e853aad37e1ec77251f58383e6a47db4404fdf2 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 5 Mar 2025 08:18:26 +0000 Subject: [PATCH 654/675] Wrote a test for 1d data. --- test/utest_new_sasdata.py | 85 ++------------------------------------- 1 file changed, 3 insertions(+), 82 deletions(-) diff --git a/test/utest_new_sasdata.py b/test/utest_new_sasdata.py index cc497d9ae..dd77717ff 100644 --- a/test/utest_new_sasdata.py +++ b/test/utest_new_sasdata.py @@ -1,6 +1,7 @@ import numpy as np from sasdata.data import SasData +from sasdata.dataset_types import one_dim from sasdata.data_backing import Group from sasdata.dataset_types import one_dim, three_dim, two_dim from sasdata.metadata import Instrument, Metadata, Source @@ -23,85 +24,5 @@ def test_1d(): data = SasData('TestData', data_contents, one_dim, Group('root', {}), True) - assert all(data.abscissae.value == np.array(q)) - assert all(data.ordinate.value == np.array(i)) - - -def test_2d(): - # This could be autogenerated but I am hard coding to reduce the logic in - # the test. - qx = [1, 1, 1, 2, 2, 2, 3, 3, 3] - qy = [1, 2, 3, 1, 2, 3, 1, 2, 3] - i = [1, 2, 3] - - qx_quantity = Quantity(np.array(qx), per_angstrom) - qy_quantity = Quantity(np.array(qy), per_angstrom) - i_quantity = Quantity(np.array(i), per_centimeter) - - data_contents = { - 'Qx': qx_quantity, - 'Qy': qy_quantity, - 'I': i_quantity - } - - data = SasData('TestData', data_contents, two_dim, Group('root', {}), True) - - assert all(data.ordinate.value == np.array(i)) - assert (data.abscissae.value == np.array([[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]])).all().all() - -def test_3d(): - # test base 3D class - qx = [1, 1, 1, 2, 2, 2, 3, 3, 3] - qy = [1, 2, 3, 1, 2, 3, 1, 2, 3] - qz = [0, 1, 0, 1, 0, 1, 0, 1, 0] - i = [1, 2, 3] - - qx_quantity = Quantity(np.array(qx), per_angstrom) - qy_quantity = Quantity(np.array(qy), per_angstrom) - qz_quantity = Quantity(np.array(qz), per_angstrom) - i_quantity = Quantity(np.array(i), per_centimeter) - - data_contents = { - 'Qx': qx_quantity, - 'Qy': qy_quantity, - 'Qz': qz_quantity, - 'I': i_quantity - } - - data = SasData('TestData', data_contents, three_dim, Group('root', {}), True) - - assert (data._data_contents['Qx'].value == np.array(qx)).all() - - - - # test autogenerated qz from qx, qy, and wavelength - wavelength = Quantity(1., angstroms) - source = Source(radiation=None, - beam_shape=None, - beam_size=None, - wavelength=wavelength, - wavelength_max=None, - wavelength_min=None, - wavelength_spread=None) - instrument = Instrument(collimations=[], - source=source, - detector=[]) - metadata=Metadata(title=None, - run=[], - definition=None, - process=[], - sample=None, - instrument=instrument, - raw=None) - - data_contents = { - 'Qx': qx_quantity, - 'Qy': qy_quantity, - 'I': i_quantity - } - - data = SasData('TestData', data_contents, two_dim, metadata, True) - - deduce_qz(data) - - assert (data._data_contents['Qz'].value != (0*data._data_contents['Qx'].value)).all() + assert all(data.abscissae == np.array(q_quantity)) + assert all(data.ordinate == np.array(i_quantity)) From ebac6a05cee52fd00dfad1485aaba22dd19036ac Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 12 Feb 2025 09:17:06 +0000 Subject: [PATCH 655/675] Bring guess into sasdata. --- sasdata/guess.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/sasdata/guess.py b/sasdata/guess.py index 9bc0a7ddd..93b0b3dd8 100644 --- a/sasdata/guess.py +++ b/sasdata/guess.py @@ -17,16 +17,8 @@ def guess_columns(col_count: int, dataset_type: DatasetType) -> list[str]: # Ideally we want an exact match but if the ordering is bigger than the col # count then we can accept that as well. for order_list in dataset_type.expected_orders: - if ( - len(order_list) >= col_count - or order_list == dataset_type.expected_orders[-1] - ): - return_value = order_list[:] - # If we have any extra columns than expected, then we'll just ignore them. - excess = col_count - len(order_list) - for _ in range(excess): - return_value.append("") - return return_value + if len(order_list) >= col_count: + return order_list return dataset_type.expected_orders[-1] From 0f5134774806d7d333c28d1936be4011e5300802 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 12 Feb 2025 10:33:43 +0000 Subject: [PATCH 656/675] Bring default logic from SasView here. --- sasdata/default_units.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/sasdata/default_units.py b/sasdata/default_units.py index b71fed42f..bbb275664 100644 --- a/sasdata/default_units.py +++ b/sasdata/default_units.py @@ -5,18 +5,13 @@ from sasdata.quantities.units import NamedUnit default_units = { - "Q": [unit.per_nanometer, unit.per_angstrom, unit.per_meter], - "I": [unit.per_centimeter, unit.per_meter], - "dQ": "Q", - "dI": "I", + 'Q': [unit.per_nanometer, unit.per_angstrom, unit.per_meter], + 'I': [unit.per_centimeter, unit.per_meter] } def defaults_or_fallback(column_name: str) -> list[NamedUnit]: - value = default_units.get(column_name, unit_kinds[column_name].units) - if isinstance(value, str): - return defaults_or_fallback(value) - return value + return default_units.get(column_name, unit_kinds[column_name].units) def first_default_for_fallback(column_name: str) -> NamedUnit: From 747a6adb83484b0f58ae118c0eb7ea2cbe00fd77 Mon Sep 17 00:00:00 2001 From: James Crake-Merani Date: Wed, 12 Feb 2025 15:25:31 +0000 Subject: [PATCH 657/675] Wrote a test for the ASCII reader. --- test/utest_temp_ascii_reader.py | 41 ++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/test/utest_temp_ascii_reader.py b/test/utest_temp_ascii_reader.py index 0361ca342..8ae481ac8 100644 --- a/test/utest_temp_ascii_reader.py +++ b/test/utest_temp_ascii_reader.py @@ -1,3 +1,4 @@ +import pytest import os from typing import Literal @@ -20,24 +21,11 @@ # TODO: Look into parameterizing this, although its not trivial due to the setup, and tests being a bit different. -def find(filename: str, locations: Literal["sasdataloader", "mumag"]) -> str: - # This match statement is here in case we want to pull data out of other locations. - match locations: - case "sasdataloader": - return os.path.join( - os.path.dirname(__file__), "sasdataloader", "data", filename - ) - case "mumag": - return os.path.join( - os.path.dirname(__file__), - "mumag", - "Nanoperm_perpendicular_Honecker_et_al", - filename, - ) - +def find(filename: str) -> str: + return os.path.join(os.path.dirname(__file__), 'sasdataloader', 'data', filename) def test_ascii_1(): - filename = find("ascii_test_1.txt", "sasdataloader") + filename = 'ascii_test_1.txt' params = guess_params_from_filename(filename, one_dim) # Need to change the columns as they won't be right. # TODO: unitless @@ -63,6 +51,7 @@ def test_ascii_1(): case "dI": assert datum.value[0] == pytest.approx(0.002704) assert datum.value[-1] == pytest.approx(0.191) +<<<<<<< HEAD def test_ascii_2(): @@ -152,3 +141,23 @@ def test_mumag_metadata(): assert datum.metadata.raw.filter("applied_magnetic_field") == ["1270"] assert datum.metadata.raw.filter("saturation_magnetization") == ["1640"] assert datum.metadata.raw.filter("demagnetizing_field") == ["24"] +||||||| parent of f7982fb1 (Wrote a test for the ASCII reader.) + +def test_ascii_2(): + filename = find('test_3_columns.txt', 'sasdataloader') + params = guess_params_from_filename(filename, one_dim) + loaded_data = load_data(params)[0] + + for datum in loaded_data._data_contents: + match datum.name: + case 'Q': + assert datum.value[0] == pytest.approx(0) + assert datum.value[-1] == pytest.approx(1.22449) + case 'I': + assert datum.value[0] == pytest.approx(2.83954) + assert datum.value[-1] == pytest.approx(7.47487) + case 'dI': + assert datum.value[0] == pytest.approx(0.6) + assert datum.value[-1] == pytest.approx(1.05918) +======= +>>>>>>> f7982fb1 (Wrote a test for the ASCII reader.) From 29c5867bdf98ba921706826f682cd627f28e024c Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 17 Feb 2025 10:56:36 +0000 Subject: [PATCH 658/675] Create test option for displaying diagnostic plots --- sasdata/transforms/test_interpolation.py | 40 +++++++++++++----------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/sasdata/transforms/test_interpolation.py b/sasdata/transforms/test_interpolation.py index 97b4f7911..7c8573faf 100644 --- a/sasdata/transforms/test_interpolation.py +++ b/sasdata/transforms/test_interpolation.py @@ -18,7 +18,7 @@ @pytest.mark.parametrize("fun", test_functions) -def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]]): +def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]], order: InterpolationOptions, show_plots: bool): original_points = NamedQuantity("x_base", np.linspace(-10,10, 31), units.meters) test_points = NamedQuantity("x_test", np.linspace(-5, 5, 11), units.meters) @@ -34,13 +34,14 @@ def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Q y_values_test = y_test.in_units_of(test_units) y_values_expected = y_expected.in_units_of(test_units) - # print(y_values_test) - # print(y_values_expected) - # - # quantity_plot(original_points, y_original) - # quantity_plot(test_points, y_test) - # quantity_plot(test_points, y_expected) - # plt.show() + if show_plots: + print(y_values_test) + print(y_values_expected) + + quantity_plot(original_points, y_original) + quantity_plot(test_points, y_test) + quantity_plot(test_points, y_expected) + plt.show() assert len(y_values_test) == len(y_values_expected) @@ -49,7 +50,7 @@ def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Q @pytest.mark.parametrize("fun", test_functions) -def test_linear_interpolate_different_units(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]]): +def test_linear_interpolate_different_units(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]], order: InterpolationOptions, show_plots: bool): original_points = NamedQuantity("x_base", np.linspace(-10,10, 107), units.meters) test_points = NamedQuantity("x_test", np.linspace(-5000, 5000, 11), units.millimeters) @@ -63,15 +64,16 @@ def test_linear_interpolate_different_units(fun: Callable[[Quantity[ArrayLike]], y_values_test = y_test.in_units_of(test_units) y_values_expected = y_expected.in_units_of(test_units) - # - # print(y_values_test) - # print(y_test.in_si()) - # print(y_values_expected) - # - # plt.plot(original_points.in_si(), y_original.in_si()) - # plt.plot(test_points.in_si(), y_test.in_si(), "x") - # plt.plot(test_points.in_si(), y_expected.in_si(), "o") - # plt.show() + + if show_plots: + print(y_values_test) + print(y_test.in_si()) + print(y_values_expected) + + plt.plot(original_points.in_si(), y_original.in_si()) + plt.plot(test_points.in_si(), y_test.in_si(), "x") + plt.plot(test_points.in_si(), y_expected.in_si(), "o") + plt.show() assert len(y_values_test) == len(y_values_expected) @@ -88,4 +90,4 @@ def test_linearity_linear(): linear_points = x_and_y @ mapping for t, e in zip(new_x.in_si(), linear_points.in_si()): - assert t == pytest.approx(e, rel=1e-3) \ No newline at end of file + assert t == pytest.approx(e, rel=1e-3) From 19ab2855c922b1b1d1a1d0c75d6277028762f5e5 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 17 Feb 2025 11:05:41 +0000 Subject: [PATCH 659/675] More interpolation test into main test directory --- sasdata/transforms/test_interpolation.py | 93 ------------------------ 1 file changed, 93 deletions(-) delete mode 100644 sasdata/transforms/test_interpolation.py diff --git a/sasdata/transforms/test_interpolation.py b/sasdata/transforms/test_interpolation.py deleted file mode 100644 index 7c8573faf..000000000 --- a/sasdata/transforms/test_interpolation.py +++ /dev/null @@ -1,93 +0,0 @@ -import pytest -import numpy as np -from matplotlib import pyplot as plt -from numpy.typing import ArrayLike -from typing import Callable - -from sasdata.quantities.plotting import quantity_plot -from sasdata.quantities.quantity import NamedQuantity, Quantity -from sasdata.quantities import units - -from sasdata.transforms.rebinning import calculate_interpolation_matrix_1d, InterpolationOptions - -test_functions = [ - lambda x: x**2, - lambda x: 2*x, - lambda x: x**3 -] - - -@pytest.mark.parametrize("fun", test_functions) -def test_linear_interpolate_matrix_inside(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]], order: InterpolationOptions, show_plots: bool): - original_points = NamedQuantity("x_base", np.linspace(-10,10, 31), units.meters) - test_points = NamedQuantity("x_test", np.linspace(-5, 5, 11), units.meters) - - - mapping, _ = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) - - y_original = fun(original_points) - y_test = y_original @ mapping - y_expected = fun(test_points) - - test_units = y_expected.units - - y_values_test = y_test.in_units_of(test_units) - y_values_expected = y_expected.in_units_of(test_units) - - if show_plots: - print(y_values_test) - print(y_values_expected) - - quantity_plot(original_points, y_original) - quantity_plot(test_points, y_test) - quantity_plot(test_points, y_expected) - plt.show() - - assert len(y_values_test) == len(y_values_expected) - - for t, e in zip(y_values_test, y_values_expected): - assert t == pytest.approx(e, abs=2) - - -@pytest.mark.parametrize("fun", test_functions) -def test_linear_interpolate_different_units(fun: Callable[[Quantity[ArrayLike]], Quantity[ArrayLike]], order: InterpolationOptions, show_plots: bool): - original_points = NamedQuantity("x_base", np.linspace(-10,10, 107), units.meters) - test_points = NamedQuantity("x_test", np.linspace(-5000, 5000, 11), units.millimeters) - - mapping, _ = calculate_interpolation_matrix_1d(original_points, test_points, order=InterpolationOptions.LINEAR) - - y_original = fun(original_points) - y_test = y_original @ mapping - y_expected = fun(test_points) - - test_units = y_expected.units - - y_values_test = y_test.in_units_of(test_units) - y_values_expected = y_expected.in_units_of(test_units) - - if show_plots: - print(y_values_test) - print(y_test.in_si()) - print(y_values_expected) - - plt.plot(original_points.in_si(), y_original.in_si()) - plt.plot(test_points.in_si(), y_test.in_si(), "x") - plt.plot(test_points.in_si(), y_expected.in_si(), "o") - plt.show() - - assert len(y_values_test) == len(y_values_expected) - - for t, e in zip(y_values_test, y_values_expected): - assert t == pytest.approx(e, rel=5e-2) - -def test_linearity_linear(): - """ Test linear interpolation between two points""" - x_and_y = NamedQuantity("x_base", np.linspace(-10, 10, 2), units.meters) - new_x = NamedQuantity("x_test", np.linspace(-5000, 5000, 101), units.millimeters) - - mapping, _ = calculate_interpolation_matrix_1d(x_and_y, new_x, order=InterpolationOptions.LINEAR) - - linear_points = x_and_y @ mapping - - for t, e in zip(new_x.in_si(), linear_points.in_si()): - assert t == pytest.approx(e, rel=1e-3) From cc26e1b125031c3fb671ced5a3ab15535c337882 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Tue, 4 Mar 2025 14:16:14 +0000 Subject: [PATCH 660/675] Add tests for dataset --- sasdata/temp_hdf5_reader.py | 8 +-- .../sasdataloader/reference/MAR07232_rest.txt | 8 +++ .../nxcansas_1Dand2D_multisasdata.txt | 15 +----- .../nxcansas_1Dand2D_multisasentry.txt | 53 ++++++++++++++++++- .../reference/simpleexamplefile.txt | 2 + test/sasdataloader/reference/x25000_no_di.txt | 8 +++ test/sasdataloader/utest_sasdataload.py | 2 +- 7 files changed, 77 insertions(+), 19 deletions(-) diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index f96b2a4cb..8afebcbe2 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -144,8 +144,8 @@ def load_data(filename) -> list[SasData]: +if __name__ == "__main__": + data = load_data(test_file) -data = load_data(test_file) - -for dataset in data: - print(dataset.summary(include_raw=False)) \ No newline at end of file + for dataset in data: + print(dataset.summary(include_raw=False)) diff --git a/test/sasdataloader/reference/MAR07232_rest.txt b/test/sasdataloader/reference/MAR07232_rest.txt index 881665f79..52b350363 100644 --- a/test/sasdataloader/reference/MAR07232_rest.txt +++ b/test/sasdataloader/reference/MAR07232_rest.txt @@ -22,6 +22,8 @@ Aperture: Name: None Aperture size: None Aperture distance: None +Collimation: + Length: None Detector: Name: None Distance: None @@ -38,3 +40,9 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None + diff --git a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt index f3b00b0cf..4d8c9c6e4 100644 --- a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt +++ b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt @@ -22,6 +22,8 @@ Aperture: Name: None Aperture size: None Aperture distance: None +Collimation: + Length: None Detector: Name: None Distance: None @@ -38,21 +40,8 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None -<<<<<<< HEAD Transmission Spectrum: Name: None Timestamp: None Wavelengths: None Transmission: None - - -||||||| parent of c9f83a89 (Metadata is a dataclass) -Transmission Spectrum: - Name: None - Timestamp: None - Wavelengths: None - Transmission: None - -======= - ->>>>>>> c9f83a89 (Metadata is a dataclass) \ No newline at end of file diff --git a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt index a12583e88..dd5734a1b 100644 --- a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt +++ b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt @@ -22,6 +22,8 @@ Aperture: Name: None Aperture size: None Aperture distance: None +Collimation: + Length: None Detector: Name: None Distance: None @@ -44,6 +46,7 @@ Transmission Spectrum: Wavelengths: None Transmission: None +<<<<<<< HEAD <<<<<<< HEAD MH4_5deg_16T_SLOW, Run: 33837 ============================= @@ -87,7 +90,7 @@ Source: Wavelength Spread: None Beam Size: None -||||||| parent of 556c3b69 (Add tests for dataset) +||||||| parent of cb5f3ffb (Add tests for dataset) MH4_5deg_16T_SLOW, Run: 33837 ============================= @@ -134,3 +137,51 @@ Transmission Spectrum: Timestamp: None Wavelengths: None Transmission: None + +||||||| original + MH4_5deg_16T_SLOW, Run: 33837 + ============================= + +Definition: MH4_5deg_16T_SLOW +Process: + Name: Mantid generated CanSAS1D XML + Date: 11-May-2016 12:15:34 + Description: None + Term: None +Sample: + ID: + Transmission: None + Thickness: None + Temperature: None + Position: None + Orientation: None +Collimation: + Length: None +Detector: + Name: front-detector + Distance: 2845.260009765625 mm + Offset: None + Orientation: None + Beam center: None + Pixel size: None + Slit length: None +Detector: + Name: rear-detector + Distance: 4385.27978515625 mm + Offset: None + Orientation: None + Beam center: None + Pixel size: None + Slit length: None +Source: + Radiation: Spallation Neutron Source + Shape: None + Wavelength: None + Min. Wavelength: None + Max. Wavelength: None + Wavelength Spread: None + Beam Size: None + +======= + +>>>>>>> cb5f3ffb (Add tests for dataset) \ No newline at end of file diff --git a/test/sasdataloader/reference/simpleexamplefile.txt b/test/sasdataloader/reference/simpleexamplefile.txt index 10301146a..a81384082 100644 --- a/test/sasdataloader/reference/simpleexamplefile.txt +++ b/test/sasdataloader/reference/simpleexamplefile.txt @@ -22,6 +22,8 @@ Aperture: Name: None Aperture size: None Aperture distance: None +Collimation: + Length: None Detector: Name: None Distance: None diff --git a/test/sasdataloader/reference/x25000_no_di.txt b/test/sasdataloader/reference/x25000_no_di.txt index 08a7142e2..37f524e97 100644 --- a/test/sasdataloader/reference/x25000_no_di.txt +++ b/test/sasdataloader/reference/x25000_no_di.txt @@ -22,6 +22,8 @@ Aperture: Name: None Aperture size: None Aperture distance: None +Collimation: + Length: None Detector: Name: None Distance: None @@ -38,3 +40,9 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None + diff --git a/test/sasdataloader/utest_sasdataload.py b/test/sasdataloader/utest_sasdataload.py index 09aeef807..6acc7aa17 100644 --- a/test/sasdataloader/utest_sasdataload.py +++ b/test/sasdataloader/utest_sasdataload.py @@ -22,7 +22,7 @@ from sasdata.temp_hdf5_reader import load_data test_file_names = [ - # "simpleexamplefile", + "simpleexamplefile", "nxcansas_1Dand2D_multisasentry", "nxcansas_1Dand2D_multisasdata", "MAR07232_rest", From eaa0595ce329b7d45a7c1c69d4952a440e2f5534 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 17 Mar 2025 12:45:42 +0000 Subject: [PATCH 661/675] Parse source --- sasdata/metadata.py | 98 ++++++-------------- sasdata/temp_hdf5_reader.py | 115 +++++++++++++++++++++--- test/sasdataloader/utest_sasdataload.py | 2 +- 3 files changed, 131 insertions(+), 84 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index fb90777f5..10d3b2835 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -122,80 +122,40 @@ def summary(self): f"Collimation:\n" f" Length: {self.length.value}\n") +@dataclass +class BeamSize: + name: Optional[str] + x: Optional[Quantity[float]] + y: Optional[Quantity[float]] + z: Optional[Quantity[float]] +@dataclass class Source: - """ - Class to hold source information - """ - - def __init__(self, target_object: AccessorTarget): - # Name - self.name = StringAccessor(target_object, "name") - - # Generic radiation type (Type and probe give more specific info) [string] - self.radiation = StringAccessor(target_object, "radiation") - - # Type and probe are only written to by the NXcanSAS reader - # Specific radiation type (Synchotron X-ray, Reactor neutron, etc) [string] - self.type = StringAccessor(target_object, "type") - - # Radiation probe (generic probe such as neutron, x-ray, muon, etc) [string] - self.probe_particle = StringAccessor(target_object, "probe") - - # Beam size name - self.beam_size_name = StringAccessor(target_object, "beam_size_name") - - # Beam size [Vector] [mm] - self.beam_size = LengthAccessor[ArrayLike](target_object, - "beam_size", - "beam_size.units", - default_unit=units.millimeters) - - # Beam shape [string] - self.beam_shape = StringAccessor(target_object, "beam_shape") - - # Wavelength [float] [Angstrom] - self.wavelength = LengthAccessor[float](target_object, - "wavelength", - "wavelength.units", - default_unit=units.angstroms) - - # Minimum wavelength [float] [Angstrom] - self.wavelength_min = LengthAccessor[float](target_object, - "wavelength_min", - "wavelength_min.units", - default_unit=units.angstroms) - - # Maximum wavelength [float] [Angstrom] - self.wavelength_max = LengthAccessor[float](target_object, - "wavelength_min", - "wavelength_max.units", - default_unit=units.angstroms) - - # Wavelength spread [float] [Angstrom] - # Quantity because it might have other units, such as percent - self.wavelength_spread = QuantityAccessor[float](target_object, - "wavelength_spread", - "wavelength_spread.units", - default_unit=units.angstroms) + radiation: str + beam_shape: str + beam_size: Optional[BeamSize] + wavelength : Quantity[float] + wavelength_min : Quantity[float] + wavelength_max : Quantity[float] + wavelength_spread : Quantity[float] def summary(self) -> str: - - if self.radiation.value is None and self.type.value and self.probe_particle.value: + if self.radiation is None and self.type.value and self.probe_particle.value: radiation = f"{self.type.value} {self.probe_particle.value}" else: - radiation = f"{self.radiation.value}" - - return (f"Source:\n" - f" Radiation: {radiation}\n" - f" Shape: {self.beam_shape.value}\n" - f" Wavelength: {self.wavelength.value}\n" - f" Min. Wavelength: {self.wavelength_min.value}\n" - f" Max. Wavelength: {self.wavelength_max.value}\n" - f" Wavelength Spread: {self.wavelength_spread.value}\n" - f" Beam Size: {self.beam_size.value}\n") + radiation = f"{self.radiation}" + return ( + f"Source:\n" + f" Radiation: {radiation}\n" + f" Shape: {self.beam_shape}\n" + f" Wavelength: {self.wavelength}\n" + f" Min. Wavelength: {self.wavelength_min}\n" + f" Max. Wavelength: {self.wavelength_max}\n" + f" Wavelength Spread: {self.wavelength_spread}\n" + f" Beam Size: {self.beam_size}\n" + ) """ @@ -336,11 +296,11 @@ def summary(self) -> str: class Instrument: - def __init__(self, target: AccessorTarget): + def __init__(self, target: AccessorTarget, collimations: list[Collimation], source: Source): self.aperture = Aperture(target.with_path_prefix("sasaperture|aperture")) self.collimation = Collimation(target.with_path_prefix("sascollimation|collimation")) self.detector = Detector(target.with_path_prefix("sasdetector|detector")) - self.source = Source(target.with_path_prefix("sassource|source")) + self.source = source def summary(self): return ( self.aperture.summary() + @@ -398,5 +358,5 @@ def summary(self): f"Definition: {self.title}\n" + self.process.summary() + self.sample.summary() + - self.instrument.summary() + + (self.instrument.summary() if self.instrument else "") + self.transmission_spectrum.summary()) diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 8afebcbe2..6abf0f025 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -13,6 +13,8 @@ from sasdata.data import SasData from sasdata.data_backing import Dataset as SASDataDataset, Group as SASDataGroup +from sasdata.metadata import Instrument, Collimation, Aperture, Source +from sasdata.quantities.accessors import AccessorTarget from sasdata.quantities.quantity import NamedQuantity from sasdata.quantities import units @@ -36,29 +38,33 @@ def recurse_hdf5(hdf5_entry): data = hdf5_entry[()][0].decode("utf-8") return SASDataDataset[str]( - name=hdf5_entry.name, - data=data, - attributes=attributes) + name=hdf5_entry.name, data=data, attributes=attributes + ) else: data = np.array(hdf5_entry, dtype=hdf5_entry.dtype) return SASDataDataset[np.ndarray]( - name=hdf5_entry.name, - data=data, - attributes=attributes) + name=hdf5_entry.name, data=data, attributes=attributes + ) elif isinstance(hdf5_entry, HDF5Group): return SASDataGroup( name=hdf5_entry.name, - children={key: recurse_hdf5(hdf5_entry[key]) for key in hdf5_entry.keys()}) + children={key: recurse_hdf5(hdf5_entry[key]) for key in hdf5_entry.keys()}, + ) else: - raise TypeError(f"Unknown type found during HDF5 parsing: {type(hdf5_entry)} ({hdf5_entry})") + raise TypeError( + f"Unknown type found during HDF5 parsing: {type(hdf5_entry)} ({hdf5_entry})" + ) + GET_UNITS_FROM_ELSEWHERE = units.meters + + def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: - """ In the context of NeXus files, load a group of data entries that are organised together + """In the context of NeXus files, load a group of data entries that are organised together match up the units and errors with their values""" # Gather together data with its error terms @@ -67,7 +73,6 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: entries = {} for name in node.children: - child = node.children[name] if "units" in child.attributes: @@ -75,9 +80,9 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: else: units = GET_UNITS_FROM_ELSEWHERE - quantity = NamedQuantity(name=name_prefix+child.name, - value=child.data, - units=units) + quantity = NamedQuantity( + name=name_prefix + child.name, value=child.data, units=units + ) # Turns out people can't be trusted to use the same keys here if "uncertainty" in child.attributes or "uncertainties" in child.attributes: @@ -104,6 +109,77 @@ def connected_data(node: SASDataGroup, name_prefix="") -> list[NamedQuantity]: return output +def parse_apertures(node) -> list[Aperture]: + result = [] + aps = [a for a in node if "aperture" in a] + for ap in aps: + distance = None + size = None + if "distance" in node[ap]: + distance = node[ap]["distance"] + if "size" in node[ap]: + x = y = z = None + if "x" in node[ap]: + x = node[ap]["size"]["x"] + if "y" in node[ap]: + y = node[ap]["size"]["y"] + if "z" in node[ap]: + z = node[ap]["size"]["z"] + if x is not None or y is not None or z is not None: + size = (x, y, z) + result.append(Aperture(distance=distance, size=size, size_name=size_name, name=name, apType=apType)) + return result + + +def parse_source(node) -> Source: + beam_shape = None + beam_size = None + wavelength = None + wavelength_min = None + wavelength_max = None + wavelength_spread = None + if "beam_shape" in node: + beam_shape = node["beam_shape"] + if "wavelength" in node: + wavelength = node["wavelength"] + if "wavelength_min" in node: + wavelength = node["wavelength_min"] + if "wavelength_max" in node: + wavelength = node["wavelength_max"] + if "wavelength_spread" in node: + wavelength = node["wavelength_spread"] + return Source( + radiation=node["radiation"].asstr()[0], + beam_shape=beam_shape, + beam_size=beam_size, + wavelength=wavelength, + wavelength_min=wavelength_min, + wavelength_max=wavelength_max, + wavelength_spread=wavelength_spread, + ) + + +def parse_collimation(node) -> Collimation: + if "length" in node: + length = node["length"] + else: + length = None + return Collimation(length=length, apertures=parse_apertures(node)) + + +def parse_instrument(raw, node) -> Instrument: + collimations = [ + parse_collimation(node[x]) + for x in node + if "collimation" in x + ] + return Instrument( + raw, + collimations=collimations, + source=parse_source(node["sassource"]), + ) + + def load_data(filename) -> list[SasData]: with h5py.File(filename, 'r') as f: @@ -132,13 +208,24 @@ def load_data(filename) -> list[SasData]: else: raw_metadata[key] = recurse_hdf5(component) + instrument = None + if "sasinstrument" in f["sasentry01"]: + instrument = parse_instrument( + AccessorTarget(SASDataGroup("root", raw_metadata)).with_path_prefix( + "sasinstrument|instrument" + ), + f["sasentry01"]["sasinstrument"], + ) loaded_data.append( SasData( name=root_key, data_contents=data_contents, raw_metadata=SASDataGroup("root", raw_metadata), - verbose=False)) + instrument=instrument, + verbose=False, + ) + ) return loaded_data diff --git a/test/sasdataloader/utest_sasdataload.py b/test/sasdataloader/utest_sasdataload.py index 6acc7aa17..09aeef807 100644 --- a/test/sasdataloader/utest_sasdataload.py +++ b/test/sasdataloader/utest_sasdataload.py @@ -22,7 +22,7 @@ from sasdata.temp_hdf5_reader import load_data test_file_names = [ - "simpleexamplefile", + # "simpleexamplefile", "nxcansas_1Dand2D_multisasentry", "nxcansas_1Dand2D_multisasdata", "MAR07232_rest", From db349ee849e50884434a072f87db655ce75ecda8 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 17 Mar 2025 14:34:42 +0000 Subject: [PATCH 662/675] Instrument is a data class --- sasdata/metadata.py | 10 +++++----- sasdata/temp_hdf5_reader.py | 8 +------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 10d3b2835..bee4eeea9 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -295,12 +295,12 @@ def summary(self) -> str: f" Transmission: {self.transmission.value}\n") +@dataclass class Instrument: - def __init__(self, target: AccessorTarget, collimations: list[Collimation], source: Source): - self.aperture = Aperture(target.with_path_prefix("sasaperture|aperture")) - self.collimation = Collimation(target.with_path_prefix("sascollimation|collimation")) - self.detector = Detector(target.with_path_prefix("sasdetector|detector")) - self.source = source + collimations : list[Collimation] + source : Source + detector : list[Detector] + def summary(self): return ( self.aperture.summary() + diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 6abf0f025..e6e4028b8 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -168,14 +168,8 @@ def parse_collimation(node) -> Collimation: def parse_instrument(raw, node) -> Instrument: - collimations = [ - parse_collimation(node[x]) - for x in node - if "collimation" in x - ] return Instrument( - raw, - collimations=collimations, + collimations= [parse_collimation(node[x]) for x in node if "collimation" in x], source=parse_source(node["sassource"]), ) From b093660ed131da7604a5a8ca9258887907765076 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 17 Mar 2025 16:41:32 +0000 Subject: [PATCH 663/675] Metadata is a dataclass --- sasdata/metadata.py | 45 ++++--------------- sasdata/temp_hdf5_reader.py | 16 +++---- .../sasdataloader/reference/MAR07232_rest.txt | 12 +++++ .../nxcansas_1Dand2D_multisasdata.txt | 5 --- .../nxcansas_1Dand2D_multisasentry.txt | 9 ++-- test/sasdataloader/reference/x25000_no_di.txt | 6 --- 6 files changed, 33 insertions(+), 60 deletions(-) diff --git a/sasdata/metadata.py b/sasdata/metadata.py index bee4eeea9..145243c9a 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -259,41 +259,6 @@ def summary(self): f" Notes: {self.notes.value}\n" ) -class TransmissionSpectrum: - """ - Class that holds information about transmission spectrum - for white beams and spallation sources. - """ - def __init__(self, target_object: AccessorTarget): - # TODO: Needs to be multiple instances - self.name = StringAccessor(target_object, "name") - self.timestamp = StringAccessor(target_object, "timestamp") - - # Wavelength (float) [A] - self.wavelength = LengthAccessor[ArrayLike](target_object, - "wavelength", - "wavelength.units") - - # Transmission (float) [unit less] - self.transmission = DimensionlessAccessor[ArrayLike](target_object, - "transmission", - "units", - default_unit=units.none) - - # Transmission Deviation (float) [unit less] - self.transmission_deviation = DimensionlessAccessor[ArrayLike](target_object, - "transmission_deviation", - "transmission_deviation.units", - default_unit=units.none) - - - def summary(self) -> str: - return (f"Transmission Spectrum:\n" - f" Name: {self.name.value}\n" - f" Timestamp: {self.timestamp.value}\n" - f" Wavelengths: {self.wavelength.value}\n" - f" Transmission: {self.transmission.value}\n") - @dataclass class Instrument: @@ -332,6 +297,7 @@ def decode_string(data): else: return str(data) +@dataclass(kw_only=True) class Metadata: def __init__(self, target: AccessorTarget): self._target = target @@ -348,6 +314,12 @@ def __init__(self, target: AccessorTarget): self.title: str = decode_string(self._title.value) self.run: str = decode_string(self._run.value) self.definition: str = decode_string(self._definition.value) + title: Optional[str] + run: list[str] + definition: Optional[str] + process: list[str] + sample: Optional[Sample] + instrument: Optional[Instrument] def summary(self): return ( @@ -358,5 +330,4 @@ def summary(self): f"Definition: {self.title}\n" + self.process.summary() + self.sample.summary() + - (self.instrument.summary() if self.instrument else "") + - self.transmission_spectrum.summary()) + (self.instrument.summary() if self.instrument else "")) diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index e6e4028b8..fad06ba7d 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -202,14 +202,14 @@ def load_data(filename) -> list[SasData]: else: raw_metadata[key] = recurse_hdf5(component) - instrument = None - if "sasinstrument" in f["sasentry01"]: - instrument = parse_instrument( - AccessorTarget(SASDataGroup("root", raw_metadata)).with_path_prefix( - "sasinstrument|instrument" - ), - f["sasentry01"]["sasinstrument"], - ) + instrument = opt_parse(f["sasentry01"], "sasinstrument", parse_instrument) + sample = opt_parse(f["sasentry01"], "sassample", parse_sample) + process = [parse_process(f["sasentry01"][p]) for p in f["sasentry01"] if "sasprocess" in p] + title = opt_parse(f["sasentry01"], "title", parse_string) + run = [parse_string(f["sasentry01"][r]) for r in f["sasentry01"] if "run" in r] + definition = opt_parse(f["sasentry01"], "definition", parse_string) + + metadata = Metadata(process=process, instrument=instrument, sample=sample, title=title, run=run, definition=definition) loaded_data.append( SasData( diff --git a/test/sasdataloader/reference/MAR07232_rest.txt b/test/sasdataloader/reference/MAR07232_rest.txt index 52b350363..8a9e16ce4 100644 --- a/test/sasdataloader/reference/MAR07232_rest.txt +++ b/test/sasdataloader/reference/MAR07232_rest.txt @@ -40,9 +40,21 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None +<<<<<<< HEAD Transmission Spectrum: Name: None Timestamp: None Wavelengths: None Transmission: None + +||||||| parent of 0b6ad967 (Metadata is a dataclass) +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None + +======= + +>>>>>>> 0b6ad967 (Metadata is a dataclass) \ No newline at end of file diff --git a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt index 4d8c9c6e4..5763cf71f 100644 --- a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt +++ b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasdata.txt @@ -40,8 +40,3 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None -Transmission Spectrum: - Name: None - Timestamp: None - Wavelengths: None - Transmission: None diff --git a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt index dd5734a1b..769f5ee49 100644 --- a/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt +++ b/test/sasdataloader/reference/nxcansas_1Dand2D_multisasentry.txt @@ -181,7 +181,8 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None - -======= - ->>>>>>> cb5f3ffb (Add tests for dataset) \ No newline at end of file +Transmission Spectrum: + Name: None + Timestamp: None + Wavelengths: None + Transmission: None diff --git a/test/sasdataloader/reference/x25000_no_di.txt b/test/sasdataloader/reference/x25000_no_di.txt index 37f524e97..a36a709bb 100644 --- a/test/sasdataloader/reference/x25000_no_di.txt +++ b/test/sasdataloader/reference/x25000_no_di.txt @@ -40,9 +40,3 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None -Transmission Spectrum: - Name: None - Timestamp: None - Wavelengths: None - Transmission: None - From 029b02f402ab85a754e12af0a2c250e19c64d53c Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Fri, 4 Apr 2025 12:46:30 +0100 Subject: [PATCH 664/675] Start adding official tests for xml file loading --- test/sasdataloader/reference/ISIS_1_0.txt | 5 +-- test/sasdataloader/reference/ISIS_1_1.txt | 5 +-- .../reference/ISIS_1_1_doubletrans.txt | 5 +-- .../reference/ISIS_1_1_notrans.txt | 5 +-- .../reference/TestExtensions.txt | 9 ++--- .../reference/valid_cansas_xml.txt | 5 +-- test/sasdataloader/utest_sasdataload.py | 39 ++++++++++++++++--- 7 files changed, 48 insertions(+), 25 deletions(-) diff --git a/test/sasdataloader/reference/ISIS_1_0.txt b/test/sasdataloader/reference/ISIS_1_0.txt index 57ff7ed28..14f071620 100644 --- a/test/sasdataloader/reference/ISIS_1_0.txt +++ b/test/sasdataloader/reference/ISIS_1_0.txt @@ -11,9 +11,7 @@ Process: Name: Mantid generated CanSAS1D XML Date: 02-Aug-2013 16:54:14 Description: None - Terms: - svn: 2.5.3 - user_file: K:/masks/MASKLOQ_MAN_132E_Lu_Banjo_12mm.txt + Term: {'svn': '2.5.3', 'user_file': 'K:/masks/MASKLOQ_MAN_132E_Lu_Banjo_12mm.txt'} Sample: ID: TK49 c10_SANS Transmission: None @@ -47,3 +45,4 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None + diff --git a/test/sasdataloader/reference/ISIS_1_1.txt b/test/sasdataloader/reference/ISIS_1_1.txt index 48df868f5..731c1b6cf 100644 --- a/test/sasdataloader/reference/ISIS_1_1.txt +++ b/test/sasdataloader/reference/ISIS_1_1.txt @@ -11,9 +11,7 @@ Process: Name: Mantid generated CanSAS1D XML Date: 02-Aug-2013 16:53:56 Description: None - Terms: - svn: 2.5.3 - user_file: K:/masks/MASKLOQ_MAN_132E_Lu_Banjo_12mm.txt + Term: {'svn': '2.5.3', 'user_file': 'K:/masks/MASKLOQ_MAN_132E_Lu_Banjo_12mm.txt'} Sample: ID: TK49 c10_SANS Transmission: None @@ -47,3 +45,4 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None + diff --git a/test/sasdataloader/reference/ISIS_1_1_doubletrans.txt b/test/sasdataloader/reference/ISIS_1_1_doubletrans.txt index 48df868f5..731c1b6cf 100644 --- a/test/sasdataloader/reference/ISIS_1_1_doubletrans.txt +++ b/test/sasdataloader/reference/ISIS_1_1_doubletrans.txt @@ -11,9 +11,7 @@ Process: Name: Mantid generated CanSAS1D XML Date: 02-Aug-2013 16:53:56 Description: None - Terms: - svn: 2.5.3 - user_file: K:/masks/MASKLOQ_MAN_132E_Lu_Banjo_12mm.txt + Term: {'svn': '2.5.3', 'user_file': 'K:/masks/MASKLOQ_MAN_132E_Lu_Banjo_12mm.txt'} Sample: ID: TK49 c10_SANS Transmission: None @@ -47,3 +45,4 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None + diff --git a/test/sasdataloader/reference/ISIS_1_1_notrans.txt b/test/sasdataloader/reference/ISIS_1_1_notrans.txt index 48df868f5..731c1b6cf 100644 --- a/test/sasdataloader/reference/ISIS_1_1_notrans.txt +++ b/test/sasdataloader/reference/ISIS_1_1_notrans.txt @@ -11,9 +11,7 @@ Process: Name: Mantid generated CanSAS1D XML Date: 02-Aug-2013 16:53:56 Description: None - Terms: - svn: 2.5.3 - user_file: K:/masks/MASKLOQ_MAN_132E_Lu_Banjo_12mm.txt + Term: {'svn': '2.5.3', 'user_file': 'K:/masks/MASKLOQ_MAN_132E_Lu_Banjo_12mm.txt'} Sample: ID: TK49 c10_SANS Transmission: None @@ -47,3 +45,4 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None + diff --git a/test/sasdataloader/reference/TestExtensions.txt b/test/sasdataloader/reference/TestExtensions.txt index e833c9da7..d04c7ef45 100644 --- a/test/sasdataloader/reference/TestExtensions.txt +++ b/test/sasdataloader/reference/TestExtensions.txt @@ -1,4 +1,4 @@ -TK49 c10_SANS +None Q I Metadata: @@ -10,10 +10,8 @@ Definition: TK49 c10_SANS Process: Name: Mantid generated CanSAS1D XML Date: 02-Aug-2013 16:53:56 - Description: - Terms: - svn: 2.5.3 - user_file: K:/masks/MASKLOQ_MAN_132E_Lu_Banjo_12mm.txt + Description: None + Term: {'svn': '2.5.3', 'user_file': 'K:/masks/MASKLOQ_MAN_132E_Lu_Banjo_12mm.txt'} Sample: ID: TK49 c10_SANS Transmission: None @@ -47,3 +45,4 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None + diff --git a/test/sasdataloader/reference/valid_cansas_xml.txt b/test/sasdataloader/reference/valid_cansas_xml.txt index 3b3993aba..5e73076fa 100644 --- a/test/sasdataloader/reference/valid_cansas_xml.txt +++ b/test/sasdataloader/reference/valid_cansas_xml.txt @@ -11,9 +11,7 @@ Process: Name: Mantid generated CanSAS1D XML Date: 10-Oct-2013 16:00:29 Description: None - Terms: - svn: 2.6.20130902.1504 - user_file: K:/masks/MASKLOQ_MAN_133D_Xpress_8mm.txt + Term: {'svn': '2.6.20130902.1504', 'user_file': 'K:/masks/MASKLOQ_MAN_133D_Xpress_8mm.txt'} Sample: ID: LOQ_Standard_TK49_SANS Transmission: None @@ -47,3 +45,4 @@ Source: Max. Wavelength: None Wavelength Spread: None Beam Size: None + diff --git a/test/sasdataloader/utest_sasdataload.py b/test/sasdataloader/utest_sasdataload.py index 09aeef807..952abdc79 100644 --- a/test/sasdataloader/utest_sasdataload.py +++ b/test/sasdataloader/utest_sasdataload.py @@ -19,9 +19,10 @@ from sasdata.dataloader.readers.xml_reader import XMLreader from sasdata.dataloader.readers.cansas_reader import Reader from sasdata.dataloader.readers.cansas_constants import CansasConstants -from sasdata.temp_hdf5_reader import load_data +from sasdata.temp_hdf5_reader import load_data as hdf_load_data +from sasdata.temp_xml_reader import load_data as xml_load_data -test_file_names = [ +test_hdf_file_names = [ # "simpleexamplefile", "nxcansas_1Dand2D_multisasentry", "nxcansas_1Dand2D_multisasdata", @@ -29,16 +30,44 @@ "x25000_no_di", ] +test_xml_file_names = [ + "ISIS_1_0", + "ISIS_1_1", + "ISIS_1_1_doubletrans", + "ISIS_1_1_notrans", + "TestExtensions", + # "cansas1d", + # "cansas1d_badunits", + # "cansas1d_notitle", + # "cansas1d_slit", + # "cansas1d_units", + # "cansas_test", + # "cansas_test_modified", + # "cansas_xml_multisasentry_multisasdata", + "valid_cansas_xml", +] + def local_load(path: str): """Get local file path""" return os.path.join(os.path.dirname(__file__), path) +@pytest.mark.sasdata +@pytest.mark.parametrize("f", test_hdf_file_names) +def test_hdf_load_file(f): + data = hdf_load_data(local_load(f"data/{f}.h5")) + + with open(local_load(f"reference/{f}.txt")) as infile: + expected = "".join(infile.readlines()) + keys = sorted([d for d in data]) + assert "".join(data[k].summary() for k in keys) == expected + + @pytest.mark.current -@pytest.mark.parametrize("f", test_file_names) -def test_load_file(f): - data = load_data(local_load(f"data/{f}.h5")) +@pytest.mark.parametrize("f", test_xml_file_names) +def test_xml_load_file(f): + data = xml_load_data(local_load(f"data/{f}.xml")) with open(local_load(f"reference/{f}.txt")) as infile: expected = "".join(infile.readlines()) From d34c74a9a01d7ee111a556098667578441fdcd5e Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Fri, 4 Apr 2025 15:21:27 +0100 Subject: [PATCH 665/675] Better work at handling comments --- sasdata/temp_xml_reader.py | 8 +++-- test/sasdataloader/reference/cansas1d.txt | 41 ++++------------------- test/sasdataloader/utest_sasdataload.py | 2 +- 3 files changed, 13 insertions(+), 38 deletions(-) diff --git a/sasdata/temp_xml_reader.py b/sasdata/temp_xml_reader.py index 174fe7389..a76850c92 100644 --- a/sasdata/temp_xml_reader.py +++ b/sasdata/temp_xml_reader.py @@ -45,7 +45,11 @@ def parse_quantity(node: etree._Element, version: str) -> Quantity[float]: """Pull a single quantity with length units out of an XML node""" magnitude = float(parse_string(node, version)) try: - unit = unit_parser.parse(node.attrib["unit"]) + """Pull a single quantity with length units out of an XML node""" + body = "".join(node.itertext()) # Needed to parse all text, even after comments + magnitude = float(body) + unit = node.attrib["unit"] + return Quantity(magnitude, unit_parser.parse(unit)) except ValueError: logger.warning( f'Could not parse unit "{node.attrib["unit"]}". Marking value as unitless' @@ -215,7 +219,7 @@ def parse_data(node: etree._Element, version: str, metadata: Metadata) -> dict[s struct = {} for value in idata.getchildren(): name = etree.QName(value).localname - if value.text is None or parse_string(value, version).strip() == "": + if value.text is None or value.text.strip() == "": continue if name not in us: unit = ( diff --git a/test/sasdataloader/reference/cansas1d.txt b/test/sasdataloader/reference/cansas1d.txt index e16366bba..30396ca9d 100644 --- a/test/sasdataloader/reference/cansas1d.txt +++ b/test/sasdataloader/reference/cansas1d.txt @@ -1,4 +1,4 @@ -Test title +None Q I Metadata: @@ -11,34 +11,12 @@ Process: Name: spol Date: 04-Sep-2007 18:35:02 Description: None - Terms: - radialstep: 10.0 mm - sector_width: 180.0 deg - sector_orient: 0.0 deg - MASK_file: USER:MASK.COM - Notes: - AvA1 0.0000E+00 AsA2 1.0000E+00 XvA3 1.0526E+03 XsA4 - 5.2200E-02 XfA5 0.0000E+00 - S... 13597 0 2.26E+02 2A 5mM 0%D2O Sbak 13594 0 1.13E+02 - H2O Buffer - V... 13552 3 1.00E+00 H2O5m + Term: {'radialstep': '10.000', 'sector_width': '180.0', 'sector_orient': '0.0', 'MASK_file': 'USER:MASK.COM'} Process: Name: NCNR-IGOR Date: 03-SEP-2006 11:42:47 - Description: - Terms: - average_type: Circular - SAM_file: SEP06064.SA3_AJJ_L205 - BKD_file: SEP06064.SA3_AJJ_L205 - EMP_file: SEP06064.SA3_AJJ_L205 - DIV_file: SEP06064.SA3_AJJ_L205 - MASK_file: SEP06064.SA3_AJJ_L205 - ABS:TSTAND: 1 - ABS:DSTAND: 1.0 mm - ABS:IZERO: 230.09 - ABS:XSECT: 1.0 mm - Notes: - No Information + Description: None + Term: {'average_type': 'Circular', 'SAM_file': 'SEP06064.SA3_AJJ_L205', 'BKD_file': 'SEP06064.SA3_AJJ_L205', 'EMP_file': 'SEP06064.SA3_AJJ_L205', 'DIV_file': 'SEP06064.SA3_AJJ_L205', 'MASK_file': 'SEP06064.SA3_AJJ_L205', 'ABS:TSTAND': '1', 'ABS:DSTAND': '1', 'ABS:IZERO': '230.09', 'ABS:XSECT': '1'} Sample: ID: SI600-new-long Transmission: 0.327 @@ -48,14 +26,6 @@ Sample: Orientation: Rot3(roll=22.5 deg, pitch=0.02 deg, yaw=None) Collimation: Length: 123.0 mm - Aperture: - Name: source - Aperture size: Vec3(x=50.0 mm, y=None, z=None) - Aperture distance: 11.0 m - Aperture: - Name: sample - Aperture size: Vec3(x=1.0 mm, y=None, z=None) - Aperture distance: None Detector: Name: fictional hybrid Distance: 4.15 m @@ -71,4 +41,5 @@ Source: Min. Wavelength: 0.22 nm Max. Wavelength: 1.0 nm Wavelength Spread: 14.3 % - Beam Size: BeamSize(name='bm', size=Vec3(x=12.0 mm, y=13.0 mm, z=None)) + Beam Size: BeamSize(name=None, size=None) + diff --git a/test/sasdataloader/utest_sasdataload.py b/test/sasdataloader/utest_sasdataload.py index 952abdc79..aa10daccc 100644 --- a/test/sasdataloader/utest_sasdataload.py +++ b/test/sasdataloader/utest_sasdataload.py @@ -36,7 +36,7 @@ "ISIS_1_1_doubletrans", "ISIS_1_1_notrans", "TestExtensions", - # "cansas1d", + "cansas1d", # "cansas1d_badunits", # "cansas1d_notitle", # "cansas1d_slit", From a4d58d3576306d2707e5a96d5f62905e83958d90 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Fri, 4 Apr 2025 15:45:39 +0100 Subject: [PATCH 666/675] More tests of xml --- .../reference/cansas1d_notitle.txt | 41 +++---------------- .../sasdataloader/reference/cansas1d_slit.txt | 41 ++++--------------- test/sasdataloader/reference/cansas_test.txt | 17 ++------ .../reference/cansas_test_modified.txt | 17 ++------ test/sasdataloader/utest_sasdataload.py | 10 ++--- 5 files changed, 26 insertions(+), 100 deletions(-) diff --git a/test/sasdataloader/reference/cansas1d_notitle.txt b/test/sasdataloader/reference/cansas1d_notitle.txt index ca8abac25..706b6b80f 100644 --- a/test/sasdataloader/reference/cansas1d_notitle.txt +++ b/test/sasdataloader/reference/cansas1d_notitle.txt @@ -1,44 +1,22 @@ -SasData01 +None Q I Metadata: None, Run: 1234 - =============== + =========== Definition: None Process: Name: spol Date: 04-Sep-2007 18:35:02 Description: None - Terms: - radialstep: 10.0 mm - sector_width: 180.0 deg - sector_orient: 0.0 deg - MASK_file: USER:MASK.COM - Notes: - AvA1 0.0000E+00 AsA2 1.0000E+00 XvA3 1.0526E+03 XsA4 - 5.2200E-02 XfA5 0.0000E+00 - S... 13597 0 2.26E+02 2A 5mM 0%D2O Sbak 13594 0 1.13E+02 - H2O Buffer - V... 13552 3 1.00E+00 H2O5m + Term: {'radialstep': '10.000', 'sector_width': '180.0', 'sector_orient': '0.0', 'MASK_file': 'USER:MASK.COM'} Process: Name: NCNR-IGOR Date: 03-SEP-2006 11:42:47 Description: - Terms: - average_type: Circular - SAM_file: SEP06064.SA3_AJJ_L205 - BKD_file: SEP06064.SA3_AJJ_L205 - EMP_file: SEP06064.SA3_AJJ_L205 - DIV_file: SEP06064.SA3_AJJ_L205 - MASK_file: SEP06064.SA3_AJJ_L205 - ABS:TSTAND: 1 - ABS:DSTAND: 1.0 mm - ABS:IZERO: 230.09 - ABS:XSECT: 1.0 mm - Notes: - No Information + Term: {'average_type': 'Circular', 'SAM_file': 'SEP06064.SA3_AJJ_L205', 'BKD_file': 'SEP06064.SA3_AJJ_L205', 'EMP_file': 'SEP06064.SA3_AJJ_L205', 'DIV_file': 'SEP06064.SA3_AJJ_L205', 'MASK_file': 'SEP06064.SA3_AJJ_L205', 'ABS:TSTAND': '1', 'ABS:DSTAND': '1', 'ABS:IZERO': '230.09', 'ABS:XSECT': '1'} Sample: ID: SI600-new-long Transmission: 0.327 @@ -48,14 +26,6 @@ Sample: Orientation: Rot3(roll=22.5 deg, pitch=0.02 deg, yaw=None) Collimation: Length: 123.0 mm - Aperture: - Name: source - Aperture size: Vec3(x=50.0 mm, y=None, z=None) - Aperture distance: 11.0 m - Aperture: - Name: sample - Aperture size: Vec3(x=1.0 mm, y=None, z=None) - Aperture distance: None Detector: Name: fictional hybrid Distance: 4.15 m @@ -71,4 +41,5 @@ Source: Min. Wavelength: 0.22 nm Max. Wavelength: 1.0 nm Wavelength Spread: 14.3 % - Beam Size: BeamSize(name='bm', size=Vec3(x=12.0 mm, y=13.0 mm, z=None)) + Beam Size: BeamSize(name=None, size=None) + diff --git a/test/sasdataloader/reference/cansas1d_slit.txt b/test/sasdataloader/reference/cansas1d_slit.txt index cbc9bdcfe..36f1b2945 100644 --- a/test/sasdataloader/reference/cansas1d_slit.txt +++ b/test/sasdataloader/reference/cansas1d_slit.txt @@ -1,8 +1,8 @@ -Test title - dQw +None + I dQl Q - I + dQw Metadata: Test title, Run: 1234 @@ -13,32 +13,12 @@ Process: Name: spol Date: 04-Sep-2007 18:35:02 Description: None - Terms: - radialstep: 10.0 mm - sector_width: 180.0 deg - sector_orient: 0.0 deg - MASK_file: USER:MASK.COM - Notes: - AvA1 0.0000E+00 AsA2 1.0000E+00 XvA3 1.0526E+03 XsA4 - 5.2200E-02 XfA5 0.0000E+00 - S... 13597 0 2.26E+02 2A 5mM 0%D2O Sbak 13594 0 1.13E+02 - H2O Buffer - V... 13552 3 1.00E+00 H2O5m + Term: {'radialstep': '10.000', 'sector_width': '180.0', 'sector_orient': '0.0', 'MASK_file': 'USER:MASK.COM'} Process: Name: NCNR-IGOR Date: 03-SEP-2006 11:42:47 Description: - Terms: - average_type: Circular - SAM_file: SEP06064.SA3_AJJ_L205 - BKD_file: SEP06064.SA3_AJJ_L205 - EMP_file: SEP06064.SA3_AJJ_L205 - DIV_file: SEP06064.SA3_AJJ_L205 - MASK_file: SEP06064.SA3_AJJ_L205 - ABS:TSTAND: 1 - ABS:DSTAND: 1.0 mm - ABS:IZERO: 230.09 - ABS:XSECT: 1.0 mm + Term: {'average_type': 'Circular', 'SAM_file': 'SEP06064.SA3_AJJ_L205', 'BKD_file': 'SEP06064.SA3_AJJ_L205', 'EMP_file': 'SEP06064.SA3_AJJ_L205', 'DIV_file': 'SEP06064.SA3_AJJ_L205', 'MASK_file': 'SEP06064.SA3_AJJ_L205', 'ABS:TSTAND': '1', 'ABS:DSTAND': '1', 'ABS:IZERO': '230.09', 'ABS:XSECT': '1'} Sample: ID: SI600-new-long Transmission: 0.327 @@ -48,14 +28,6 @@ Sample: Orientation: Rot3(roll=22.5 deg, pitch=0.02 deg, yaw=None) Collimation: Length: 123.0 mm - Aperture: - Name: source - Aperture size: Vec3(x=50.0 mm, y=None, z=None) - Aperture distance: 11.0 m - Aperture: - Name: sample - Aperture size: Vec3(x=1.0 mm, y=None, z=None) - Aperture distance: None Detector: Name: fictional hybrid Distance: 4.15 m @@ -71,4 +43,5 @@ Source: Min. Wavelength: 0.22 nm Max. Wavelength: 1.0 nm Wavelength Spread: 14.3 % - Beam Size: BeamSize(name='bm', size=Vec3(x=12.0 mm, y=13.0 mm, z=None)) + Beam Size: BeamSize(name=None, size=None) + diff --git a/test/sasdataloader/reference/cansas_test.txt b/test/sasdataloader/reference/cansas_test.txt index 60333cc05..dbdc7d19e 100644 --- a/test/sasdataloader/reference/cansas_test.txt +++ b/test/sasdataloader/reference/cansas_test.txt @@ -1,4 +1,4 @@ -ILL-D11 example1: 2A 5mM 0%D2O +None Q I Metadata: @@ -11,17 +11,7 @@ Process: Name: spol Date: 04-Sep-2007 18:12:27 Description: None - Terms: - radialstep: 10.0 mm - sector_width: 180.0 deg - sector_orient: 0.0 deg - Flux_monitor: 1.00 - Count_time: 900.0 s - Q_resolution: estimated - Notes: - AvA1 0.0000E+00 AsA2 1.0000E+00 XvA3 1.1111E+01 XsA4 5.2200E-02 XfA5 0.0000E+00 - S... 13586 0 5.63E+01 2A 5mM 0%D2O Sbak 13567 0 1.88E+01 H2O buffer - Sbak 13533 0 7.49E+00 H2O buffer V... 13552 1 1.00E+00 Normal 10m from + Term: {'radialstep': '10.000', 'sector_width': '180.0', 'sector_orient': '0.0', 'Flux_monitor': '1.00', 'Count_time': '900.000', 'Q_resolution': 'estimated'} Sample: ID: None Transmission: 0.0 @@ -46,4 +36,5 @@ Source: Min. Wavelength: None Max. Wavelength: None Wavelength Spread: 0.0 Å - Beam Size: BeamSize(name=None, size=Vec3(x=30.0 mm, y=0.0 mm, z=None)) + Beam Size: BeamSize(name=None, size=None) + diff --git a/test/sasdataloader/reference/cansas_test_modified.txt b/test/sasdataloader/reference/cansas_test_modified.txt index 9ccd51e73..c961777e3 100644 --- a/test/sasdataloader/reference/cansas_test_modified.txt +++ b/test/sasdataloader/reference/cansas_test_modified.txt @@ -1,4 +1,4 @@ -ILL-D11 example1: 2A 5mM 0%D2O +None Q I Metadata: @@ -11,17 +11,7 @@ Process: Name: spol Date: 04-Sep-2007 18:12:27 Description: None - Terms: - radialstep: 10.0 mm - sector_width: 180.0 deg - sector_orient: 0.0 deg - Flux_monitor: 1.00 - Count_time: 900.0 s - Q_resolution: estimated - Notes: - AvA1 0.0000E+00 AsA2 1.0000E+00 XvA3 1.1111E+01 XsA4 5.2200E-02 XfA5 0.0000E+00 - S... 13586 0 5.63E+01 2A 5mM 0%D2O Sbak 13567 0 1.88E+01 H2O buffer - Sbak 13533 0 7.49E+00 H2O buffer V... 13552 1 1.00E+00 Normal 10m from + Term: {'radialstep': '10.000', 'sector_width': '180.0', 'sector_orient': '0.0', 'Flux_monitor': '1.00', 'Count_time': '900.000', 'Q_resolution': 'estimated'} Sample: ID: This is a test file Transmission: 0.0 @@ -46,4 +36,5 @@ Source: Min. Wavelength: None Max. Wavelength: None Wavelength Spread: 0.0 Å - Beam Size: BeamSize(name=None, size=Vec3(x=30.0 mm, y=0.0 mm, z=None)) + Beam Size: BeamSize(name=None, size=None) + diff --git a/test/sasdataloader/utest_sasdataload.py b/test/sasdataloader/utest_sasdataload.py index aa10daccc..d89a157b1 100644 --- a/test/sasdataloader/utest_sasdataload.py +++ b/test/sasdataloader/utest_sasdataload.py @@ -38,12 +38,12 @@ "TestExtensions", "cansas1d", # "cansas1d_badunits", - # "cansas1d_notitle", - # "cansas1d_slit", + "cansas1d_notitle", + "cansas1d_slit", # "cansas1d_units", - # "cansas_test", - # "cansas_test_modified", - # "cansas_xml_multisasentry_multisasdata", + "cansas_test", + "cansas_test_modified", + "cansas_xml_multisasentry_multisasdata", "valid_cansas_xml", ] From b7bd79b8153deb56292d627b5451ab6f188870c6 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Fri, 4 Apr 2025 17:20:02 +0100 Subject: [PATCH 667/675] Mark weird units, but continue parsing --- sasdata/temp_xml_reader.py | 6 +-- .../reference/cansas1d_badunits.txt | 41 ++++--------------- test/sasdataloader/utest_sasdataload.py | 2 +- 3 files changed, 9 insertions(+), 40 deletions(-) diff --git a/sasdata/temp_xml_reader.py b/sasdata/temp_xml_reader.py index a76850c92..b6ea5f2cb 100644 --- a/sasdata/temp_xml_reader.py +++ b/sasdata/temp_xml_reader.py @@ -45,11 +45,7 @@ def parse_quantity(node: etree._Element, version: str) -> Quantity[float]: """Pull a single quantity with length units out of an XML node""" magnitude = float(parse_string(node, version)) try: - """Pull a single quantity with length units out of an XML node""" - body = "".join(node.itertext()) # Needed to parse all text, even after comments - magnitude = float(body) - unit = node.attrib["unit"] - return Quantity(magnitude, unit_parser.parse(unit)) + unit = unit_parser.parse(node.attrib["unit"]) except ValueError: logger.warning( f'Could not parse unit "{node.attrib["unit"]}". Marking value as unitless' diff --git a/test/sasdataloader/reference/cansas1d_badunits.txt b/test/sasdataloader/reference/cansas1d_badunits.txt index 4c61f204e..d3c46303a 100644 --- a/test/sasdataloader/reference/cansas1d_badunits.txt +++ b/test/sasdataloader/reference/cansas1d_badunits.txt @@ -1,4 +1,4 @@ -Test title +None Q I Metadata: @@ -11,32 +11,12 @@ Process: Name: spol Date: 04-Sep-2007 18:35:02 Description: None - Terms: - radialstep: 10.0 mm - sector_width: 4.1416 rad - sector_orient: 0.0 deg - MASK_file: USER:MASK.COM - Notes: - AvA1 0.0000E+00 AsA2 1.0000E+00 XvA3 1.0526E+03 XsA4 - 5.2200E-02 XfA5 0.0000E+00 - S... 13597 0 2.26E+02 2A 5mM 0%D2O Sbak 13594 0 1.13E+02 - H2O Buffer - V... 13552 3 1.00E+00 H2O5m + Term: {'radialstep': '10.000', 'sector_width': '4.1416', 'sector_orient': '0.0', 'MASK_file': 'USER:MASK.COM'} Process: Name: NCNR-IGOR Date: 03-SEP-2006 11:42:47 Description: - Terms: - average_type: Circular - SAM_file: SEP06064.SA3_AJJ_L205 - BKD_file: SEP06064.SA3_AJJ_L205 - EMP_file: SEP06064.SA3_AJJ_L205 - DIV_file: SEP06064.SA3_AJJ_L205 - MASK_file: SEP06064.SA3_AJJ_L205 - ABS:TSTAND: 1 - ABS:DSTAND: 1.0 mm - ABS:IZERO: 230.09 - ABS:XSECT: 1.0 mm + Term: {'average_type': 'Circular', 'SAM_file': 'SEP06064.SA3_AJJ_L205', 'BKD_file': 'SEP06064.SA3_AJJ_L205', 'EMP_file': 'SEP06064.SA3_AJJ_L205', 'DIV_file': 'SEP06064.SA3_AJJ_L205', 'MASK_file': 'SEP06064.SA3_AJJ_L205', 'ABS:TSTAND': '1', 'ABS:DSTAND': '1', 'ABS:IZERO': '230.09', 'ABS:XSECT': '1'} Sample: ID: SI600-new-long Transmission: 0.327 @@ -46,19 +26,11 @@ Sample: Orientation: Rot3(roll=0.39269908 rad, pitch=0.00034906585 rad, yaw=None) Collimation: Length: 0.123 m - Aperture: - Name: source - Aperture size: Vec3(x=50000.0 µm, y=None, z=None) - Aperture distance: 1100.0 cm - Aperture: - Name: sample - Aperture size: Vec3(x=0.1 cm, y=None, z=None) - Aperture distance: None Detector: Name: fictional hybrid Distance: 4.15 m - Offset: Vec3(x=1000000.0 nm, y=2000.0 µm, z=None) - Orientation: Rot3(roll=0.0174533 rad, pitch=0.0 rad, yaw=0.0 rad) + Offset: Vec3(x=1000000.0 nm, y=2000.0 none, z=None) + Orientation: Rot3(roll=0.0174533 none, pitch=0.0 rad, yaw=0.0 none) Beam center: Vec3(x=0.32264 m, y=0.32768 m, z=None) Pixel size: Vec3(x=0.5 cm, y=0.5 cm, z=None) Slit length: None @@ -69,4 +41,5 @@ Source: Min. Wavelength: 2.2 Å Max. Wavelength: 10.0 Å Wavelength Spread: 14.3 % - Beam Size: BeamSize(name='bm', size=Vec3(x=0.012 m, y=13000.0 µm, z=None)) + Beam Size: BeamSize(name=None, size=None) + diff --git a/test/sasdataloader/utest_sasdataload.py b/test/sasdataloader/utest_sasdataload.py index d89a157b1..dc732a920 100644 --- a/test/sasdataloader/utest_sasdataload.py +++ b/test/sasdataloader/utest_sasdataload.py @@ -37,7 +37,7 @@ "ISIS_1_1_notrans", "TestExtensions", "cansas1d", - # "cansas1d_badunits", + "cansas1d_badunits", "cansas1d_notitle", "cansas1d_slit", # "cansas1d_units", From 8b130c332d430fb71d66502620e894c5b779a4e8 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Fri, 4 Apr 2025 17:22:41 +0100 Subject: [PATCH 668/675] Add last xml test --- .../reference/cansas1d_units.txt | 41 ++++--------------- test/sasdataloader/utest_sasdataload.py | 2 +- 2 files changed, 8 insertions(+), 35 deletions(-) diff --git a/test/sasdataloader/reference/cansas1d_units.txt b/test/sasdataloader/reference/cansas1d_units.txt index 68db7bc45..2d613c0f3 100644 --- a/test/sasdataloader/reference/cansas1d_units.txt +++ b/test/sasdataloader/reference/cansas1d_units.txt @@ -1,4 +1,4 @@ -Test title +None Q I Metadata: @@ -11,32 +11,12 @@ Process: Name: spol Date: 04-Sep-2007 18:35:02 Description: None - Terms: - radialstep: 10.0 mm - sector_width: 4.1416 rad - sector_orient: 0.0 deg - MASK_file: USER:MASK.COM - Notes: - AvA1 0.0000E+00 AsA2 1.0000E+00 XvA3 1.0526E+03 XsA4 - 5.2200E-02 XfA5 0.0000E+00 - S... 13597 0 2.26E+02 2A 5mM 0%D2O Sbak 13594 0 1.13E+02 - H2O Buffer - V... 13552 3 1.00E+00 H2O5m + Term: {'radialstep': '10.000', 'sector_width': '4.1416', 'sector_orient': '0.0', 'MASK_file': 'USER:MASK.COM'} Process: Name: NCNR-IGOR Date: 03-SEP-2006 11:42:47 Description: - Terms: - average_type: Circular - SAM_file: SEP06064.SA3_AJJ_L205 - BKD_file: SEP06064.SA3_AJJ_L205 - EMP_file: SEP06064.SA3_AJJ_L205 - DIV_file: SEP06064.SA3_AJJ_L205 - MASK_file: SEP06064.SA3_AJJ_L205 - ABS:TSTAND: 1 - ABS:DSTAND: 1.0 mm - ABS:IZERO: 230.09 - ABS:XSECT: 1.0 mm + Term: {'average_type': 'Circular', 'SAM_file': 'SEP06064.SA3_AJJ_L205', 'BKD_file': 'SEP06064.SA3_AJJ_L205', 'EMP_file': 'SEP06064.SA3_AJJ_L205', 'DIV_file': 'SEP06064.SA3_AJJ_L205', 'MASK_file': 'SEP06064.SA3_AJJ_L205', 'ABS:TSTAND': '1', 'ABS:DSTAND': '1', 'ABS:IZERO': '230.09', 'ABS:XSECT': '1'} Sample: ID: SI600-new-long Transmission: 0.327 @@ -46,19 +26,11 @@ Sample: Orientation: Rot3(roll=0.39269908 rad, pitch=0.00034906585 rad, yaw=None) Collimation: Length: 0.123 m - Aperture: - Name: source - Aperture size: Vec3(x=50000.0 µm, y=None, z=None) - Aperture distance: 1100.0 cm - Aperture: - Name: sample - Aperture size: Vec3(x=0.1 cm, y=None, z=None) - Aperture distance: None Detector: Name: fictional hybrid Distance: 4.15 m - Offset: Vec3(x=1000000.0 nm, y=2000.0 µm, z=None) - Orientation: Rot3(roll=0.0174533 rad, pitch=0.0 rad, yaw=0.0 rad) + Offset: Vec3(x=1000000.0 nm, y=2000.0 none, z=None) + Orientation: Rot3(roll=0.0174533 none, pitch=0.0 rad, yaw=0.0 none) Beam center: Vec3(x=0.32264 m, y=0.32768 m, z=None) Pixel size: Vec3(x=0.5 cm, y=0.5 cm, z=None) Slit length: None @@ -69,4 +41,5 @@ Source: Min. Wavelength: 2.2 Å Max. Wavelength: 10.0 Å Wavelength Spread: 14.3 % - Beam Size: BeamSize(name='bm', size=Vec3(x=0.012 m, y=13000.0 µm, z=None)) + Beam Size: BeamSize(name=None, size=None) + diff --git a/test/sasdataloader/utest_sasdataload.py b/test/sasdataloader/utest_sasdataload.py index dc732a920..fe8e87ceb 100644 --- a/test/sasdataloader/utest_sasdataload.py +++ b/test/sasdataloader/utest_sasdataload.py @@ -40,7 +40,7 @@ "cansas1d_badunits", "cansas1d_notitle", "cansas1d_slit", - # "cansas1d_units", + "cansas1d_units", "cansas_test", "cansas_test_modified", "cansas_xml_multisasentry_multisasdata", From 554ca720061bdb9fa2dcf2ec17a44c064f191ca5 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Fri, 4 Apr 2025 17:39:24 +0100 Subject: [PATCH 669/675] Fix parsing of BeamSize --- test/sasdataloader/reference/cansas1d.txt | 3 +-- test/sasdataloader/reference/cansas1d_badunits.txt | 3 +-- test/sasdataloader/reference/cansas1d_notitle.txt | 3 +-- test/sasdataloader/reference/cansas1d_slit.txt | 3 +-- test/sasdataloader/reference/cansas1d_units.txt | 3 +-- test/sasdataloader/reference/cansas_test.txt | 3 +-- test/sasdataloader/reference/cansas_test_modified.txt | 3 +-- 7 files changed, 7 insertions(+), 14 deletions(-) diff --git a/test/sasdataloader/reference/cansas1d.txt b/test/sasdataloader/reference/cansas1d.txt index 30396ca9d..7563b9b65 100644 --- a/test/sasdataloader/reference/cansas1d.txt +++ b/test/sasdataloader/reference/cansas1d.txt @@ -41,5 +41,4 @@ Source: Min. Wavelength: 0.22 nm Max. Wavelength: 1.0 nm Wavelength Spread: 14.3 % - Beam Size: BeamSize(name=None, size=None) - + Beam Size: BeamSize(name='bm', size=Vec3(x=12.0 mm, y=13.0 mm, z=None)) diff --git a/test/sasdataloader/reference/cansas1d_badunits.txt b/test/sasdataloader/reference/cansas1d_badunits.txt index d3c46303a..3868aead0 100644 --- a/test/sasdataloader/reference/cansas1d_badunits.txt +++ b/test/sasdataloader/reference/cansas1d_badunits.txt @@ -41,5 +41,4 @@ Source: Min. Wavelength: 2.2 Å Max. Wavelength: 10.0 Å Wavelength Spread: 14.3 % - Beam Size: BeamSize(name=None, size=None) - + Beam Size: BeamSize(name='bm', size=Vec3(x=0.012 m, y=13000.0 none, z=None)) diff --git a/test/sasdataloader/reference/cansas1d_notitle.txt b/test/sasdataloader/reference/cansas1d_notitle.txt index 706b6b80f..b21da686f 100644 --- a/test/sasdataloader/reference/cansas1d_notitle.txt +++ b/test/sasdataloader/reference/cansas1d_notitle.txt @@ -41,5 +41,4 @@ Source: Min. Wavelength: 0.22 nm Max. Wavelength: 1.0 nm Wavelength Spread: 14.3 % - Beam Size: BeamSize(name=None, size=None) - + Beam Size: BeamSize(name='bm', size=Vec3(x=12.0 mm, y=13.0 mm, z=None)) diff --git a/test/sasdataloader/reference/cansas1d_slit.txt b/test/sasdataloader/reference/cansas1d_slit.txt index 36f1b2945..7ef9093f8 100644 --- a/test/sasdataloader/reference/cansas1d_slit.txt +++ b/test/sasdataloader/reference/cansas1d_slit.txt @@ -43,5 +43,4 @@ Source: Min. Wavelength: 0.22 nm Max. Wavelength: 1.0 nm Wavelength Spread: 14.3 % - Beam Size: BeamSize(name=None, size=None) - + Beam Size: BeamSize(name='bm', size=Vec3(x=12.0 mm, y=13.0 mm, z=None)) diff --git a/test/sasdataloader/reference/cansas1d_units.txt b/test/sasdataloader/reference/cansas1d_units.txt index 2d613c0f3..21845aa8c 100644 --- a/test/sasdataloader/reference/cansas1d_units.txt +++ b/test/sasdataloader/reference/cansas1d_units.txt @@ -41,5 +41,4 @@ Source: Min. Wavelength: 2.2 Å Max. Wavelength: 10.0 Å Wavelength Spread: 14.3 % - Beam Size: BeamSize(name=None, size=None) - + Beam Size: BeamSize(name='bm', size=Vec3(x=0.012 m, y=13000.0 none, z=None)) diff --git a/test/sasdataloader/reference/cansas_test.txt b/test/sasdataloader/reference/cansas_test.txt index dbdc7d19e..46f1e4e87 100644 --- a/test/sasdataloader/reference/cansas_test.txt +++ b/test/sasdataloader/reference/cansas_test.txt @@ -36,5 +36,4 @@ Source: Min. Wavelength: None Max. Wavelength: None Wavelength Spread: 0.0 Å - Beam Size: BeamSize(name=None, size=None) - + Beam Size: BeamSize(name=None, size=Vec3(x=30.0 mm, y=0.0 mm, z=None)) diff --git a/test/sasdataloader/reference/cansas_test_modified.txt b/test/sasdataloader/reference/cansas_test_modified.txt index c961777e3..56d65ae88 100644 --- a/test/sasdataloader/reference/cansas_test_modified.txt +++ b/test/sasdataloader/reference/cansas_test_modified.txt @@ -36,5 +36,4 @@ Source: Min. Wavelength: None Max. Wavelength: None Wavelength Spread: 0.0 Å - Beam Size: BeamSize(name=None, size=None) - + Beam Size: BeamSize(name=None, size=Vec3(x=30.0 mm, y=0.0 mm, z=None)) From 796e65e70e1a65d9a80d689c8e6b6ca50b9a50b6 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Tue, 6 May 2025 14:06:15 +0100 Subject: [PATCH 670/675] Skeleton framework for SESANS data --- sasdata/temp_sesans_reader.py | 177 ++---------------- .../sasdataloader/reference/sphere2micron.txt | 21 +-- test/sasdataloader/utest_new_sesans.py | 121 +----------- 3 files changed, 28 insertions(+), 291 deletions(-) diff --git a/sasdata/temp_sesans_reader.py b/sasdata/temp_sesans_reader.py index 23d561593..af33072f2 100644 --- a/sasdata/temp_sesans_reader.py +++ b/sasdata/temp_sesans_reader.py @@ -2,68 +2,29 @@ Import SESANS data in SasData format """ -import re -from collections import defaultdict -from itertools import groupby - -import numpy as np - from sasdata.data import SasData from sasdata.data_util.loader_exceptions import FileContentsException -from sasdata.dataset_types import sesans -from sasdata.metadata import ( - Metadata, - MetaNode, - Process, - Sample, -) -from sasdata.quantities import unit_parser, units +from sasdata.dataset_types import one_dim from sasdata.quantities.quantity import Quantity +from sasdata.metadata import Metadata, Sample def parse_version(lines: list[str]) -> tuple[str, list[str]]: + import re + header = lines[0] - m = re.search(r"FileFormatVersion\s+(\S+)", header) + m = re.search("FileFormatVersion\s+(\S+)", header) if m is None: - raise FileContentsException( - "Sesans file does not contain File Format Version header" - ) + raise FileContentsException("Alleged Sesans file does not contain File Format Version header") return (m.group(0), lines[1:]) - -def parse_title(kvs: dict[str, str]) -> str: - """Get the title from the key value store""" - if "Title" in kvs: - return kvs["Title"] - elif "DataFileTitle" in kvs: - return kvs["DataFileTitle"] - for k, v in kvs.items(): - if "Title" in k: - return v - return "" - - -def parse_kvs_quantity(key: str, kvs: dict[str, str]) -> Quantity | None: - if key not in kvs or key + "_unit" not in kvs: - return None - return Quantity(value=float(kvs[key]), units=unit_parser.parse(kvs[key + "_unit"])) - - -def parse_sample(kvs: dict[str, str]) -> Sample: - """Get the sample info from the key value store""" - - thickness = parse_kvs_quantity("Thickness", kvs) - if thickness is None: - raise FileContentsException( - "SES format must include sample thickness to normalise calculations" - ) - - return Sample( - name=kvs.get("Sample"), +def parse_metadata(lines: list[str]) -> tuple[Metadata, list[str]]: + sample = Sample( + name=None, sample_id=None, - thickness=thickness, + thickness=None, transmission=None, temperature=None, position=None, @@ -71,133 +32,31 @@ def parse_sample(kvs: dict[str, str]) -> Sample: details=[], ) - -def parse_process(kvs: dict[str, str]) -> Process: - ymax = parse_kvs_quantity("Theta_ymax", kvs) - zmax = parse_kvs_quantity("Theta_zmax", kvs) - orientation = kvs.get("Orientation") - - if ymax is None: - raise FileContentsException("SES file must specify Theta_ymax") - if zmax is None: - raise FileContentsException("SES file must specify Theta_zmax") - if orientation is None: - raise FileContentsException("SES file must include encoding orientation") - - terms: dict[str, str | Quantity] = { - "ymax": ymax, - "zmax": zmax, - "orientation": orientation, - } - - return Process( - name="SESANS Processing", - date=None, - description="Polarisation measurement through a SESANS instrument", - terms=terms, - notes=[], - ) - - -def parse_metanode(kvs: dict[str, str]) -> MetaNode: - """Convert header into metanode""" - contents: list[MetaNode] = [] - title = parse_title(kvs) - - for k, v in kvs.items(): - if v.endswith("_unit") and v[:-5] in kvs: - # This is the unit for another term - continue - if v + "_unit" in kvs: - contents.append( - MetaNode( - name=k, - attrs={}, - contents=Quantity( - value=float(v), units=unit_parser.parse(kvs[k + "_unit"]) - ), - ) - ) - else: - contents.append(MetaNode(name=k, attrs={}, contents=v)) - - return MetaNode(name=title, attrs={}, contents=contents) - - -def parse_metadata(lines: list[str]) -> tuple[Metadata, dict[str, str], list[str]]: - parts = [ - [y for y in x] - for (_, x) in groupby(lines, lambda x: x.startswith("BEGIN_DATA")) - ] - - if len(parts) != 3: - raise FileContentsException("SES file should have exactly one data section") - - # Parse key value store - kvs: dict[str, str] = {} - for line in parts[0]: - m = re.search(r"(\S+)\s+(.+)\n", line) - if not m: - continue - kvs[m.group(1)] = m.group(2) - return ( Metadata( - process=[parse_process(kvs)], + process=[], instrument=None, - sample=parse_sample(kvs), - title=parse_title(kvs), + sample=sample, + title="Title", run=[], definition=None, - raw=parse_metanode(kvs), ), - kvs, - parts[2], + lines, ) -def parse_data(lines: list[str], kvs: dict[str, str]) -> dict[str, Quantity]: - +def parse_data(lines: list[str]) -> dict[str, Quantity]: data_contents: dict[str, Quantity] = {} - headers = lines[0].split() - points = defaultdict(list) - for line in lines[1:]: - values = line.split() - for idx, v in enumerate(values): - points[headers[idx]].append(float(v)) - - for h in points.keys(): - if h.endswith("_error") and h[:-6] in headers: - # This was an error line - continue - unit = units.none - if h + "_unit" in kvs: - unit = unit_parser.parse(kvs[h + "_unit"]) - - error = None - if h + "_error" in headers: - error = np.asarray(points[h + "_error"]) - - data_contents[h] = Quantity( - value=np.asarray(points[h]), - units=unit, - standard_error=error, - ) - - for required in ["SpinEchoLength", "Depolarisation", "Wavelength"]: - if required not in data_contents: - raise FileContentsException(f"SES file missing {required}") - return data_contents def parse_sesans(lines: list[str]) -> SasData: version, lines = parse_version(lines) - metadata, kvs, lines = parse_metadata(lines) - data_contents = parse_data(lines, kvs) + metadata, lines = parse_metadata(lines) + data_contents = parse_data(lines) return SasData( name="Sesans", - dataset_type=sesans, + dataset_type=one_dim, data_contents=data_contents, metadata=metadata, verbose=False, diff --git a/test/sasdataloader/reference/sphere2micron.txt b/test/sasdataloader/reference/sphere2micron.txt index 3df15c420..c1b9f06c7 100644 --- a/test/sasdataloader/reference/sphere2micron.txt +++ b/test/sasdataloader/reference/sphere2micron.txt @@ -1,26 +1,15 @@ Sesans - Wavelength - SpinEchoLength - Polarisation - Depolarisation Metadata: - Polystyrene of Markus Strobl, Full Sine, ++ only, Run: [] - ========================================================== + Title, Run: [] + ============ -Definition: Polystyrene of Markus Strobl, Full Sine, ++ only -Process: - Name: SESANS Processing - Date: None - Description: Polarisation measurement through a SESANS instrument - Terms: - ymax: 0.0168 rad - zmax: 0.0168 rad - orientation: Z +Definition: Title Sample: ID: None Transmission: None - Thickness: 0.2 cm + Thickness: None Temperature: None Position: None Orientation: None + diff --git a/test/sasdataloader/utest_new_sesans.py b/test/sasdataloader/utest_new_sesans.py index d91f8bedf..7de67fd90 100644 --- a/test/sasdataloader/utest_new_sesans.py +++ b/test/sasdataloader/utest_new_sesans.py @@ -3,24 +3,11 @@ """ import os - -import numpy as np import pytest -from sasdata.model_requirements import ( - ComposeRequirements, - ModellingRequirements, - NullModel, - PinholeModel, - SesansModel, - SlitModel, - guess_requirements, -) -from sasdata.quantities import unit_parser, units -from sasdata.quantities.quantity import Quantity -from sasdata.temp_sesans_reader import load_data -test_file_names = ["sphere2micron", "sphere_isis"] +from sasdata.temp_hdf5_reader import load_data +from sasdata.temp_sesans_reader import load_data def local_load(path: str): @@ -29,107 +16,9 @@ def local_load(path: str): @pytest.mark.sesans -@pytest.mark.parametrize("f", test_file_names) -def test_load_file(f): - data = load_data(local_load(f"sesans_data/{f}.ses")) +def test_load_file(): + data = load_data(local_load(f"sesans_data/sphere2micron.ses")) - with open(local_load(f"reference/{f}.txt")) as infile: + with open(local_load(f"reference/sphere2micron.txt")) as infile: expected = "".join(infile.readlines()) assert data.summary() == expected - -@pytest.mark.sesans -def test_sesans_modelling(): - data = load_data(local_load("sesans_data/sphere2micron.ses")) - req = guess_requirements(data) - assert type(req) is SesansModel - - def form_volume(x): - return np.pi * 4.0 / 3.0 * x**3 - - radius = 10000 - contrast = 5.4e-7 # Contrast is hard coded for best fit - form = contrast * form_volume(radius) - f2 = 1.0e-4*form*form - - xi_squared = smear(req, data._data_contents["SpinEchoLength"].value, full_data=data, y = data._data_contents["Depolarisation"].in_units_of(unit_parser.parse("A-2 cm-1")) / f2, radius=radius) - assert 1.0 < xi_squared < 1.5 - -@pytest.mark.sesans -def test_pinhole_zero(): - assert pinhole_smear(0) == 0 - -@pytest.mark.sesans -def test_pinhole_smear(): - smearing = [3**x for x in range(-1, 6)] - smears = [pinhole_smear(x) for x in smearing] - old = 0 - for factor, smear in zip(smearing, smears): - print(factor, smear) - assert old < smear < 1.2 - old = smear - assert pinhole_smear(3**6) > 1.2 - -@pytest.mark.sesans -def test_slit_zero(): - assert slit_smear(0, 0) == 0 - -@pytest.mark.sesans -def test_slit_smear(): - smears = [(length, width, slit_smear(3**length, 3**width)) for length in range(-1, 6) for width in range(-1, 6) if length + width == 6] - for length, width, smear in smears: - print(length, width, smear) - assert smear < 1.2 - assert slit_smear(3**6, 1) > 1.2 - assert slit_smear(1, 3**6) > 1.2 - - -def slit_smear(length: float, width): - data = Quantity(np.linspace(1e-4, 1e-1, 1000), units.per_angstrom) - diff = np.diff(data.value, prepend=0) - req = SlitModel(diff * length, diff * width) - return smear(req, data.value) - - -def pinhole_smear(smearing: float): - data = Quantity(np.linspace(1e-4, 1e-1, 1000), units.per_angstrom) - req = PinholeModel(np.diff(data.value, prepend=0) * smearing) - return smear(req, data.value) - - -def smear(req: ModellingRequirements, data: np.ndarray, y=None, full_data=None, radius=200): - def sphere(q): - def sas_3j1x_x(x): - return (np.sin(x) - x * np.cos(x))/x**3 - - return sas_3j1x_x(q * radius)**2 - - inner_q = req.preprocess_q(data, full_data) - result = req.postprocess_iq(sphere(inner_q), data) - - if y is None: - y = sphere(data) - - xi_squared = np.sum( ((y - result)/result )**2 ) / len(y) - return xi_squared - - - -@pytest.mark.sesans -def test_model_algebra(): - ses = SesansModel() - pin = PinholeModel(np.linspace(1e-3, 1, 1000)) - slit = SlitModel(np.linspace(1e-3, 1, 1000), np.linspace(1e-3, 1, 1000)) - null = NullModel() - - # Ignore slit smearing if we perform a sesans transform afterwards - assert type(pin + ses) is SesansModel - assert type(slit + ses) is SesansModel - # However, it is possible for the spin echo lengths to have some - # smearing between them. - assert type(ses + pin) is ComposeRequirements - assert type(null + ses) is SesansModel - assert type(null + slit) is SlitModel - assert type(null + pin) is PinholeModel - assert type(ses + null) is SesansModel - assert type(pin + null) is PinholeModel - assert type(slit + null) is SlitModel From 1f6dae08e5f797cd3f9d7edb8510652583b8dba1 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Tue, 6 May 2025 16:49:16 +0100 Subject: [PATCH 671/675] Multiple SESANS files in reader test --- test/sasdataloader/reference/sphere_isis.txt | 20 ++++++++------------ test/sasdataloader/utest_new_sesans.py | 9 ++++++--- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/test/sasdataloader/reference/sphere_isis.txt b/test/sasdataloader/reference/sphere_isis.txt index e92cfbf3a..f14039cdc 100644 --- a/test/sasdataloader/reference/sphere_isis.txt +++ b/test/sasdataloader/reference/sphere_isis.txt @@ -1,21 +1,10 @@ Sesans - Wavelength - SpinEchoLength - Depolarisation Metadata: PMMA in Mixed Deuterated decalin, Run: [] - ========================================= + ======================================= Definition: PMMA in Mixed Deuterated decalin -Process: - Name: SESANS Processing - Date: None - Description: Polarisation measurement through a SESANS instrument - Terms: - ymax: 0.09 rad - zmax: 0.09 rad - orientation: Z Sample: ID: None Transmission: None @@ -23,3 +12,10 @@ Sample: Temperature: None Position: None Orientation: None +Collimation: + Length: None + Aperture: + Name: Virtual Acceptance + Aperture size: Vec3(x=0 m, y=89.75817418995052 m, z=89.75817418995052 m) + Aperture distance: 1 km + diff --git a/test/sasdataloader/utest_new_sesans.py b/test/sasdataloader/utest_new_sesans.py index 7de67fd90..65a2af508 100644 --- a/test/sasdataloader/utest_new_sesans.py +++ b/test/sasdataloader/utest_new_sesans.py @@ -9,6 +9,8 @@ from sasdata.temp_hdf5_reader import load_data from sasdata.temp_sesans_reader import load_data +test_file_names = ["sphere2micron", "sphere_isis"] + def local_load(path: str): """Get local file path""" @@ -16,9 +18,10 @@ def local_load(path: str): @pytest.mark.sesans -def test_load_file(): - data = load_data(local_load(f"sesans_data/sphere2micron.ses")) +@pytest.mark.parametrize("f", test_file_names) +def test_load_file(f): + data = load_data(local_load(f"sesans_data/{f}.ses")) - with open(local_load(f"reference/sphere2micron.txt")) as infile: + with open(local_load(f"reference/{f}.txt")) as infile: expected = "".join(infile.readlines()) assert data.summary() == expected From 79c973f859e2d40d7190d59c4601e9fb99da05b3 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Wed, 7 May 2025 13:06:41 +0100 Subject: [PATCH 672/675] SESANS metadata as a process, not an aperture --- sasdata/temp_sesans_reader.py | 91 ++++++++++++++++++- .../sasdataloader/reference/sphere2micron.txt | 11 ++- test/sasdataloader/reference/sphere_isis.txt | 15 +-- 3 files changed, 104 insertions(+), 13 deletions(-) diff --git a/sasdata/temp_sesans_reader.py b/sasdata/temp_sesans_reader.py index af33072f2..243fbc5ae 100644 --- a/sasdata/temp_sesans_reader.py +++ b/sasdata/temp_sesans_reader.py @@ -6,7 +6,20 @@ from sasdata.data_util.loader_exceptions import FileContentsException from sasdata.dataset_types import one_dim from sasdata.quantities.quantity import Quantity -from sasdata.metadata import Metadata, Sample +from sasdata.metadata import ( + Metadata, + Sample, + Instrument, + Collimation, + Aperture, + Vec3, + MetaNode, + Process, +) +from sasdata.quantities import unit_parser, units +from itertools import groupby +import re +import numpy as np def parse_version(lines: list[str]) -> tuple[str, list[str]]: @@ -32,12 +45,82 @@ def parse_metadata(lines: list[str]) -> tuple[Metadata, list[str]]: details=[], ) + +def parse_process(kvs: dict[str, str]) -> Process: + ymax = parse_kvs_quantity("Theta_ymax", kvs) + zmax = parse_kvs_quantity("Theta_zmax", kvs) + orientation = parse_kvs_text("Orientation", kvs) + + if ymax is None: + raise FileContentsException("SES file must specify Theta_ymax") + if zmax is None: + raise FileContentsException("SES file must specify Theta_zmax") + if orientation is None: + raise FileContentsException("SES file must include encoding orientation") + + terms: dict[str, str | Quantity[float]] = { + "ymax": ymax, + "zmax": zmax, + "orientation": orientation, + } + + return Process( + name="SESANS Processing", + date=None, + description="Polarisation measurement through a SESANS instrument", + terms=terms, + notes=[], + ) + + +def parse_metanode(kvs: dict[str, str]) -> MetaNode: + """Convert header into metanode""" + contents: list[MetaNode] = [] + title = parse_title(kvs) + + for k, v in kvs.items(): + if v.endswith("_unit") and v[:-5] in kvs: + # This is the unit for another term + continue + if v + "_unit" in kvs: + contents.append( + MetaNode( + name=k, + attrs={}, + contents=Quantity( + value=float(v), units=unit_parser.parse(kvs[k + "_unit"]) + ), + ) + ) + else: + contents.append(MetaNode(name=k, attrs={}, contents=v)) + + return MetaNode(name=title, attrs={}, contents=contents) + + +def parse_metadata(lines: list[str]) -> tuple[Metadata, dict[str, str], list[str]]: + parts = [ + [y for y in x] + for (_, x) in groupby(lines, lambda x: x.startswith("BEGIN_DATA")) + ] + + if len(parts) != 3: + raise FileContentsException("SES file should have exactly one data section") + + # Parse key value store + kvs: dict[str, str] = {} + for line in parts[0]: + m = re.search("(\S+)\s+(.+)\n", line) + if not m: + continue + kvs[m.group(1)] = m.group(2) + return ( Metadata( - process=[], + process=[parse_process(kvs)], instrument=None, - sample=sample, - title="Title", + sample=parse_sample(kvs), + title=parse_title(kvs), run=[], definition=None, ), diff --git a/test/sasdataloader/reference/sphere2micron.txt b/test/sasdataloader/reference/sphere2micron.txt index c1b9f06c7..87f3b192d 100644 --- a/test/sasdataloader/reference/sphere2micron.txt +++ b/test/sasdataloader/reference/sphere2micron.txt @@ -4,7 +4,15 @@ Metadata: Title, Run: [] ============ -Definition: Title +Definition: Polystyrene of Markus Strobl, Full Sine, ++ only +Process: + Name: SESANS Processing + Date: None + Description: Polarisation measurement through a SESANS instrument + Terms: + ymax: 0.0168 rad + zmax: 0.0168 rad + orientation: Z Sample: ID: None Transmission: None @@ -12,4 +20,3 @@ Sample: Temperature: None Position: None Orientation: None - diff --git a/test/sasdataloader/reference/sphere_isis.txt b/test/sasdataloader/reference/sphere_isis.txt index f14039cdc..53cd10147 100644 --- a/test/sasdataloader/reference/sphere_isis.txt +++ b/test/sasdataloader/reference/sphere_isis.txt @@ -5,6 +5,14 @@ Metadata: ======================================= Definition: PMMA in Mixed Deuterated decalin +Process: + Name: SESANS Processing + Date: None + Description: Polarisation measurement through a SESANS instrument + Terms: + ymax: 0.09 rad + zmax: 0.09 rad + orientation: Z Sample: ID: None Transmission: None @@ -12,10 +20,3 @@ Sample: Temperature: None Position: None Orientation: None -Collimation: - Length: None - Aperture: - Name: Virtual Acceptance - Aperture size: Vec3(x=0 m, y=89.75817418995052 m, z=89.75817418995052 m) - Aperture distance: 1 km - From 87ca591558833237d4e840331b70b775155d4718 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Tue, 10 Jun 2025 15:42:32 +0100 Subject: [PATCH 673/675] More tests for quantities --- sasdata/quantities/math_operations_test.py | 152 -------------------- sasdata/quantities/numerical_encoding.py | 4 +- sasdata/quantities/operations_test.py | 68 --------- sasdata/quantities/quantities_tests.py | 124 ----------------- sasdata/quantities/quantity_error_tests.py | 153 --------------------- sasdata/quantities/units_tests.py | 46 ------- test/quantities/utest_math_operations.py | 6 +- test/quantities/utest_operations.py | 30 +--- test/quantities/utest_quantities.py | 18 +-- test/quantities/utest_quantity_error.py | 7 +- test/quantities/utest_units.py | 32 +---- 11 files changed, 18 insertions(+), 622 deletions(-) delete mode 100644 sasdata/quantities/math_operations_test.py delete mode 100644 sasdata/quantities/operations_test.py delete mode 100644 sasdata/quantities/quantities_tests.py delete mode 100644 sasdata/quantities/quantity_error_tests.py delete mode 100644 sasdata/quantities/units_tests.py diff --git a/sasdata/quantities/math_operations_test.py b/sasdata/quantities/math_operations_test.py deleted file mode 100644 index 5bda5a2cd..000000000 --- a/sasdata/quantities/math_operations_test.py +++ /dev/null @@ -1,152 +0,0 @@ -""" Tests for math operations """ - -import pytest - -import numpy as np -from sasdata.quantities.quantity import NamedQuantity, tensordot, transpose -from sasdata.quantities import units - -order_list = [ - [0, 1, 2, 3], - [0, 2, 1], - [1, 0], - [0, 1], - [2, 0, 1], - [3, 1, 2, 0] -] - -@pytest.mark.parametrize("order", order_list) -def test_transpose_raw(order: list[int]): - """ Check that the transpose operation changes the order of indices correctly - uses sizes as way of tracking""" - - input_shape = tuple([i+1 for i in range(len(order))]) - expected_shape = tuple([i+1 for i in order]) - - input_mat = np.zeros(input_shape) - - measured_mat = transpose(input_mat, axes=tuple(order)) - - assert measured_mat.shape == expected_shape - - -@pytest.mark.parametrize("order", order_list) -def test_transpose_raw(order: list[int]): - """ Check that the transpose operation changes the order of indices correctly - uses sizes as way of tracking""" - input_shape = tuple([i + 1 for i in range(len(order))]) - expected_shape = tuple([i + 1 for i in order]) - - input_mat = NamedQuantity("testmat", np.zeros(input_shape), units=units.none) - - measured_mat = transpose(input_mat, axes=tuple(order)) - - assert measured_mat.value.shape == expected_shape - - -rng_seed = 1979 -tensor_product_with_identity_sizes = (4,6,5) - -@pytest.mark.parametrize("index, size", [tup for tup in enumerate(tensor_product_with_identity_sizes)]) -def test_tensor_product_with_identity_quantities(index, size): - """ Check the correctness of the tensor product by multiplying by the identity (quantity, quantity)""" - np.random.seed(rng_seed) - - x = NamedQuantity("x", np.random.rand(*tensor_product_with_identity_sizes), units=units.meters) - y = NamedQuantity("y", np.eye(size), units.seconds) - - z = tensordot(x, y, index, 0) - - # Check units - assert z.units == units.meters * units.seconds - - # Expected sizes - last index gets moved to end - output_order = [i for i in (0, 1, 2) if i != index] + [index] - output_sizes = [tensor_product_with_identity_sizes[i] for i in output_order] - - assert z.value.shape == tuple(output_sizes) - - # Restore original order and check - reverse_order = [-1, -1, -1] - for to_index, from_index in enumerate(output_order): - reverse_order[from_index] = to_index - - z_reordered = transpose(z, axes = tuple(reverse_order)) - - assert z_reordered.value.shape == tensor_product_with_identity_sizes - - # Check values - - mat_in = x.in_si() - mat_out = transpose(z, axes=tuple(reverse_order)).in_si() - - assert np.all(np.abs(mat_in - mat_out) < 1e-10) - - -@pytest.mark.parametrize("index, size", [tup for tup in enumerate(tensor_product_with_identity_sizes)]) -def test_tensor_product_with_identity_quantity_matrix(index, size): - """ Check the correctness of the tensor product by multiplying by the identity (quantity, matrix)""" - np.random.seed(rng_seed) - - x = NamedQuantity("x", np.random.rand(*tensor_product_with_identity_sizes), units.meters) - y = np.eye(size) - - z = tensordot(x, y, index, 0) - - assert z.units == units.meters - - # Expected sizes - last index gets moved to end - output_order = [i for i in (0, 1, 2) if i != index] + [index] - output_sizes = [tensor_product_with_identity_sizes[i] for i in output_order] - - assert z.value.shape == tuple(output_sizes) - - # Restore original order and check - reverse_order = [-1, -1, -1] - for to_index, from_index in enumerate(output_order): - reverse_order[from_index] = to_index - - z_reordered = transpose(z, axes = tuple(reverse_order)) - - assert z_reordered.value.shape == tensor_product_with_identity_sizes - - # Check values - - mat_in = x.in_si() - mat_out = transpose(z, axes=tuple(reverse_order)).in_si() - - assert np.all(np.abs(mat_in - mat_out) < 1e-10) - - -@pytest.mark.parametrize("index, size", [tup for tup in enumerate(tensor_product_with_identity_sizes)]) -def test_tensor_product_with_identity_matrix_quantity(index, size): - """ Check the correctness of the tensor product by multiplying by the identity (matrix, quantity)""" - np.random.seed(rng_seed) - - x = np.random.rand(*tensor_product_with_identity_sizes) - y = NamedQuantity("y", np.eye(size), units.seconds) - - z = tensordot(x, y, index, 0) - - assert z.units == units.seconds - - - # Expected sizes - last index gets moved to end - output_order = [i for i in (0, 1, 2) if i != index] + [index] - output_sizes = [tensor_product_with_identity_sizes[i] for i in output_order] - - assert z.value.shape == tuple(output_sizes) - - # Restore original order and check - reverse_order = [-1, -1, -1] - for to_index, from_index in enumerate(output_order): - reverse_order[from_index] = to_index - - z_reordered = transpose(z, axes = tuple(reverse_order)) - - assert z_reordered.value.shape == tensor_product_with_identity_sizes - - # Check values - - mat_in = x - mat_out = transpose(z, axes=tuple(reverse_order)).in_si() - - assert np.all(np.abs(mat_in - mat_out) < 1e-10) diff --git a/sasdata/quantities/numerical_encoding.py b/sasdata/quantities/numerical_encoding.py index 66b24b8ff..0a5b0ae13 100644 --- a/sasdata/quantities/numerical_encoding.py +++ b/sasdata/quantities/numerical_encoding.py @@ -15,12 +15,12 @@ def numerical_encode(obj: int | float | np.ndarray | coo_matrix | coo_array | cs elif isinstance(obj, float): return {"type": "float", - "value": base64.b64encode(bytearray(struct.pack('d', obj)))} + "value": base64.b64encode(bytearray(struct.pack('d', obj))).decode("utf-8")} elif isinstance(obj, np.ndarray): return { "type": "numpy", - "value": base64.b64encode(obj.tobytes()), + "value": base64.b64encode(obj.tobytes()).decode("utf-8"), "dtype": obj.dtype.str, "shape": list(obj.shape) } diff --git a/sasdata/quantities/operations_test.py b/sasdata/quantities/operations_test.py deleted file mode 100644 index 0899eee7f..000000000 --- a/sasdata/quantities/operations_test.py +++ /dev/null @@ -1,68 +0,0 @@ -import pytest - -from sasdata.quantities.operations import Operation, \ - Neg, Inv, \ - Add, Sub, Mul, Div, Pow, \ - Variable, Constant, AdditiveIdentity, MultiplicativeIdentity - -operation_with_everything = \ - Div( - Pow( - Mul( - Sub( - Add( - Neg(Inv(MultiplicativeIdentity())), - Variable("x")), - Constant(7)), - AdditiveIdentity()), - 2), - Variable("y")) - -def test_serialise_deserialise(): - print(operation_with_everything._serialise_json()) - - serialised = operation_with_everything.serialise() - deserialised = Operation.deserialise(serialised) - reserialised = deserialised.serialise() - - assert serialised == reserialised - - -@pytest.mark.parametrize("op, a, b, result", [ - (Add, 1, 1, 2), - (Add, 7, 8, 15), - (Sub, 1, 1, 0), - (Sub, 7, 8, -1), - (Mul, 1, 1, 1), - (Mul, 7, 8, 56), - (Div, 1, 1, 1), - (Div, 7, 8, 7/8), - (Pow, 1, 1, 1), - (Pow, 7, 2, 49)]) -def test_binary_evaluation(op, a, b, result): - f = op(Constant(a), b if op == Pow else Constant(b)) - assert f.evaluate({}) == result - -x = Variable("x") -y = Variable("y") -z = Variable("z") -@pytest.mark.parametrize("x_over_x", [ - Div(x,x), - Mul(Inv(x), x), - Mul(x, Inv(x)), -]) -def test_dx_over_x_by_dx_should_be_zero(x_over_x): - - - dfdx = x_over_x.derivative(x) - - print(dfdx.summary()) - - assert dfdx == AdditiveIdentity() - - -def test_d_xyz_by_components_should_be_1(): - f = Mul(Mul(x, y), z) - assert f.derivative(x).derivative(y).derivative(z) == MultiplicativeIdentity() - - diff --git a/sasdata/quantities/quantities_tests.py b/sasdata/quantities/quantities_tests.py deleted file mode 100644 index 8ab0a41ce..000000000 --- a/sasdata/quantities/quantities_tests.py +++ /dev/null @@ -1,124 +0,0 @@ -import numpy as np - -from sasdata.quantities.quantity import Quantity, UnitError -import sasdata.quantities.units as units -import sasdata.quantities.si as si -import pytest -def test_in_units_of_calculation(): - """ Just a couple of unit conversions """ - assert Quantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 - assert Quantity(10, units.minutes).in_units_of(units.seconds) == 600 - assert Quantity(7, units.kilonewtons).in_units_of(units.kg_force) == pytest.approx(7000 / 9.81, abs=1) - assert Quantity(0, units.meters).in_units_of(units.exameters) == 0 - - -def test_unit_compounding_pow(): - """ Test units compound correctly when __pow__ is used""" - assert (Quantity(1, units.millimeters) ** 2).in_units_of(units.square_meters) == 1e-6 - assert (Quantity(1, units.minutes) ** 3).in_units_of(units.seconds ** 3) == 60 ** 3 - -def test_pow_scaling(): - q2 = Quantity(1000, units.millimeters)**2 - assert q2.units.scale == 1e-6 - assert q2.value == 1e6 - - -def test_unit_compounding_mul(): - """ Test units compound correctly when __mul__ is used""" - assert (Quantity(4, units.minutes) * Quantity(0.25, units.hertz)).in_units_of(units.none) == 60 - assert (Quantity(250, units.volts) * Quantity(8, units.amperes)).in_units_of(units.kilowatts) == 2 - -def test_unit_compounding_div(): - """ Test units compound correctly when __truediv__ is used""" - assert (Quantity(10, units.kilometers) / Quantity(2, units.minutes) - ).in_units_of(units.meters_per_second) == pytest.approx(250/3, abs=1e-6) - - assert (Quantity(1, units.nanowebers) / (Quantity(1, units.millimeters) ** 2)).in_units_of(units.millitesla) == 1 - -def test_value_mul(): - """ Test value part of quantities multiply correctly""" - assert (Quantity(1j, units.seconds) * Quantity(1j, units.watts)).in_units_of(units.joules) == -1 - -def test_scalar_mul(): - assert (Quantity(1, units.seconds) * 10).in_units_of(units.seconds) == 10 - assert (10 * Quantity(1, units.seconds)).in_units_of(units.seconds) == 10 - assert (1000 * Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1 - -def test_scalar_div(): - - assert (Quantity(1, units.seconds) / 10).in_units_of(units.seconds) == 0.1 - assert (10 / Quantity(1, units.seconds)).in_units_of(units.hertz) == 10 - assert (0.001 / Quantity(1, units.milliseconds)).in_units_of(units.hertz) == 1 - -def test_good_add_sub(): - """ Test that adding and subtracting units works """ - assert (Quantity(1, units.seconds) + Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 1.001 - assert (Quantity(1, units.seconds) - Quantity(1, units.milliseconds)).in_units_of(units.seconds) == 0.999 - - assert (Quantity(1, units.inches) + Quantity(1, units.feet)).in_units_of(units.inches) == pytest.approx(13, abs=1e-8) - -@pytest.mark.parametrize("unit_in, power, unit_out", [ - (units.meters**2, 1/2, units.meters), - (units.meters**3, 1/3, units.meters), - (units.meters**3, 2/3, units.meters**2), - (units.meters**3, -5/3, units.meters**-5), - (units.none, 1/10, units.none), - (units.none, 19/17, units.none), - (units.none, np.pi, units.none) -]) -def test_good_non_integer_unit_powers(unit_in, power, unit_out): - """ Check that we can do various square and cube root stuff if we need to, - If dimensionless, we should be able to do arbitrary powers - """ - assert unit_in**power == unit_out - -@pytest.mark.parametrize("unit, power", [ - (units.meters, 1/2), - (units.milliohms, 1/3), - (units.meters, 3/2), - (units.meters**2, 2/3) -]) -def test_bad_non_integer_unit_powers(unit, power): - """ Check that we get an error if we try and do something silly with powers""" - with pytest.raises(units.DimensionError): - x = unit**power - - -@pytest.mark.parametrize("unit_1", si.all_si) -@pytest.mark.parametrize("unit_2", si.all_si) -def test_mixed_quantity_add_sub(unit_1, unit_2): - if unit_1.equivalent(unit_2): - assert (Quantity(0, unit_1) + Quantity(0, unit_2)).in_units_of(unit_1) == 0 - - else: - with pytest.raises(UnitError): - Quantity(1, unit_1) + Quantity(1, unit_2) - -def assert_unit_ratio(u1: units.Unit, u2: units.Unit, value: float, abs=1e-9): - """ Helper function for testing units that are multiples of each other """ - - assert u1.equivalent(u2), "Units should be compatible for this test" - assert (Quantity(1, u1) / Quantity(1, u2)).in_units_of(units.none) == pytest.approx(value, abs=abs) - - -def test_american_units(): - assert_unit_ratio(units.feet, units.inches, 12) - assert_unit_ratio(units.yards, units.inches, 36) - assert_unit_ratio(units.miles, units.inches, 63360) - assert_unit_ratio(units.pounds_force_per_square_inch, units.pounds_force / (units.inches**2), 1, abs=1e-5) - -def test_percent(): - assert Quantity(5, units.percent).in_units_of(units.none) == pytest.approx(0.05, 1e-10) - -@pytest.mark.parametrize("unit_1", si.all_si) -@pytest.mark.parametrize("unit_2", si.all_si) -def test_conversion_errors(unit_1, unit_2): - """ Test conversion errors are thrown when units are not compatible """ - - if unit_1 == unit_2: - assert Quantity(1, unit_1).in_units_of(unit_2) == 1 - - else: - with pytest.raises(UnitError): - Quantity(1, units.seconds).in_units_of(units.meters) - diff --git a/sasdata/quantities/quantity_error_tests.py b/sasdata/quantities/quantity_error_tests.py deleted file mode 100644 index f03ef59c1..000000000 --- a/sasdata/quantities/quantity_error_tests.py +++ /dev/null @@ -1,153 +0,0 @@ -from sasdata.quantities import units -from sasdata.quantities.quantity import NamedQuantity -import pytest -import numpy as np - -@pytest.mark.parametrize("x_err, y_err, x_units, y_units", - [(1, 1, units.meters, units.meters), - (1, 1, units.centimeters, units.centimeters), - (1, 2, units.meters, units.millimeters)]) -def test_addition_propagation(x_err, y_err, x_units, y_units): - """ Test that errors in addition of independent variables works with different units in the mix""" - - expected_err = np.sqrt((x_err*x_units.scale)**2 + (y_err*y_units.scale)**2) - - x = NamedQuantity("x", 0, x_units, standard_error=x_err) - y = NamedQuantity("y", 0, y_units, standard_error=y_err) - - _, err = (x + y).in_si_with_standard_error() - - assert err == pytest.approx(expected_err, abs=1e-8) -@pytest.mark.parametrize("x_val, y_val, x_units, y_units", - [(1, 1, units.meters, units.meters), - (1, 1, units.centimeters, units.centimeters), - (2, 2, units.meters, units.meters), - (1, 2, units.centimeters, units.centimeters), - (1, 2, units.meters, units.millimeters), - (3, 4, units.milliseconds, units.microseconds), - (0, 1, units.meters, units.meters)]) -def test_asymmetry_propagation(x_val, y_val, x_units, y_units): - - x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(x_val)) - y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(y_val)) - - x_si, x_err = x.in_si_with_standard_error() - y_si, y_err = y.in_si_with_standard_error() - - numerator = x-y - denominator = x+y - a = numerator/denominator - - # Check numerator and denominator - expected_error = np.sqrt(x_err ** 2 + y_err ** 2) - - value, error = numerator.in_si_with_standard_error() - assert error == pytest.approx(expected_error, rel=1e-6) - - value, error = denominator.in_si_with_standard_error() - assert error == pytest.approx(expected_error, rel=1e-6) - - # check whole thing - value, error = a.in_si_with_standard_error() - expected_error = (2 / (x_si + y_si)**2) * np.sqrt((x_err*y_si)**2 + (y_err*x_si)**2) - assert error == pytest.approx(expected_error, rel=1e-6) - -@pytest.mark.parametrize("x_val, y_val, x_units, y_units", - [(1, 1, units.meters, units.meters), - (1, 1, units.centimeters, units.centimeters), - (2, 2, units.meters, units.meters), - (1, 2, units.centimeters, units.centimeters), - (1, 2, units.meters, units.millimeters), - (3, 4, units.milliseconds, units.microseconds), - (0, 1, units.meters, units.meters)]) -def test_power_propagation(x_val, y_val, x_units, y_units): - - x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(x_val)) - y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(y_val)) - - x_si, x_err = x.in_si_with_standard_error() - y_si, y_err = y.in_si_with_standard_error() - - x_var = x_err ** 2 - y_var = y_err ** 2 - - z = (x*y)**3 - - # check whole thing - value, error = z.in_si_with_standard_error() - expected_variance = 9*((x_si*y_si)**4)*(x_var*y_si*y_si + x_si*x_si*y_var) - assert error == pytest.approx(np.sqrt(expected_variance), rel=1e-6) - -@pytest.mark.parametrize("k", [0.1, 0.5, 1, 2, 10]) -@pytest.mark.parametrize("x_val, y_val, x_units, y_units", - [(1, 1, units.meters, units.meters), - (1, 1, units.centimeters, units.centimeters), - (2, 2, units.meters, units.meters), - (1, 2, units.centimeters, units.centimeters), - (1, 2, units.meters, units.millimeters), - (3, 4, units.milliseconds, units.microseconds), - (0, 1, units.meters, units.meters), - (0, 0, units.meters, units.meters)]) -def test_complex_power_propagation(x_val, y_val, x_units, y_units, k): - - x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(k*x_val)) - y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(k*y_val)) - - x_si, x_err = x.in_si_with_standard_error() - y_si, y_err = y.in_si_with_standard_error() - - x_var = x_err ** 2 - y_var = y_err ** 2 - - z = (x+y)**3 + x**3 + y**3 - - value, error = z.in_si_with_standard_error() - expected_variance = \ - 9*x_var*(x_si**2 + (x_si+y_si)**2)**2 + \ - 9*y_var*(y_si**2 + (x_si+y_si)**2)**2 - - assert error == pytest.approx(np.sqrt(expected_variance), rel=1e-6) - -@pytest.mark.parametrize("k_x", [0.1, 0.5, 1, 2, 10]) -@pytest.mark.parametrize("k_y", [0.1, 0.5, 1, 2, 10]) -@pytest.mark.parametrize("x_val, y_val, x_units, y_units", - [(1, 1, units.meters, units.meters), - (1, 1, units.centimeters, units.centimeters), - (2, 2, units.meters, units.meters), - (1, 2, units.centimeters, units.centimeters), - (1, 2, units.meters, units.millimeters), - (3, 4, units.milliseconds, units.microseconds), - (0, 1, units.meters, units.meters), - (0, 0, units.meters, units.meters)]) -def test_complex_propagation(x_val, y_val, x_units, y_units, k_x, k_y): - - x = NamedQuantity("x", x_val, x_units, standard_error=np.sqrt(k_x*x_val)) - y = NamedQuantity("y", y_val, y_units, standard_error=np.sqrt(k_y*y_val)) - - cx = NamedQuantity("cx", 1.7, x_units) - cy = NamedQuantity("cy", 1.2, y_units) - c0 = 4*NamedQuantity("c0", value=7, units=units.none) - - cx_si = cx.in_si() - cy_si = cy.in_si() - - c0_si = c0.in_si() - - x_si, x_err = x.in_si_with_standard_error() - y_si, y_err = y.in_si_with_standard_error() - - x_var = x_err ** 2 - y_var = y_err ** 2 - - z = (((x-cx)**4 + (y-cy)**4)**(1/4)) + c0*(-x-y) - - value, error = z.in_si_with_standard_error() - - denom_factor = ((x_si - cx_si)**4 + (y_si - cy_si)**4)**(-3/4) - x_num = (cx_si - x_si)**3 - y_num = (cy_si - y_si)**3 - - expected_variance = x_var*(c0_si + x_num*denom_factor)**2 + y_var*(c0_si + y_num*denom_factor)**2 - - assert error == pytest.approx(np.sqrt(expected_variance), rel=1e-8) - diff --git a/sasdata/quantities/units_tests.py b/sasdata/quantities/units_tests.py deleted file mode 100644 index 9fea2a6b8..000000000 --- a/sasdata/quantities/units_tests.py +++ /dev/null @@ -1,46 +0,0 @@ -import sasdata.quantities.units as units -from sasdata.quantities.units import Unit - -class EqualUnits: - def __init__(self, test_name: str, *units): - self.test_name = "Equality: " + test_name - self.units: list[Unit] = list(units) - - def run_test(self): - for i, unit_1 in enumerate(self.units): - for unit_2 in self.units[i+1:]: - assert unit_1.equivalent(unit_2), "Units should be equivalent" - assert unit_1 == unit_2, "Units should be equal" - - -class EquivalentButUnequalUnits: - def __init__(self, test_name: str, *units): - self.test_name = "Equivalence: " + test_name - self.units: list[Unit] = list(units) - - def run_test(self): - for i, unit_1 in enumerate(self.units): - for unit_2 in self.units[i+1:]: - assert unit_1.equivalent(unit_2), "Units should be equivalent" - assert unit_1 != unit_2, "Units should not be equal" - - -tests = [ - - EqualUnits("Pressure", - units.pascals, - units.newtons / units.meters ** 2, - units.micronewtons * units.millimeters ** -2), - - EqualUnits("Resistance", - units.ohms, - units.volts / units.amperes, - 1e-3/units.millisiemens) - - -] - - -for test in tests: - print(test.test_name) - test.run_test() diff --git a/test/quantities/utest_math_operations.py b/test/quantities/utest_math_operations.py index 85e9ba1d6..5bda5a2cd 100644 --- a/test/quantities/utest_math_operations.py +++ b/test/quantities/utest_math_operations.py @@ -1,10 +1,10 @@ """ Tests for math operations """ -import numpy as np import pytest -from sasdata.quantities import units +import numpy as np from sasdata.quantities.quantity import NamedQuantity, tensordot, transpose +from sasdata.quantities import units order_list = [ [0, 1, 2, 3], @@ -30,7 +30,7 @@ def test_transpose_raw(order: list[int]): @pytest.mark.parametrize("order", order_list) -def test_transpose_raw_with_quantity(order: list[int]): +def test_transpose_raw(order: list[int]): """ Check that the transpose operation changes the order of indices correctly - uses sizes as way of tracking""" input_shape = tuple([i + 1 for i in range(len(order))]) expected_shape = tuple([i + 1 for i in order]) diff --git a/test/quantities/utest_operations.py b/test/quantities/utest_operations.py index 5b64eab0e..a2158f0f8 100644 --- a/test/quantities/utest_operations.py +++ b/test/quantities/utest_operations.py @@ -3,32 +3,10 @@ import numpy as np import pytest -from sasdata.quantities.quantity import ( - Add, - AdditiveIdentity, - ArcCos, - ArcSin, - ArcTan, - Constant, - Cos, - Div, - Dot, - Exp, - Inv, - Ln, - Log, - MatMul, - Mul, - MultiplicativeIdentity, - Neg, - Operation, - Pow, - Sin, - Sub, - Tan, - Transpose, - Variable, -) +from sasdata.quantities.quantity import Operation, \ + Neg, Inv, \ + Add, Sub, Mul, Div, Pow, \ + Variable, Constant, AdditiveIdentity, MultiplicativeIdentity x = Variable("x") y = Variable("y") diff --git a/test/quantities/utest_quantities.py b/test/quantities/utest_quantities.py index 7edfd14df..596360e7c 100644 --- a/test/quantities/utest_quantities.py +++ b/test/quantities/utest_quantities.py @@ -1,11 +1,9 @@ import numpy as np -import pytest -import sasdata.quantities.si as si -import sasdata.quantities.units as units from sasdata.quantities.quantity import Quantity, UnitError - - +import sasdata.quantities.units as units +import sasdata.quantities.si as si +import pytest def test_in_units_of_calculation(): """ Just a couple of unit conversions """ assert Quantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 @@ -83,7 +81,7 @@ def test_good_non_integer_unit_powers(unit_in, power, unit_out): def test_bad_non_integer_unit_powers(unit, power): """ Check that we get an error if we try and do something silly with powers""" with pytest.raises(units.DimensionError): - unit**power + x = unit**power @pytest.mark.parametrize("unit_1", si.all_si) @@ -131,11 +129,3 @@ def test_equality(): assert Quantity(1.0, units.angstroms) == Quantity(0.1, units.nanometers) assert Quantity(1.0, units.angstroms) != Quantity(0.1, units.angstroms) assert Quantity(1.0, units.angstroms) == Quantity(1.0e-10, units.meters) - -@pytest.mark.quantity -def test_explicit_format(): - value = Quantity(1.0, units.electronvolts) - assert value.explicitly_formatted("J") == "1.602176634e-19 J" - assert value.explicitly_formatted("N m") == "1.602176634e-19 N m" - assert value.explicitly_formatted("m N") == "1.602176634e-19 m N" - assert value.explicitly_formatted("m kilogram m / hour / year") == "1.8201532008477443e-08 m kilogram m / hour / year" diff --git a/test/quantities/utest_quantity_error.py b/test/quantities/utest_quantity_error.py index ce8be4cee..f03ef59c1 100644 --- a/test/quantities/utest_quantity_error.py +++ b/test/quantities/utest_quantity_error.py @@ -1,9 +1,7 @@ -import numpy as np -import pytest - from sasdata.quantities import units from sasdata.quantities.quantity import NamedQuantity - +import pytest +import numpy as np @pytest.mark.parametrize("x_err, y_err, x_units, y_units", [(1, 1, units.meters, units.meters), @@ -20,7 +18,6 @@ def test_addition_propagation(x_err, y_err, x_units, y_units): _, err = (x + y).in_si_with_standard_error() assert err == pytest.approx(expected_err, abs=1e-8) - @pytest.mark.parametrize("x_val, y_val, x_units, y_units", [(1, 1, units.meters, units.meters), (1, 1, units.centimeters, units.centimeters), diff --git a/test/quantities/utest_units.py b/test/quantities/utest_units.py index 3bc775313..9fea2a6b8 100644 --- a/test/quantities/utest_units.py +++ b/test/quantities/utest_units.py @@ -1,9 +1,6 @@ -import math - import sasdata.quantities.units as units from sasdata.quantities.units import Unit - class EqualUnits: def __init__(self, test_name: str, *units): self.test_name = "Equality: " + test_name @@ -11,7 +8,7 @@ def __init__(self, test_name: str, *units): def run_test(self): for i, unit_1 in enumerate(self.units): - for unit_2 in self.units[i + 1 :]: + for unit_2 in self.units[i+1:]: assert unit_1.equivalent(unit_2), "Units should be equivalent" assert unit_1 == unit_2, "Units should be equal" @@ -23,22 +20,11 @@ def __init__(self, test_name: str, *units): def run_test(self): for i, unit_1 in enumerate(self.units): - for unit_2 in self.units[i + 1 :]: + for unit_2 in self.units[i+1:]: assert unit_1.equivalent(unit_2), "Units should be equivalent" assert unit_1 != unit_2, "Units should not be equal" -class DissimilarUnits: - def __init__(self, test_name: str, *units): - self.test_name = "Dissimilar: " + test_name - self.units: list[Unit] = list(units) - - def run_test(self): - for i, unit_1 in enumerate(self.units): - for unit_2 in self.units[i + 1 :]: - assert not unit_1.equivalent(unit_2), "Units should not be equivalent" - - tests = [ EqualUnits("Pressure", @@ -49,19 +35,7 @@ def run_test(self): EqualUnits("Resistance", units.ohms, units.volts / units.amperes, - 1e-3/units.millisiemens), - - EquivalentButUnequalUnits("Angular frequency", - units.rotations / units.minutes, - units.degrees * units.hertz), - - EqualUnits("Angular frequency", - (units.rotations/units.minutes ), - (units.radians*units.hertz) * 2 * math.pi/60.0), - - DissimilarUnits("Frequency and Angular frequency", - (units.rotations/units.minutes), - (units.hertz)), + 1e-3/units.millisiemens) ] From 1c4aaccfad168c993deab1ac61c6c8162b911672 Mon Sep 17 00:00:00 2001 From: PaulSharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Thu, 17 Jul 2025 10:47:07 +0100 Subject: [PATCH 674/675] Applies auto fixes for ruff rule F401 --- sasdata/data.py | 7 ++----- .../dataloader/readers/cansas_reader_HDF5.py | 17 +++++------------ sasdata/dataloader/readers/danse_reader.py | 2 +- sasdata/quantities/_accessor_base.py | 7 +++---- sasdata/quantities/_build_tables.py | 2 +- sasdata/quantities/_units_base.py | 2 +- sasdata/quantities/accessors.py | 7 +++---- sasdata/quantities/quantity_examples.py | 2 +- sasdata/quantities/units.py | 2 +- sasdata/slicing/transforms.py | 4 ++-- sasdata/temp_hdf5_reader.py | 1 - sasdata/transforms/rebinning.py | 2 -- test/sasdataloader/utest_sasdataload.py | 15 --------------- test/slicers/utest_point_assignment.py | 4 +--- test/utest_sasdata.py | 7 +++++++ test/utest_trend.py | 4 +--- 16 files changed, 29 insertions(+), 56 deletions(-) diff --git a/sasdata/data.py b/sasdata/data.py index 9b15cb3b2..df91b05c5 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,11 +1,8 @@ -from enum import Enum -from typing import TypeVar, Any, Self -from dataclasses import dataclass + + from sasdata.quantities.quantity import NamedQuantity from sasdata.metadata import Metadata -from sasdata.quantities.accessors import AccessorTarget -from sasdata.data_backing import Group, key_tree class SasData: diff --git a/sasdata/dataloader/readers/cansas_reader_HDF5.py b/sasdata/dataloader/readers/cansas_reader_HDF5.py index 0f588bce1..586d8f479 100644 --- a/sasdata/dataloader/readers/cansas_reader_HDF5.py +++ b/sasdata/dataloader/readers/cansas_reader_HDF5.py @@ -9,20 +9,13 @@ import h5py import numpy as np +import re +import traceback +from typing import Any, Union, Optional +from sasdata.dataloader.data_info import plottable_1D, plottable_2D, Data1D, Data2D, DataInfo, Process, Aperture,\ + Collimation, TransmissionSpectrum, Detector from sasdata.data_util.loader_exceptions import FileContentsException -from sasdata.dataloader.data_info import ( - Aperture, - Collimation, - Data1D, - Data2D, - DataInfo, - Detector, - Process, - TransmissionSpectrum, - plottable_1D, - plottable_2D, -) from sasdata.dataloader.filereader import FileReader, decode logger = logging.getLogger(__name__) diff --git a/sasdata/dataloader/readers/danse_reader.py b/sasdata/dataloader/readers/danse_reader.py index d1851c77d..4f6fcf782 100644 --- a/sasdata/dataloader/readers/danse_reader.py +++ b/sasdata/dataloader/readers/danse_reader.py @@ -11,7 +11,7 @@ #This work benefited from DANSE software developed under NSF award DMR-0520547. #copyright 2008, University of Tennessee ############################################################################# -import logging +import os import os import numpy as np diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index b56ecce68..22f6aadd6 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -1,13 +1,12 @@ -from typing import TypeVar, Sequence +from typing import TypeVar from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units -from sasdata.quantities.units import Dimensions, Unit -from sasdata.quantities.unit_parser import parse_unit, parse_unit_from_group +from sasdata.quantities.units import Unit +from sasdata.quantities.unit_parser import parse_unit from sasdata.data_backing import Group, Dataset -import logging # logger = logging.getLogger("Accessors") class LoggerDummy: def info(self, data): diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 546720d29..8250c7640 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -4,7 +4,7 @@ import numpy as np from collections import defaultdict, namedtuple -from _units_base import Dimensions, Unit +from _units_base import Dimensions from _autogen_warning import warning_text Magnitude = namedtuple("Magnitude", ["symbol", "special_symbol", "latex_symbol", "name", "scale"]) diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 518c3d4e3..89423b8db 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Sequence, Self, TypeVar +from typing import Sequence, Self from fractions import Fraction import numpy as np diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 2f1f2514a..38a8fcb88 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -78,16 +78,15 @@ """ -from typing import TypeVar, Sequence +from typing import TypeVar from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units -from sasdata.quantities.units import Dimensions, Unit -from sasdata.quantities.unit_parser import parse_unit, parse_unit_from_group +from sasdata.quantities.units import Unit +from sasdata.quantities.unit_parser import parse_unit from sasdata.data_backing import Group, Dataset -import logging # logger = logging.getLogger("Accessors") class LoggerDummy: def info(self, data): diff --git a/sasdata/quantities/quantity_examples.py b/sasdata/quantities/quantity_examples.py index 8c505e59e..3675d62fa 100644 --- a/sasdata/quantities/quantity_examples.py +++ b/sasdata/quantities/quantity_examples.py @@ -1,4 +1,4 @@ -from sasdata.quantities.quantity import Quantity, NamedQuantity +from sasdata.quantities.quantity import NamedQuantity from sasdata.quantities import units x = NamedQuantity("x", 1, units.meters, standard_error=1) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index bd35dc36e..2366f4c44 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -83,7 +83,7 @@ # from dataclasses import dataclass -from typing import Sequence, Self, TypeVar +from typing import Sequence, Self from fractions import Fraction import numpy as np diff --git a/sasdata/slicing/transforms.py b/sasdata/slicing/transforms.py index 8c9b2c69c..323a7a52f 100644 --- a/sasdata/slicing/transforms.py +++ b/sasdata/slicing/transforms.py @@ -1,5 +1,5 @@ import numpy as np -from scipy.spatial import Voronoi, Delaunay +from scipy.spatial import Voronoi import matplotlib.pyplot as plt from matplotlib import cm @@ -55,4 +55,4 @@ def get_data_mesh(x, y, data): plt.show() -get_data_mesh(qx.reshape(-1), qy.reshape(-1), data) +get_data_mesh(qx.reshape(-1), qy.reshape(-1), data) \ No newline at end of file diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index fad06ba7d..1ffaa3149 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -1,4 +1,3 @@ -import os import h5py diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index fe96bcb72..20e5294fc 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -1,9 +1,7 @@ """ Algorithms for interpolation and rebinning """ -from typing import TypeVar import numpy as np from numpy._typing import ArrayLike -from scipy.interpolate import interp1d from sasdata.quantities.quantity import Quantity from scipy.sparse import coo_matrix diff --git a/test/sasdataloader/utest_sasdataload.py b/test/sasdataloader/utest_sasdataload.py index fe8e87ceb..8488360a1 100644 --- a/test/sasdataloader/utest_sasdataload.py +++ b/test/sasdataloader/utest_sasdataload.py @@ -3,22 +3,7 @@ """ import os -import unittest import pytest -import logging -import warnings -from io import StringIO - -from lxml import etree -from lxml.etree import XMLSyntaxError -from xml.dom import minidom - -from sasdata.dataloader.filereader import decode -from sasdata.dataloader.loader import Loader -from sasdata.dataloader.data_info import Data1D, Data2D -from sasdata.dataloader.readers.xml_reader import XMLreader -from sasdata.dataloader.readers.cansas_reader import Reader -from sasdata.dataloader.readers.cansas_constants import CansasConstants from sasdata.temp_hdf5_reader import load_data as hdf_load_data from sasdata.temp_xml_reader import load_data as xml_load_data diff --git a/test/slicers/utest_point_assignment.py b/test/slicers/utest_point_assignment.py index 64ed24e42..ba785a503 100644 --- a/test/slicers/utest_point_assignment.py +++ b/test/slicers/utest_point_assignment.py @@ -1,7 +1,5 @@ -from test.slicers.meshes_for_testing import location_test_mesh, location_test_points_x, location_test_points_y - def test_location_assignment(): - pass + pass \ No newline at end of file diff --git a/test/utest_sasdata.py b/test/utest_sasdata.py index cebc748e2..391768382 100644 --- a/test/utest_sasdata.py +++ b/test/utest_sasdata.py @@ -9,6 +9,13 @@ logging.config.fileConfig(LOGGER_CONFIG_FILE) logger = logging.getLogger(__name__) +try: + pass +except: + logger.error("xmlrunner needs to be installed to run these tests") + logger.error("Try easy_install unittest-xml-reporting") + sys.exit(1) + # Check whether we have matplotlib installed HAS_MPL_WX = True try: diff --git a/test/utest_trend.py b/test/utest_trend.py index e86c480f1..eb86266ec 100644 --- a/test/utest_trend.py +++ b/test/utest_trend.py @@ -2,13 +2,11 @@ import pytest from os import path, listdir -from sasdata.ascii_reader_metadata import AsciiMetadataCategory, AsciiReaderMetadata +from sasdata.ascii_reader_metadata import AsciiMetadataCategory from sasdata.quantities.units import per_nanometer from sasdata.temp_ascii_reader import AsciiReaderParams import sasdata.temp_ascii_reader as ascii_reader -from sasdata.ascii_reader_metadata import AsciiMetadataCategory from sasdata.quantities.units import per_angstrom, per_nanometer -from sasdata.temp_ascii_reader import AsciiReaderParams from sasdata.trend import Trend test_directories = [ From b6c0eae3e71246a010c32e690404d7eac3d2af7a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2026 17:55:32 +0000 Subject: [PATCH 675/675] [pre-commit.ci lite] apply automatic fixes for ruff linting errors --- sasdata/ascii_reader_metadata.py | 3 +- sasdata/data.py | 4 +- sasdata/data_backing.py | 4 +- .../dataloader/readers/cansas_reader_HDF5.py | 17 +++-- sasdata/dataloader/readers/danse_reader.py | 1 - sasdata/fair_database/data/admin.py | 2 +- sasdata/fair_database/data/forms.py | 2 +- sasdata/fair_database/data/models.py | 2 +- sasdata/fair_database/data/serializers.py | 6 +- .../fair_database/data/test/test_datafile.py | 9 ++- .../fair_database/data/test/test_dataset.py | 5 +- .../data/test/test_operation_tree.py | 5 +- .../data/test/test_published_state.py | 3 +- .../fair_database/data/test/test_session.py | 5 +- sasdata/fair_database/data/views.py | 38 +++++------ .../fair_database/test_permissions.py | 3 +- .../fair_database/upload_example_data.py | 12 ++-- sasdata/fair_database/user_app/serializers.py | 3 +- sasdata/fair_database/user_app/tests.py | 3 +- sasdata/fair_database/user_app/urls.py | 3 +- sasdata/fair_database/user_app/views.py | 9 ++- sasdata/manual_tests/interpolation.py | 11 ++-- sasdata/metadata.py | 11 +++- sasdata/model_requirements.py | 4 +- sasdata/quantities/_accessor_base.py | 6 +- sasdata/quantities/_autogen_warning.py | 2 +- sasdata/quantities/_build_tables.py | 11 ++-- sasdata/quantities/_units_base.py | 5 +- sasdata/quantities/absolute_temperature.py | 3 +- sasdata/quantities/accessors.py | 64 +++++++++--------- sasdata/quantities/numerical_encoding.py | 6 +- sasdata/quantities/operations_examples.py | 2 +- sasdata/quantities/plotting.py | 2 +- sasdata/quantities/quantity.py | 16 ++--- sasdata/quantities/quantity_examples.py | 4 +- sasdata/quantities/si.py | 38 ++++++----- sasdata/quantities/unit_parser.py | 5 +- sasdata/quantities/units.py | 66 ++++++++++--------- sasdata/slicing/meshes/mesh.py | 6 +- sasdata/slicing/meshes/meshmerge.py | 9 +-- sasdata/slicing/meshes/util.py | 3 +- sasdata/slicing/rebinning.py | 9 ++- sasdata/slicing/slicer_demo.py | 7 +- sasdata/slicing/slicers/AnularSector.py | 3 +- sasdata/slicing/transforms.py | 7 +- sasdata/temp_ascii_reader.py | 4 +- sasdata/temp_hdf5_reader.py | 16 ++--- sasdata/temp_sesans_reader.py | 20 +++--- sasdata/transforms/rebinning.py | 7 +- sasdata/trend.py | 2 - sasdata/util.py | 5 +- test/quantities/utest_math_operations.py | 4 +- test/quantities/utest_operations.py | 18 +++-- test/quantities/utest_quantities.py | 8 ++- test/quantities/utest_quantity_error.py | 6 +- test/quantities/utest_units.py | 1 + test/sasdataloader/utest_new_sesans.py | 2 +- test/sasdataloader/utest_sasdataload.py | 2 + test/slicers/meshes_for_testing.py | 2 +- test/slicers/utest_meshmerge.py | 3 +- test/slicers/utest_point_assignment.py | 2 +- test/transforms/utest_interpolation.py | 10 +-- test/utest_new_sasdata.py | 7 +- test/utest_trend.py | 5 +- test/utest_unit_parser.py | 6 +- 65 files changed, 286 insertions(+), 283 deletions(-) diff --git a/sasdata/ascii_reader_metadata.py b/sasdata/ascii_reader_metadata.py index ca7a577c6..d507bbb3f 100644 --- a/sasdata/ascii_reader_metadata.py +++ b/sasdata/ascii_reader_metadata.py @@ -1,7 +1,6 @@ -import re from dataclasses import dataclass, field -from typing import TypeVar from re import split as re_split +from typing import TypeVar initial_metadata = { 'source': ['name', 'radiation', 'type', 'probe_particle', 'beam_size_name', 'beam_size', 'beam_shape', 'wavelength', 'wavelength_min', 'wavelength_max', 'wavelength_spread'], diff --git a/sasdata/data.py b/sasdata/data.py index df91b05c5..596d12097 100644 --- a/sasdata/data.py +++ b/sasdata/data.py @@ -1,8 +1,8 @@ -from sasdata.quantities.quantity import NamedQuantity from sasdata.metadata import Metadata +from sasdata.quantities.quantity import NamedQuantity class SasData: @@ -30,7 +30,7 @@ def summary(self, indent = " ", include_raw=False): for data in self._data_contents: s += f"{indent}{data}\n" - s += f"Metadata:\n" + s += "Metadata:\n" s += "\n" s += self.metadata.summary() diff --git a/sasdata/data_backing.py b/sasdata/data_backing.py index 564f466a6..210ac33ca 100644 --- a/sasdata/data_backing.py +++ b/sasdata/data_backing.py @@ -1,6 +1,6 @@ -from typing import TypeVar, Self from dataclasses import dataclass from enum import Enum +from typing import Self, TypeVar from sasdata.quantities.quantity import NamedQuantity @@ -123,4 +123,4 @@ def key_tree(data: Group | Dataset, indent_amount=0, indent: str = " ") -> str: s += indent*indent_amount + key + "\n" s += key_tree(data.attributes[key], indent_amount=indent_amount+1, indent=indent) - return s \ No newline at end of file + return s diff --git a/sasdata/dataloader/readers/cansas_reader_HDF5.py b/sasdata/dataloader/readers/cansas_reader_HDF5.py index 586d8f479..0f588bce1 100644 --- a/sasdata/dataloader/readers/cansas_reader_HDF5.py +++ b/sasdata/dataloader/readers/cansas_reader_HDF5.py @@ -9,13 +9,20 @@ import h5py import numpy as np -import re -import traceback -from typing import Any, Union, Optional -from sasdata.dataloader.data_info import plottable_1D, plottable_2D, Data1D, Data2D, DataInfo, Process, Aperture,\ - Collimation, TransmissionSpectrum, Detector from sasdata.data_util.loader_exceptions import FileContentsException +from sasdata.dataloader.data_info import ( + Aperture, + Collimation, + Data1D, + Data2D, + DataInfo, + Detector, + Process, + TransmissionSpectrum, + plottable_1D, + plottable_2D, +) from sasdata.dataloader.filereader import FileReader, decode logger = logging.getLogger(__name__) diff --git a/sasdata/dataloader/readers/danse_reader.py b/sasdata/dataloader/readers/danse_reader.py index 4f6fcf782..10a1c515c 100644 --- a/sasdata/dataloader/readers/danse_reader.py +++ b/sasdata/dataloader/readers/danse_reader.py @@ -12,7 +12,6 @@ #copyright 2008, University of Tennessee ############################################################################# import os -import os import numpy as np diff --git a/sasdata/fair_database/data/admin.py b/sasdata/fair_database/data/admin.py index 1a5e431cc..2134875a6 100644 --- a/sasdata/fair_database/data/admin.py +++ b/sasdata/fair_database/data/admin.py @@ -1,5 +1,5 @@ -from django.contrib import admin from data import models +from django.contrib import admin admin.site.register(models.DataFile) admin.site.register(models.Session) diff --git a/sasdata/fair_database/data/forms.py b/sasdata/fair_database/data/forms.py index fde1813df..519556e2f 100644 --- a/sasdata/fair_database/data/forms.py +++ b/sasdata/fair_database/data/forms.py @@ -1,5 +1,5 @@ -from django import forms from data.models import DataFile +from django import forms # Create the form class. diff --git a/sasdata/fair_database/data/models.py b/sasdata/fair_database/data/models.py index 09bae3c07..3a36dc831 100644 --- a/sasdata/fair_database/data/models.py +++ b/sasdata/fair_database/data/models.py @@ -1,6 +1,6 @@ -from django.db import models from django.contrib.auth.models import User from django.core.files.storage import FileSystemStorage +from django.db import models # method for empty list default value diff --git a/sasdata/fair_database/data/serializers.py b/sasdata/fair_database/data/serializers.py index 3eb712199..dfb7ece52 100644 --- a/sasdata/fair_database/data/serializers.py +++ b/sasdata/fair_database/data/serializers.py @@ -1,9 +1,7 @@ -from django.core.exceptions import ObjectDoesNotExist -from rest_framework import serializers - from data import models +from django.core.exceptions import ObjectDoesNotExist from fair_database import permissions - +from rest_framework import serializers # TODO: more custom validation, particularly for specific nested dictionary structures # TODO: custom update methods for nested structures diff --git a/sasdata/fair_database/data/test/test_datafile.py b/sasdata/fair_database/data/test/test_datafile.py index 9c9aae858..ac8dbb480 100644 --- a/sasdata/fair_database/data/test/test_datafile.py +++ b/sasdata/fair_database/data/test/test_datafile.py @@ -1,14 +1,13 @@ import os import shutil +from data.models import DataFile from django.conf import settings -from django.test import TestCase -from django.db.models import Max from django.contrib.auth.models import User -from rest_framework.test import APIClient, APITestCase +from django.db.models import Max +from django.test import TestCase from rest_framework import status - -from data.models import DataFile +from rest_framework.test import APIClient, APITestCase # path to a file in example_data/1d_data diff --git a/sasdata/fair_database/data/test/test_dataset.py b/sasdata/fair_database/data/test/test_dataset.py index 414ea72a8..aecfb7247 100644 --- a/sasdata/fair_database/data/test/test_dataset.py +++ b/sasdata/fair_database/data/test/test_dataset.py @@ -1,13 +1,12 @@ import os import shutil +from data.models import DataFile, DataSet, MetaData, OperationTree, Quantity from django.conf import settings from django.contrib.auth.models import User from django.db.models import Max -from rest_framework.test import APIClient, APITestCase from rest_framework import status - -from data.models import DataFile, DataSet, MetaData, OperationTree, Quantity +from rest_framework.test import APIClient, APITestCase # path to a file in example_data/1d_data diff --git a/sasdata/fair_database/data/test/test_operation_tree.py b/sasdata/fair_database/data/test/test_operation_tree.py index de3ee25ef..90a26d81b 100644 --- a/sasdata/fair_database/data/test/test_operation_tree.py +++ b/sasdata/fair_database/data/test/test_operation_tree.py @@ -1,9 +1,8 @@ +from data.models import DataSet, MetaData, OperationTree, Quantity, ReferenceQuantity from django.contrib.auth.models import User from django.db.models import Max -from rest_framework.test import APIClient, APITestCase from rest_framework import status - -from data.models import DataSet, MetaData, OperationTree, Quantity, ReferenceQuantity +from rest_framework.test import APIClient, APITestCase class TestCreateOperationTree(APITestCase): diff --git a/sasdata/fair_database/data/test/test_published_state.py b/sasdata/fair_database/data/test/test_published_state.py index d54f13121..20072f3b1 100644 --- a/sasdata/fair_database/data/test/test_published_state.py +++ b/sasdata/fair_database/data/test/test_published_state.py @@ -1,10 +1,9 @@ +from data.models import PublishedState, Session from django.contrib.auth.models import User from django.db.models import Max from rest_framework import status from rest_framework.test import APIClient, APITestCase -from data.models import PublishedState, Session - # TODO: account for non-placeholder doi # Get the placeholder DOI for a session based on id diff --git a/sasdata/fair_database/data/test/test_session.py b/sasdata/fair_database/data/test/test_session.py index 1f1f95a7b..fc185f8fd 100644 --- a/sasdata/fair_database/data/test/test_session.py +++ b/sasdata/fair_database/data/test/test_session.py @@ -1,9 +1,8 @@ +from data.models import DataSet, PublishedState, Session from django.contrib.auth.models import User from django.db.models import Max -from rest_framework.test import APIClient, APITestCase from rest_framework import status - -from data.models import DataSet, PublishedState, Session +from rest_framework.test import APIClient, APITestCase class TestSession(APITestCase): diff --git a/sasdata/fair_database/data/views.py b/sasdata/fair_database/data/views.py index 7399e6764..fc1c547c1 100644 --- a/sasdata/fair_database/data/views.py +++ b/sasdata/fair_database/data/views.py @@ -1,33 +1,33 @@ -import os import json +import os +from data.forms import DataFileForm +from data.models import DataFile, DataSet, PublishedState, Session +from data.serializers import ( + AccessManagementSerializer, + DataFileSerializer, + DataSetSerializer, + PublishedStateSerializer, + PublishedStateUpdateSerializer, + SessionSerializer, +) from django.contrib.auth.models import User -from django.shortcuts import get_object_or_404 from django.http import ( + FileResponse, + Http404, + HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, - HttpResponse, - Http404, - FileResponse, ) -from rest_framework.response import Response +from django.shortcuts import get_object_or_404 +from drf_spectacular.utils import extend_schema +from fair_database import permissions +from fair_database.permissions import DataPermission from rest_framework import status +from rest_framework.response import Response from rest_framework.views import APIView -from drf_spectacular.utils import extend_schema from sasdata.dataloader.loader import Loader -from data.serializers import ( - DataFileSerializer, - DataSetSerializer, - AccessManagementSerializer, - SessionSerializer, - PublishedStateSerializer, - PublishedStateUpdateSerializer, -) -from data.models import DataFile, DataSet, PublishedState, Session -from data.forms import DataFileForm -from fair_database import permissions -from fair_database.permissions import DataPermission class DataFileView(APIView): diff --git a/sasdata/fair_database/fair_database/test_permissions.py b/sasdata/fair_database/fair_database/test_permissions.py index df76d1a64..ffb55dbd7 100644 --- a/sasdata/fair_database/fair_database/test_permissions.py +++ b/sasdata/fair_database/fair_database/test_permissions.py @@ -1,13 +1,12 @@ import os import shutil +from data.models import DataFile from django.conf import settings from django.contrib.auth.models import User from rest_framework import status from rest_framework.test import APITestCase -from data.models import DataFile - def find(filename): return os.path.join( diff --git a/sasdata/fair_database/fair_database/upload_example_data.py b/sasdata/fair_database/fair_database/upload_example_data.py index 60e21af48..79de203d4 100644 --- a/sasdata/fair_database/fair_database/upload_example_data.py +++ b/sasdata/fair_database/fair_database/upload_example_data.py @@ -1,16 +1,16 @@ -import os import logging -import requests - +import os from glob import glob +import requests + EXAMPLE_DATA_DIR = os.environ.get("EXAMPLE_DATA_DIR", "../../example_data") def parse_1D(): dir_1d = os.path.join(EXAMPLE_DATA_DIR, "1d_data") if not os.path.isdir(dir_1d): - logging.error("1D Data directory not found at: {}".format(dir_1d)) + logging.error(f"1D Data directory not found at: {dir_1d}") return for file_path in glob(os.path.join(dir_1d, "*")): upload_file(file_path) @@ -19,7 +19,7 @@ def parse_1D(): def parse_2D(): dir_2d = os.path.join(EXAMPLE_DATA_DIR, "2d_data") if not os.path.isdir(dir_2d): - logging.error("2D Data directory not found at: {}".format(dir_2d)) + logging.error(f"2D Data directory not found at: {dir_2d}") return for file_path in glob(os.path.join(dir_2d, "*")): upload_file(file_path) @@ -28,7 +28,7 @@ def parse_2D(): def parse_sesans(): sesans_dir = os.path.join(EXAMPLE_DATA_DIR, "sesans_data") if not os.path.isdir(sesans_dir): - logging.error("Sesans Data directory not found at: {}".format(sesans_dir)) + logging.error(f"Sesans Data directory not found at: {sesans_dir}") return for file_path in glob(os.path.join(sesans_dir, "*")): upload_file(file_path) diff --git a/sasdata/fair_database/user_app/serializers.py b/sasdata/fair_database/user_app/serializers.py index c739fea2a..4993a7ab3 100644 --- a/sasdata/fair_database/user_app/serializers.py +++ b/sasdata/fair_database/user_app/serializers.py @@ -1,6 +1,5 @@ -from rest_framework import serializers - from dj_rest_auth.serializers import UserDetailsSerializer +from rest_framework import serializers class KnoxSerializer(serializers.Serializer): diff --git a/sasdata/fair_database/user_app/tests.py b/sasdata/fair_database/user_app/tests.py index 7d0bae94c..8943ab40e 100644 --- a/sasdata/fair_database/user_app/tests.py +++ b/sasdata/fair_database/user_app/tests.py @@ -1,9 +1,8 @@ +from django.contrib.auth.models import User from django.test import TestCase from rest_framework import status from rest_framework.test import APIClient -from django.contrib.auth.models import User - # Create your tests here. class AuthTests(TestCase): diff --git a/sasdata/fair_database/user_app/urls.py b/sasdata/fair_database/user_app/urls.py index cf44e8d6c..e393cb4b6 100644 --- a/sasdata/fair_database/user_app/urls.py +++ b/sasdata/fair_database/user_app/urls.py @@ -1,5 +1,6 @@ +from dj_rest_auth.views import LogoutView, PasswordChangeView, UserDetailsView from django.urls import path -from dj_rest_auth.views import LogoutView, UserDetailsView, PasswordChangeView + from .views import KnoxLoginView, KnoxRegisterView """Urls for authentication. Orcid login not functional. See settings.py for ORCID activation.""" diff --git a/sasdata/fair_database/user_app/views.py b/sasdata/fair_database/user_app/views.py index 32113a749..3a033add4 100644 --- a/sasdata/fair_database/user_app/views.py +++ b/sasdata/fair_database/user_app/views.py @@ -1,10 +1,9 @@ -from rest_framework.response import Response -from dj_rest_auth.views import LoginView -from dj_rest_auth.registration.views import RegisterView, SocialLoginView -from allauth.account.utils import complete_signup from allauth.account import app_settings as allauth_settings +from allauth.account.utils import complete_signup from allauth.socialaccount.providers.orcid.views import OrcidOAuth2Adapter - +from dj_rest_auth.registration.views import RegisterView, SocialLoginView +from dj_rest_auth.views import LoginView +from rest_framework.response import Response from user_app.serializers import KnoxSerializer from user_app.util import create_knox_token diff --git a/sasdata/manual_tests/interpolation.py b/sasdata/manual_tests/interpolation.py index c46078ba7..59d53815c 100644 --- a/sasdata/manual_tests/interpolation.py +++ b/sasdata/manual_tests/interpolation.py @@ -1,12 +1,11 @@ -import numpy as np import matplotlib.pyplot as plt +import numpy as np -from sasdata.quantities.quantity import NamedQuantity -from sasdata.quantities.plotting import quantity_plot from sasdata.quantities import units +from sasdata.quantities.plotting import quantity_plot +from sasdata.quantities.quantity import NamedQuantity +from sasdata.transforms.rebinning import InterpolationOptions, calculate_interpolation_matrix_1d -from sasdata.transforms.rebinning import calculate_interpolation_matrix_1d -from sasdata.transforms.rebinning import InterpolationOptions def linear_interpolation_check(): @@ -41,4 +40,4 @@ def linear_interpolation_check(): -linear_interpolation_check() \ No newline at end of file +linear_interpolation_check() diff --git a/sasdata/metadata.py b/sasdata/metadata.py index 145243c9a..da0ed9c7e 100644 --- a/sasdata/metadata.py +++ b/sasdata/metadata.py @@ -1,12 +1,17 @@ -from tokenize import String import numpy as np from numpy.typing import ArrayLike import sasdata.quantities.units as units from sasdata.quantities.absolute_temperature import AbsoluteTemperatureAccessor -from sasdata.quantities.accessors import StringAccessor, LengthAccessor, AngleAccessor, QuantityAccessor, \ - DimensionlessAccessor, FloatAccessor, TemperatureAccessor, AccessorTarget +from sasdata.quantities.accessors import ( + AccessorTarget, + AngleAccessor, + FloatAccessor, + LengthAccessor, + QuantityAccessor, + StringAccessor, +) class Detector: diff --git a/sasdata/model_requirements.py b/sasdata/model_requirements.py index 5d68ad1b4..262f662e8 100644 --- a/sasdata/model_requirements.py +++ b/sasdata/model_requirements.py @@ -1,9 +1,9 @@ from dataclasses import dataclass import numpy as np +from transforms.operation import Operation from sasdata.metadata import Metadata -from transforms.operation import Operation @dataclass @@ -20,4 +20,4 @@ def from_qi_transformation(self, data: np.ndarray, metadata: Metadata) -> np.nda def guess_requirements(abscissae, ordinate) -> ModellingRequirements: - """ Use names of axes and units to guess what kind of processing needs to be done """ \ No newline at end of file + """ Use names of axes and units to guess what kind of processing needs to be done """ diff --git a/sasdata/quantities/_accessor_base.py b/sasdata/quantities/_accessor_base.py index 22f6aadd6..09a1b6e01 100644 --- a/sasdata/quantities/_accessor_base.py +++ b/sasdata/quantities/_accessor_base.py @@ -1,11 +1,11 @@ from typing import TypeVar -from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units -from sasdata.quantities.units import Unit +from sasdata.data_backing import Dataset, Group +from sasdata.quantities.quantity import Quantity from sasdata.quantities.unit_parser import parse_unit +from sasdata.quantities.units import Unit -from sasdata.data_backing import Group, Dataset # logger = logging.getLogger("Accessors") class LoggerDummy: diff --git a/sasdata/quantities/_autogen_warning.py b/sasdata/quantities/_autogen_warning.py index 5adb4b569..fc8be67dc 100644 --- a/sasdata/quantities/_autogen_warning.py +++ b/sasdata/quantities/_autogen_warning.py @@ -76,4 +76,4 @@ -""" \ No newline at end of file +""" diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index 8250c7640..5af6a92bb 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -2,10 +2,11 @@ Builds a data file containing details of units """ -import numpy as np from collections import defaultdict, namedtuple -from _units_base import Dimensions + +import numpy as np from _autogen_warning import warning_text +from _units_base import Dimensions Magnitude = namedtuple("Magnitude", ["symbol", "special_symbol", "latex_symbol", "name", "scale"]) @@ -133,7 +134,7 @@ def format_name(name: str): "# Included from _units_base.py\n" "#\n\n") - with open("_units_base.py", 'r') as base: + with open("_units_base.py") as base: for line in base: # unicode_superscript is a local module when called from # _unit_tables.py but a submodule of sasdata.quantities @@ -405,7 +406,7 @@ def format_name(name: str): fid.write('"""'+(warning_text%"_build_tables.py, _accessor_base.py")+'"""\n\n') - with open("_accessor_base.py", 'r') as base: + with open("_accessor_base.py") as base: for line in base: fid.write(line) @@ -440,4 +441,4 @@ def format_name(name: str): fid.write("\nall_si = [\n") for name in si_unit_names: fid.write(f" {name},\n") - fid.write("]\n") \ No newline at end of file + fid.write("]\n") diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 89423b8db..d8ec39b45 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -1,11 +1,12 @@ +from collections.abc import Sequence from dataclasses import dataclass -from typing import Sequence, Self from fractions import Fraction +from typing import Self import numpy as np - from unicode_superscript import int_as_unicode_superscript + class DimensionError(Exception): pass diff --git a/sasdata/quantities/absolute_temperature.py b/sasdata/quantities/absolute_temperature.py index 71f75fb32..108f30760 100644 --- a/sasdata/quantities/absolute_temperature.py +++ b/sasdata/quantities/absolute_temperature.py @@ -1,8 +1,7 @@ from typing import TypeVar -from sasdata.quantities.quantity import Quantity from sasdata.quantities.accessors import TemperatureAccessor - +from sasdata.quantities.quantity import Quantity DataType = TypeVar("DataType") class AbsoluteTemperatureAccessor(TemperatureAccessor[DataType]): diff --git a/sasdata/quantities/accessors.py b/sasdata/quantities/accessors.py index 38a8fcb88..a7d23fd27 100644 --- a/sasdata/quantities/accessors.py +++ b/sasdata/quantities/accessors.py @@ -80,12 +80,12 @@ from typing import TypeVar -from sasdata.quantities.quantity import Quantity import sasdata.quantities.units as units -from sasdata.quantities.units import Unit +from sasdata.data_backing import Dataset, Group +from sasdata.quantities.quantity import Quantity from sasdata.quantities.unit_parser import parse_unit +from sasdata.quantities.units import Unit -from sasdata.data_backing import Group, Dataset # logger = logging.getLogger("Accessors") class LoggerDummy: @@ -233,7 +233,7 @@ def quantity(self): class LengthAccessor[T](QuantityAccessor[T]): dimension_name = 'length' - + @property def meters(self) -> T: quantity = self.quantity @@ -398,7 +398,7 @@ def inches(self) -> T: class AreaAccessor[T](QuantityAccessor[T]): dimension_name = 'area' - + @property def square_meters(self) -> T: quantity = self.quantity @@ -563,7 +563,7 @@ def square_inches(self) -> T: class VolumeAccessor[T](QuantityAccessor[T]): dimension_name = 'volume' - + @property def litres(self) -> T: quantity = self.quantity @@ -736,7 +736,7 @@ def cubic_inches(self) -> T: class InverselengthAccessor[T](QuantityAccessor[T]): dimension_name = 'inverse_length' - + @property def per_meter(self) -> T: quantity = self.quantity @@ -901,7 +901,7 @@ def per_inch(self) -> T: class InverseareaAccessor[T](QuantityAccessor[T]): dimension_name = 'inverse_area' - + @property def per_square_meter(self) -> T: quantity = self.quantity @@ -1066,7 +1066,7 @@ def per_square_inch(self) -> T: class InversevolumeAccessor[T](QuantityAccessor[T]): dimension_name = 'inverse_volume' - + @property def per_cubic_meter(self) -> T: quantity = self.quantity @@ -1231,7 +1231,7 @@ def per_cubic_inch(self) -> T: class TimeAccessor[T](QuantityAccessor[T]): dimension_name = 'time' - + @property def seconds(self) -> T: quantity = self.quantity @@ -1324,7 +1324,7 @@ def years(self) -> T: class RateAccessor[T](QuantityAccessor[T]): dimension_name = 'rate' - + @property def hertz(self) -> T: quantity = self.quantity @@ -1433,7 +1433,7 @@ def attohertz(self) -> T: class SpeedAccessor[T](QuantityAccessor[T]): dimension_name = 'speed' - + @property def meters_per_second(self) -> T: quantity = self.quantity @@ -3198,7 +3198,7 @@ def inches_per_year(self) -> T: class AccelerationAccessor[T](QuantityAccessor[T]): dimension_name = 'acceleration' - + @property def meters_per_square_second(self) -> T: quantity = self.quantity @@ -4963,7 +4963,7 @@ def inches_per_square_year(self) -> T: class DensityAccessor[T](QuantityAccessor[T]): dimension_name = 'density' - + @property def grams_per_cubic_meter(self) -> T: quantity = self.quantity @@ -7528,7 +7528,7 @@ def ounces_per_cubic_inch(self) -> T: class ForceAccessor[T](QuantityAccessor[T]): dimension_name = 'force' - + @property def newtons(self) -> T: quantity = self.quantity @@ -7653,7 +7653,7 @@ def pounds_force(self) -> T: class PressureAccessor[T](QuantityAccessor[T]): dimension_name = 'pressure' - + @property def pascals(self) -> T: quantity = self.quantity @@ -7770,7 +7770,7 @@ def pounds_force_per_square_inch(self) -> T: class EnergyAccessor[T](QuantityAccessor[T]): dimension_name = 'energy' - + @property def joules(self) -> T: quantity = self.quantity @@ -7983,7 +7983,7 @@ def attoelectronvolts(self) -> T: class PowerAccessor[T](QuantityAccessor[T]): dimension_name = 'power' - + @property def watts(self) -> T: quantity = self.quantity @@ -8092,7 +8092,7 @@ def attowatts(self) -> T: class ChargeAccessor[T](QuantityAccessor[T]): dimension_name = 'charge' - + @property def coulombs(self) -> T: quantity = self.quantity @@ -8201,7 +8201,7 @@ def attocoulombs(self) -> T: class PotentialAccessor[T](QuantityAccessor[T]): dimension_name = 'potential' - + @property def volts(self) -> T: quantity = self.quantity @@ -8310,7 +8310,7 @@ def attovolts(self) -> T: class ResistanceAccessor[T](QuantityAccessor[T]): dimension_name = 'resistance' - + @property def ohms(self) -> T: quantity = self.quantity @@ -8419,7 +8419,7 @@ def attoohms(self) -> T: class CapacitanceAccessor[T](QuantityAccessor[T]): dimension_name = 'capacitance' - + @property def farads(self) -> T: quantity = self.quantity @@ -8528,7 +8528,7 @@ def attofarads(self) -> T: class ConductanceAccessor[T](QuantityAccessor[T]): dimension_name = 'conductance' - + @property def siemens(self) -> T: quantity = self.quantity @@ -8637,7 +8637,7 @@ def attosiemens(self) -> T: class MagneticfluxAccessor[T](QuantityAccessor[T]): dimension_name = 'magnetic_flux' - + @property def webers(self) -> T: quantity = self.quantity @@ -8746,7 +8746,7 @@ def attowebers(self) -> T: class MagneticfluxdensityAccessor[T](QuantityAccessor[T]): dimension_name = 'magnetic_flux_density' - + @property def tesla(self) -> T: quantity = self.quantity @@ -8855,7 +8855,7 @@ def attotesla(self) -> T: class InductanceAccessor[T](QuantityAccessor[T]): dimension_name = 'inductance' - + @property def henry(self) -> T: quantity = self.quantity @@ -8964,7 +8964,7 @@ def attohenry(self) -> T: class TemperatureAccessor[T](QuantityAccessor[T]): dimension_name = 'temperature' - + @property def kelvin(self) -> T: quantity = self.quantity @@ -9081,7 +9081,7 @@ def degrees_celsius(self) -> T: class DimensionlessAccessor[T](QuantityAccessor[T]): dimension_name = 'dimensionless' - + @property def none(self) -> T: quantity = self.quantity @@ -9102,7 +9102,7 @@ def percent(self) -> T: class AngleAccessor[T](QuantityAccessor[T]): dimension_name = 'angle' - + @property def degrees(self) -> T: quantity = self.quantity @@ -9123,7 +9123,7 @@ def radians(self) -> T: class SolidangleAccessor[T](QuantityAccessor[T]): dimension_name = 'solid_angle' - + @property def stradians(self) -> T: quantity = self.quantity @@ -9136,7 +9136,7 @@ def stradians(self) -> T: class AmountAccessor[T](QuantityAccessor[T]): dimension_name = 'amount' - + @property def moles(self) -> T: quantity = self.quantity @@ -9197,7 +9197,7 @@ def attomoles(self) -> T: class ConcentrationAccessor[T](QuantityAccessor[T]): dimension_name = 'concentration' - + @property def moles_per_cubic_meter(self) -> T: quantity = self.quantity diff --git a/sasdata/quantities/numerical_encoding.py b/sasdata/quantities/numerical_encoding.py index 0a5b0ae13..64264aab7 100644 --- a/sasdata/quantities/numerical_encoding.py +++ b/sasdata/quantities/numerical_encoding.py @@ -1,11 +1,11 @@ -import numpy as np -from scipy.sparse import coo_matrix, csr_matrix, csc_matrix, coo_array, csr_array, csc_array - import base64 import struct +import numpy as np +from scipy.sparse import coo_array, coo_matrix, csc_array, csc_matrix, csr_array, csr_matrix + def numerical_encode(obj: int | float | np.ndarray | coo_matrix | coo_array | csr_matrix | csr_array | csc_matrix | csc_array): diff --git a/sasdata/quantities/operations_examples.py b/sasdata/quantities/operations_examples.py index 4509a86ac..29b50ccc0 100644 --- a/sasdata/quantities/operations_examples.py +++ b/sasdata/quantities/operations_examples.py @@ -1,4 +1,4 @@ -from sasdata.quantities.operations import Variable, Mul +from sasdata.quantities.operations import Mul, Variable x = Variable("x") y = Variable("y") diff --git a/sasdata/quantities/plotting.py b/sasdata/quantities/plotting.py index 854e23f5e..d4a99295c 100644 --- a/sasdata/quantities/plotting.py +++ b/sasdata/quantities/plotting.py @@ -1,7 +1,7 @@ import matplotlib.pyplot as plt from numpy.typing import ArrayLike -from sasdata.quantities.quantity import Quantity, NamedQuantity +from sasdata.quantities.quantity import NamedQuantity, Quantity def quantity_plot(x: Quantity[ArrayLike], y: Quantity[ArrayLike], *args, **kwargs): diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 57cde09c8..5ac546024 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -1,20 +1,16 @@ -from typing import Self +import hashlib +import json +from typing import Any, Self, TypeVar, Union import numpy as np from numpy._typing import ArrayLike from sasdata.quantities import units from sasdata.quantities.numerical_encoding import numerical_decode, numerical_encode -from sasdata.quantities.units import Unit, NamedUnit - -import hashlib - -from typing import Any, TypeVar, Union - -import json +from sasdata.quantities.units import NamedUnit, Unit T = TypeVar("T") @@ -220,7 +216,7 @@ def deserialise_json(json_data: dict) -> "Operation": @staticmethod def _deserialise(parameters: dict) -> "Operation": - raise NotImplementedError(f"Deserialise not implemented for this class") + raise NotImplementedError("Deserialise not implemented for this class") def serialise(self) -> str: return json.dumps(self._serialise_json()) @@ -1455,4 +1451,4 @@ def variance(self) -> Quantity: if self._variance_cache is None: self._variance_cache = self.history.variance_propagate(self.units) - return self._variance_cache \ No newline at end of file + return self._variance_cache diff --git a/sasdata/quantities/quantity_examples.py b/sasdata/quantities/quantity_examples.py index 3675d62fa..cc12640db 100644 --- a/sasdata/quantities/quantity_examples.py +++ b/sasdata/quantities/quantity_examples.py @@ -1,8 +1,8 @@ -from sasdata.quantities.quantity import NamedQuantity from sasdata.quantities import units +from sasdata.quantities.quantity import NamedQuantity x = NamedQuantity("x", 1, units.meters, standard_error=1) y = NamedQuantity("y", 1, units.decimeters, standard_error=1) print(x+y) -print((x+y).to_units_of(units.centimeters)) \ No newline at end of file +print((x+y).to_units_of(units.centimeters)) diff --git a/sasdata/quantities/si.py b/sasdata/quantities/si.py index 871b6ee26..6e2b77169 100644 --- a/sasdata/quantities/si.py +++ b/sasdata/quantities/si.py @@ -78,24 +78,26 @@ """ -from sasdata.quantities.units import meters -from sasdata.quantities.units import seconds -from sasdata.quantities.units import amperes -from sasdata.quantities.units import kelvin -from sasdata.quantities.units import hertz -from sasdata.quantities.units import newtons -from sasdata.quantities.units import pascals -from sasdata.quantities.units import joules -from sasdata.quantities.units import watts -from sasdata.quantities.units import coulombs -from sasdata.quantities.units import volts -from sasdata.quantities.units import ohms -from sasdata.quantities.units import farads -from sasdata.quantities.units import siemens -from sasdata.quantities.units import webers -from sasdata.quantities.units import tesla -from sasdata.quantities.units import henry -from sasdata.quantities.units import kilograms +from sasdata.quantities.units import ( + amperes, + coulombs, + farads, + henry, + hertz, + joules, + kelvin, + kilograms, + meters, + newtons, + ohms, + pascals, + seconds, + siemens, + tesla, + volts, + watts, + webers, +) all_si = [ meters, diff --git a/sasdata/quantities/unit_parser.py b/sasdata/quantities/unit_parser.py index 082e6e340..26def035d 100644 --- a/sasdata/quantities/unit_parser.py +++ b/sasdata/quantities/unit_parser.py @@ -1,6 +1,7 @@ -from sasdata.quantities.units import Dimensions, NamedUnit, Unit, symbol_lookup, unit_groups, UnitGroup from re import findall, fullmatch +from sasdata.quantities.units import Dimensions, NamedUnit, Unit, UnitGroup, symbol_lookup, unit_groups + # TODO: This shouldn't be in this file but I don't want to edit Lucas' code before he is finished. all_units_groups = [group.units for group in unit_groups.values()] @@ -16,7 +17,7 @@ def split_unit_str(unit_str: str) -> list[str]: def validate_unit_str(unit_str: str) -> bool: """Validate whether unit_str is valid. This doesn't mean that the unit specified in unit_str exists but rather it only consists of letters, and numbers as a unit string should.""" - return not fullmatch(r'[A-Za-zΩ%Å^1-9\-\+/\ \.]+', unit_str) is None + return fullmatch(r'[A-Za-zΩ%Å^1-9\-\+/\ \.]+', unit_str) is not None def parse_single_unit(unit_str: str, unit_group: UnitGroup | None = None, longest_unit: bool = True) -> tuple[Unit | None, str]: """Attempts to find a single unit for unit_str. Return this unit, and the remaining string in a tuple. If a unit diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 2366f4c44..7c2698bcd 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -82,14 +82,16 @@ # Included from _units_base.py # +from collections.abc import Sequence from dataclasses import dataclass -from typing import Sequence, Self from fractions import Fraction +from typing import Self import numpy as np from sasdata.quantities.unicode_superscript import int_as_unicode_superscript + class DimensionError(Exception): pass @@ -428,7 +430,7 @@ def __init__(self, name: str, units: list[NamedUnit]): # -# Specific units +# Specific units # meters = NamedUnit(1, Dimensions(1, 0, 0, 0, 0, 0, 0),name='meters',ascii_symbol='m',symbol='m') @@ -2038,12 +2040,12 @@ def __init__(self, name: str, units: list[NamedUnit]): # -# Units by type +# Units by type # length = UnitGroup( - name = 'length', + name = 'length', units = [ meters, exameters, @@ -2068,7 +2070,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) area = UnitGroup( - name = 'area', + name = 'area', units = [ square_meters, square_exameters, @@ -2093,7 +2095,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) volume = UnitGroup( - name = 'volume', + name = 'volume', units = [ litres, cubic_meters, @@ -2119,7 +2121,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) inverse_length = UnitGroup( - name = 'inverse_length', + name = 'inverse_length', units = [ per_meter, per_exameter, @@ -2144,7 +2146,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) inverse_area = UnitGroup( - name = 'inverse_area', + name = 'inverse_area', units = [ per_square_meter, per_square_exameter, @@ -2169,7 +2171,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) inverse_volume = UnitGroup( - name = 'inverse_volume', + name = 'inverse_volume', units = [ per_cubic_meter, per_cubic_exameter, @@ -2194,7 +2196,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) time = UnitGroup( - name = 'time', + name = 'time', units = [ seconds, milliseconds, @@ -2210,7 +2212,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) rate = UnitGroup( - name = 'rate', + name = 'rate', units = [ hertz, exahertz, @@ -2229,7 +2231,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) speed = UnitGroup( - name = 'speed', + name = 'speed', units = [ meters_per_second, meters_per_millisecond, @@ -2454,7 +2456,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) acceleration = UnitGroup( - name = 'acceleration', + name = 'acceleration', units = [ meters_per_square_second, meters_per_square_millisecond, @@ -2679,7 +2681,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) density = UnitGroup( - name = 'density', + name = 'density', units = [ grams_per_cubic_meter, exagrams_per_cubic_meter, @@ -3004,7 +3006,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) force = UnitGroup( - name = 'force', + name = 'force', units = [ newtons, exanewtons, @@ -3024,7 +3026,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) pressure = UnitGroup( - name = 'pressure', + name = 'pressure', units = [ pascals, exapascals, @@ -3043,7 +3045,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) energy = UnitGroup( - name = 'energy', + name = 'energy', units = [ joules, exajoules, @@ -3074,7 +3076,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) power = UnitGroup( - name = 'power', + name = 'power', units = [ watts, exawatts, @@ -3092,7 +3094,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) charge = UnitGroup( - name = 'charge', + name = 'charge', units = [ coulombs, exacoulombs, @@ -3110,7 +3112,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) potential = UnitGroup( - name = 'potential', + name = 'potential', units = [ volts, exavolts, @@ -3128,7 +3130,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) resistance = UnitGroup( - name = 'resistance', + name = 'resistance', units = [ ohms, exaohms, @@ -3146,7 +3148,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) capacitance = UnitGroup( - name = 'capacitance', + name = 'capacitance', units = [ farads, exafarads, @@ -3164,7 +3166,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) conductance = UnitGroup( - name = 'conductance', + name = 'conductance', units = [ siemens, exasiemens, @@ -3182,7 +3184,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) magnetic_flux = UnitGroup( - name = 'magnetic_flux', + name = 'magnetic_flux', units = [ webers, exawebers, @@ -3200,7 +3202,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) magnetic_flux_density = UnitGroup( - name = 'magnetic_flux_density', + name = 'magnetic_flux_density', units = [ tesla, exatesla, @@ -3218,7 +3220,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) inductance = UnitGroup( - name = 'inductance', + name = 'inductance', units = [ henry, exahenry, @@ -3236,7 +3238,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) temperature = UnitGroup( - name = 'temperature', + name = 'temperature', units = [ kelvin, exakelvin, @@ -3255,27 +3257,27 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) dimensionless = UnitGroup( - name = 'dimensionless', + name = 'dimensionless', units = [ none, percent, ]) angle = UnitGroup( - name = 'angle', + name = 'angle', units = [ degrees, radians, ]) solid_angle = UnitGroup( - name = 'solid_angle', + name = 'solid_angle', units = [ stradians, ]) amount = UnitGroup( - name = 'amount', + name = 'amount', units = [ moles, millimoles, @@ -3287,7 +3289,7 @@ def __init__(self, name: str, units: list[NamedUnit]): ]) concentration = UnitGroup( - name = 'concentration', + name = 'concentration', units = [ moles_per_cubic_meter, millimoles_per_cubic_meter, diff --git a/sasdata/slicing/meshes/mesh.py b/sasdata/slicing/meshes/mesh.py index e1d6c8a96..8cfd8a6a0 100644 --- a/sasdata/slicing/meshes/mesh.py +++ b/sasdata/slicing/meshes/mesh.py @@ -1,13 +1,13 @@ -from typing import Sequence - -import numpy as np +from collections.abc import Sequence import matplotlib.pyplot as plt +import numpy as np from matplotlib import cm from matplotlib.collections import LineCollection from sasdata.slicing.meshes.util import closed_loop_edges + class Mesh: def __init__(self, points: np.ndarray, diff --git a/sasdata/slicing/meshes/meshmerge.py b/sasdata/slicing/meshes/meshmerge.py index 2060cc7b4..ae950803e 100644 --- a/sasdata/slicing/meshes/meshmerge.py +++ b/sasdata/slicing/meshes/meshmerge.py @@ -1,9 +1,10 @@ +import time + import numpy as np -from sasdata.slicing.meshes.mesh import Mesh from sasdata.slicing.meshes.delaunay_mesh import delaunay_mesh +from sasdata.slicing.meshes.mesh import Mesh -import time def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray]: """ Take two lists of polygons and find their intersections @@ -72,8 +73,8 @@ def meshmerge(mesh_a: Mesh, mesh_b: Mesh) -> tuple[Mesh, np.ndarray, np.ndarray] # Find the points where s and t are in (0, 1) intersection_inds = np.logical_and( - np.logical_and(0 < st[:, 0], st[:, 0] < 1), - np.logical_and(0 < st[:, 1], st[:, 1] < 1)) + np.logical_and(st[:, 0] > 0, st[:, 0] < 1), + np.logical_and(st[:, 1] > 0, st[:, 1] < 1)) start_points_for_intersections = p1[non_singular][intersection_inds, :] deltas_for_intersections = delta1[non_singular][intersection_inds, :] diff --git a/sasdata/slicing/meshes/util.py b/sasdata/slicing/meshes/util.py index 56584434d..da5b6e370 100644 --- a/sasdata/slicing/meshes/util.py +++ b/sasdata/slicing/meshes/util.py @@ -1,4 +1,5 @@ -from typing import Sequence, TypeVar +from collections.abc import Sequence +from typing import TypeVar T = TypeVar("T") diff --git a/sasdata/slicing/rebinning.py b/sasdata/slicing/rebinning.py index f2c76de64..060b2e0ca 100644 --- a/sasdata/slicing/rebinning.py +++ b/sasdata/slicing/rebinning.py @@ -1,14 +1,13 @@ +import time from abc import ABC, abstractmethod -from typing import Optional from dataclasses import dataclass import numpy as np from sasdata.slicing.meshes.mesh import Mesh -from sasdata.slicing.meshes.voronoi_mesh import voronoi_mesh from sasdata.slicing.meshes.meshmerge import meshmerge +from sasdata.slicing.meshes.voronoi_mesh import voronoi_mesh -import time @dataclass class CacheData: @@ -24,10 +23,10 @@ class Rebinner(ABC): def __init__(self): """ Base class for rebinning methods""" - self._bin_mesh_cache: Optional[Mesh] = None # cached version of the output bin mesh + self._bin_mesh_cache: Mesh | None = None # cached version of the output bin mesh # Output dependent caching - self._input_cache: Optional[CacheData] = None + self._input_cache: CacheData | None = None @abstractmethod diff --git a/sasdata/slicing/slicer_demo.py b/sasdata/slicing/slicer_demo.py index af3ee985f..5626ded4d 100644 --- a/sasdata/slicing/slicer_demo.py +++ b/sasdata/slicing/slicer_demo.py @@ -1,13 +1,10 @@ """ Dev docs: Demo to show the behaviour of the re-binning methods """ -import numpy as np - import matplotlib.pyplot as plt +import numpy as np -from sasdata.slicing.slicers.AnularSector import AnularSector from sasdata.slicing.meshes.voronoi_mesh import voronoi_mesh - - +from sasdata.slicing.slicers.AnularSector import AnularSector if __name__ == "__main__": q_range = 1.5 diff --git a/sasdata/slicing/slicers/AnularSector.py b/sasdata/slicing/slicers/AnularSector.py index 06ee3fe32..56ed5f262 100644 --- a/sasdata/slicing/slicers/AnularSector.py +++ b/sasdata/slicing/slicers/AnularSector.py @@ -1,7 +1,8 @@ import numpy as np -from sasdata.slicing.rebinning import Rebinner from sasdata.slicing.meshes.mesh import Mesh +from sasdata.slicing.rebinning import Rebinner + class AnularSector(Rebinner): """ A single annular sector (wedge sum)""" diff --git a/sasdata/slicing/transforms.py b/sasdata/slicing/transforms.py index 323a7a52f..03ee1968e 100644 --- a/sasdata/slicing/transforms.py +++ b/sasdata/slicing/transforms.py @@ -1,8 +1,7 @@ -import numpy as np -from scipy.spatial import Voronoi import matplotlib.pyplot as plt +import numpy as np from matplotlib import cm - +from scipy.spatial import Voronoi # Some test data @@ -55,4 +54,4 @@ def get_data_mesh(x, y, data): plt.show() -get_data_mesh(qx.reshape(-1), qy.reshape(-1), data) \ No newline at end of file +get_data_mesh(qx.reshape(-1), qy.reshape(-1), data) diff --git a/sasdata/temp_ascii_reader.py b/sasdata/temp_ascii_reader.py index 73d486460..d1cb9ec7a 100644 --- a/sasdata/temp_ascii_reader.py +++ b/sasdata/temp_ascii_reader.py @@ -1,8 +1,10 @@ #!/usr/bin/env python +from enum import Enum + from sasdata.data import SasData from sasdata.quantities.units import NamedUnit -from enum import Enum + class AsciiSeparator(Enum): Comma = (0,) diff --git a/sasdata/temp_hdf5_reader.py b/sasdata/temp_hdf5_reader.py index 1ffaa3149..435f597ad 100644 --- a/sasdata/temp_hdf5_reader.py +++ b/sasdata/temp_hdf5_reader.py @@ -1,22 +1,16 @@ -import h5py - - import logging +import h5py import numpy as np - - from h5py._hl.dataset import Dataset as HDF5Dataset from h5py._hl.group import Group as HDF5Group - from sasdata.data import SasData -from sasdata.data_backing import Dataset as SASDataDataset, Group as SASDataGroup -from sasdata.metadata import Instrument, Collimation, Aperture, Source -from sasdata.quantities.accessors import AccessorTarget - -from sasdata.quantities.quantity import NamedQuantity +from sasdata.data_backing import Dataset as SASDataDataset +from sasdata.data_backing import Group as SASDataGroup +from sasdata.metadata import Aperture, Collimation, Instrument, Source from sasdata.quantities import units +from sasdata.quantities.quantity import NamedQuantity from sasdata.quantities.unit_parser import parse # test_file = "./example_data/1d_data/33837rear_1D_1.75_16.5_NXcanSAS_v3.h5" diff --git a/sasdata/temp_sesans_reader.py b/sasdata/temp_sesans_reader.py index 243fbc5ae..b6cfb6861 100644 --- a/sasdata/temp_sesans_reader.py +++ b/sasdata/temp_sesans_reader.py @@ -2,31 +2,27 @@ Import SESANS data in SasData format """ +import re +from itertools import groupby + from sasdata.data import SasData from sasdata.data_util.loader_exceptions import FileContentsException from sasdata.dataset_types import one_dim -from sasdata.quantities.quantity import Quantity from sasdata.metadata import ( Metadata, - Sample, - Instrument, - Collimation, - Aperture, - Vec3, MetaNode, Process, + Sample, ) -from sasdata.quantities import unit_parser, units -from itertools import groupby -import re -import numpy as np +from sasdata.quantities import unit_parser +from sasdata.quantities.quantity import Quantity def parse_version(lines: list[str]) -> tuple[str, list[str]]: import re header = lines[0] - m = re.search("FileFormatVersion\s+(\S+)", header) + m = re.search(r"FileFormatVersion\s+(\S+)", header) if m is None: raise FileContentsException("Alleged Sesans file does not contain File Format Version header") @@ -110,7 +106,7 @@ def parse_metadata(lines: list[str]) -> tuple[Metadata, dict[str, str], list[str # Parse key value store kvs: dict[str, str] = {} for line in parts[0]: - m = re.search("(\S+)\s+(.+)\n", line) + m = re.search("(\\S+)\\s+(.+)\n", line) if not m: continue kvs[m.group(1)] = m.group(2) diff --git a/sasdata/transforms/rebinning.py b/sasdata/transforms/rebinning.py index 20e5294fc..e5f48b18c 100644 --- a/sasdata/transforms/rebinning.py +++ b/sasdata/transforms/rebinning.py @@ -1,12 +1,13 @@ """ Algorithms for interpolation and rebinning """ +from enum import Enum + import numpy as np from numpy._typing import ArrayLike +from scipy.sparse import coo_matrix from sasdata.quantities.quantity import Quantity -from scipy.sparse import coo_matrix -from enum import Enum class InterpolationOptions(Enum): NEAREST_NEIGHBOUR = 0 @@ -200,4 +201,4 @@ def rebin(data: Quantity[ArrayLike], i.e. non-projective rebinning. """ - pass \ No newline at end of file + pass diff --git a/sasdata/trend.py b/sasdata/trend.py index 40c0e8a90..16d1c67f3 100644 --- a/sasdata/trend.py +++ b/sasdata/trend.py @@ -2,8 +2,6 @@ from dataclasses import dataclass -import numpy as np - from sasdata.data import SasData # Axis strs refer to the name of their associated NamedQuantity. diff --git a/sasdata/util.py b/sasdata/util.py index 2cd6e3f48..8decc68be 100644 --- a/sasdata/util.py +++ b/sasdata/util.py @@ -1,4 +1,5 @@ -from typing import TypeVar, Callable +from collections.abc import Callable +from typing import TypeVar T = TypeVar("T") @@ -14,4 +15,4 @@ def wrapper() -> T: return cache_state[1] - return wrapper \ No newline at end of file + return wrapper diff --git a/test/quantities/utest_math_operations.py b/test/quantities/utest_math_operations.py index 5bda5a2cd..67e10813c 100644 --- a/test/quantities/utest_math_operations.py +++ b/test/quantities/utest_math_operations.py @@ -1,10 +1,10 @@ """ Tests for math operations """ +import numpy as np import pytest -import numpy as np -from sasdata.quantities.quantity import NamedQuantity, tensordot, transpose from sasdata.quantities import units +from sasdata.quantities.quantity import NamedQuantity, tensordot, transpose order_list = [ [0, 1, 2, 3], diff --git a/test/quantities/utest_operations.py b/test/quantities/utest_operations.py index a2158f0f8..f4599f1b4 100644 --- a/test/quantities/utest_operations.py +++ b/test/quantities/utest_operations.py @@ -3,10 +3,20 @@ import numpy as np import pytest -from sasdata.quantities.quantity import Operation, \ - Neg, Inv, \ - Add, Sub, Mul, Div, Pow, \ - Variable, Constant, AdditiveIdentity, MultiplicativeIdentity +from sasdata.quantities.quantity import ( + Add, + AdditiveIdentity, + Constant, + Div, + Inv, + Mul, + MultiplicativeIdentity, + Neg, + Operation, + Pow, + Sub, + Variable, +) x = Variable("x") y = Variable("y") diff --git a/test/quantities/utest_quantities.py b/test/quantities/utest_quantities.py index 596360e7c..3ac18282d 100644 --- a/test/quantities/utest_quantities.py +++ b/test/quantities/utest_quantities.py @@ -1,9 +1,11 @@ import numpy as np +import pytest -from sasdata.quantities.quantity import Quantity, UnitError -import sasdata.quantities.units as units import sasdata.quantities.si as si -import pytest +import sasdata.quantities.units as units +from sasdata.quantities.quantity import Quantity, UnitError + + def test_in_units_of_calculation(): """ Just a couple of unit conversions """ assert Quantity(1, units.meters).in_units_of(units.kilometers) == 1e-3 diff --git a/test/quantities/utest_quantity_error.py b/test/quantities/utest_quantity_error.py index f03ef59c1..e8e55337d 100644 --- a/test/quantities/utest_quantity_error.py +++ b/test/quantities/utest_quantity_error.py @@ -1,7 +1,9 @@ +import numpy as np +import pytest + from sasdata.quantities import units from sasdata.quantities.quantity import NamedQuantity -import pytest -import numpy as np + @pytest.mark.parametrize("x_err, y_err, x_units, y_units", [(1, 1, units.meters, units.meters), diff --git a/test/quantities/utest_units.py b/test/quantities/utest_units.py index 9fea2a6b8..258143e2a 100644 --- a/test/quantities/utest_units.py +++ b/test/quantities/utest_units.py @@ -1,6 +1,7 @@ import sasdata.quantities.units as units from sasdata.quantities.units import Unit + class EqualUnits: def __init__(self, test_name: str, *units): self.test_name = "Equality: " + test_name diff --git a/test/sasdataloader/utest_new_sesans.py b/test/sasdataloader/utest_new_sesans.py index 65a2af508..c17155094 100644 --- a/test/sasdataloader/utest_new_sesans.py +++ b/test/sasdataloader/utest_new_sesans.py @@ -3,8 +3,8 @@ """ import os -import pytest +import pytest from sasdata.temp_hdf5_reader import load_data from sasdata.temp_sesans_reader import load_data diff --git a/test/sasdataloader/utest_sasdataload.py b/test/sasdataloader/utest_sasdataload.py index 8488360a1..7952d6a28 100644 --- a/test/sasdataloader/utest_sasdataload.py +++ b/test/sasdataloader/utest_sasdataload.py @@ -3,7 +3,9 @@ """ import os + import pytest + from sasdata.temp_hdf5_reader import load_data as hdf_load_data from sasdata.temp_xml_reader import load_data as xml_load_data diff --git a/test/slicers/meshes_for_testing.py b/test/slicers/meshes_for_testing.py index fb346e79c..7e39ec75c 100644 --- a/test/slicers/meshes_for_testing.py +++ b/test/slicers/meshes_for_testing.py @@ -4,9 +4,9 @@ import numpy as np -from sasdata.slicing.meshes.voronoi_mesh import voronoi_mesh from sasdata.slicing.meshes.mesh import Mesh from sasdata.slicing.meshes.meshmerge import meshmerge +from sasdata.slicing.meshes.voronoi_mesh import voronoi_mesh coords = np.arange(-4, 5) grid_mesh = voronoi_mesh(*np.meshgrid(coords, coords)) diff --git a/test/slicers/utest_meshmerge.py b/test/slicers/utest_meshmerge.py index d83892def..a4a1645c2 100644 --- a/test/slicers/utest_meshmerge.py +++ b/test/slicers/utest_meshmerge.py @@ -5,8 +5,7 @@ """ from sasdata.slicing.meshes.meshmerge import meshmerge -from test.slicers.meshes_for_testing import ( - grid_mesh, shape_mesh, expected_grid_mappings, expected_shape_mappings) +from test.slicers.meshes_for_testing import expected_grid_mappings, expected_shape_mappings, grid_mesh, shape_mesh def test_meshmerge_mappings(): diff --git a/test/slicers/utest_point_assignment.py b/test/slicers/utest_point_assignment.py index ba785a503..a82f1c790 100644 --- a/test/slicers/utest_point_assignment.py +++ b/test/slicers/utest_point_assignment.py @@ -2,4 +2,4 @@ def test_location_assignment(): - pass \ No newline at end of file + pass diff --git a/test/transforms/utest_interpolation.py b/test/transforms/utest_interpolation.py index 7011d48a3..9ac701ceb 100644 --- a/test/transforms/utest_interpolation.py +++ b/test/transforms/utest_interpolation.py @@ -1,14 +1,14 @@ -import pytest +from collections.abc import Callable + import numpy as np +import pytest from matplotlib import pyplot as plt from numpy.typing import ArrayLike -from typing import Callable +from sasdata.quantities import units from sasdata.quantities.plotting import quantity_plot from sasdata.quantities.quantity import NamedQuantity, Quantity -from sasdata.quantities import units - -from sasdata.transforms.rebinning import calculate_interpolation_matrix_1d, InterpolationOptions +from sasdata.transforms.rebinning import InterpolationOptions, calculate_interpolation_matrix_1d test_functions = [ lambda x: x**2, diff --git a/test/utest_new_sasdata.py b/test/utest_new_sasdata.py index dd77717ff..62e5e4ab7 100644 --- a/test/utest_new_sasdata.py +++ b/test/utest_new_sasdata.py @@ -1,13 +1,10 @@ import numpy as np from sasdata.data import SasData -from sasdata.dataset_types import one_dim from sasdata.data_backing import Group -from sasdata.dataset_types import one_dim, three_dim, two_dim -from sasdata.metadata import Instrument, Metadata, Source -from sasdata.postprocess import deduce_qz +from sasdata.dataset_types import one_dim from sasdata.quantities.quantity import Quantity -from sasdata.quantities.units import angstroms, per_angstrom, per_centimeter +from sasdata.quantities.units import per_angstrom, per_centimeter def test_1d(): diff --git a/test/utest_trend.py b/test/utest_trend.py index eb86266ec..cae81131a 100644 --- a/test/utest_trend.py +++ b/test/utest_trend.py @@ -1,12 +1,11 @@ from os import listdir, path import pytest -from os import path, listdir + +import sasdata.temp_ascii_reader as ascii_reader from sasdata.ascii_reader_metadata import AsciiMetadataCategory from sasdata.quantities.units import per_nanometer from sasdata.temp_ascii_reader import AsciiReaderParams -import sasdata.temp_ascii_reader as ascii_reader -from sasdata.quantities.units import per_angstrom, per_nanometer from sasdata.trend import Trend test_directories = [ diff --git a/test/utest_unit_parser.py b/test/utest_unit_parser.py index afce93f13..7bdcf997f 100644 --- a/test/utest_unit_parser.py +++ b/test/utest_unit_parser.py @@ -1,9 +1,9 @@ -from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group, parse_unit +import pytest + from sasdata.quantities import units +from sasdata.quantities.unit_parser import parse_named_unit, parse_named_unit_from_group, parse_unit from sasdata.quantities.units import Unit -import pytest - named_units_for_testing = [ ('m', units.meters), ('A-1', units.per_angstrom),