Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,120 +6,139 @@ While it is not feasible for us to maintain separate "Getting Started with OpenT
%% Using OpenTDv242 with MATLAB
% CRTech
% Tested with MATLAB R2023b

% OpenTD is an Application Programming Interface (API) for Thermal Desktop
% (TD) that allows you to automate many of the tasks currently performed
% interactively using TD’s Graphical User Interface (GUI). OpenTD gives you
% the tools to programmatically create, query, edit, delete, and run
% models. You can use any .NET language to interact with OpenTD (C#,
% VB.NET, F\#, etc.) or any system that can load .NET assemblies such as
% MATLAB or Python.

% Regardless of how you interact with OpenTD, you’ll need to have at least
% an intermediate understanding of .NET object-oriented programming. If you
% are starting from scratch, we recommend learning C#, since it is the
% language that we support. However, we understand that there might be
% compelling reasons for you to connect to OpenTD via MATLAB. It is
% possible, although the way MATLAB handles .NET enums is awkward and
% MATLAB does not support implicit constructors.

% To get started with OpenTD, read "Getting Started with OpenTDv242.pdf",
% which can be found in your TD v241 installation directory under "Manual".
% The Getting Started guide explains the fundamental concepts of OpenTD,
% using several C# examples. We've ported one of those examples to MATLAB
% below.

%% The "Create and Run a Case" example ported to MATLAB
% See "Getting Started with OpenTDv242.pdf" in your TD v241 installation
% directory under "Manual" for an explanation of this script.

% Note: Please contact us at crtech.support@ansys.com if you think there are
% better ways to use OpenTD with MATLAB, especially with regard to .NET
% enums and implicit constructors. For examples of awkward code, see how a
% node is set to be a boundary node and how the InitialTemp of a node is
% set -- in the script below vs. in the original C#.

openTD = NET.addAssembly('OpenTDv242');
import OpenTDv242.\*;
import OpenTDv242.*;
td = ThermalDesktop;
td.Connect();
% \*\*\* Create a simple model of a heated bar \*\*\*

% *** Create a simple model of a heated bar ***
barNodes = NET.createArray('OpenTDv242.Node', 10);
for i = 1:10
n = td.CreateNode();
n.Submodel = SubmodelNameData('bar');
n.Id = i;
n.MassVol = 10;
n.Origin = Point3d(0.01 \* (i - 1), 1, 0);
n.InitialTemp = Dimensional(n.InitialTemp, 300);
n.Update();
barNodes(i) = n;
n = td.CreateNode();
n.Submodel = SubmodelNameData('bar');
n.Id = i;
n.MassVol = 10;
n.Origin = Point3d(0.01 * (i - 1), 1, 0);
n.InitialTemp = Dimensional(n.InitialTemp, 300);
n.Update();
barNodes(i) = n;
end
for i = 1:9
c = td.CreateConductor(...
Connection(barNodes(i).Handle), Connection(barNodes(i+1).Handle));
c.Submodel = SubmodelNameData('bar');
c.Value = 0.1;
c.Update();
c = td.CreateConductor(...
Connection(barNodes(i).Handle), Connection(barNodes(i+1).Handle));
c.Submodel = SubmodelNameData('bar');
c.Value = 0.1;
c.Update();
end

roomAir = td.CreateNode();
roomAir.Submodel = SubmodelNameData('room');
roomAir.NodeType = OpenTDv242.('RcNodeData+NodeTypes').BOUNDARY;
roomAir.Origin = Point3d(0.055, 1.1, 0);
roomAir.InitialTemp = Dimensional(roomAir.InitialTemp, 300);
roomAir.Update();

barConnections = NET.createGeneric(...
'System.Collections.Generic.List', {'OpenTDv242.Connection'},10);
'System.Collections.Generic.List', {'OpenTDv242.Connection'},10);
for i = 1:10
barConnections.Add(Connection(barNodes(i).Handle));
barConnections.Add(Connection(barNodes(i).Handle));
end
convection = td.CreateConductor(...
Connection(roomAir.Handle), barConnections);
Connection(roomAir.Handle), barConnections);
convection.Value = 1;
convection.Submodel = SubmodelNameData('room');
convection.Update();

qTorch = td.CreateSymbol('qTorch', '80');
heatLoadConnections = NET.createGeneric(...
'System.Collections.Generic.List', {'OpenTDv242.Connection'},1);
'System.Collections.Generic.List', {'OpenTDv242.Connection'},1);
heatLoadConnections.Add(Connection(barNodes(1).Handle));
torch = td.CreateHeatLoad(heatLoadConnections);
torch.ValueExp.Value = qTorch.Name;
torch.Submodel = SubmodelNameData('torch');
torch.Update();

