Skip to content

Commit

Permalink
Added Node methods for changing and querying selections.
Browse files Browse the repository at this point in the history
  • Loading branch information
john-hen committed Oct 15, 2021
1 parent f7c7166 commit 8f11c0e
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 21 deletions.
108 changes: 108 additions & 0 deletions mph/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from jpype import JDouble # Java float
from jpype import JString # Java string
from jpype import JArray # Java array
from jpype import JClass # Java class
from numpy import integer # NumPy integer
from pathlib import Path # file-system path
from re import split # string splitting
from json import load as json_load # JSON decoder
Expand Down Expand Up @@ -331,6 +333,108 @@ def properties(self):
names = sorted(str(name) for name in java.properties())
return {name: get(java, name) for name in names}

def select(self, entity):
"""
Assigns `entity` as the node's selection.
`entity` can either be another node representing a selection
feature, in which case a "named" selection is created. Or it
can be a list/array of integers denoting domain, boundary,
edge, or point numbers (depending on which of those the selection
requires), producing a "manual" selection. It may also be `'all'`
to select everything or `None` to clear the selection.
Raises `NotImplementedError` if the node (that this method is
called on) is a geometry node. These may be supported in a
future release. Meanwhile, access their Java methods directly.
Raises `TypeError` if the node does not have a selection and
is not itself an "explicit" selection.
"""
java = self.java
if not java:
error = f'Node "{self}" does not exist in model tree.'
log.error(error)
raise LookupError(error)
if isinstance(java, JClass('com.comsol.model.GeomFeature')):
error = "Use the Java layer to change a geometry node's selection."
log.error(error)
raise NotImplementedError(error)
try:
java = java.selection()
except Exception:
if any(not hasattr(java, attr) for attr in ('set', 'all')):
error = f'Node "{self}" has no and is no (explicit) selection.'
log.error(error)
raise TypeError(error) from None
if isinstance(entity, Node):
if not hasattr(java, 'named'):
error = f'Node "{self}" does not support named selections.'
log.error(error)
raise TypeError(error)
node = entity
if not node.exists():
error = f'Assigned node "{node}" does not exist.'
log.error(error)
raise LookupError(error)
java.named(node.tag())
elif isinstance(entity, (list, ndarray)):
java.set(cast(entity) if len(entity) else None)
elif isinstance(entity, (int, integer)):
java.set(cast(entity))
elif entity == 'all':
java.all()
elif entity is None:
java.set(cast(None))
else:
error = "Entity must be a node, 'all', or an array of integers."
log.error(error)
raise ValueError(error)

def selection(self):
"""
Returns the entity or entities the node has selected.
If it is a "named" selection, the corresponding selection node
is returned. If it is a "manual" selection, an array of domain,
boundary, edge, or point numbers is returned (depending on
which of those the selection holds). `None` is returned if
nothing is selected.
Raises `NotImplementedError` if the node is a geometry node.
These may be supported in a future release. Meanwhile, access
their Java methods directly. Raises `TypeError` if the node
does not have a selection and is not itself a selection.
"""
java = self.java
if not java:
error = f'Node "{self}" does not exist in model tree.'
log.error(error)
raise LookupError(error)
if isinstance(java, JClass('com.comsol.model.GeomFeature')):
error = "Use the Java layer to query a geometry node's selection."
log.error(error)
raise NotImplementedError(error)
try:
java = java.selection()
except Exception:
if not hasattr(java, 'entities'):
error = f'Node "{self}" has no and is no selection.'
log.error(error)
raise TypeError(error) from None
tag = str(java.named()) if hasattr(java, 'named') else None
if tag:
for node in self.model/'selections':
if tag == node.tag():
break
else:
error = f'Found no selection with reported tag "{tag}".'
log.error(error)
raise LookupError(error)
return node
else:
entities = java.entities()
return array(entities) if entities else None

