There is a newer version of the record available.

Published November 25, 2025 | Version v2.0.0
Software Open

aatmdelissen/pyMOTO: v2.0.0

  • 1. TNO
  • 2. Delft University of Technology
  • 3. University of Luxembourg

Description

What's Changed

Main improvements with syntax change:

  • More clear intent of code
  • Much less boilerplate code. Adding modules to a Network does not require append anymore, but modules are automatically added to Network, user can select which Network(s) to use with context manager (with:)
  • Modules now accept optional positional arguments (not keyword arguments)
  • Standard functions __init__ and __call__ are used, for much better IDE integration and code hints
  • Normal variables can now be used instead of or in combination with signals as module inputs
  • The module response is run once upon creation, which is more convenient for debugging

An examples gallery is added on the documentation page, which contains all examples in the new syntax style. Also, several new examples were added.

Renamed (old names will raise deprecation warning):

  • MathGeneral -> MathExpression
  • DomainDefinition -> VoxelDomain
  • DyadCarrier -> DyadicMatrix
  • VecSet -> SetValue
  • ConcatSignal -> Concatenate

Module improvements:

  • AutoDiff module improvements, supporting backend based on HIPS autograd as alternative to jax
  • WriteToVTI now supports writing complex vectors
  • Scaling now supports double-sided constraints, where both a minimum and maximum value is requested
  • AssembleGeneral now supports non-square matrices. Also, multiple element matrices can be passed with each their own individual scaling values to support multi-material topology optimization. The matrix assembly is also sped up by storing the sparsity pattern.
  • OverhangFilter now supports freely choosing overhang angle for additive manufacturing, instead of only 45 degrees (for details see linked paper).

New modules:

  • SplitComplex - Splits complex value into its real and imaginary parts
  • Print - Print values in a signal to console
  • SetValue - Set values in a numpy array for specified indices
  • AddMatrix - Linear combination of sparse matrices
  • Conjugate - Complex conjugation
  • TransientSolve - Calculation of transient response with numerical time-stepping (implemented for first order system)
  • SeriesToVTI - Export of time-series data to VTI format (Paraview)

Other improvements:

  • New methods in VoxelDomain (formerly DomainDefinition) (element_size, domain_size, size, get_dofnumber, get_element_indices)
  • VoxelDomain now also can voxelize mesh objects (e.g. from STL) using the methods create_for_mesh, which generates a suitable domain, and voxelize which then converts a triangle mesh into voxel elements.
  • Switch from using pypardiso to directly using mkl for usage of Pardiso solver. Also enable solving matrices with complex and/or single precision.
  • MMA now supports unconstrained optimization (only an objective), see issue #7
  • MMA optimizer can now also switch to the globally convergent version (GCMMA) with option mmaversion='GCMMA')
  • MMA speedups
  • SLP optimization algorithm

Software implementation:

  • Improve publishing scripts
  • Refactoring and formatting (ruff)
  • Switch to hatchling as build backend to better support VSCode integration

How to port your code to version 2.0

Module definition

Old style (pymoto < 2)

class MyModule(pym.Module):
    def _prepare(self, val1, val2=0.5)
        ...  # Preparations here

    def _response(self, x, *args):
        if len(args) == 1:
            y = args[0]
        ...  # Response calculation here
        return z

    def _sensitivity(self, dz):
        .... # Do sensitivity calculations here
        return dx, ...

In the new style (pymoto >= 2) this becomes

class MyModule(pym.Module):
    # __init__ function is used instead of _prepare
    def __init__(self, val1, val2=0.5): 
         ...  # Preparations here

    # __call__ function is used instead of _response
    def __call__(self, x, y=None):  # Optional positional arguments are now supported
        ...  # Response calculation here
        return z
    
    # No changes to sensitivity
    def _sensitivity(self, dz):
        .... # Do sensitivity calculations here
        return dx, ...

Module creation and usage

Old style (pymoto < 2)

fn = pym.Network()
sz1 = fn.append(MyModule(sx, val1=1.0, val2=3.0))  # Module with one input
sz2 = fn.append(MyModule([pym.Signal('constant', 1.0), sz1], val1=2.0))  # Module with two inputs

fn.response()  # Only after calling `response()`, the response functions in the network will be run for the first time

pym.minimize_mma(fn, sx, sz2)  # Optimize something

New style (pymoto >= 2)

with pym.Network() as fn:  # Network as context manager
    sz1 = MyModule(1.0, val2=3.0)(sx)  # First options for the module (for __init__), after that the signal values (for __call__)
    sz2 = MyModule(2.0)(1.0, sz1)  # Constant values can be used directly as module input

# At this point all the response function in the network have been run once.

...  # Change inputs signals
fn.response()  # Usage of network remains the same. For updated values, response() can be called to re-calculate responses

pym.minimize_mma(sx, sz2, function=fn)  # Passing function to the optimizer is now optional. If not passed, a global network will be used instead of the network called `fn`.

Full Changelog: https://github.com/aatmdelissen/pyMOTO/compare/v1.5.1...v2.0.0

Files

aatmdelissen/pyMOTO-v2.0.0.zip

Files (24.1 MB)

Name Size Download all
md5:9648b9b164e319773611ada54f135942
24.1 MB Preview Download

Additional details

Related works