How do I set metadata of an existing Measure instance?

11 weeks ago by
I want to be able to set metadata, specifically the quadrature degree, of an already existing Measure instance.

Based on what I read from help(fenics.Measure),

Help on class Measure in module ufl.measure:

class Measure(builtins.object)
| Methods defined here:
| __add__(self, other)
| Add two measures (self+other).
| Creates an intermediate object used for the notation
| expr * (dx(1) + dx(2)) := expr * dx(1) + expr * dx(2)
| __call__(self, subdomain_id=None, metadata=None, domain=None, subdomain_data=None, degree=None, scheme=None, rule=None)
| Reconfigure measure with new domain specification or metadata.

I tried

import fenics

dx = fenics.Measure("dx")

dx8 = fenics.Measure("dx", metadata={"quadrature_degree":  8})

dx(metadata={"quadrature_degree":  8})

assert(dx.metadata() == dx8.metadata())

but unfortunately, Measure.__call__, actually returns a new instance, and does not modify the caller in place as I had expected from reading the doc.

I also tried
dx.metadata()["quadrature_degree"] = 8​

which throws the error

This is a frozen unique empty dictionary object, inserting values is an error.

So how can I do this? Essentially I am looking for setter method for Measure.metadata. I've read the whole doc and tried digging around with dx.__dict__ or dx._asdict(). I haven't looked at the source code of Measure yet.

I'm using FEniCS 2017.2.0.

Edit: AllAnswered isn't indenting the outputs that I tried to indent, so feel free to suggest a way to format the output from help and the error.
Community: FEniCS Project
Also since I am essentially looking for a "setter", now I tried doing this directly:

dx.metadata = dx(metadata={"quadrature_degree":  8}).metadata()​

but this throws the error "AttributeError: 'Measure' object attribute 'metadata' is read-only".

so perhaps FEniCS purposefully does not allow me to do this.
written 11 weeks ago by Alexander G. Zimmerman  
You could always do
dx._metadata = dx(metadata={"quadrature_degree":  8}).metadata()​​
but I don't think that's considered best practice...

I did some tinkering and found that the "reconstruct" method seems to be a reliable way to set specific attributes (without altering others on a seemingly-random basis, the way __call__ does).  It still technically creates a new instance, but you may or may not find the below test code useful:

from dolfin import *

# This adds to and/or overwrites specific attributes of the an
# existing metadata dictionary.
def addMeta(m1,m2):
    import ufl.utils.dicts
    if(not isinstance(m1,ufl.utils.dicts.EmptyDictType)):
        return m1.update(m2)
    return m2


dx = dx.reconstruct(metadata=addMeta(dx.metadata(),{"a":"b"}))


dx = dx.reconstruct(subdomain_id=0)


dx = dx.reconstruct(subdomain_data


dx = dx.reconstruct(metadata=addMeta(dx.metadata(),{"quadrate_degree":8}))


dx = dx.reconstruct(metadata=addMeta(dx.metadata(),
                                     {"quadrate_degree":7, "c":"d"}))

written 11 weeks ago by David Kamensky  
Unfortunately creating a new instance doesn't solve my current problem; but thanks for pointing out the difference between __call__ and reconstruct. I'm sure I would have been perplexed by this at some point.
written 10 weeks ago by Alexander G. Zimmerman  

1 Answer

10 weeks ago by
As David suggested in the comments, the following runs without error:

import fenics

dx = fenics.Measure("dx")

dx8 = fenics.Measure("dx", metadata={"quadrature_degree":  8})

dx._metadata = dx8.metadata()

assert(dx.metadata() == dx8.metadata())

Given how metadata is "hidden" without an obvious setter, I'm assuming we aren't supposed to do this. That being said, I haven't actually tested if this has any adverse effects when performing quadrature.
Please login to add an answer/comment or follow this question.

Similar posts:
Search »