Dynamo for RevitのDictionaryの使用方法と注意点

Dynamo

背景

 先日、Dynamo for Revitについて知人と意見交換したところDictionaryノードについて説明が欲しいとリクエストをいただいたため、当記事の執筆に至りました。
また、過去にDictionaryノードを利用したところ当記事の「Dicutionaryの注意点と対処方法」に示すようなエラーへの対処方法がすぐに浮かばず、Dynamoのグラフの完成間近の状況においてDictionaryノードをすべて別のノードに作り替えるという経験をしました。同じ問題を防ぐために記録を残したいと考えました。
 Revitの要素はカテゴリ・タイプ・レベル・マテリアルなど様々な属性情報を有し、Dynamoにおいてはそれらの属性情報を利用した要素の抽出が肝要となります。そこで当記事ではDictionaryノードの基本的な使用方法やRevitからDynamoへの要素の抽出における利用方法について言及させていただきます。一つの例としてご参考いただければ幸いです。
 Dictionaryの使用方法についてはDynamo Primerなどの他のいくつかの学習リソースでも紹介されています。必要に応じて併せてご参照下さい。

概要

 Dynamo for RevitのDictionaryノードを利用し、複数のカテゴリの要素の中から特定のマテリアルの要素のみを抽出するグラフを作成します。

目的

  • Dynamo for RevitのDictionaryノードの基本的な仕組みと使用方法の確認
  • List.FilterByBoolMaskノードでも同じような処理が可能ですが、Dictionaryノードを利用することでノードを簡潔化できることを確認
  • Dictionaryノードの注意点と対処方法の確認

Dictionaryの概要

 Dictionaryはプログラミング言語に共通する考え方であり、他の文献でも紹介されていますが、ここでも簡単に紹介させていただきます。

機能

 Dictionaryは2種類のデータを関連付けて登録・参照することができる機能です。連想配列とも呼ばれます。関連付けするデータをキーと値と称します。例えば以下の表のようにキーと値を定義することが可能です。以下の例ではキーと値がそれぞれ文字列となっております。DynamoのDictionaryではキーには文字列、値には文字列、実数、論理値、幾何情報、要素など任意の型のデータを定義可能です。

キー(文字列)値(文字列)
Wall壁_1
Column柱_1
Beam梁_1
Dictionaryのキーと値の例

 また、1つのキーに対して、複数個の値を定義することも可能です。DynamoではキーにRevitの要素の属性情報、値に要素などを定義し、Revitの属性情報を利用して必要な情報を参照・抽出する際に使用します。属性情報としてはカテゴリ・タイプ・レベル・マテリアルなど必要に応じて様々な情報を利用します。

キー(文字列)値(文字列)
Wall壁_1
壁_2
壁_3
Column柱_1
Beam梁_1
梁_2
1つのキーに対して複数の値を持つDictionaryの例

参考

PythonのDictionary(辞書)の紹介

辞書 - python.jp
リスト で紹介したリストオブジェクトは、順番に並んだ値をひとまとめの情報として管理するためのオブジェクトでした。リストオブジェクトに登録された値を取り出すときには、リストオブジェクト のように、参照する値の順番を指定します。リストオブジェク...

DynamoにおけるDictionaryの利用方法

使用例 | Dynamo Primer

利用例と作成手順

 利用例を確認の上Dynamoのグラフを作成します。複数のカテゴリの要素の中から特定のマテリアルを含む要素のみを抽出するグラフを作成します。キーは要素のマテリアルの名前、値は要素を定義します。グラフの全体図は以下の通りです。所々に[Object.Identity]ノード(何もしないノード)を配置しておりますが、ノード整理用のため省略可能です。全体の画像の文字が小さくそのままでは見ずらいため、ご覧になる場合はダウンロードしデスクトップで開いて下さい。

マテリアルによる要素の抽出のグラフの全体図

処理のフローの概要

  • カテゴリによる要素の抽出
  • マテリアルが設定された要素の抽出
  • 要素とマテリアルの取得
  • 要素とマテリアルのディクショナリによる抽出

