analog-daddy 3.0.1.dev1__tar.gz → 3.0.1.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {analog_daddy-3.0.1.dev1 → analog_daddy-3.0.1.dev2}/PKG-INFO +1 -1
- {analog_daddy-3.0.1.dev1 → analog_daddy-3.0.1.dev2}/pyproject.toml +2 -1
- {analog_daddy-3.0.1.dev1 → analog_daddy-3.0.1.dev2}/pyproject.toml.orig +2 -1
- analog_daddy-3.0.1.dev2/src/analog_daddy/__init__.py +5 -0
- {analog_daddy-3.0.1.dev1 → analog_daddy-3.0.1.dev2}/src/analog_daddy/mootils.py +33 -0
- {analog_daddy-3.0.1.dev1 → analog_daddy-3.0.1.dev2}/src/analog_daddy/plexpr.py +81 -2
- {analog_daddy-3.0.1.dev1 → analog_daddy-3.0.1.dev2}/src/analog_daddy/plutils.py +86 -28
- analog_daddy-3.0.1.dev2/src/analog_daddy/units.py +337 -0
- analog_daddy-3.0.1.dev1/src/analog_daddy/__init__.py +0 -1
- {analog_daddy-3.0.1.dev1 → analog_daddy-3.0.1.dev2}/README.md +0 -0
- {analog_daddy-3.0.1.dev1 → analog_daddy-3.0.1.dev2}/src/analog_daddy/__main__.py +0 -0
- {analog_daddy-3.0.1.dev1 → analog_daddy-3.0.1.dev2}/src/analog_daddy/cli.py +0 -0
- {analog_daddy-3.0.1.dev1 → analog_daddy-3.0.1.dev2}/src/analog_daddy/gtutils.py +0 -0
- {analog_daddy-3.0.1.dev1 → analog_daddy-3.0.1.dev2}/src/analog_daddy/py.typed +0 -0
- {analog_daddy-3.0.1.dev1 → analog_daddy-3.0.1.dev2}/src/analog_daddy/roles.py +0 -0
- {analog_daddy-3.0.1.dev1 → analog_daddy-3.0.1.dev2}/src/analog_daddy/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: analog-daddy
|
|
3
|
-
Version: 3.0.1.
|
|
3
|
+
Version: 3.0.1.dev2
|
|
4
4
|
Summary: This library is an attempt to make transistor sizing for Analog Design less painful. Additionally, provides helper utilities to minimize churn and errors in repetative tasks.
|
|
5
5
|
Author: Siddhant Laddha
|
|
6
6
|
Author-email: Siddhant Laddha <25705822+siddhantladdha@users.noreply.github.com>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "analog-daddy"
|
|
3
|
-
version = "3.0.1.
|
|
3
|
+
version = "3.0.1.dev2"
|
|
4
4
|
description = "This library is an attempt to make transistor sizing for Analog Design less painful. Additionally, provides helper utilities to minimize churn and errors in repetative tasks."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">= 3.12"
|
|
@@ -46,6 +46,7 @@ analog_daddy = "analog_daddy.cli:app"
|
|
|
46
46
|
[dependency-groups]
|
|
47
47
|
dev = [
|
|
48
48
|
"format-docstring",
|
|
49
|
+
"hypothesis",
|
|
49
50
|
"pytest",
|
|
50
51
|
"ruff",
|
|
51
52
|
"ty",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "analog-daddy"
|
|
3
|
-
version = "3.0.1.
|
|
3
|
+
version = "3.0.1.dev2"
|
|
4
4
|
description = "This library is an attempt to make transistor sizing for Analog Design less painful. Additionally, provides helper utilities to minimize churn and errors in repetative tasks."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">= 3.12"
|
|
@@ -44,6 +44,7 @@ analog_daddy = "analog_daddy.cli:app"
|
|
|
44
44
|
[dependency-groups]
|
|
45
45
|
dev = [
|
|
46
46
|
"format-docstring",
|
|
47
|
+
"hypothesis",
|
|
47
48
|
"pytest",
|
|
48
49
|
"ruff",
|
|
49
50
|
"ty",
|
|
@@ -14,6 +14,7 @@ import marimo as mo
|
|
|
14
14
|
import polars as pl
|
|
15
15
|
import polars.selectors as cs
|
|
16
16
|
from rich.console import Console, RenderableType
|
|
17
|
+
from wigglystuff import CopyToClipboard
|
|
17
18
|
|
|
18
19
|
from analog_daddy.utils import render
|
|
19
20
|
|
|
@@ -121,3 +122,35 @@ def rich_md(
|
|
|
121
122
|
console = Console(file=capture, **console_kwargs)
|
|
122
123
|
console.print(*renderables, **print_kwargs)
|
|
123
124
|
return mo.Html(console.export_html(**export_html_kwargs))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def df_repr_docstr_clip(
|
|
128
|
+
df: pl.DataFrame, max_rows: int = 3
|
|
129
|
+
) -> CopyToClipboard:
|
|
130
|
+
"""
|
|
131
|
+
Copy the repr of a polars DataFrame to the clipboard.
|
|
132
|
+
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
df : pl.DataFrame
|
|
136
|
+
The DataFrame to copy the repr of.
|
|
137
|
+
max_rows : int, default=3
|
|
138
|
+
The maximum number of rows to include in the repr.
|
|
139
|
+
|
|
140
|
+
Returns
|
|
141
|
+
-------
|
|
142
|
+
CopyToClipboard
|
|
143
|
+
A CopyToClipboard object containing the repr of the DataFrame.
|
|
144
|
+
|
|
145
|
+
Examples
|
|
146
|
+
--------
|
|
147
|
+
>>> a = pl.DataFrame(
|
|
148
|
+
... {"x": [9.0, 11.0], "foo": [10.0, 10.0], "bar": [5.0, 5.0]},
|
|
149
|
+
... )
|
|
150
|
+
>>> df_repr_docstr_clip(a).text_to_copy.startswith("shape: (2, 3)")
|
|
151
|
+
True
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
return CopyToClipboard(
|
|
155
|
+
text_to_copy=df.slice(offset=0, length=max_rows).__repr__(),
|
|
156
|
+
)
|
|
@@ -440,7 +440,51 @@ def spec(
|
|
|
440
440
|
raise ValueError(f"bad spec: {spec}")
|
|
441
441
|
|
|
442
442
|
|
|
443
|
-
def list_cast():
|
|
443
|
+
def list_cast() -> tuple[pl.Expr, pl.Expr]:
|
|
444
|
+
"""
|
|
445
|
+
Cast list columns to list[str]
|
|
446
|
+
|
|
447
|
+
Uses ``render`` to convert numeric values and casts categorical
|
|
448
|
+
values to string.
|
|
449
|
+
|
|
450
|
+
Returns
|
|
451
|
+
-------
|
|
452
|
+
tuple[pl.Expr, pl.Expr]
|
|
453
|
+
|
|
454
|
+
Examples
|
|
455
|
+
--------
|
|
456
|
+
>>> (
|
|
457
|
+
... df := pl.DataFrame(
|
|
458
|
+
... {"a": [[1000, 2.5e-6]], "b": [["x", "y"]]},
|
|
459
|
+
... schema={
|
|
460
|
+
... "a": pl.List(pl.Float64),
|
|
461
|
+
... "b": pl.List(pl.Categorical),
|
|
462
|
+
... },
|
|
463
|
+
... )
|
|
464
|
+
... )
|
|
465
|
+
shape: (1, 2)
|
|
466
|
+
┌─────────────────────┬────────────┐
|
|
467
|
+
│ a ┆ b │
|
|
468
|
+
│ --- ┆ --- │
|
|
469
|
+
│ list[f64] ┆ list[cat] │
|
|
470
|
+
╞═════════════════════╪════════════╡
|
|
471
|
+
│ [1000.0, 0.0000025] ┆ ["x", "y"] │
|
|
472
|
+
└─────────────────────┴────────────┘
|
|
473
|
+
>>> out = df.with_columns(*list_cast())
|
|
474
|
+
>>> out
|
|
475
|
+
shape: (1, 2)
|
|
476
|
+
┌────────────────┬────────────┐
|
|
477
|
+
│ a ┆ b │
|
|
478
|
+
│ --- ┆ --- │
|
|
479
|
+
│ list[str] ┆ list[str] │
|
|
480
|
+
╞════════════════╪════════════╡
|
|
481
|
+
│ ["1k", "2.5u"] ┆ ["x", "y"] │
|
|
482
|
+
└────────────────┴────────────┘
|
|
483
|
+
>>> out.schema.contains_dtype(pl.String, recursive=True)
|
|
484
|
+
True
|
|
485
|
+
>>> out.schema.contains_dtype(pl.Categorical, recursive=True)
|
|
486
|
+
False
|
|
487
|
+
"""
|
|
444
488
|
return (
|
|
445
489
|
cs.list(cs.numeric()).list.eval(
|
|
446
490
|
pl.element().map_elements(
|
|
@@ -452,7 +496,42 @@ def list_cast():
|
|
|
452
496
|
)
|
|
453
497
|
|
|
454
498
|
|
|
455
|
-
def list_render_unique(max_len: int = 3):
|
|
499
|
+
def list_render_unique(max_len: int = 3) -> pl.Expr:
|
|
500
|
+
"""
|
|
501
|
+
Render unique values of list[str] columns as a comma-joined string.
|
|
502
|
+
|
|
503
|
+
Lists with more than ``max_len`` unique values are truncated and
|
|
504
|
+
suffixed with the count of omitted values. The ordering is not
|
|
505
|
+
deterministic since ``pl.Expr.unique`` doesn't maintain order.
|
|
506
|
+
|
|
507
|
+
Parameters
|
|
508
|
+
----------
|
|
509
|
+
max_len : int, default=3
|
|
510
|
+
Maximum number of unique values to show before truncating.
|
|
511
|
+
|
|
512
|
+
Returns
|
|
513
|
+
-------
|
|
514
|
+
pl.Expr
|
|
515
|
+
Expression (over all list[str] columns) yielding a string.
|
|
516
|
+
|
|
517
|
+
Examples
|
|
518
|
+
--------
|
|
519
|
+
>>> (df := pl.DataFrame({"a": [["x", "x"], ["a", "b", "c", "d", "e"]]}))
|
|
520
|
+
shape: (2, 1)
|
|
521
|
+
┌───────────────────┐
|
|
522
|
+
│ a │
|
|
523
|
+
│ --- │
|
|
524
|
+
│ list[str] │
|
|
525
|
+
╞═══════════════════╡
|
|
526
|
+
│ ["x", "x"] │
|
|
527
|
+
│ ["a", "b", … "e"] │
|
|
528
|
+
└───────────────────┘
|
|
529
|
+
>>> out = df.select(list_render_unique(max_len=3))["a"].to_list()
|
|
530
|
+
>>> out[0]
|
|
531
|
+
'x'
|
|
532
|
+
>>> out[1].endswith(" + 2 more")
|
|
533
|
+
True
|
|
534
|
+
"""
|
|
456
535
|
ulist = cs.list(cs.string()).list.unique().list
|
|
457
536
|
n = ulist.len()
|
|
458
537
|
head_n = ulist.head(max_len).list
|
|
@@ -22,15 +22,36 @@ def drop_all_null(df: pl.DataFrame) -> pl.DataFrame:
|
|
|
22
22
|
|
|
23
23
|
Examples
|
|
24
24
|
--------
|
|
25
|
-
>>>
|
|
26
|
-
...
|
|
27
|
-
...
|
|
28
|
-
...
|
|
29
|
-
...
|
|
30
|
-
...
|
|
25
|
+
>>> (
|
|
26
|
+
... df := pl.DataFrame(
|
|
27
|
+
... {
|
|
28
|
+
... "a": [None, None, None],
|
|
29
|
+
... "b": [1, 2, None],
|
|
30
|
+
... "c": ["x", "y", ""],
|
|
31
|
+
... }
|
|
32
|
+
... )
|
|
31
33
|
... )
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
shape: (3, 3)
|
|
35
|
+
┌──────┬──────┬─────┐
|
|
36
|
+
│ a ┆ b ┆ c │
|
|
37
|
+
│ --- ┆ --- ┆ --- │
|
|
38
|
+
│ null ┆ i64 ┆ str │
|
|
39
|
+
╞══════╪══════╪═════╡
|
|
40
|
+
│ null ┆ 1 ┆ x │
|
|
41
|
+
│ null ┆ 2 ┆ y │
|
|
42
|
+
│ null ┆ null ┆ │
|
|
43
|
+
└──────┴──────┴─────┘
|
|
44
|
+
>>> drop_all_null(df)
|
|
45
|
+
shape: (3, 2)
|
|
46
|
+
┌──────┬─────┐
|
|
47
|
+
│ b ┆ c │
|
|
48
|
+
│ --- ┆ --- │
|
|
49
|
+
│ i64 ┆ str │
|
|
50
|
+
╞══════╪═════╡
|
|
51
|
+
│ 1 ┆ x │
|
|
52
|
+
│ 2 ┆ y │
|
|
53
|
+
│ null ┆ │
|
|
54
|
+
└──────┴─────┘
|
|
34
55
|
"""
|
|
35
56
|
return df[[ser.name for ser in df if ser.null_count() != df.height]]
|
|
36
57
|
|
|
@@ -51,12 +72,24 @@ def filter_all_null(df: pl.DataFrame) -> pl.DataFrame:
|
|
|
51
72
|
|
|
52
73
|
Examples
|
|
53
74
|
--------
|
|
54
|
-
>>>
|
|
55
|
-
...
|
|
56
|
-
...
|
|
57
|
-
...
|
|
58
|
-
...
|
|
75
|
+
>>> (
|
|
76
|
+
... df := pl.DataFrame(
|
|
77
|
+
... {
|
|
78
|
+
... "a": [1, None, 3],
|
|
79
|
+
... "b": ["x", None, None],
|
|
80
|
+
... }
|
|
81
|
+
... )
|
|
59
82
|
... )
|
|
83
|
+
shape: (3, 2)
|
|
84
|
+
┌──────┬──────┐
|
|
85
|
+
│ a ┆ b │
|
|
86
|
+
│ --- ┆ --- │
|
|
87
|
+
│ i64 ┆ str │
|
|
88
|
+
╞══════╪══════╡
|
|
89
|
+
│ 1 ┆ x │
|
|
90
|
+
│ null ┆ null │
|
|
91
|
+
│ 3 ┆ null │
|
|
92
|
+
└──────┴──────┘
|
|
60
93
|
>>> filter_all_null(df)
|
|
61
94
|
shape: (2, 2)
|
|
62
95
|
┌─────┬──────┐
|
|
@@ -110,17 +143,30 @@ def scan_nested_params(
|
|
|
110
143
|
|
|
111
144
|
Examples
|
|
112
145
|
--------
|
|
113
|
-
>>>
|
|
114
|
-
...
|
|
115
|
-
...
|
|
116
|
-
... "
|
|
117
|
-
...
|
|
118
|
-
...
|
|
119
|
-
...
|
|
120
|
-
...
|
|
121
|
-
...
|
|
122
|
-
...
|
|
146
|
+
>>> (
|
|
147
|
+
... df := pl.DataFrame(
|
|
148
|
+
... {
|
|
149
|
+
... "Point": [
|
|
150
|
+
... "Parameters: var1=-12k, var_b=1n",
|
|
151
|
+
... "1",
|
|
152
|
+
... "Parameters: var3=-12k",
|
|
153
|
+
... "2",
|
|
154
|
+
... ],
|
|
155
|
+
... "Value": [None, 10, 1, None],
|
|
156
|
+
... }
|
|
157
|
+
... )
|
|
123
158
|
... )
|
|
159
|
+
shape: (4, 2)
|
|
160
|
+
┌─────────────────────────────────┬───────┐
|
|
161
|
+
│ Point ┆ Value │
|
|
162
|
+
│ --- ┆ --- │
|
|
163
|
+
│ str ┆ i64 │
|
|
164
|
+
╞═════════════════════════════════╪═══════╡
|
|
165
|
+
│ Parameters: var1=-12k, var_b=1… ┆ null │
|
|
166
|
+
│ 1 ┆ 10 │
|
|
167
|
+
│ Parameters: var3=-12k ┆ 1 │
|
|
168
|
+
│ 2 ┆ null │
|
|
169
|
+
└─────────────────────────────────┴───────┘
|
|
124
170
|
>>> scan_nested_params(df, scan_col="Point")
|
|
125
171
|
['var1', 'var_b']
|
|
126
172
|
"""
|
|
@@ -169,12 +215,24 @@ def explode_nested_params(
|
|
|
169
215
|
|
|
170
216
|
Examples
|
|
171
217
|
--------
|
|
172
|
-
>>>
|
|
173
|
-
...
|
|
174
|
-
...
|
|
175
|
-
...
|
|
176
|
-
...
|
|
218
|
+
>>> (
|
|
219
|
+
... df := pl.DataFrame(
|
|
220
|
+
... {
|
|
221
|
+
... "Point": ["Parameters: ds=0, gs=1", "1", "2"],
|
|
222
|
+
... "Value": [None, 10, 20],
|
|
223
|
+
... }
|
|
224
|
+
... )
|
|
177
225
|
... )
|
|
226
|
+
shape: (3, 2)
|
|
227
|
+
┌────────────────────────┬───────┐
|
|
228
|
+
│ Point ┆ Value │
|
|
229
|
+
│ --- ┆ --- │
|
|
230
|
+
│ str ┆ i64 │
|
|
231
|
+
╞════════════════════════╪═══════╡
|
|
232
|
+
│ Parameters: ds=0, gs=1 ┆ null │
|
|
233
|
+
│ 1 ┆ 10 │
|
|
234
|
+
│ 2 ┆ 20 │
|
|
235
|
+
└────────────────────────┴───────┘
|
|
178
236
|
>>> explode_nested_params(df, keys=["ds", "gs"])
|
|
179
237
|
shape: (2, 4)
|
|
180
238
|
┌───────┬───────┬─────┬─────┐
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pydantic ready ``quantiphy.Quantity`` units
|
|
3
|
+
|
|
4
|
+
Examples
|
|
5
|
+
--------
|
|
6
|
+
|
|
7
|
+
>>> from pydantic import BaseModel
|
|
8
|
+
>>> class RC(BaseModel):
|
|
9
|
+
... resistance: Resistance
|
|
10
|
+
... time: Time
|
|
11
|
+
>>> rc = RC(resistance="4.7 kOhm", time="1n")
|
|
12
|
+
>>> rc.resistance
|
|
13
|
+
Resistance('4.7 kOhm')
|
|
14
|
+
>>> rc.time
|
|
15
|
+
Time('1 ns')
|
|
16
|
+
>>> rc.model_dump_json()
|
|
17
|
+
'{"resistance":4700.0,"time":1e-9}'
|
|
18
|
+
>>> rc.model_dump_json(context={"quantity_format": "str"})
|
|
19
|
+
'{"resistance":"4.7 kOhm","time":"1 ns"}'
|
|
20
|
+
>>> rc.model_dump_json(context={"quantity_format": "as_tuple"})
|
|
21
|
+
'{"resistance":[4700.0,"Ohm"],"time":[1e-9,"s"]}'
|
|
22
|
+
|
|
23
|
+
Notes
|
|
24
|
+
-----
|
|
25
|
+
|
|
26
|
+
The following approaches were considered based on Pydantic docs and
|
|
27
|
+
discussions with a LLM model for parts which confused me.
|
|
28
|
+
|
|
29
|
+
Chosen approach: Put the pydantic hook on the type itself. Works
|
|
30
|
+
everywhere automatically, best autocomplete, but ties Time to pydantic
|
|
31
|
+
and uses a lower-level API pydantic calls experimental.
|
|
32
|
+
|
|
33
|
+
- Annotate as str, secretly swap it to Time in a validator.
|
|
34
|
+
Rejected since: the type hint lie, so
|
|
35
|
+
autocomplete/type-checker/serialization all get confused.
|
|
36
|
+
- model_validator(mode="after"). Only makes sense when one field needs to be derived from
|
|
37
|
+
another (cross-field logic), not for a single field's own conversion.
|
|
38
|
+
- Approach from pydantic_pint seemed unncessary, but might have to consider if
|
|
39
|
+
there are any issues.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from typing import TYPE_CHECKING, Any, Self
|
|
43
|
+
|
|
44
|
+
from pydantic import (
|
|
45
|
+
GetCoreSchemaHandler,
|
|
46
|
+
GetJsonSchemaHandler,
|
|
47
|
+
SerializationInfo,
|
|
48
|
+
)
|
|
49
|
+
from pydantic.json_schema import JsonSchemaValue
|
|
50
|
+
from pydantic_core import core_schema
|
|
51
|
+
from quantiphy import (
|
|
52
|
+
QuantiPhyError,
|
|
53
|
+
UnitConversion,
|
|
54
|
+
as_real,
|
|
55
|
+
as_tuple,
|
|
56
|
+
)
|
|
57
|
+
from quantiphy import Quantity as _Quantity
|
|
58
|
+
|
|
59
|
+
UnitConversion(["ohm", "Ohms", "ohms", "Ω"], "Ohm")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class Quantity(_Quantity):
|
|
63
|
+
"""
|
|
64
|
+
Wrapper around ``quantiphy.Quantity`` to add Pydantic support.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
units: str = ""
|
|
68
|
+
|
|
69
|
+
if TYPE_CHECKING:
|
|
70
|
+
|
|
71
|
+
def __new__(
|
|
72
|
+
cls,
|
|
73
|
+
value: float | str | Self,
|
|
74
|
+
*args: Any,
|
|
75
|
+
**kwargs: Any,
|
|
76
|
+
) -> Self: ...
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def __get_pydantic_core_schema__(
|
|
80
|
+
cls, _source_type, _handler: GetCoreSchemaHandler
|
|
81
|
+
) -> core_schema.CoreSchema:
|
|
82
|
+
"""
|
|
83
|
+
Generate a Pydantic core schema for the Quantity type.
|
|
84
|
+
|
|
85
|
+
The schema is union based with the serialization support for
|
|
86
|
+
as_real, str, and as_tuple serialization options using the
|
|
87
|
+
context dictionary.
|
|
88
|
+
"""
|
|
89
|
+
return core_schema.union_schema(
|
|
90
|
+
[
|
|
91
|
+
core_schema.is_instance_schema(cls),
|
|
92
|
+
core_schema.no_info_plain_validator_function(cls._parse),
|
|
93
|
+
],
|
|
94
|
+
mode="left_to_right",
|
|
95
|
+
serialization=core_schema.plain_serializer_function_ser_schema(
|
|
96
|
+
cls._serialize,
|
|
97
|
+
info_arg=True,
|
|
98
|
+
return_schema=core_schema.union_schema(
|
|
99
|
+
[
|
|
100
|
+
core_schema.str_schema(),
|
|
101
|
+
core_schema.float_schema(),
|
|
102
|
+
core_schema.tuple_schema(
|
|
103
|
+
items_schema=[
|
|
104
|
+
core_schema.float_schema(),
|
|
105
|
+
core_schema.str_schema(),
|
|
106
|
+
],
|
|
107
|
+
strict=True,
|
|
108
|
+
),
|
|
109
|
+
]
|
|
110
|
+
),
|
|
111
|
+
),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
@classmethod
|
|
115
|
+
def _parse(cls, value: float | str) -> Self:
|
|
116
|
+
"""
|
|
117
|
+
Parse value using Quantiphy's native mechanism.
|
|
118
|
+
"""
|
|
119
|
+
try:
|
|
120
|
+
return cls(value)
|
|
121
|
+
except QuantiPhyError as error:
|
|
122
|
+
# TODO: Consider custom error message for user.
|
|
123
|
+
raise ValueError(str(error)) from error
|
|
124
|
+
|
|
125
|
+
@classmethod
|
|
126
|
+
def __get_pydantic_json_schema__(
|
|
127
|
+
cls,
|
|
128
|
+
_core_schema: core_schema.CoreSchema,
|
|
129
|
+
handler: GetJsonSchemaHandler,
|
|
130
|
+
) -> JsonSchemaValue:
|
|
131
|
+
"""Generate a JSON schema for the Quantity type."""
|
|
132
|
+
json_schema = handler(core_schema.str_schema())
|
|
133
|
+
json_schema["description"] = (
|
|
134
|
+
f"Physical quantity with units: {cls.units}"
|
|
135
|
+
if cls.units
|
|
136
|
+
else "Quantiphy quantity without units"
|
|
137
|
+
)
|
|
138
|
+
json_schema["examples"] = (
|
|
139
|
+
[f"1 {cls.units}", f"1 n{cls.units}", f"1 M{cls.units}"]
|
|
140
|
+
if cls.units
|
|
141
|
+
else ["1", "1n", "1M"]
|
|
142
|
+
)
|
|
143
|
+
return json_schema
|
|
144
|
+
|
|
145
|
+
def _serialize(
|
|
146
|
+
self, info: SerializationInfo
|
|
147
|
+
) -> str | float | tuple[float, str]:
|
|
148
|
+
"""Customizable options to serialize the quantity."""
|
|
149
|
+
if isinstance(info.context, dict):
|
|
150
|
+
quantity_format = info.context.get("quantity_format", "as_real")
|
|
151
|
+
match quantity_format:
|
|
152
|
+
case "as_real":
|
|
153
|
+
return as_real(self)
|
|
154
|
+
case "str":
|
|
155
|
+
return str(self)
|
|
156
|
+
case "as_tuple":
|
|
157
|
+
return as_tuple(self)
|
|
158
|
+
case _:
|
|
159
|
+
raise ValueError(
|
|
160
|
+
f"Unknown quantity_format value: {quantity_format}"
|
|
161
|
+
)
|
|
162
|
+
else:
|
|
163
|
+
return as_real(self)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class Time(Quantity):
|
|
167
|
+
"""
|
|
168
|
+
Time quantity with units of seconds.
|
|
169
|
+
|
|
170
|
+
Attributes
|
|
171
|
+
----------
|
|
172
|
+
units : str, default="s"
|
|
173
|
+
Unit symbol.
|
|
174
|
+
|
|
175
|
+
Examples
|
|
176
|
+
--------
|
|
177
|
+
>>> Time("1 ns")
|
|
178
|
+
Time('1 ns')
|
|
179
|
+
>>> Time(1e-9)
|
|
180
|
+
Time('1 ns')
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
units: str = "s"
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class Voltage(Quantity):
|
|
187
|
+
"""
|
|
188
|
+
Voltage quantity with units of Volts.
|
|
189
|
+
|
|
190
|
+
Attributes
|
|
191
|
+
----------
|
|
192
|
+
units : str, default="V"
|
|
193
|
+
Unit symbol.
|
|
194
|
+
|
|
195
|
+
Examples
|
|
196
|
+
--------
|
|
197
|
+
>>> Voltage("1 uV")
|
|
198
|
+
Voltage('1 uV')
|
|
199
|
+
>>> Voltage(1e-9)
|
|
200
|
+
Voltage('1 nV')
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
units: str = "V"
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class Current(Quantity):
|
|
207
|
+
"""
|
|
208
|
+
Current quantity with units of Amperes.
|
|
209
|
+
|
|
210
|
+
Attributes
|
|
211
|
+
----------
|
|
212
|
+
units : str, default="A"
|
|
213
|
+
Unit symbol.
|
|
214
|
+
|
|
215
|
+
Examples
|
|
216
|
+
--------
|
|
217
|
+
>>> Current("1 uA")
|
|
218
|
+
Current('1 uA')
|
|
219
|
+
>>> Current(1e-9)
|
|
220
|
+
Current('1 nA')
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
units: str = "A"
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class Power(Quantity):
|
|
227
|
+
"""
|
|
228
|
+
Power quantity with units of Watts.
|
|
229
|
+
|
|
230
|
+
Attributes
|
|
231
|
+
----------
|
|
232
|
+
units : str, default="W"
|
|
233
|
+
Unit symbol.
|
|
234
|
+
|
|
235
|
+
Examples
|
|
236
|
+
--------
|
|
237
|
+
>>> Power("12 mW")
|
|
238
|
+
Power('12 mW')
|
|
239
|
+
>>> Power(1e3)
|
|
240
|
+
Power('1 kW')
|
|
241
|
+
"""
|
|
242
|
+
|
|
243
|
+
units: str = "W"
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class Resistance(Quantity):
|
|
247
|
+
"""
|
|
248
|
+
Resistance quantity with units of Ohms/ohm/etc.
|
|
249
|
+
|
|
250
|
+
Attributes
|
|
251
|
+
----------
|
|
252
|
+
units : str, default="Ohm"
|
|
253
|
+
Unit symbol.
|
|
254
|
+
|
|
255
|
+
Examples
|
|
256
|
+
--------
|
|
257
|
+
>>> Resistance("5 ohms")
|
|
258
|
+
Resistance('5 Ohm')
|
|
259
|
+
>>> Resistance("5 mΩ")
|
|
260
|
+
Resistance('5 mOhm')
|
|
261
|
+
>>> Resistance(50)
|
|
262
|
+
Resistance('50 Ohm')
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
units: str = "Ohm"
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class Capacitance(Quantity):
|
|
269
|
+
"""
|
|
270
|
+
Capacitance quantity with units of Farads.
|
|
271
|
+
|
|
272
|
+
Attributes
|
|
273
|
+
----------
|
|
274
|
+
units : str, default="F"
|
|
275
|
+
Unit symbol.
|
|
276
|
+
|
|
277
|
+
Examples
|
|
278
|
+
--------
|
|
279
|
+
>>> Capacitance("5e-15 F")
|
|
280
|
+
Capacitance('5 fF')
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
units: str = "F"
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class Inductance(Quantity):
|
|
287
|
+
"""
|
|
288
|
+
Inductance quantity with units of Henry.
|
|
289
|
+
|
|
290
|
+
Attributes
|
|
291
|
+
----------
|
|
292
|
+
units : str, default="H"
|
|
293
|
+
Unit symbol.
|
|
294
|
+
|
|
295
|
+
Examples
|
|
296
|
+
--------
|
|
297
|
+
>>> Inductance(5e-9)
|
|
298
|
+
Inductance('5 nH')
|
|
299
|
+
"""
|
|
300
|
+
|
|
301
|
+
units: str = "H"
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class Temperature(Quantity):
|
|
305
|
+
"""
|
|
306
|
+
Temperature quantity with units of Celsius.
|
|
307
|
+
|
|
308
|
+
Attributes
|
|
309
|
+
----------
|
|
310
|
+
units : str, default="C"
|
|
311
|
+
Unit symbol.
|
|
312
|
+
|
|
313
|
+
Examples
|
|
314
|
+
--------
|
|
315
|
+
>>> Temperature(25)
|
|
316
|
+
Temperature('25 C')
|
|
317
|
+
"""
|
|
318
|
+
|
|
319
|
+
units: str = "C"
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
class Frequency(Quantity):
|
|
323
|
+
"""
|
|
324
|
+
Frequency quantity with units of Hertz.
|
|
325
|
+
|
|
326
|
+
Attributes
|
|
327
|
+
----------
|
|
328
|
+
units : str, default="Hz"
|
|
329
|
+
Unit symbol.
|
|
330
|
+
|
|
331
|
+
Examples
|
|
332
|
+
--------
|
|
333
|
+
>>> Frequency(5e9)
|
|
334
|
+
Frequency('5 GHz')
|
|
335
|
+
"""
|
|
336
|
+
|
|
337
|
+
units: str = "Hz"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Top-level package for Analog Daddy."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|