Writing mesh files

To write a 2DM mesh file, instantiate a py2dm.Writer object.

As with py2dm.Reader, the preferred way to do this is via a context manager. This ensures the file is written and closed properly after writing.

Basic usage

You can add geometries to the mesh using the py2dm.Writer.element(), py2dm.Writer.node(), and py2dm.Writer.node_string() methods.

These methods support two modes. In the first, the target object is instantiated first and passed to the method as a finished instance:

with py2dm.Writer('path/to/mesh.2dm') as mesh:
   my_node = py2dm.Node(1, 25.0, 20.0, 5.0)
   ...
   mesh.node(my_nodes)

This is the recommended mode when using Py2DM objects as your main data type, such as when copying entities across files or when merging two files.

Note

Any mutable lists are deep copied when passing a class instance to any of these methods. There is no danger of unintentional mutation of an object’s data after it was passed to this method.

Alternatively, all the above methods also support a factory interface, where the values passed are forwarded directly to the corresponding object constructor:

with py2dm.Writer('path/to/mesh.2dm') as mesh:
   mesh.node(1, 25.0, 20.0, 5.0)

When creating elements via the factory pattern, you must also specify the class or 2DM card of the element type to create:

with py2dm.Writer('path/to/mesh.2dm') as mesh:
   mesh.element('E3T', 1, 1, 2, 3)
   # Or, alternatively:
   mesh.element(py2dm.Element3T, 2, 3, 2, 4)

Automatically assigned IDs

The py2dm.Writer.node() and py2dm.Writer.element() methods both allow passing negative integers as IDs, in which case the ID will be determined based on the number of existing nodes/elements in the mesh. The assigned ID is then returned:

with py2dm.Writer('path/to/mesh.2dm') as mesh:
   mesh.node(-1, -1.0, 1.0, 0.0)  # returns 1
   mesh.node(-1, 1.0, -1.0, 0.0)  # returns 2
   mesh.node(-1, 1.0, 1.0, 0.0)  # returns 3
   mesh.element('E3T', -1, 1, 2, 3)  # returns 1

Writing chunks

The py2dm.Writer.element(), py2dm.Writer.node(), and py2dm.Writer.node_string() methods do not commit any changes to disk. Instead, they write the elements to an internal cache that is later written to disk in a single chunk.

When the py2dm.Writer class’s context manager is existed, this is done automatically. Until then, the entire node and element list is cached in memory.

For small meshes (<100’000 elements), this is not a problem. However, for larger meshes it may be preferable to write the file in chunks of a few thousand entities (i.e. rows) at a time.

To do this, the py2dm.Writer class provides the py2dm.Writer.flush_elements(), py2dm.Writer.flush_nodes(), and py2dm.Writer.flush_node_strings() methods respectively, which allow committing the cache to disk, freeing up its memory:

import random
import py2dm

def random_point(scale):
  return tuple((random.random()*scale for _ in range(3)))

with py2dm.Reader('path/to/mesh.2dm') as mesh:
   for i in range(1_000_000):
     mesh.node(-1, *random_point())
     if i % 10_000 == 0:
      mesh.flush_nodes()
   ...

This is particularly useful when converting large files from other formats, since both the read and write operations can be done without loading either file into memory completely.

Important

When writing files in chunks, it is not possible to intermix entity types. This is due to the node and element ID ranges needing to be consecutive blocks in the file.

If you already committed nodes to a file and write any elements or node strings, you can no longer add any new nodes without an error being raised (and vice-versa).

Writer class interface

class py2dm.Writer

Py2DM writer class used to validate and write 2DM files.

Any elements are initially written to memory and only get committed to disk once the writer is closed or one of the flush_*() methods is called.

__init__(filepath: str, **kwargs)None

Create a new mesh writer.

Parameters
  • filepath (str | pathlib.Path) – Path to the mesh file to write.

  • encoding (str) – Text encoding to use for the file.

name: str

Display name of the mesh.

A custom name to store in the mesh to aid with identification. Can be written to disk as part of a write_header() call.

Type

str

property closed

Return whether the underlying file is closed.

After closing (either via the close() method or by leaving the reader’s context manager), any operations requiring use of the underlying file will raise a py2dm.errors.FileIsClosedError.

Type

bool

property materials_per_element

Number of materials per element.