環境

  • Revit 2020
  • Dynamo 2.3.0

[Dictionary.ByKeysValues]などのDictionary関連のノードが存在すれば他のバージョンでも問題ありません。

作成手順

Revitのファイルを用意

 ファイルについてはマテリアルが設定された要素が配置されていれば任意のモデルでも差し支えありませんが、ここでは「rac_advanced_sample_project.rvt」を使用します。Revitにて[ファイル]>[開く]>[サンプルファイル]で該当データを開きます。

カテゴリによる要素の抽出

 ここからDynamoの操作に入ります。一例として、Revitのモデルから5つのカテゴリの要素を抽出します。要素の抽出は異なる方法でも問題ありません。ライブラリからノードを配置・接続します。

以下の箇条書きはライブラリの階層とノードの名前を示しています。

  • [Revit]>[Selection]>[Categories]:Revitのカテゴリを選択
  • [List]>[Generate]>[List.Create]:リストを作成
  • [Revit]>[Selection]>[All Elements of Category]:カテゴリから要素を抽出
  • [List]>[Modify]>[List.Flatten]:カテゴリ別に分かれた要素のリストを平坦化

マテリアルが設定された要素の抽出

 マテリアルが設定されていない要素が含まれるケースに対応するために、各要素にマテリアルが設定されているか判定し要素を抽出します。

ライブラリから以下のノードを配置・接続します。

  • [Revit]>[Elements]>[Element]>[Element.GetMaterials]:要素からマテリアルの名前を取得
  • [Revit]>[Elements]>[Material]>[Material.Name]:要素のマテリアルの名前を文字列に変換
  • [List]>[Inspect]>[List.IsEmpty]:マテリアルのリストが空白になっているか判定
  • [List]>[Modify]>[List.FilterByBoolMask]:マテリアルのリストの空白の判定結果に基づき、要素を抽出
  • [List]>[Modify]>[List.Flatten]:リストを平坦化

要素とマテリアルの取得

 マテリアルと要素の数を揃えます。1つの要素に対して複数のマテリアルが設定されているケースに対応するため、マテリアルの個数分だけ各要素を繰り返します。特に複層の壁や床などは複数のマテリアルが設定されている場合があります。

  • [List]>[Generate]>[List.Cycle]:マテリアルの個数分だけ要素を繰り返します。各要素をマテリアル数分だけ繰り返すようにするためにノードを右クリックし、[レーシング]>[最短]に設定します。
  • [List]>[Inspect]>[List.Count]:マテリアルの個数を取得します。各要素のリストにおけるマテリアルの個数を取得するために入力のリストのレベルを2(@L2)に設定します。
  • [Element.GetMaterials]、[Material.Name]:要素のマテリアル名を取得
  • [List.Flatten]:要素とマテリアルのリストをそれぞれ平坦化し揃えます。

要素とマテリアルのDictionaryによる抽出

 キーをマテリアル名、値を要素としてDictionaryを定義します。作成したDictionaryから特定のキーの要素を抽出します。本データでは一例で2つのコンクリートマテリアル「Concrete – Cast In Situ」と「Concrete – Cast-in-Place Concrete」を抽出します。

キー(マテリアルの名前)値(要素)
マテリアル_1要素_1
要素_2
要素_3
マテリアル_2要素_4
マテリアル_3要素_5
要素_6
  • [List]>[Modify]>[List.GroupByKey]:マテリアルごとの要素のリストを作成します。Dicutionary作成用のノード[Dictionary.ByKeysValues]に必要な出力を得ることが可能です。この2つのノードは組み合わせて使用することが多いです。マテリアルごとの要素のリストは以下の表のようなイメージです。出力端子の[Group]ではグループ化された要素のリスト、[unique keys]ではユニークなマテリアル名が出力されます。
  • [Dictionary]>[Dictionary.ByKeysValues]:キーをマテリアル名、値を要素としてDictionaryを定義します。
  • [Dictionary]>[Dictionary.ValueAtKey]:Dictionaryから指定したキーの値を取得します。マテリアル名を入力し要素を取得します。
  • [Input]>[Basic]>[String]:取得したいマテリアル名を入力します。ここでは「Concrete – Cast In Situ」と「Concrete – Cast-in-Place Concrete」としています。
  • [List.Flatten]:要素のリストを平坦化

Dicutionaryの注意点と対処方法

 Dictionaryに含まれないキーの値の取得をしようとした場合の注意点と対処方法を示します。

 [Dictionary.ValueAtKey]にてDictionaryのキーに含まれていないキーを入力するとエラーとなりnullを出力します。[Dictionary.ValueAtKey]の処理としては一見問題なさそうですが、このノードの後の処理も全てnullとなってしまい意図した結果が得られないことがあります。

エラーの再現

 例として前の章のグラフで取得した要素のパラメータの値と要素の個数を取得するというケースを考えます。前の章で作成した要素の取得結果より「Concrete – Cast In Situ」を持つ要素は2つの床ですが、この床がDictionaryに存在しない場合の振る舞いを確認します。

抽出する要素のカテゴリの変更

 前章で作成したグラフの左端と右端を変更します。中央部は変更しないため説明を省略します。左端の[List Create]に入力されている「床」のカテゴリのワイヤを外します。[List Create]の[-]ボタンをクリックし、端子の数を減らしその他のワイヤを接続し直します。

要素のパラメータ値・個数の取得

 以下の図のようにグラフの右側を編集します。実行すると、[Dictionary.ValueAtKey]にてDictionaryにキーが存在しないというエラーが発生し[List.Count]にて要素の個数が得られないことを確認できます。なお今回のデータでは要素の「コメント」は空欄であり[Element.GetParameter.ValueByName]の値も空欄となりますが手順確認の上では問題ありません。

  • [List.Flatten]:リストを平坦化
  • [Revit]>[Elements]>[Element]>[Element.GetParameter.ValueByName]:要素のパラメータの値を取得
  • [String]:パラメータ名として「コメント」と入力します。
  • [List.Count]:要素の個数を取得

対処方法

 Dictionaryにキーが含まれない場合の対処方法を示します。以下のように新たにノードを配置・接続します。Dictionaryにキーが含まれない場合はnullではなくEmptyを返すようにします。

  • [List.FilterByBoolMask]:[mask]端子に入力したBool値によってリストをフィルタします。今回はDictionaryにキーが含まれない場合は空のEmptyを出力するようにします。出力結果をDictionary.ValueAtKeyに接続します。
  • [List]>[Inspect]>[List.Contains]:Dictionaryにキーが含まれているかを判定します。リストに特定の要素が含まれている場合はTrue、含まれない場合はFalseを返します。

 実行すると、上図のように[Element.GetParameterValueByName]では「Empty List」、[List.Count]では「0」となり途中で止まることなく末端のノードまで処理が実行されます。

備考

 Dictionaryを使用せずに[List.FilterByBoolMask]を利用して同じ結果を得る方法を示します。Dictionaryは要素の抽出において便利で使いやすいですが、上記の通り工夫が必要になりノード数が増えてしまうことがあります。もっとノード数を減らせる方法がありましたらコメントいただけますと幸いです。

 [List.FilterByBoolMask]ではリストに該当する(条件がTrueになる)要素が存在しない場合はEmptyを出力するため、Dictionaryで行ったような対処は不要です。場面によってはDictionaryの代わりに[List.FilterByBoolMask]を使用することもあります。

 以下の図のように[List.FilterByBoolMask]を利用することで同じ結果を得ることも可能です。[==]ノードでマテリアルの名前が等しいかを判定し、抽出するという仕組みです。抽出したいマテリアルの個数分同じ抽出のためのノードを繰り返し配置することになります。抽出したいキーの個数が多い場合はPythonスクリプトでのループ処理するなど工夫が必要と考えられます。左端のノード以外は同様のため、省略させていただきます。主要なノードを以下に記載します。

[Math]>[Operators]>[==]:マテリアルの名前が等しいか判定します。等しい場合はTrue、異なる場合はFalseを返します。

[List.FilterByBoolMask]:マテリアルの名前が等しい要素のみ抽出します。