def toggle(self, action='flip'):
"""
Enables or disables the node.
Expand Down Expand Up @@ -547,10 +651,14 @@ def cast(value):
"""Casts a value from its Python data type to a suitable Java data type."""
if isinstance(value, Node):
return JString(value.tag())
elif value is None:
return value
elif isinstance(value, bool):
return JBoolean(value)
elif isinstance(value, int):
return JInt(value)
elif isinstance(value, integer):
return JInt(int(value))
elif isinstance(value, float):
return JDouble(value)
elif isinstance(value, str):
Expand Down
42 changes: 21 additions & 21 deletions tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def capacitor():
media = selections.create('Union', name='media')
media.property('input', [medium1, medium2])
domains = selections.create('Explicit', name='domains')
domains.java.all()
domains.select('all')
exterior = selections.create('Adjacent', name='exterior')
exterior.property('input', [domains])
axis = selections.create('Box', name='axis')
Expand All @@ -119,42 +119,42 @@ def capacitor():
physics = model/'physics'
es = physics.create('Electrostatics', geometry, name='electrostatic')
es.java.field('electricpotential').field('V_es')
es.java.selection().named(media.tag())
es.select(media)
es.java.prop('d').set('d', 'l')
(es/'Charge Conservation 1').rename('Laplace equation')
(es/'Zero Charge 1').rename('zero charge')
(es/'Initial Values 1').rename('initial values')
anode = es.create('ElectricPotential', 1, name='anode')
anode.java.selection().named(anode_surface.tag())
anode.select(anode_surface)
anode.property('V0', '+U/2')
cathode = es.create('ElectricPotential', 1, name='cathode')
cathode.java.selection().named(cathode_surface.tag())
cathode.select(cathode_surface)
cathode.property('V0', '-U/2')
ec = physics.create('ConductiveMedia', geometry, name='electric currents')
ec.java.field('electricpotential').field('V_ec')
ec.java.selection().named(media.tag())
ec.select(media)
ec.java.prop('d').set('d', 'l')
(ec/'Current Conservation 1').rename('current conservation')
(ec/'Electric Insulation 1').rename('insulation')
(ec/'Initial Values 1').rename('initial values')
anode = ec.create('ElectricPotential', 1, name='anode')
anode.java.selection().named(anode_surface.tag())
anode.select(anode_surface)
anode.property('V0', '+U/2*step(t[1/s])')
cathode = ec.create('ElectricPotential', 1, name='cathode')
cathode.java.selection().named(cathode_surface.tag())
cathode.select(cathode_surface)
cathode.property('V0', '-U/2*step(t[1/s])')

materials = model/'materials'
medium1 = materials.create('Common', name='medium 1')
medium1.java.selection().named((model/'selections'/'medium 1').tag())
medium1.select(model/'selections'/'medium 1')
medium1.java.propertyGroup('def').set('relpermittivity',
['1', '0', '0', '0', '1', '0', '0', '0', '1'])
medium1.java.propertyGroup('def').set('relpermittivity_symmetry', '0')
medium1.java.propertyGroup('def').set('electricconductivity',
['1e-10', '0', '0', '0', '1e-10', '0', '0', '0', '1e-10'])
medium1.java.propertyGroup('def').set('electricconductivity_symmetry', '0')
medium2 = materials.create('Common', name='medium 2')
medium2.java.selection().named((model/'selections'/'medium 2').tag())
medium2.select(model/'selections'/'medium 2')
medium2.java.propertyGroup('def').set('relpermittivity',
['2', '0', '0', '0', '2', '0', '0', '0', '2'])
medium2.java.propertyGroup('def').set('relpermittivity_symmetry', '0')
Expand Down Expand Up @@ -330,7 +330,7 @@ def capacitor():
plot.property('ylabel', '|E| (V/m)')
plot.property('ylabelactive', True)
graph = plot.create('PointGraph', name='medium 1')
graph.java.selection().named((selections/'probe 1').tag())
graph.select(selections/'probe 1')
graph.property('expr', 'ec.normE')
graph.property('linecolor', 'blue')
graph.property('linewidth', 3)
Expand All @@ -340,7 +340,7 @@ def capacitor():
graph.property('legendmethod', 'manual')
graph.property('legends', ['medium 1'])
graph = plot.create('PointGraph', name='medium 2')
graph.java.selection().named((selections/'probe 2').tag())
graph.select(selections/'probe 2')
graph.property('expr', 'ec.normE')
graph.property('linecolor', 'red')
graph.property('linewidth', 3)
Expand Down Expand Up @@ -459,16 +459,16 @@ def needle():

selections = model/'selections'
domains = selections.create('Explicit', name='domains')
domains.java.all()
domains.select('all')
exterior = selections.create('Adjacent', name='exterior')
exterior.property('input', [domains])
electrode = selections.create('Explicit', name='electrode')
electrode.java.set(2)
electrode.select(2)
surface = selections.create('Adjacent', name='surface')
surface.property('input', [electrode])
insulation = selections.create('Explicit', name='insulation')
insulation.java.geom(geometry.tag(), 2)
insulation.java.set(4)
insulation.select(4)
ground = selections.create('Difference', name='ground')
ground.property('entitydim', 2)
ground.property('add', [exterior])
Expand All @@ -479,25 +479,25 @@ def needle():

physics = model/'physics'
field = physics.create('Electrostatics', geometry, name='field')
field.java.selection().named(vacuum.tag())
field.select(vacuum)
(field/'Charge Conservation 1').rename('Laplace equation')
(field/'Zero Charge 1').rename('zero charge')
(field/'Initial Values 1').rename('initial values')
(field/'Laplace equation').property('epsilonr_mat', 'userdef')
electrode = field.create('ElectricPotential', 2, name='electrode')
electrode.java.selection().named(surface.tag())
electrode.select(surface)
electrode.property('V0', '-U')
ground = field.create('Ground', 2, name='ground')
ground.java.selection().named('dif1')
ground.select(selections/'ground')
electrons = physics.create('ChargedParticleTracing', geometry,
name='electrons')
electrons.java.selection().named(vacuum.tag())
electrons.select(vacuum)
(electrons/'Wall 1').rename('walls')
(electrons/'Particle Properties 1').rename('properties')
electrons.java.prop('RelativisticCorrection').set('RelativisticCorrection',
JInt(1))
emission = electrons.create('Inlet', 2, name='emission')
emission.java.selection().named(surface.tag())
emission.select(surface)
emission.property('InitialPosition', 'Density')
emission.property('N', 20)
emission.property('dpro',
Expand All @@ -508,7 +508,7 @@ def needle():
emission.property('L0', [[0], [0], [1]])
emission.property('Ep0', '1[eV]')
force = electrons.create('ElectricForce', 3, name='force')
force.java.selection().all()
force.select('all')
force.property('E_src', 'root.comp1.es.Ex')

meshes = model/'meshes'
Expand Down Expand Up @@ -559,7 +559,7 @@ def needle():
datasets = model/'datasets'
(datasets/'study//Solution 1').rename('solution')
datasets.create('Surface', name='surface')
(datasets/'surface').java.selection().named(surface.tag())
(datasets/'surface').select(surface)
datasets.create('Particle', name='electrons')

plots = model/'plots'
Expand Down
57 changes: 57 additions & 0 deletions tests/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,61 @@ def test_properties():
assert ('funcname', 'step') in function.properties().items()


def test_select():
with logging_disabled():
with raises(LookupError):
Node(model, 'selections/non-existing').select(None)
with raises(NotImplementedError):
Node(model, 'geometries/geometry/rounded').select(None)
with raises(TypeError):
Node(model, 'parameters').select(None)
with raises(TypeError):
Node(model, 'functions/step').select(None)
with raises(TypeError):
Node(model, 'selections/domains').select(Node(model, ''))
cathode = Node(model, 'physics/electrostatic/cathode')
with raises(LookupError):
cathode.select(Node(model, 'selections/non-existing'))
with raises(ValueError):
cathode.select('invalid argument')


def test_selection():
cathode = Node(model, 'physics/electrostatic/cathode')
surface = Node(model, 'selections/cathode surface')
cathode.select(surface)
assert cathode.selection() == surface
cathode.select([1, 2, 3])
assert (cathode.selection() == array([1, 2, 3])).all()
cathode.select(array([1, 2, 3]))
assert (cathode.selection() == array([1, 2, 3])).all()
cathode.select(1)
assert (cathode.selection() == array([1])).all()
cathode.select(array([1])[0])
assert (cathode.selection() == array([1])).all()
cathode.select(None)
assert cathode.selection() is None
cathode.select('all')
assert (cathode.selection() == array(range(1, 27))).all()
domains = Node(model, 'selections/domains')
assert (domains.selection() == array([1, 2, 3, 4])).all()
domains.select([1, 2, 3])
assert (domains.selection() == array([1, 2, 3])).all()
domains.select(None)
assert domains.selection() is None
domains.select('all')
assert (domains.selection() == array([1, 2, 3, 4])).all()
with logging_disabled():
with raises(LookupError):
Node(model, 'selections/non-existing').selection()
with raises(NotImplementedError):
Node(model, 'geometries/geometry/rounded').selection()
with raises(TypeError):
Node(model, 'parameters').selection()
with raises(TypeError):
Node(model, 'functions/step').selection()


def test_toggle():
node = Node(model, 'functions/step')
assert node.java.isActive()
Expand Down Expand Up @@ -496,6 +551,8 @@ def test_tree():
test_rename()
test_property()
test_properties()
test_select()
test_selection()
test_toggle()
test_run()
test_create()
Expand Down

0 comments on commit 8f11c0e

Please sign in to comment.