Modelicaのシンプルな戦闘モデル



良い一日! 最近、 Modelica言語やOpenModelicaの無料実装などのモデリングツールについて学びましたが、これについてHabréに関する記事が1つしかなかったことに驚きました。 トピックはやや珍しいので、私の頭から取られたいくつかの例によって、詳細は私たち自身の肌に理解されなければなりませんでした。 この記事では、言語の概念(基本)のいくつかを理解しながら、単純な戦闘モデル(たとえば)を作成する方法について説明します。







注:この記事の準備には、最新のOpenModelicaナイトビルド(rev18625)が使用されました。 インストール時には、スペースなしのパスを指定する必要があることに注意してください。



挑戦する



戦争を、異なる力(比ではあるが強さではない)を所有している2人の敵が同じ目標を達成するプロセスとして想像してください。 明確にするために、歩兵(地上部隊)が足を踏み入れたときに領土が征服されたと仮定します。 海は領土に含まれていません。 敵の領土全体が征服されたときに勝利がカウントされます。



一般的に言えば、戦争には経済も含まれますが、そのようなことをモデル化することはスペースを取りすぎます。 ここで、海軍、陸軍、航空の3種類の軍隊について考えてみましょう(核兵器も考慮しません)。 それぞれに攻撃に対する抵抗力、またはヘルス(1ユニット)、および攻撃自体の強さ(弾丸の数で測定します)があります。 そして、数ユニットごとに、新しいユニットが戦闘に入ります。



戦闘は、前線が水平方向に走る広場で行われ、右側では、海が操業を行います。 正方形の辺の長さはLで表されます(明確にするために、100キロメートルとします)。 その後、サイドAが勝ったエリアはA * Lに等しくなり、敵に征服されます-(LA)*L。Aの変化は力のバランスに依存します。 前部の力が大きいほど、Aの変化が大きくなります(より大きな力が有利になります)。 明確にするために、片側の歩兵ユニットは、単位時間あたり1キロずつ前線を進めます(戦闘がない場合)。



概念Modelica



Modelicaでのモデリングの基礎はクラスです(および宣言/アプリケーションの観点からのバリエーションですが、これについては後で詳しく説明します)。 Modelicaのクラスは、フィールドとメソッド(Modelicaでは関数)だけでなく、変数を相互にバインドする方程式も含むため、通常のクラスとは多少異なります。 フィールドには、さまざまな種類の「変動性」-定数(および定数があります)、パラメーター(現在のシミュレーションでは変更されません)、および実際には変数もあります。 クラスのフィールドは、組み込み型(ブール、実数など)のオブジェクト、またはカスタム型のいずれかです。 より複雑な概念は、「テンプレート」機能です。フィールドのタイプ、その再定義を置き換えます。



クラスは継承できます(複数のダイヤモンドのみを含む)。 Modelicaの場合、これは、方程式を含む、継承されたすべてのコンテンツが継承されたクラスにコピーされることを意味します。 そして、ここでModelicaの主なルールに言及する必要があります-変​​数の方程式の数は変数の数に対応する必要があります。 これ以上でもそれ以下でもありません。



問題を解決する例



クラスの最も単純なタイプであるレコードには、方程式を含めることはできません。

最初に、抽象的なタイプの戦闘ユニットを定義します。

UnitData.mo
record UnitData "Abstract army unit record" parameter Real unitHealth; parameter Real unitAttack; parameter Real supplyTime; parameter Real supplyNumber; end UnitData;
      
      







このコードは、4つの初期化されていないパラメーターを宣言します。 クラスと変数を宣言した後、引用符でドキュメント文字列を追加できます。 一般に、Modelica-C ++-スタイルのコメント、つまり//または/ * * /(この種のコメントは環境によって無視されます)。



次のステップは、オブジェクトが軍事ユニットであり、軍事ユニット+数量と同じことを特徴とする方程式を持つクラスを宣言することです。

Forces.mo
 class Forces "Abstract army forces class" extends UnitData; parameter Real startNumber = 0; Real number (start = startNumber); Boolean destroyed; equation destroyed = number < 0; when destroyed then reinit(number, 0); end when; when sample(supplyTime, supplyTime) then reinit(number, pre(number) + supplyNumber); end when; end Forces;
      
      







