Skip to content

What is the recommended order for enforce_spd, normalize and intersect with multiple fields? #227

@stephankramer

Description

@stephankramer

In #224, @Patol75 wrote:

It took me a while, but I have something that seems to be working. However, given that it is the first time I get to that level of complexity with animate, I would not mind if you could give my workflow a quick look. In particular, I am not exactly sure when and in which order normalise, enforce_spd, and compute_hessian should be called. The function I use if the following:

def adapt_mesh(
    mesh: fd.MeshGeometry, mesh_fields: dict[str, dict[str, fd.Function | bool]]
) -> fd.MeshGeometry:
    for _ in range(prms.adapt_calls):
        M = fd.TensorFunctionSpace(mesh, "CG", 1)

        metric_fields = []
        for field, field_specs in mesh_fields.items():
            if isinstance(field_specs["add_to_metric"], list):
                for dim, add_to_metric in enumerate(field_specs["add_to_metric"]):
                    if add_to_metric:
                        metric_fields.append(field.subfunctions[dim])
            elif field_specs["add_to_metric"]:
                metric_fields.append(field)

        for field in metric_fields:
            if isinstance(field.ufl_element(), fd.VectorElement):
                metric_fields.remove(field)
                for dim in range(field.ufl_shape[0]):
                    metric_fields.append(field.sub(dim))

        metrics = []
        for field in metric_fields:
            # Firedrake function for a metric over a mesh where a field lives
            metric = RiemannianMetric(M, name=f"Metric ({field.name()})")
            metric.set_parameters(prms.metric_parameters)  # Set metric parameters
            metric.compute_hessian(field)  # Field Hessian
            metric.normalise()  # Rescale metric to achieve the desired target complexity
            metric.enforce_spd()  # Ensure boundedness (symmetric positive-definite)
            metrics.append(metric)

        overall_metric = metrics[0].copy(deepcopy=True)  # Overall metric
        overall_metric.rename("Metric (overall)")
        # Minimum in all directions across all metrics
        overall_metric.intersect(*metrics[1:])
        overall_metric.enforce_spd()  # Ensure boundedness (symmetric positive-definite)

        mesh = adapt(mesh, overall_metric)  # Generate new mesh based on overall metric
        # Ensure boundary coordinates are not exceeded
        mesh.coordinates.dat.data[:, 0].clip(
            0.0, prms.domain_dims[0], out=mesh.coordinates.dat.data[:, 0]
        )
        mesh.coordinates.dat.data[:, 1].clip(
            0.0, prms.domain_dims[1], out=mesh.coordinates.dat.data[:, 1]
        )
        mesh.cartesian = True  # Geometry tag informing other G-ADOPT objects

        fields = list(mesh_fields)
        for field in fields:
            field_specs = mesh_fields.pop(field)

            if isinstance(
                field.function_space().topological,
                fd.functionspaceimpl.MixedFunctionSpace,
            ):
                spaces = []
                for dim, element in enumerate(field.ufl_element().sub_elements):
                    spaces.append(fd.FunctionSpace(mesh, element))

                mixed_space = fd.MixedFunctionSpace(spaces)
                new_field = fd.Function(mixed_space, name=field.name())
                for sub_field, new_sub_field in zip(
                    field.subfunctions, new_field.subfunctions
                ):
                    new_sub_field.rename(sub_field.name())
                    new_sub_field.interpolate(sub_field)
            else:
                space = fd.FunctionSpace(mesh, field.ufl_element())
                new_field = fd.Function(space, name=field.name())
                new_field.interpolate(field)

            mesh_fields[new_field] = field_specs

    return mesh

mesh_fields is defined as follows:

mesh_fields = {
    stokes: {"add_to_metric": [True, False]},
    T: {"add_to_metric": True},
    psi: {"add_to_metric": True},
}

Thank you for your time and help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions