Skip to content

pointwise/GlyphClientPython

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Glyph API for Python

Copyright 2021 Cadence Design Systems, Inc. All rights reserved worldwide.

This is a Python implementation of the Pointwise Glyph API. Glyph is implemented as a set of Tcl procedures that have an object-oriented feel. Pointwise supports non-Tcl scripting (or binary code) applications through a feature called Glyph Server, first avaialable in V18.0. This package allows low level communication with a Glyph Server, but doing so amounts to constructing Glyph/Tcl commands as strings and sending them to the Glyph Server for processing. The recommended way to use this package however is to use the higher level Glyph API for Python. This API leverages some of the introspective features of the Python language to automatically convert Python expressions into Glyph/Tcl command strings to be executed on the Glyph Server, and to convert the results back into Python objects.

Glyph commands are formulated in Python using a JSON structure and passed to Glyph through a special dispatching command on the server that handles only JSON-encoded commands. (This dispatching command is available in Pointwise V18.2 and later.) The results from this command are returned as JSON structures that are subsequently processed and converted into Python number, string and special GlyphObj objects.

The core components of this API are the GlyphObj and GlyphVar classes. All Glyph objects and classes are represented as instances of GlyphObj, which provides transparent access to all Glyph actions as though they were implemented in Python. GlyphVar provides a way to set and access Tcl variables on the server, primarily for use with Glyph actions that accept Tcl variable names as arguments.

Installation

The easiest way to install is using PIP:

python -m pip install --user --upgrade pointwise-glyph-client

The client can also be pulled from this repository and added to PYTHONPATH environment variable.

Usage

The most basic usage of the Glyph API for Python is to:

  1. Import GlyphClient from pointwise package
  2. Create a GlyphClient object
  3. Request a GlyphAPI object from the client object, which automatically connects to the GlyphServer if the GlyphClient is not already connected.
  4. Issue Glyph actions through the GlyphAPI

Example Usage

from pointwise import GlyphClient

glf = GlyphClient()
pw = glf.get_glyphapi()

pw.Connector.setCalculateDimensionMethod("Spacing")
pw.Connector.setCalculateDimensionSpacing(.3)
with pw.Application.begin("Create") as creator:
    conic1 = pw.SegmentConic()
    conic1.addPoint((-25,8,0))
    conic1.addPoint((-8,8,0))
    conic1.setIntersectPoint((-20,20,0))
    conic2 = pw.SegmentConic()
    conic2.addPoint(conic1.getPoint(conic1.getPointCount()))
    conic2.addPoint((10,16,0))
    conic2.setShoulderPoint((8,8,0))
    con = pw.Connector()
    con.addSegment(conic1)
    con.addSegment(conic2)
    con.calculateDimension()

Usage Notes

Platform Support

The Glyph API has been sufficiently tested with both Python 2.6+ and Python 3.3+ on Windows and Linux. It has not been tested extensively on Mac OS/X. The API also depends on a widely-available third-party library called numpy. It is recommended that at least version 1.12 be installed, but older versions may work as well.

GlyphClient object

GlyphClient implements only the client-server communication from the Python script to a Pointwise server. It can be used completely independently from the Glyph API as it provides methods for connecting to a server, evaluating Tcl/Glyph expressions, retrieving the raw Tcl string results, and disconnecting from the server.

A GlyphClient can be used as a Python context manager. This allows a script to access a Pointwise Glyph server using idiomatic context management.

Example:

with GlyphClient() as glf:
    glf.eval("puts {Hello World}")

The GlyphClient constructor has an optional argument of a port number. Most of the time this can be left unspecified, which means it will use the value of the PWI_GLYPH_SERVER_PORT, or 2807 if the environment variable is not defined. When running a script from within the Pointwise GUI, before the script is executed, the PWI_GLYPH_SERVER_PORT environment variable is set to the current port that the Glyph Server has been configured to listen to. Only when a script needs to communicate with multiple instances of Pointwise would specifying the port be necessary, and it is recommended that the script allow user input for specifying the port so that the script is not hard coded.

The GlyphClient constructor can also start Pointwise in batch mode as a subprocess with a actively listening Glyph Server by specifying the port number as zero. Note that this will consume a Pointwise license, if one is available. Standard and error output from the Pointwise subprocess can be captured by specifying a callback function.

Note that, in order to use port=0, the directory path of the Pointwise-installed version of 'tclsh' (for Windows platforms) or the 'pointwise' launch script (for non-Windows) must be in the environment PATH variable, along with any other necessary environment needed to run the script.

Example:

def echo(text):
    print("Server:", text)

with GlyphClient(port=0, callback=echo) as glf:
    glf.puts("Hello World")

Should produce:

Server: Hello World

GlyphAPI object

GlyphAPI extends the GlyphClient functionality by providing the transparent access needed to make Glyph calls in a very Pythonic manner. A GlyphAPI object should never be constructed directly, and only be created by a connected GlyphClient object. Connections to multiple Pointwise servers are possible, and all Glyph actions invoked within the context of a GlyphAPI are done so on the associated server connection.

Example:

glf1 = GlyphClient(port=2807)
glf2 = GlyphClient(port=2808)

pw1 = glf1.get_glyphapi()
pw2 = glf2.get_glyphapi()

con1 = pw1.GridEntity.getByName("con-1")
con2 = pw2.GridEntity.getByNAme("con-2")

con1.join(con2) # Behavior undefined!

GlyphVar object

A GlyphVar is required for Glyph actions that expect a Tcl variable name as an argument. These actions typically set the variable to some ancillary result value, independent of the action's direct result. A GlyphVar object is not coupled to a specific GlyphClient connection, as it is used only in the context of a Glyph action in order to retrieve some result value stored in a Tcl variable. A GlyphVar may be assigned a Tcl variable name, but it is not required; when unassigned, a unique temporary Tcl variable name will be generated.

Example:

poleDoms = GlyphVar()
pw.BlockStructured.createFromDomains(doms, poleDomains=poleDoms)
for d in poleDoms.value: print(d.getName())

GlyphObj object

GlyphObj is the primary Python interface to Glyph classes, objects and their associated actions. A GlyphObj instance is created automatically in the following ways:

  • When the method name of a call to GlyphAPI matches the base name of a published Glyph class name (Application for pw::Application)
  • When the result of some Glyph action returns a Glyph function name (a Glyph object, such as ::pw::Connector_1)
  • When a GlyphVar's value contains a Glyph function name (Glyph object)
  • When constructed directly using a Glyph function name (Glyph object)

Notes:

  • The list of Glyph classes that are wrap-able with GlyphObj are those that are returned from the Tcl command 'pw::Application getAllCommandNames'. This list does not include classes that are for internal use only, nor the classes that implement the Glyph Server itself (e.g., pw::Script).

Examples:

# There are two GlyphObj instances created here, one for "pw::Connector" class
# and one for "::pw::Connector_1" object returned by pw.Connector()
con1 = pw.Connector()

# There are two GlyphObj instances created here as well, one for
# "pw::GridEntity" class and one for "::pw::Connector_1" object returned
# by "pw::GridEntity getByName con-1"
con2 = pw.GridEntity.getByName("con-1")

# This generates GlyphObj instances for "pw::BlockStructured", all the blocks
# returned by "createFromDomains" and all the domains (if any) returned in
# the "pdoms" Tcl variable passed to the action.
poleDoms = GlyphVar("pdoms")
blk = pw.BlockStructured.createFromDomains(doms, poleDomains=poleDoms)
for d in poleDoms.value: print(d.getName())

Generating Glyph Actions Automatically

Glyph actions are method invocations on either a Glyph class or a Glyph function (object). (These are called "functions" because Glyph generates a mapping from a Tcl proc to an internal object as a way of simulating object-oriented behavior in Glyph. This is a common pattern in Tcl package implementations.) There are two types of actions: static actions and instance actions. Further, every Glyph object that can be instantiated directly in a script has a static "create" action. So, by exploiting Python language features, the following syntaxes generates an associated Tcl/Glyph command:

  • A GlyphObj that represents a Glyph class that is called directly (i.e., appears to be a Python constructor) becomes a "create" action call. Arguments can be passed to these constructor-type calls as needed and as allowed by the corresponding Glyph "create" action.
  • A method call on a GlyphObj that represents a Glyph class is translated into a static action call on the Glyph class.
  • A method call on a GlyphObj that represents a Glyph object is translated into an instance action call on the object.

Note: Some Glyph action names conflict with Python reserved words (e.g. pw::Grid import). For conflicting action names, an underscore must be appended to the Python function name:

# This invokes "pw::Grid import $filename"
pw.Grid.import_(filename)

Example:

# This invokes "pw::Connector create" with no arguments
con = pw.Connector()

# This invokes "pw::Examine create ConnectorLengthI"
exam = pw.Examine("ConnectorLengthI")