このファイルの例を使用すると、継承をトレースしたり、Modelicaイベントを操作したりできます。

破壊された方程式=数値<0; 特別なreinit演算子が使用される場合、最初のブロック内で質問を引き起こしてはなりません。これは、変数値を2番目のパラメーターとして式の結果に設定します。 pre(前のステップの値)と同様に、reinitはwhenブロック内でのみ使用できます。 方程式(つまり方程式)がx = x + 1のようにならないように、preを使用する必要があります。



組み込みのサンプル関数は、シミュレーション時間(時間)が最初のパラメーター(最初の呼び出し)と等しい場合にtrueを返し、次に2番目のパラメーターに等しい間隔で(この場合は等しい-ヘルプはsupplyTimeの瞬間から等間隔で到着します)。



次に、敵の軍隊を表すクラスが役立ちます。

EnemyForces.mo
 class EnemyForces "Enemy forces references" replaceable Forces enemyAir; replaceable Forces enemySea; replaceable Forces enemyLand; end EnemyForces;
      
      









変数を置換可能として指定すると、EnemyForcesオブジェクトを宣言するときに変数のタイプを再定義できます。



今、私たちは最も重要なものに来ます。 実際には軍隊を表すクラス。

Army.mo
 class Army class LandArmy = Forces(unitHealth = 100, unitAttack = 400, supplyTime = 0.25, supplyNumber = 800); class AirArmy = Forces(unitHealth = 400, unitAttack = 750, supplyTime = 0.5, supplyNumber = 400); class SeaArmy = Forces(unitHealth = 3000, unitAttack = 7000, supplyTime = 1, supplyNumber = 20); SeaArmy seaArmy; AirArmy airArmy; LandArmy landArmy; EnemyForces enemyForces (redeclare Army.AirArmy enemyAir, redeclare Army.SeaArmy enemySea, redeclare Army.LandArmy enemyLand); Real armyForce; equation armyForce = seaArmy.unitHealth * seaArmy.number + airArmy.unitHealth * airArmy.number + landArmy.unitHealth * landArmy.number; der(seaArmy.number) = if not seaArmy.destroyed then -(enemyForces.enemyAir.number * enemyForces.enemyAir.unitAttack + enemyForces.enemySea.number * enemyForces.enemySea.unitAttack) / seaArmy.unitHealth else 0; der(airArmy.number) = if not airArmy.destroyed then -(enemyForces.enemyAir.number * enemyForces.enemyAir.unitAttack + enemyForces.enemySea.number * enemyForces.enemySea.unitAttack + enemyForces.enemyLand.number * enemyForces.enemyLand.unitAttack) / airArmy.unitHealth else 0; der(landArmy.number) = if not landArmy.destroyed then -(enemyForces.enemyAir.number * enemyForces.enemyAir.unitAttack + enemyForces.enemyLand.number * enemyForces.enemyLand.unitAttack) / landArmy.unitHealth else 0; end Army;
      
      









ここで何が起こっていますか。 最初に、特定のクラスの軍事支部を作成し、それらを既存のForcesクラスと単純に同等化し、そのパラメーターを初期化します。 オブジェクトをすぐに宣言します。 次は、置換可能な変数をredeclareキーワードで置き換えながら、敵軍への参照として使用されるオブジェクトを作成します。 「分析の結論」のために、純粋にarmyForce変数が必要です。

方程式について-最初の方程式は、軍の力が存在するすべてのユニットの装甲であることを意味します。

次の3つの式の意味:

  1. 海軍が破壊されない場合、あらゆる瞬間に、空軍と海軍によるダメージはすべての装甲から減少し、
  2. 空軍が破壊されない場合、いつでも敵軍によるダメージはすべての装甲から減少し、
  3. 同様に地上部隊については、海軍がそれらを損傷しないことを除いて




der(var)という表記は、var(変数)の時間微分です。



最後に、武力紛争自体について説明します。

