Source code for openpnm.io._stl

import logging
import numpy as _np
from openpnm.io import _parse_filename
logger = logging.getLogger(__name__)


[docs] def network_to_stl(network, filename=None, maxsize='auto', fileformat='STL Format', logger_level=0): r""" Saves (transient/steady-state) data from the given objects into the specified file. Parameters ---------- network : Network. The network containing the desired data. phases : list[Phase] (place holder, default is None). List of phases containing the desired data. filename : str (optional). The name of the file containing the data to export. maxsize : a float or a string "auto" (optional). The maximum size of the mesh elements allowed. "auto" corresponds to an automatic determination based on pores and throats sizes. Any float value will be used as a maximum size. Small values result in finner meshes, but slower mesh calculations. fileformat : str (optional). Default is "STL Format" which corresponds to STL format. Other formats such as Gmsh and Fluent are supported (see ngsolve.org). logger_level : integer between 0 and 7 (optional). Default is 0. The logger level set in netgen package. Notes ----- The STL Format is a Standard Triangle (or Tessellation) Language supported by many CAD packages and used for 3D printing. Export to this format may be slow since a 3D closed surface mesh is built. Requires installation of "netgen". """ try: import netgen.csg as csg except ModuleNotFoundError: raise Exception('Module "netgen" not found.') try: from netgen.meshing import SetMessageImportance as log log(logger_level) except ModuleNotFoundError: logger.warning('Module "netgen.meshing" not found.' + ' The "logger_level" will be ignored.') # Temporarily add endpoints to network so STL class works network["throat.endpoints.head"] = network.coords[network.conns[:, 0]] network["throat.endpoints.tail"] = network.coords[network.conns[:, 1]] filename = network.name if filename is None else filename path = _parse_filename(filename=filename, ext='stl') # Path is a pathlib object, so slice it up as needed fname_stl = path.name # Correct connections where 'pore.diameter' = 'throat.diameter' dt = network['throat.diameter'].copy() dp = network['pore.diameter'][network['throat.conns']] dt[dp[:, 0] == dt] *= 0.99 dt[dp[:, 1] == dt] *= 0.99 scale = max(network['pore.diameter'].max(), dt.max(), network['throat.length'].max()) if maxsize == 'auto': maxsize = min(network['pore.diameter'].min(), dt.min(), network['throat.length'].min()) geo = csg.CSGeometry() # Define pores geometry = csg.Sphere(csg.Pnt(network['pore.coords'][0, 0]/scale, network['pore.coords'][0, 1]/scale, network['pore.coords'][0, 2]/scale), network['pore.diameter'][0]/scale/2) for p in range(1, network.Np): pore = csg.Sphere(csg.Pnt(network['pore.coords'][p, 0]/scale, network['pore.coords'][p, 1]/scale, network['pore.coords'][p, 2]/scale), network['pore.diameter'][p]/scale/2) geometry += pore # Define throats for t in range(network.Nt): A = network['throat.endpoints.tail'][t, :]/scale B = network['throat.endpoints.head'][t, :]/scale V = (B-A)/_np.linalg.norm(B-A) plane1 = csg.Plane(csg.Pnt(A[0], A[1], A[2]), csg.Vec(-V[0], -V[1], -V[2])) plane2 = csg.Plane(csg.Pnt(B[0], B[1], B[2]), csg.Vec(V[0], V[1], V[2])) cylinder = csg.Cylinder(csg.Pnt(A[0], A[1], A[2]), csg.Pnt(B[0], B[1], B[2]), dt[t]/scale/2) throat = cylinder * plane1 * plane2 geometry += throat # Add pore and throats to geometry, build mesh, rescale, and export geo.Add(geometry) mesh = geo.GenerateMesh(maxh=maxsize/scale) mesh.Scale(scale) mesh.Export(filename=fname_stl, format=fileformat) # Remove endpoints label from network del network["throat.endpoints"]