td.ZoomExtents();
% \*\*\* End simple model creation \*\*\*
% *** End simple model creation ***

% Create a transient case and run it:
nominal = td.CreateCaseSet(...
'transient with nominal torch', '', 'torchNom');
'transient with nominal torch', '', 'torchNom');
nominal.SteadyState = 0;
nominal.Transient = 1;
nominal.SindaControl.timend...
= Dimensional(nominal.SindaControl.timend, 600);
= Dimensional(nominal.SindaControl.timend, 600);
nominal.Update();
nominal.Run();

% Create a cold case by overriding a symbol, and run it:
cold = td.CreateCaseSet(...
'transient with cold torch', '', 'torchCold');
'transient with cold torch', '', 'torchCold');
cold.SteadyState = 0;
cold.Transient = 1;
cold.SindaControl.timend...
= Dimensional(nominal.SindaControl.timend, 1200);
= Dimensional(nominal.SindaControl.timend, 1200);
cold.SymbolNames.Add(qTorch.Name);
cold.SymbolValues.Add('50');
cold.SymbolComments.Add('cold torch heat input');
cold.SaveAll = 1;
cold.Update();
cold.Run();

%% Working with Dimensionals

% All dimensional quanitities in the API are stored using a custom .NET
% generic type called a Dimensional. For example, a Dimensional<Temp>
% stores temperatures. Using C#, Dimensionals are implicitly cast to and
% from doubles as required, but this does not appear to work in MATLAB.
% Instead, we've overloaded the double function and created a Dimensional
% function to explicitly cast doubles to Dimensionals.

function x = double(Dimensional)
% Cast a .NET generic Dimensional type to a double
x = Dimensional.op_Implicit(Dimensional);
x = Dimensional.op_Implicit(Dimensional);
end

function x = Dimensional(Dimensional, double)
% Cast a double to a .NET generic Dimensional type
x = Dimensional.op_Implicit(double);
x = Dimensional.op_Implicit(double);
end

%% Acknowledgements

% Thank you to Dan Hensley and Daniel Reasa with ATA Engineering for
% performing some of the early work to determine how to use OpenTD with
% MATLAB.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,74 @@
While it is not feasible for us to maintain separate "Getting Started with OpenTD" guides for every programming language, we would still like to help you get started with OpenTD, even if you are not using C#. The following .py script is a MATLAB port of the program in the [Create and run a case](working-with-case-sets.md#create-and-run-a-case) section. This can be used as a sort of "Rosetta Stone" to help you translate other C# examples to Python. It uses the pythonnet module, found at: [http://pythonnet.github.io/](http://pythonnet.github.io/).