Conflict.mo
 model Conflict "Conflict of two armies model" parameter Real armyASea = 200; parameter Real armyAAir = 1000; parameter Real armyALand = 4000; parameter Real armyBSea = 100; parameter Real armyBAir = 1800; parameter Real armyBLand = 3600; Army armyA ( seaArmy.startNumber = armyASea, airArmy.startNumber = armyAAir, landArmy.startNumber = armyALand, enemyForces.enemyAir.startNumber = armyBAir, enemyForces.enemySea.startNumber = armyBSea, enemyForces.enemyLand.startNumber = armyBLand ); Army armyB ( seaArmy.startNumber = armyBSea, airArmy.startNumber = armyBAir, landArmy.startNumber = armyBLand, enemyForces.enemyAir.startNumber = armyAAir, enemyForces.enemySea.startNumber = armyASea, enemyForces.enemyLand.startNumber = armyALand ); parameter Real L = 100; parameter Real landArmySpeed = 1; Real A (start = L / 2); equation armyA.enemyForces.enemyAir.number = armyB.airArmy.number; armyA.enemyForces.enemySea.number = armyB.seaArmy.number; armyA.enemyForces.enemyLand.number = armyB.landArmy.number; armyB.enemyForces.enemyAir.number = armyA.airArmy.number; armyB.enemyForces.enemySea.number = armyA.seaArmy.number; armyB.enemyForces.enemyLand.number = armyA.landArmy.number; when A > L then terminate("Army A wins"); end when; when A < 0 then terminate("Army B wins"); end when; der (A) = (armyA.landArmy.number - armyB.landArmy.number) * landArmySpeed / L; end Conflict;
      
      









ここでは、キーワードモデルが使用されます。 これもクラスですが、最適化に使用できるのはモデルのみです(これについては後ほど)。

ここでは詳しく説明しませんが、戦争当事者の宣言に加えて、敵AがBであり、その逆も同様であるということを確立する方程式があります。 2つのwhen-conditionsは、何が起こったかを示すマークでモデリングを終了することを意味します。



打ち上げと結果



モデルを実行するには、次のように入力してシェルOpenModelicaを使用できます。

シェルOpenModelicaコマンド
 loadModel(Modelica) loadFile("d:/path/UnitData.mo") loadFile("d:/path/Forces.mo") loadFile("d:/path/EnemyForces.mo") loadFile("d:/path/Army.mo") loadFile("d:/path/Conflict.mo") simulate(Conflict, stopTime = 2) plot({armyA.armyForce, armyB.armyForce}, xRange = {0, 2}) plot({armyA.landArmy.number, armyB.landArmy.number, armyA.seaArmy.number, armyB.seaArmy.number, armyA.airArmy.number, armyB.airArmy.number}, xRange = {0, 2}) plot(A, xRange = {0, 2})
      
      









対応する構造を下の写真に示します。



これは、時間の力の比率がどのように見えるかです。



これは、部隊のタイプに関する時間の力の相関関係がどのように見えるかです。



そして、最前線も同様です。 グラフから、軍隊Aが最初に勝利したが、軍隊Bが勝利したことが明らかです。



OpenModelicaによる最適化



モデル化できるものに加えて、モデルによって提起された質問への回答も見つけることができます。 たとえば、陸軍Bが最初の最低地上部隊数で勝つことを自問すると、OpenModelicaに含まれるOMOptimツールを使用すると、次の図を取得できます。





結論の代わりに



OpenModelicaは、英語に精通している人のために文書化されています。 一般的に、このツールは有望です。 これまでのところ、エラーメッセージはあまり語られておらず、デバッグ情報は簡単に解釈されるとは言っていません。 しかし、一般的には、十分な関心と時間で-すべてが敗北します。



それでも結論の代わりに



戦闘モデルが非常に単純であり、それが反映されている場合、現実は、(この場合、最小限の)軍隊の優位性が勝利を保証しない可能性があるという事実にのみあり、最終結果はシステムの構造(誰が誰に当たるか)によって決定されます。初期条件よりも。



便利なリンク



優れたチュートリアル (英語)。

Webリファレンス (英語)。



更新

しばらくして、「アーマー」と呼ぶのはアーマーがやや急いでいることに気付きました。 ただし、軽い変更:

Army.moの変更
 class Army //... Real armyHealth, armyStrength; equation armyHealth = seaArmy.unitHealth * seaArmy.number + airArmy.unitHealth * airArmy.number + landArmy.unitHealth * landArmy.number; armyStrength = seaArmy.unitAttack * seaArmy.number + airArmy.unitAttack * airArmy.number + landArmy.unitAttack * landArmy.number; //... end Army;
      
      







すでに述べた結論を確認する写真を見ることができます。

グラフ





All Articles