MPh Documentation

repository·main·Indexed 17 days ago

https://github.com/mph-py/mph

A Pythonic scripting interface for Comsol Multiphysics that uses JPype to bridge Python with the Comsol Java API. MPh allows developers to automate simulation workflows, including loading .mph models, modifying parameters, running simulations, and evaluating results. It provides an idiomatic Python API for model navigation and manipulation, while still allowing full access to the Comsol API via the .java attribute.

Tokens
8.2K
Snippets
29
Records
38
Agent score
66%

What's inside MPh

  1. Overview of MPh

    main

    MPh is a Pythonic scripting interface for Comsol Multiphysics. It uses JPype to bridge Python with the Comsol Java API, allowing developers to automate simulation workflows using Python instead of Matlab or native Java.

    Common tasks supported by the library include:

    • Loading .mph models from files.
    • Modifying model parameters.
    • Importing data.
    • Running simulations.
    • Evaluating results.
    • Exporting data.

    Note: MPh is an open-source project and is not affiliated with Comsol Inc.

  2. Access the full Comsol API via the .java attribute

    main

    While MPh provides an idiomatic Python API for common tasks, you can access the complete Comsol API using the .java attribute on Client or Model instances. This attribute exposes the underlying Java layer via JPype, mapping to Comsol's ModelUtil and model objects respectively. This allows you to translate existing Java or Matlab code from Comsol documentation directly into Python with minimal changes (e.g., replacing Java array syntax like new String[]{...} with Python lists [...]).

    import mph
    
    client = mph.start()
    pmymodel = client.create('Model')
    model = pymodel.java
    
    # Now 'model' is a Java object allowing direct Comsol API calls
    model.modelNode().create("comp1")
    model.geom().create("geom1", 3)
    model.geom("geom1").feature().create("blk1", "Block")
    model.geom("geom1").feature("blk1").set("size", ["0.1", "0.2", "0.5"])
    model.geom("geom1").run("fin")
    
    # To save the model using the Python wrapper
    pmymodel.save('model')
  3. Use the Node class and division operator for model navigation

    main

    For more flexible model navigation, use the Node class. model.create() returns Node instances, which support the division operator (/) for path traversal, similar to pathlib.Path. This allows you to build hierarchical references cleanly.

    Path Syntax Rules:

    • Division Operator: model/'geometries'/'geometry'/'ice block' refers to a specific node.
    • String Paths: model/'geometries/geometry/ice block' is also valid.
    • Root Reference: Use model/'' or model/None to refer to the root.
    • Escaping Slashes: If a node name contains a literal forward slash (e.g., ice/frozen water), escape it by doubling the slash: geometry/'ice//frozen water'.
    import mph
    client = mph.start()
    model = client.create('block of ice')
    
    # Using the division operator for navigation
    geometries = model/'geometries'
    geometry = geometries.create(3, name='geometry')
    block = geometry.create('Block', name='ice block')
    block.property('size', ('0.1', '0.2', '0.5'))
    
    model.build(geometry)
  4. Run parallel simulations using multiple processes

    main

    Because MPh uses JPype, it is limited to one Java Virtual Machine (JVM) per Python process. Additionally, the Comsol API does not support running more than one client within the same Java program.

    To achieve parallel execution (e.g., for a parameter sweep distributed over multiple CPU cores), you must start each simulation as a separate Python subprocess rather than using threads within a single process.

  5. Configure Client Mode: Stand-alone vs Client-Server

    main

    MPh supports two modes of operation via the Client class or the mph.option() configuration:

    1. Stand-alone mode: The client runs inside the same process as Python. It is more lightweight and faster for frequent API calls (like navigating the model tree with mph.tree()), but it is harder to set up on Unix-like systems.
    2. Client-server mode: A 'thin' client connects to a separate Comsol server via a network socket. This is the default mode because it works out of the box on all platforms, but it has higher overhead for frequent API interactions.

    Configuration Options

    • Via Client class: If you provide a network port value, it runs in client-server mode. If you do not provide a port, it attempts stand-alone mode.
    • Via mph.option(): You can set the 'session' option to:
      • 'client-server' (Default)
      • 'stand-alone'
      • 'platform-dependent' (Uses stand-alone on Windows, client-server on Linux/macOS)
  6. Run multiple Comsol sessions in parallel using multiprocessing

    main

    Because a single Python process cannot run more than one Comsol session, you must use the multiprocessing module to achieve parallel execution. This allows you to run multiple Python processes, each managing its own mph.start() session, to perform tasks like parameter sweeps across multiple CPU cores.

    Implementation Pattern

    1. Define a Worker Function: The function should call mph.start(cores=1) inside the process to initialize a local Comsol session. It should pull tasks from a multiprocessing.Queue (jobs), perform the simulation, and push results to another multiprocessing.Queue (results).
    2. Initialize Queues: Create a multiprocessing.Queue() for jobs and an empty one for results.
    3. Spawn Processes: Use multiprocessing.Process to start multiple workers. It is recommended to keep references to these process objects in a list to prevent garbage collection from terminating them prematurely.
    4. Collect Results: In the main process, iterate through the results queue to retrieve the completed data. Note that because processes run asynchronously, results may not be returned in the same order they were submitted.

    This approach provides more programmatic control than Comsol's internal 'parametric sweep', making it suitable for iterative optimization algorithms like genetic algorithms.

    import mph
    import multiprocessing
    import queue
    
    # 1. Define the worker
    def worker(jobs, results):
        client = mph.start(cores=1)
        model = client.load('capacitor.mph')
        while True:
            try:
                d = jobs.get(block=False)
            except queue.Empty:
                break
            model.parameter('d', f'{d} [mm]')
            model.solve('static')
            C = model.evaluate('2*es.intWe/U^2', 'pF')
            results.put((d, C))
    
    # 2. Setup data and queues
    values = [0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]
    jobs = multiprocessing.Queue()
    for d in values:
        jobs.put(d)
    results = multiprocessing.Queue()
    
    # 3. Start workers
    processes = []
    for _ in range(4):
        process = multiprocessing.Process(target=worker, args=(jobs, results))
        process.start()
        processes.append(process)
    
    # 4. Collect results
    for _ in values:
        (d, C) = results.get()
        print(f"Distance: {d}, Capacitance: {C}")
  7. Fix java.lang.UnsatisfiedLinkError on Linux and macOS

    main

    On Linux and macOS, stand-alone mode requires manual configuration of shared-library paths to avoid java.lang.UnsatisfiedLinkError. You must add the Comsol library directories to the LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) environment variables.

    Example for Comsol 6.3 on Linux: Add the following to your .bashrc (adjust ComsolDir to your actual installation path):

    # Help MPh find Comsol's shared libraries in stand-alone mode.
    ComsolDir=/usr/local/comsol63/multiphysics
    export LD_LIBRARY_PATH=\ 
    $ComsolDir/lib/glnxa64:\
    $ComsolDir/lib/glnxa64/gcc:\
    $ComsolDir/ext/graphicsmagick/glnxa64:\
    $ComsolDir/ext/cadimport/glnxa64:\
    $LD_LIBRARY_PATH

    Note for macOS users: The ComsolDir path typically follows the pattern /Applications/COMSOL63/Multiphysics. Always consult your specific Comsol version's documentation for the correct library paths.

  8. Use developer helper scripts

    main

    The tools directory contains helper scripts to run development tasks. You can execute these using uv run.

    Common tasks include:

    • Linting: uv run tools/lint_code.py (equivalent to uv run ruff check)
    • Type Checking: uv run tools/check_types.py
    • Documentation Rendering: uv run tools/render_docs.py
    • Building Wheels: uv run tools/build_wheel.py
    • Running Tests: uv run tools/run_tests.py
    • Measuring Coverage: uv run tools/measure_coverage.py
    • Reporting Coverage: uv run tools/report_coverage.py (requires an upload token in your shell environment)
    uv run tools/lint_code.py
  9. Save a model and its solution

    main

    Use the model.save() method to save the current model state, including the solution and mesh data.

    • Calling model.save() without arguments will overwrite the original file used to load the model.
    • To save to a new file, provide a filename string. The .mph extension is automatically appended if it is not provided.
    # Overwrite the existing file
    model.save()
    
    # Save to a new filename (extension added automatically)
    model.save('capacitor_solved')
  10. Compact a model by clearing history and solutions

    main

    If you want to save disk space and only need to preserve the modeling details without the heavy solution or mesh data, you can prune the model before saving.

    1. Use model.clear() to remove certain data.
    2. Use model.reset() to reset the modeling history (the log of feature creation, deletion, and modification).
    3. Call model.save() to write the compacted model to disk.
    model.clear()
    model.reset()
    model.save('capacitor_compacted')
  11. Install MPh via pip

    main

    MPh can be installed from PyPI using pip. The installation automatically handles dependencies like JPype (required for the Python-to-Comsol Java API bridge) and NumPy (required for fast numerical arrays).

    To install:

    pip install MPh

    To remove the package:

    pip uninstall MPh

    Note: Uninstalling MPh will not remove its dependencies.

    Windows Users: If you encounter issues, avoid the Microsoft Store version of Python. Instead, use the 64-bit installers from [python.org] to ensure compatibility with Comsol's architecture.

  12. Set up local development with UV

    main

    To develop with mph locally, it is recommended to use [UV] to manage dependencies.

    1. Install uv globally:
      • Windows: winget install astral-sh.uv
      • Linux: curl -LsSf https://astral.sh/uv/install.sh | sh
      • macOS: brew install uv
    2. Clone the repository.
    3. Run uv sync in the project root to create a virtual environment in .venv with all dependencies defined in pyproject.toml.

    Alternatively, you can install the project in editable mode into an existing environment using:

    uv pip install --group dev --editable .
    uv sync