# This invokes "pw::Connector getAdjacentConnectors $cons"
cons = pw.Connector.getAdjacentConnectors(cons)

# This invokes "$con1 join $con2"
con1 = con1.join(con2)

Passing Arguments and Flags to Glyph Actions

Many Glyph actions accept both positional and flag arguments. The Python equivalent of these are, respectively, positional and keyword arguments, but there are some strict rules that must be followed in order for the corresponding Glyph action commands to be generated correctly. All positional arguments must appear first in the Python method invocation, as is the requirement of the language, followed by all optional keyword arguments. GlyphObj converts all keyword arguments in the following way:

  • If the keyword does not end in an underscore ("_"):
    • If the keyword argument is False, the flag is not added to the command
    • Otherwise, the keyword is prepended with a dash ("-") and added to the command. Then:
      • If the keyword argument is a bool and is True, no argument is added to the command
      • Otherwise, the keyword argument value is added as a single element to the Glyph command
  • If the keyword ends in an underscore, special handling is used:
    • The keyword is prepended with a dash, and the trailing underscore is removed, and the flag is added to the command. Then:
      • If the keyword argument value is a list or tuple of values, each value is added as a separate command argument. Note that any embedded list/tuple will remain as a Tcl list in the Glyph action command.
      • Otherwise, the keyword argument value is added to the command, even if a boolean value.

Note that any positional argument that is a list or tuple will be passed as a Tcl list in the command.

Examples:

# set pt [$con getPosition -arc 1.0]
pt = con.getPosition(1.0, arc=True)

# set pt [$con getXYZ 1]
pt = con.getXYZ(1, arc=False)

# set ents [$bc getEntities -visibility true]
ents = bc.getEntities(visibility_=True)

# pw::Entity project -type Linear -axis {0 0 0} {0 0 1} $ents
pw.Entity.project(ents, type="Linear", axis_=[(0, 0, 0), (0, 0, 1)])

# $shape polygon -points { { 0 0 0 } { 0 1 0 } { 1 0 0 } }
shape.polygon(points=[(0, 0, 0), (0, 1, 0), (1, 0, 0)])

Glyph Objects as Context Managers

In many cases it is convenient to use a GlyphObj that represents certain transient Glyph objects as Python context managers. Specifically, Glyph Mode and Examine objects are typically short-lived and are used in very specific contexts. For these Glyph object types only, context management is implemented in GlyphObj.

Examples:

with pw.Application.begin("Create") as creator:
    con = pw.Connector()
    ...
    # a mode will be implicitly ended when the context ends, unless
    # an exception occurs, in which case the mode is aborted and all
    # modifications made in the mode are discarded

with pw.Examine("BlockJacobian") as exam:
    exam.addEntity([blk1, blk2])
    exam.examine()
    ...
    # Examine objects are automatically deleted when the context exits,

Glyph Utility Classes

The standard Tcl/Glyph command set includes a number of utility classes to perform vector algebra, extent box computation, transformation matrices, etc. To improve the overall usefulness and speed of this API, these classes were implemented directly in Python, rather than through the Glyph Server. Many of the mathematical vector and matrix operations are performed using the "numpy" package. These utilty classes include, along with their Glyph counterparts:

  • Vector2 - pwu::Vector2
  • Vector3 - pwu::Vector3
  • Quaternion - pwu::Quaternion
  • Plane - pwu::Plane
  • Transform - pwu::Transform
  • Extents - pwu::Extents

Nearly the complete set of functions documented at https://www.pointwise.com/glyph under the "Utilities" section have been implemented as Python classes.

Example:

# set v1 [pwu::Vector3 set { 0 1 2 }
v1 = Vector3([0, 1, 2])  # Vector3(0, 0, 0) also works

# set v2 [pwu::Vector3 add $v1 { 2 4 6 }
v2 = v1 + Vector3(2, 4, 6)

# set v3 [pwu::Vector3 cross $v1 $v2]
v3 = v1 * v2             # cross product

# set v3 [pwu::Vector3 normalize $v3]
v3 = v3.normalize()

Disclaimer

This file is licensed under the Cadence Public License Version 1.0 (the "License"), a copy of which is found in the LICENSE file, and is distributed "AS IS." TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, CADENCE DISCLAIMS ALL WARRANTIES AND IN NO EVENT SHALL BE LIABLE TO ANY PARTY FOR ANY DAMAGES ARISING OUT OF OR RELATING TO USE OF THIS FILE. Please see the License for the full text of applicable terms.

About

Python implementation of a client which communicates with the Pointwise Glyph Server.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages