次元と単位¶
Graphcal は次元(コンパイル時の型)と単位(値レベルのスケーリング係数)を分離しています。このページでは、次元の代数、単位の定義、変換、そしてプレリュードについて説明します。
次元¶
次元は物理量の種類(例えば長さ、時間、質量)を表します。次元は基本次元の上の代数を形成します。
基本次元¶
プレリュードは 8 つの基本次元を提供します。
| 基本次元 | 記号 |
|---|---|
Length |
L |
Time |
T |
Mass |
M |
Temperature |
Θ |
ElectricCurrent |
I |
Amount |
N |
LuminousIntensity |
J |
Angle |
A |
加えて、特別な次元 Dimensionless(単位元)があります。
派生次元¶
プレリュードは Velocity、Acceleration、Force、Energy、Power、Frequency、Pressure、Area、Volume といった一般的な派生次元をすでに提供しています。派生次元を定義するのは、モデルがプロジェクト固有の量の種類を必要とする場合だけにしてください。
派生次元は代数的な式を使います。許可される演算は次のとおりです。
| 演算 | 構文 | 例 |
|---|---|---|
| 乗算 | A * B |
Mass * Length |
| 除算 | A / B |
Length / Time |
| べき乗 | A^n |
Time^2 |
指数は非ゼロの整数(Time^2、Length^-1)または括弧で囲まれた有理数(Length^(1/2)、m^(-3/2))です。同じ指数の文法が、変換先を含む次元式と単位式の両方に適用されます。
ユーザー定義の基本次元¶
新しい基本次元は base dim 構文で宣言します。
その上で、そこから派生次元を構築します。
次元代数の規則¶
内部的には、次元は有理数の指数を持つ基本次元の積として表現されます。コンパイラーは次の計算を行います。
Length * Length = Length^2Length / Length = Dimensionless(Mass * Length / Time^2) * (Length / Time) = Mass * Length^2 / Time^3
2 つの式が次元的に互換であるのは、同じ正準形に簡約される場合に限ります。
単位¶
単位は特定の次元に結び付いた値レベルのスケーリング係数です。単位は数値が SI 基本単位にどう対応するかを定義します。
プレリュードの単位¶
| 次元 | 単位 |
|---|---|
| Length | m, km (1000 m), cm (0.01 m), mm (0.001 m) |
| Time | s, min (60 s), h (3600 s) |
| Mass | kg, g (0.001 kg) |
| Temperature | K |
| ElectricCurrent | A |
| Amount | mol |
| LuminousIntensity | cd |
| Angle | rad, deg (π/180 rad) |
| Force | N (kg*m/s^2), kN (1000 N) |
| Energy | J (N*m), kJ (1000 J) |
| Power | W (J/s), kW (1000 W) |
| Pressure | Pa (N/m^2), kPa (1000 Pa), MPa (1e6 Pa) |
| Frequency | Hz (1/s) |
カスタム単位の定義¶
const unit mile: Length = 1609.344 m;
const unit knot: Velocity = 0.514444 m/s;
base unit bit: Information; // canonical unit for a user-defined dimension
const unit byte: Information = 8.0 bit;
const unit kB: Information = 1000.0 byte;
base unit 宣言(= ... を持たない base unit bit: Information;)は、ユーザー定義の基本次元に対する唯一の正準単位を定義します。これは、その次元が単一の基本次元に簡約され、かつその基本次元がまだ基本単位を持たない場合にのみ有効です。プレリュードの次元はすでに正準単位を持ち、複合的な派生次元はすでに正準表現を持つため、base unit furlong: Length; や base unit kt: Velocity; のような宣言は拒否されます(D036)。追加の単位はすべて明示的な = ... のスケール付きで定義してください。これにより、偶発的なスケール 1 のエイリアスが無関係な単位を黙って同一視してしまうことを防ぎます。基本単位以外の単位は常に = ... の本体を持たなければなりません。コンパイル時のスケールには const unit を、実行時依存のスケールには単なる unit を使います。定数性はキーワードによって決まります。静的に見える本体を持つ単なる unit であっても、const node からは使用できません。const unit のスケールは @ 参照を含むことができず、const node の本体ではプレリュードの単位、base unit、const unit 宣言のみを使用できます。
素の Temperature に対するユーザー単位定義は拒否されます(D014)。一般的な温度単位(°C、°F)はオフセットを持つアフィンなスケールであり、乗法的な単位定義では表現できません。const unit C: Temperature = 1.0 K; は 300 K を無意味な 300 C として表示してしまいます。絶対温度は K のままにするか、オフセットを式の中で明示的にモデル化してください。Temperature を含む複合次元(例えば Temperature / Time)は引き続き単位定義を受け付けます。差や変化率ではオフセットが打ち消されるためです。
右辺の単位式は宣言された次元とちょうど一致しなければなりません。例えば const unit wrong: Length = 1.0 h; は、時間(hour)のスケールを Length に付与するのではなく拒否されます。この規則は、複合的および逆数の単位式を含め、静的な定義にも実行時依存の定義にも等しく適用されます。
単位のスケール係数は正かつ有限でなければなりません。const unit z: Length = 0.0 m; のような静的な単位定義、負のスケール、オーバーフローするスケールは拒否されます。動的な単位スケールには、評価時に同じ具体値の検査が適用されます。
単位のスコープ¶
静的な単位は、インポートされる他のすべてのカテゴリーと同じスコープ規則に従います。修飾なしの参照(@a -> mile)は、ファイル自身の単位スコープ、すなわちプレリュードの単位、ファイル自身の宣言、選択的にインポートされた静的単位(import app.units::{ unit mile };)に対して解決されます。エイリアス付きでインポートされたモジュールは、その pub const unit 宣言をそのエイリアスの下で公開します。import app.units as u; により、静的単位は u::mile として、かつ u::mile としてのみ利用可能になります。
import app.units as u; // defines `pub const unit mile: Length = 1609.344 m;`
param a: Length = 3218.688 m;
node b: Length = @a -> u::mile; // 2 u::mile
エイリアス経由でインポートされた単位を修飾なしの名前で参照すると、未知の単位エラー(D003)になります。単位メンバーは、インポートされる他のすべてのカテゴリーと同じ明示的な境界を使います。すなわち alias::unit、またはドット区切りで子 DAG をたどった後の alias.child::unit です。
各エイリアスが自身の名前をスコープするため、2 つのモジュールが同じ単位名を異なる形で定義しても両方とも利用可能なままです。ua::mile と ub::mile が衝突することはありません。同じ修飾なしの名前を 2 つのモジュールから選択的にインポートすることは、他の名前の衝突と同様に重複インポートとして拒否されます。
ローカルの const unit 定義は、どちらのインポート形式でも、本体の中でインポートされた const 単位を参照できます。import app.units as u; の後では const unit halfmile: Length = 0.5 u::mile;、import app.units::{ unit mile }; の後では const unit halfmile: Length = 0.5 mile; と書けます。
動的単位¶
単位のスケール係数は、@ 参照を含む括弧付きの式を使うことで、実行時の値(パラメーターやノード)に依存させることができます。
base dim Money;
base unit USD: Money;
param usd_per_eur: Dimensionless = 1.08;
unit EUR: Money = (@usd_per_eur) USD;
ここでは 1 EUR = usd_per_eur USD です。スケール係数は実行時に評価されるため、評価時に usd_per_eur を束縛する(例えば --param 'usd_per_eur=1.20' によって)と、EUR 建てのすべての値がそれに応じて変わります。
動的単位の定義は、その単位が一度も使われなくても完全に検査されます。スケール式はスカラー型 Dimensionless を持たなければなりません。次元付きの量、Bool、Int、構造体やユニオン、インデックス付きの値は拒否されます(D032)。実行時のパラメーターとノードは参照できますが、アサーションと外部プラグイン関数は参照できません。右辺の単位式は引き続き宣言された次元とちょうど一致しなければなりません(D031)。
評価まで遅延されるのはスケールの具体値だけであり、それは参照されるパラメーターとノードが計算された後に行われます。その値は正かつ有限でなければなりません。そうでない場合、その単位を使うすべての宣言は、フォールバックのスケールを受け取るのではなく失敗します。
単なる unit は、そのパラメーターとノードがスケールを決定する具体的な実行時 DAG インスタンスに属します。この性質はソース上のマーカーに従うものであり、定数畳み込みには従いません。すべての単なる unit は、右辺に @ 参照を含まない場合であってもインポート不可です(M025)。ブループリントとして安定した単位が import 境界を越える必要がある場合は const unit を使ってください。
各 include や直接呼び出しは、それぞれの束縛に基づいて動的単位のスケールを決定し、出力もその表示単位を保持します。モジュール include ではインスタンスの名前空間(fx::EUR)を通して単位を使え、選択的 include ではエイリアス(unit EUR as euros)を付けられます。呼び出し側の注釈で使う次元や基本単位には、引き続き明示的な import 宣言が必要です。
単位の使用¶
数値リテラルに単位を付けます。
param altitude: Length = 200.0 km;
param duration: Time = 1.5 h;
const node c: Velocity = 299792458.0 m/s;
複合的な単位式もサポートされています。
単位のサフィックスは、逆数の略記 1/unit や括弧で囲まれた単位グループで始めることもできます。
逆数の分子として有効なのは、ちょうど整数 1 だけです。フォーマッターは冗長なグループ化を正規化するため、3.0 (m/s) は 3.0 m/s として整形されます。
量リテラルが生成する SI 値は有限のままでなければなりません。例えば、数値と単位スケールの積がオーバーフローするリテラルは、inf ではなくエラーになります。
単位変換¶
-> 演算子は、値を同じ次元の別の単位に変換します。
param alt: Length = 200.0 km;
node alt_m: Length = @alt -> m; // 200000.0 m
node alt_cm: Length = @alt -> cm; // 20000000.0 cm
変換元と変換先は同じ次元を共有しなければなりません。互換性のない次元の間で変換しようとするとコンパイル時エラーになります。
-> はインデックス付きの値に対して要素ごとに分配されます。Length[R](または多軸の Length[R, C])に対する @x -> km は、表示単位をすべての要素に適用します。
複合的な変換先は、単位ラベルの表示方法に合わせて 1/unit の逆数略記をサポートします。@f -> 1/min は @f -> min^-1 と等価です。分子として許されるのはリテラルの 1 だけです。
変換が変更するのは表示単位だけであり、計算に使われる SI 値は変わりません。参照、フィールドアクセス、インデックスアクセス、DAG の出力は、選択された表示単位を保持します。Datetime 値のタイムゾーン表示も同様です。
表示変換を行えない場合、Graphcal は**表示診断(presentation diagnostic)**を報告し、有効な SI 値を保持します。計算、ドメイン制約、アサーションの失敗が抑制されるわけではありません。特に、量リテラルには SI 値を計算するための有効な単位スケールが必要であり、スケールが無効ならその計算は失敗します。
-> は連鎖しません。1 つの式が持てる変換先は最大 1 つです。修飾なしの連鎖 @alt -> km -> m(パースエラー)も、括弧付きの形 (@alt -> km) -> m(D012 次元検査エラー)も拒否されます。効果を持ちうるのは最も外側の変換先だけなので、内側の変換はタイプミスかデッドコードのどちらかです。
変換は、その表示効果が到達できる場所、すなわち宣言本体のトップレベル、if/match の分岐、コンストラクターのフィールド初期化子、マップリテラルの要素、for 内包表記の本体、scan/unfold の init や本体(および scan のソース)でのみ許可されます。各再帰ステップは選択された表示を保持し、注釈のない計算ステップは初期の表示設定を維持します。それ以外の場所(算術のオペランド、関数の引数、比較、条件、assert の本体)では変換は黙って無効になってしまうため、拒否されます(D013)。
結果の次元の計算¶
@a + @b のような式を書くと、コンパイラーはオペランドから結果の次元を計算します。
| 式 | 結果の次元 |
|---|---|
a + b |
a と b と同じ(一致が必要) |
a - b |
a と b と同じ(一致が必要) |
a * b |
次元の積 |
a / b |
次元の商 |
a ^ n |
次元の n 乗 |
sqrt(a) |
次元の ½ 乗 |
例えば、sqrt(Length^2 / Time^2) は Length / Time(= Velocity)と推論されます。