MPh-py / MPh

Pythonic scripting interface for Comsol Multiphysics
https://mph.readthedocs.io
MIT License
281 stars 71 forks source link

Stack overflow when accessing property of scattering boundary condition #68

Closed joshuaneronha closed 2 years ago

joshuaneronha commented 2 years ago

I'm working with the electromagnetic waves / frequency domain physics with COMSOL 5.3a, and very weirdly I can't inspect or otherwise work with scattering boundary conditions. Everything else seems to work fine. For example, if I define two nodes:

node_scattering = model/'physics/Electromagnetic Waves, Frequency Domain/Scattering' node_ffd = model/'physics/Electromagnetic Waves, Frequency Domain/Far-Field Domain 1/Far-Field Calculation 1'

And then use the .properties() method on each, node_ffd has no problem executing while node_scattering raises a Java exception: 'java.lang.StackOverflowError: java.lang.StackOverflowError.' The same exception comes up when I use mph.inspect((node_scattering).java). Everything other than scattering boundary conditions seems to be fine; I've tried to delete the layer and add a new one, but if it's scattering it breaks no matter what.

I also made an extremely basic model that is literally just a cube with a scattering boundary condition, and the same exception occurs.

john-hen commented 2 years ago

Hi Joshua. I'm afraid I won't be able to fix that error as it seems to occur in Java, and not on the Python side of things. Which would mean it's either an issue with Comsol itself or with JPype (the Python-to-Java bridge), but not with MPh.

If you want to upload that basic model, I could at least tell you if I can reproduce the error. (You can attach the model to a comment if you change the file ending to something like .zip, which tricks GitHub into accepting it.) And maybe I can think of a way to find out if the culprit is Comsol or JPype. Basically, if one can write a Java program that triggers the same error, then it's on Comsol and can be reported to Comsol Support. If not, then it's an issue with JPype. I don't see how things could go wrong in MPh there, as all nodes are treated exactly the same and there's no reason why they wouldn't be.

joshuaneronha commented 2 years ago

testing.zip

Here you go! Thanks so much for your such quick help with this!

joshuaneronha commented 2 years ago

Okay -- I ended up turning it into a Java model using .java. And using the embedded Java commands everything works fine. Not sure why, exactly, but thankfully this is a bug there's an easy workaround for, so no solution to this is needed!

john-hen commented 2 years ago

Yeah, I probably spoke too soon, maybe I actually can fix this in MPh. I have reproduced the error, and there seems to be some recursion going on that eventually leads to that stack overflow. I don't really know why that happens, but I've noticed these "infinite loops" in Comsol's object hierarchy before. And that seems to be the issue here too. I don't know why there are these loops. Should be hierarchical after all. Will look into this when I have more time. Good to know you found a work-around for now, using the Java layer as a fallback.

john-hen commented 2 years ago

So, I've concluded that this is a problem in Comsol, not MPh or JPype, best as I can tell.

That stack overflow occurs when accessing the values of the properties "E0i" and "H0i" of a "Scattering Boundary Condition" node attached to an "Electromagnetic Waves, Frequency Domain" physics interface as provided by the RF module. I'm guessing these two properties are related to the "incident field". Their Java data type is StringArray, so maybe they would accept arbitrary expressions, but I've found nothing concrete on them in the Comsol documentation.

With MPh, we can reproduce the error with this script, test_MPh.py:

import mph

client = mph.start()
model  = client.load('model.mph')

parent = model/'physics'/'Electromagnetic Waves, Frequency Domain'
node   = parent/'Scattering Boundary Condition 1'
node.java.getStringArray('E0i')

I have renamed the model to model.mph, but it's the same file as testing.mph from the earlier comment. The last line in the test script raises java.lang.StackOverflowError, which comes with a trace-back of over 1000 lines. So it seems to get stuck in some infinite loop and eventually runs out of memory on the call stack.

We can also trigger the error with this Java program, TestComsol.java:

import com.comsol.model.*;
import com.comsol.model.util.*;
import com.comsol.model.physics.*;

public class TestComsol {
    public static void main(String[] args) {
        try {

            ModelUtil.initStandalone(false);
            Model model = ModelUtil.load("model", "model.mph");

            Physics parent = model.physics().get("emw");
            PhysicsFeature node = parent.feature().get("sctr1");
            node.getStringArray("E0i");

            System.exit(0);

        } catch (Throwable exception) {
            exception.printStackTrace();
            System.exit(1);
        }
    }
}

On Windows, it could be compiled and run with a batch script such as this one:

@echo off
setlocal

set ComsolDir=C:\Program Files\COMSOL\COMSOL56\Multiphysics
set path=%ComsolDir%\bin\win64;%ComsolDir%\java\win64\jre\bin

echo Compiling source.
comsolcompile TestComsol.java

echo Running program.
java -classpath ".;%ComsolDir%\plugins\*" TestComsol

The Java program also throws java.lang.StackOverflowError, with a similarly long stack trace. So this is an issue in Comsol itself as no Python library is involved.

In principle, we could catch these errors in MPh. I'll have to think whether that makes sense here. Generally speaking, we don't do that. MPh is an intermediate API, so we want to "fail fast" and not gloss over errors. Then again, the Node.properties() method always returns the names and values. There is currently no way to have it return only the names, which makes it difficult to diagnose and work around this issue. Another option would be to return the exception as the value, though that's a little strange.

The actual issue I obviously cannot fix.

john-hen commented 2 years ago

I've decided to split the difference and only catch these exceptions in mph.inspect(). This should make it easier to diagnose the problem, for example in an interactive session:

>>> mph.inspect(node)
name:    Scattering Boundary Condition 1
tag:     sctr1
type:    Scattering
display: Scattering Boundary Condition 1
doc:     emw_sctr
properties:
  IncidentField: NoIncidentField
  E0i: <java.lang.StackOverflowError>
  H0i: <java.lang.StackOverflowError>
  WaveType: PlaneWave
  …

Calling node.properties() will continue to fail (in these rare cases). That's fine because in the end this is an upstream issue that MPh shouldn't get involved in.

As a side note, if one does need to know only the property names available on a node, in order to avoid the StackOverflowError discussed here, one can always call the underlying Java method directly: node.java.properties().

john-hen commented 2 years ago

That fix of mph.inspect() is included in MPh 1.1.3, released today.