This value can be specified via the materials keyword as part of the Writer class’s initialiser, or it will be inferred from the first element passed. When write_header() is called with no elements added, the number of elements is additionally initialised to zero.

Once set, this value can not be modified. Errors will be raised for elements with fewer materials than required, and extraneous materials will be stripped as part of the element() method.

When writing the mesh to disk, this value will be stored in the NUM_MATERIALS_PER_ELEM <count> card at the top of the file.

Type

int

property num_elements

Return the number of elements in the mesh.

Type

int

property num_nodes

Return the number of nodes in the mesh.

Type

int

property num_node_strings

Return the number of node strings in the mesh.

Type

int

close()None

Close the mesh reader.

This commits any pending data to disk and closes the underlying text file. The instance will become unusable after this call.

Note

This method is called automatically when using the class via the context manager.

open()None

Open the mesh reader.

This performs the initial metadata read and sets up the class for continued access.

When calling this function manually, be sure to call close() once you no longer require file access. Alternatively, you can use the context manager interface, in which case both methods will be called automatically.

element(element: py2dm.Element)int
element(element: type[py2dm.Element] | str, id_: int, *nodes: int, materials: tuple[int | float, …] = None)int

Add an element to the mesh.

If element is an instance of a Element subclass, a deep copy will be created. In this case, no other arguments may be passed.

Alternatively, this method can be used as a factory, with the element field being either the subclass to instantiate or the name of the 2DM card of the element. When using the factory, any extra arguments are passed on to the class’s initialiser. Refer to the docstring of the respective class for supported arguments.

If the ID of the element is negative, one will be selected automatically based on the number of existing elements.

Parameters
  • element (py2dm.Element | type [py2dm.Element] | str]) – The instance or type of element to add.

  • *args (typing.Any) – Extra arguments to forward to the class’s initialiser.

  • **kwargs (typing.Any) – Extra keyword arguments to forward to the class’s initialiser.

Raises

TypeError – Raised when extra arguments are passed while also passing a py2dm.Element instance.

Returns

The ID of the element that was added.

Return type

int

node(node: py2dm.Node)int
node(node: int, x: float, y: float, z: float)int

Add a node to the mesh.

If node is an instance of Node, a deep copy will be created. In this case, no other arguments may be passed.

Alternatively, this method can be used as a factory, with the node field being used for the ID of the node to create. When using the factory, any extra arguments are passed on to the py2dm.Node initialiser. Refer to its docstring for supported arguments.

If the ID of the node is negative, one will be selected automatically based on the number of existing nodes.

Parameters
Raises

TypeError – Raised when extra arguments are passed while also passing a py2dm.Node instance.

Returns

The ID of the node that was added.

Return type

int

node_string(node_string: py2dm.NodeString)int
node_string(node_string: int, *nodes: int, name: Union[str, None] = None)int

Add a node string to the mesh.

If node_string is an instance of NodeString, a deep copy will be created. In this case, no other arguments may be passed.

Alternatively, this method can be used as a factory, with the node_string field being used for the first node ID in the string. When using the factory, any extra arguments are passed on to the py2dm.NodeString initialiser. Refer to its docstring for supported arguments.

Parameters
  • node_string – The node string instance to add, or the ID of the first node for the node string to create.

  • *args (typing.Any) – Extra arguments to forward to the py2dm.NodeString initialiser.

  • **kwargs (typing.Any) – Extra keyword arguments to forward to the py2dm.NodeString initialiser.

Raises

TypeError – Raised when extra arguments are passed while also passing a py2dm.NodeString instance.

Returns

The zero-based index of the node string in the mesh’s list of node strings.

Return type

int

flush_elements(**kwargs)None

Write the local element cache to disk.

This clears out the in-memory element cache and writes its contents to disk. This method is called automatically when the Writer is closed.

flush_nodes(**kwargs)None

Write the local node cache to disk.

This clears out the in-memory node cache and writes its contents to disk. This method is called automatically when the Writer is closed.

flush_node_strings(**kwargs)None

Write the local node string cache to disk.

This clears out the in-memory node string cache and writes its contents to disk. This method is called automatically when the Writer is closed.

write_header(signature: str = '')None

Write the header of the 2DM file.

This writes the initial MESH2D format identifier, as well as the NUM_MATERIALS_PER_ELEM field.

The optional signature argument allows specifying a custom string to append to the initial line in the form of a comment. This string may contain newlines.

Parameters

signature (str) – An authoring signature to include.