```python
\#\#\#\# Using OpenTDv242 with Python \#\#\#\#
\# CRTech
\# Feb, 2022
\# Created with Python 2.7.15 and pythonnet 2.3.0
\# OpenTD is an Application Programming Interface (API) for Thermal Desktop
\# (TD) that allows you to automate many of the tasks currently performed
\# interactively using TD's Graphical User Interface (GUI). OpenTD gives you
\# the tools to programmatically create, query, edit, delete, and run
\# models. You can use any .NET language to interact with OpenTD (C#,
\# VB.NET, F#, etc.) or any system that can load .NET assemblies such as
\# MATLAB or Python.
\# Regardless of how you interact with OpenTD, you'll need to have at least
\# an intermediate understanding of .NET object-oriented programming. If you
\# are starting from scratch, we recommend learning C#, since it is the
\# language that we support. However, we understand that there might be
\# compelling reasons for you to connect to OpenTD via Python. It is
\# possible using the pythonnet module:
\# http://pythonnet.github.io/
\# To get started with OpenTD, read "Getting Started with OpenTDv242.pdf",
\# which can be found in your TD v241 installation directory under "Manual".
\# The Getting Started guide explains the fundamental concepts of OpenTD,
\# using several C# examples. We've ported one of those examples to Python
\# below.
\#\#\#\# The "Create and Run a Case" example ported to Python \#\#\#\#
\# See "Getting Started with OpenTDv242.pdf" in your TD v241 installation
\# directory under "Manual" for an explanation of this script.
\# Note: Please contact us at crtech.support@ansys.com if you think there are
\# better ways to use OpenTD with Python, especially with regard to setting
\# dimensional values.
\# REQUIREMENT: You must install the pythonnet module to use this script.
#### Using OpenTDv242 with Python ####
# CRTech
# Feb, 2022
# Created with Python 2.7.15 and pythonnet 2.3.0

# OpenTD is an Application Programming Interface (API) for Thermal Desktop
# (TD) that allows you to automate many of the tasks currently performed
# interactively using TD's Graphical User Interface (GUI). OpenTD gives you
# the tools to programmatically create, query, edit, delete, and run
# models. You can use any .NET language to interact with OpenTD (C#,
# VB.NET, F#, etc.) or any system that can load .NET assemblies such as
# MATLAB or Python.

# Regardless of how you interact with OpenTD, you'll need to have at least
# an intermediate understanding of .NET object-oriented programming. If you
# are starting from scratch, we recommend learning C#, since it is the
# language that we support. However, we understand that there might be
# compelling reasons for you to connect to OpenTD via Python. It is
# possible using the pythonnet module:

# http://pythonnet.github.io/

# To get started with OpenTD, read "Getting Started with OpenTDv242.pdf",
# which can be found in your TD v241 installation directory under "Manual".
# The Getting Started guide explains the fundamental concepts of OpenTD,
# using several C# examples. We've ported one of those examples to Python
# below.

#### The "Create and Run a Case" example ported to Python ####
# See "Getting Started with OpenTDv242.pdf" in your TD v241 installation
# directory under "Manual" for an explanation of this script.

# Note: Please contact us at crtech.support@ansys.com if you think there are
# better ways to use OpenTD with Python, especially with regard to setting
# dimensional values.

# REQUIREMENT: You must install the pythonnet module to use this script.

import sys
import clr
\# Need to add explicit GAC path to sys.path so clr.AddReference
\# can find OpenTDv242.dll. Note the use of forward slashes in the path:

# Need to add explicit GAC path to sys.path so clr.AddReference
# can find OpenTDv242.dll. Note the use of forward slashes in the path:
sys.path.append("C:/Windows/Microsoft.NET/assembly/GAC_MSIL/OpenTDv242/ReplaceMe")
clr.AddReference("OpenTDv242")
from OpenTDv242 import \*
\# We'll want to use .NET System types and generic Lists:
from System import \*
from OpenTDv242 import *

# We'll want to use .NET System types and generic Lists:
from System import *
from System.Collections.Generic import List
\# To access dimensional quantities in OpenTD, we need to use Dimensionals.
\# These are cast to/from doubles implicitly in C#, but here we'll need to
\# refer to them explicitly. (See setting InitialTemp, below.)

# To access dimensional quantities in OpenTD, we need to use Dimensionals.
# These are cast to/from doubles implicitly in C#, but here we'll need to
# refer to them explicitly. (See setting InitialTemp, below.)
from OpenTDv242 import Dimension
from OpenTDv242.Dimension import \*
from OpenTDv242.Dimension import *

td = ThermalDesktop()
td.Connect()
\# \*\*\* Create a simple model of a heated bar \*\*\*

# *** Create a simple model of a heated bar ***
barNodes = List[Node]()
for i in range(10):
n = td.CreateNode()
n.Submodel = SubmodelNameData("bar")
n.Id = i + 1
n.MassVol = 10.0
n.Origin = Point3d(0.01 \* i, 1.0, 0.0)
n.Origin = Point3d(0.01 * i, 1.0, 0.0)
n.InitialTemp = Dimensional[Dimension.Temp](300.0)
n.Update()
barNodes.Add(n)
Expand All @@ -66,49 +79,56 @@ for i in range(9):
c.Submodel = SubmodelNameData("bar")
c.Value = 0.1
c.Update()

roomAir = td.CreateNode()
roomAir.Submodel = SubmodelNameData('room')
roomAir.NodeType = RcNodeData.NodeTypes.BOUNDARY
roomAir.Origin = Point3d(0.055, 1.1, 0.0)
roomAir.InitialTemp = Dimensional[Dimension.Temp](300.0)
roomAir.Update()

barConnections = List[Connection]()
for n in barNodes:
barConnections.Add(Connection(n))
convection = td.CreateConductor(Connection(roomAir), barConnections)
convection.Value = 1.0
convection.Submodel = SubmodelNameData("room")
convection.Update()

qTorch = td.CreateSymbol("qTorch", "80")
heatLoadConnections = List[Connection]()
heatLoadConnections.Add(Connection(barNodes[0]))
torch = td.CreateHeatLoad(heatLoadConnections)
torch.ValueExp.Value = qTorch.Name
torch.Submodel = SubmodelNameData("torch")
torch.Update()

td.ZoomExtents()
\# \*\*\* End simple model creation \*\*\*
\# Create a transient case and run it:
# *** End simple model creation ***

# Create a transient case and run it:
nominal = td.CreateCaseSet("transient with nominal torch", "", "torchNom")
nominal.SteadyState = 0
nominal.Transient = 1
nominal.SindaControl.timend = Dimensional[Dimension.Time](600.0)
nominal.Update()
nominal.Run()
\# Create a cold case by overriding a symbol, and run it:

# Create a cold case by overriding a symbol, and run it:
cold = td.CreateCaseSet("transient with cold torch", "", "torchCold")
cold.SteadyState = 0
cold.Transient = 1
cold.SindaControl.timend = Dimensional[Dimension.Time](1200.0)
cold.SaveQ = 1
cold.SymbolNames.Add(qTorch.Name)
cold.SymbolValues.Add("50")
cold.SymbolComments.Add("cold torch heat input");
cold.SaveAll = 1;
cold.SymbolComments.Add("cold torch heat input")
cold.SaveAll = 1
cold.Update()
cold.Run()
\#\#\#\# Acknowledgements
\# Thank you to James Etchells with the European Space Agency (ESA) for
\# performing some of the early work to determine how to use OpenTD with
\# Python.

#### Acknowledgements
# Thank you to James Etchells with the European Space Agency (ESA) for
# performing some of the early work to determine how to use OpenTD with
# Python.
```
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

To connect objects in TD, you will use a Connection. A Connection contains a handle and a marker. As discussed in [Create nodes and a conductor](nodes-and-conductor.md) section, a handle is a string that TD uses to uniquely identify each object in a drawing. You have probably seen them listed in the Model Browser:

![Model Browser Example](../media/model-browser-example.png)
![Model Browser Example](../media/2f2bcdeb872649910f747dfa5b80c4fa.png)

In addition to Connections, OpenTD uses handles to find objects when, for example, you call the Update() method on an object. Internally, OpenTD keeps track of which dwg contains the object, and finds it in the dwg using the Handle property of the object.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ When you run the program, an instance of TD should open and a node should be cre

Assuming it worked, let’s examine how. First, we created a `ThermalDesktop` object called `td`. This object represents one instance of TD. It has hundreds of methods for interacting with TD models. A single OpenTD client program can create an arbitrary number of ThermalDesktop instances, allowing you to manipulate several models and communicate between them.

Next, we called the `ThermalDesktop.Connect()` method. By default, this will start a new instance of TD using the latest version of AutoCAD installed. You can control how it works using the `ThermalDesktop*.ConnectConfig*` property ([Control how OpenTD connects to Thermal Desktop section](../extras.md#control-how-opentd-connects-to-thermal-desktop)).
Next, we called the `ThermalDesktop.Connect()` method. By default, this will start a new instance of TD using the latest version of AutoCAD installed. You can control how it works using the `ThermalDesktop.ConnectConfig` property ([Control how OpenTD connects to Thermal Desktop section](../extras.md#control-how-opentd-connects-to-thermal-desktop)).

`Connect()`, like most ThermalDesktop commands, is called synchronously, so it will only return control to your program once it finishes. If there is a problem, it will throw an exception. All OpenTD methods throw exceptions if there is a problem; you do not need to check return values for success.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ namespace OpenTDv242GettingStarted

How did this program work? After starting a new instance of TD, we created two `Node` objects, `n1` and `n2`, using the`CreateNode()` method.

When TD created each node, it set the `Node.*Handle*` property to a unique identifier, the same string you may have noticed in the TD Model Browser:
When TD created each node, it set the `Node.Handle` property to a unique identifier, the same string you may have noticed in the TD Model Browser:

![C:\\Users\\matt\\AppData\\Local\\Temp\\SNAGHTML29a2a79e.PNG](../media/2f2bcdeb872649910f747dfa5b80c4fa.png)

Since TD allows duplicate SINDA names for some entities, OpenTD uses AutoCAD *handles* to identify most entities uniquely. (See the [Work with connections, handles, markers, and domains section](./connections-handles-markers-domains.md) for a detailed discussion of handles and related concepts.)

Next, we called the `*CreateConductor*` method. This method accepts two `*Connections*` representing the nodes connected to the conductor. Each Connection consists of a handle and a `*marker*`. As mentioned above, a handle is a unique identifier for a TD entity. A marker is an integer that determines how something is connected such as Top or XMAX. (See the [Work with connections, handles, markers, and domains](./connections-handles-markers-domains.md) section)
Next, we called the `CreateConductor` method. This method accepts two `Connections` representing the nodes connected to the conductor. Each Connection consists of a handle and a `marker`. As mentioned above, a handle is a unique identifier for a TD entity. A marker is an integer that determines how something is connected such as Top or XMAX. (See the [Work with connections, handles, markers, and domains](./connections-handles-markers-domains.md) section)

When we called CreateConductor, we simply passed it our two nodes, n1 and n2. OpenTD knows how to implicitly create new Connections from Nodes by reading the `Node.Handle` property and assuming a default value for the`Connection.Marker` property, which is fine because node connections do not use markers. (See the [Execute TD COM commands](../extras.md#execute-td-com-commands) section for a discussion of implicit casting.)
Loading