sandialabs/omega_h

Large elements after adapt, but not requested

zhang-alvin opened this issue · 16 comments

I'm a new user to Omega_h and I'm seeing some larger than requested elements/edge lengths than what I believe I am requesting in the inputs.

I have this vertex field of desired sizes/edge-lengths shown below:
issue_omegah_desiredSize

From which I derive a 2x2 matrix to pass in to Omega_h::MetricInput:
issue_omegah_targetMetric
which seems to have converted the values to a 1/h^2 form. Only the XX component of the tensor is shown.

After calling Omega_h::generate_metric, I get the vertex field named metric which is effectively identical to the previous image, but is now only a single component:
issue_omegah_metric

Calling Omega_h::adapt() leads to:
issue_omegah_adapted

which has several really large elements. I suspect that I'm setting up the inputs incorrectly, so I'm posting the calls I used:

    Omega_h::MetricInput input;
    input.sources.push_back(Omega_h::MetricSource{OMEGA_H_GIVEN, 1.0, "target_metric",OMEGA_H_ISO_SIZE,OMEGA_H_ABSOLUTE});
    input.should_limit_gradation = false;
    input.verbose=true;

    Omega_h::generate_metric_tag(&om, input);
    auto opts = Omega_h::AdaptOpts(&om);
    opts.max_length_allowed = 1.5;
    Omega_h::adapt(&om, opts);

I would turn gradation limiting back on. Those "large" elements are the result of last-resort edge collapses done to improve quality, and the only reason I can think of for the code to get so worried about quality is that the original size field is so spotty that locally one can get into high-gradation states.

Also, in addition to limiting, I would use approach_metric instead of adapt.

In particular:

    Omega_h::MetricInput input;
    input.sources.push_back(Omega_h::MetricSource{OMEGA_H_GIVEN, 1.0, "target_metric",OMEGA_H_ISO_SIZE,OMEGA_H_ABSOLUTE});
    input.should_limit_gradation = true;
    input.verbose=true;

    Omega_h::generate_target_metric_tag(&om, input);
    auto opts = Omega_h::AdaptOpts(&om);
    Omega_h::approach_metric(&om, opts);

After thinking about it some more I think trying to do it all in one adapt could also be problematic.

Thanks @ibaned.

The sizes should already be graded in that no two edge-adjacent vertices should have sizes that have a ratio greater than 1.5. If I turn on the gradation limiting, is there a "good" value for the max gradation rate?

Turning on the gradation limiting gets me a new error:

limited gradation in 4 steps
generated metrics in 0.047728 seconds
maximum edge length 2.889738 > maximum allowed length 2.828427
signal 6 caught by pcu

Our grading algorithm is more advanced than that, and the gradation rate used in our algorithm should be close to 1.0, although decreasing it to like 0.5 can sometimes also help.

Are you sure you're calling approach_metric?

I see.

Yes I am sure I'm calling approach_metric. Is there a way to increase the verbosity?

That error only shows if your original mesh doesn't match the original metric (metric). If we're calling generate_target_metric_tag, then the metric we're forming should be called target_metric and shouldn't affect the original metric. Is there something in the code to initialize the original metric before adaptation starts?

Oh... its also dangerous I think to have the "given" field be also named target_metric. We should probably name the input field something else besides metric and target_metric.

Indeed, changing the "given" field name to something else allows approach_metric to successfully proceed!

Fantastic! Let me know if the mesh looks any better

Unfortunately, it doesn't seem like anything is happening to the mesh. Going back to your comment on the target_metric and the metric tags, do I need to explicitly define both prior to calling approach_metric? Currently, I'm only calling generate_metric_tag and not generate_target_metric_tag.

    //get the desired metric field
    auto target_metrics = Omega_h::Write<Omega_h::Real>(om.nverts() * Omega_h::symm_ncomps(om.dim()));

    auto sizeField = (&om)->get_array<Omega_h::Real>(Omega_h::VERT, "proteus_size");
    auto f = OMEGA_H_LAMBDA(Omega_h::LO vert) {
        //get value
        auto sizeVal = Omega_h::get_vector<1>(sizeField,vert);

        auto target_metric = Omega_h::compose_metric(
        Omega_h::identity_matrix<2, 2>(), Omega_h::vector_2(sizeVal[0], sizeVal[0] ) );
        Omega_h::set_vector(target_metrics, vert, Omega_h::symm2vector(target_metric));
    };

    Omega_h::parallel_for(om.nverts(), f);
    om.add_tag(Omega_h::VERT, "initial_metric", Omega_h::symm_ncomps(om.dim()), Omega_h::Reals(target_metrics) );

    //set up a metric field
    Omega_h::MetricInput input;
    input.sources.push_back(Omega_h::MetricSource{OMEGA_H_GIVEN, 1.0, "initial_metric",OMEGA_H_ISO_SIZE,OMEGA_H_ABSOLUTE});
    input.should_limit_gradation = true;
    input.verbose=true;
    Omega_h::generate_metric_tag(&om, input);
    auto opts = Omega_h::AdaptOpts(&om);
    Omega_h::approach_metric(&om,opts);

Yes, you have to call generate_target_metric_tag, because approach_metric will move the mesh towards target_metric. If you don't have anything else for the existing metric, you can call add_implied_isos_tag

I called add_implied_isos_tag() and now have a metric field and a target_metric field that look like what I should expect
issue_omegah_approach_before

Calling approach_metric still isn't doing much. (before call is top with target_metric, after call is bottom)
issue_omegah_approach_after

Does the metric field need to be associated with the Omega_h::MetricInput object in any way like what is needed with the target_metric?

after the call to approach_metric, the tag target_metric should be gone and there should remain only the tag metric. I'd confirm that before anything else. Also, in your "before" pictures, the values for target_metric don't look that much bigger than metric, its possible they are just not calling for much refinement yet.

So I think I misinterpreted the approach_metric method. I took a look at one of the test cases, cylinder_adapt_test.cpp and it uses the following call:
while (Omega_h::approach_metric(&om, opts)) Omega_h::adapt(&om, opts);
which if I use as well does lead to what I am requesting:

Band of refinement:
issue_omegah_approach_loop1

Desired mesh from initial post without the long edges/ large elements:
issue_omegah_approach_loop2

Which is to say, approach_metric doesn't actually invoke mesh modification procedures?

Whoops, that's also my bad for forgetting about that, good catch!

I think that should resolve this issue. Thanks again for the help @ibaned.