cburstedde / p4est

The "p4est" forest-of-octrees library
www.p4est.org/
GNU General Public License v2.0
261 stars 115 forks source link

Possible missing corner neighbor in p8est_mesh_ext ? #264

Closed a-colaitis closed 11 months ago

a-colaitis commented 11 months ago

Hi,

I have been using p4est for the last 6 months for building an ALE-AMR finite volume radiation hydrodynamics code. Thank you for your work on p4est !

The code is written in Fortran and I have used C interfaces to extract the connectivity and mesh information I need. I then use that information to build my own node numbering due to the unique way the finite volume nodal solver operates, notably on ghost cells and hanging nodes.

To build the node numbering, I use the p8est mesh interface to get a face, edge and corner connectivity on a 2:1 corner-balanced forest.

Lately, I have been having trouble with my numbering algorithm (these occurred when I started testing AMR intensive applications). I have narrowed down the issue to a lack of information in the corneroffset (and other corner...) arrays. The problem arises when I try to access a corner neighbor through an inter-tree boundary, and when that corner is also a hanging edge node in another tree (but not in the corner-opposed quadrant).

I made a simple example to hopefully reproduce the issue. The connectivity is a simple brick connectivity with 4 trees created with:

conn = p8est_connectivity_new_brick ( 2, 2, 1, 0, 0, 0); p4est = p4est_new (mpicomm, conn, 0, NULL, NULL);

(Note that this behavior is also observed in 1 CPU, so the mpi communicator and ghost layer is not really necessary here.)

I then use p4est_refine to refine once tree number 1 and 2, which yields the following arrangement:

fig

I create the ghost layer and mesh:

ghost = p4est_ghost_new (p4est, P4EST_CONNECT_FULL); mesh = p4est_mesh_new_ext (p4est, ghost, 1, 1, P4EST_CONNECT_FULL);

Finally, inspecting the content of the mesh, notably for inter-tree connections, I get that local_num_corners = 0. I think this is incorrect. For example, the corner neighbor of quadrant 7 in tree 1 (see picture) through corner 2 should be quadrant 10 in tree 2 through corner 5, for which the node is a corner node and not hanging. The corresponding value in quad_to_corner is -1 (which indicates a hanging node).

I somewhat suspect that the issue is that this corner is a edge hanging corner for the single quadrant in tree 0 and 3, as when I refine all trees I get the proper inter-tree information through corners.

I may be doing something wrong, I would very much appreciate your help.

Thanks in advance Arnaud Colaïtis

Edit: I realize this may be what is referred to in https://github.com/cburstedde/p4est/pull/235 ? If this is the case, has a fix been implemented ?

cburstedde commented 11 months ago

Hi Arnaud,

I have been using p4est for the last 6 months for building an ALE-AMR finite volume radiation hydrodynamics code. Thank you for your work on p4est !

glad to learn about your success, and thanks for the report.

The code is written in Fortran and I have used C interfaces to extract the connectivity and mesh information I need. I then use that information to build my own node numbering due to the unique way the finite volume nodal solver operates, notably on ghost cells and hanging nodes.

To build the node numbering, I use the p8est mesh interface to get a face, edge and corner connectivity on a 2:1 corner-balanced forest.

Lately, I have been having trouble with my numbering algorithm (these occurred when I started testing AMR intensive applications). I have narrowed down the issue to a lack of information in the corneroffset (and other corner...) arrays. The problem arises when I try to access a corner neighbor through an inter-tree boundary, and when that corner is also a hanging edge node in another tree (but not in the corner-opposed quadrant).

I made a simple example to hopefully reproduce the issue. The connectivity is a simple brick connectivity with 4 trees created with:

conn = p8est_connectivity_new_brick ( 2, 2, 1, 0, 0, 0); p4est = p4est_new (mpicomm, conn, 0, NULL, NULL);

(Note that this behavior is also observed in 1 CPU, so the mpi communicator and ghost layer is not really necessary here.)

I then use p4est_refine to refine once tree number 1 and 2, which yields the following arrangement:

fig

I create the ghost layer and mesh:

ghost = p4est_ghost_new (p4est, P4EST_CONNECT_FULL); mesh = p4est_mesh_new_ext (p4est, ghost, 1, 1, P4EST_CONNECT_FULL);

Finally, inspecting the content of the mesh, notably for inter-tree connections, I get that local_num_corners = 0. I think this is incorrect. For example, the corner neighbor of quadrant 7 in tree 1 (see picture) through corner 2 should be quadrant 10 in tree 2 through corner 5, for which the node is a corner node and not hanging. The corresponding value in quad_to_corner is -1 (which indicates a hanging node).

I somewhat suspect that the issue is that this corner is a edge hanging corner for the single quadrant in tree 0 and 3, as when I refine all trees I get the proper inter-tree information through corners.

I may be doing something wrong, I would very much appreciate your help.

Your messages it timely. My guess is that @hannesbrandt is in the middle of putting together a solution; see #262. Would you feel like adding your minimal test case with a hardcoded check for the desired conditions to our library? Please coordinate with Hannes. Thanks again!

a-colaitis commented 11 months ago

Thank you for your prompt response.

Yes of course, I can help in putting a test in place. I am an in-experienced C programmer, so I may make mistakes. I have attached a simple test case, which I cannot check completely for correctness since I do not have @hannesbrandt solution.

// ===================================================================================================================================    
//  
//
// ===================================================================================================================================    
// -----------------------------------------------------------------------------------------------------------------------------------
// INCLUDES
#include <p4est_to_p8est.h>
#include <p8est_extended.h>
#include <p8est_mesh.h>
// ===================================================================================================================================    
// Callback refinement function for the test
//
// 
// =================================================================================================================================== 

static int
refine_12 (p4est_t * p4est, p4est_topidx_t which_tree,
               p4est_quadrant_t * quadrant)
{
// -----------------------------------------------------------------------------------------------------------------------------------

   if (which_tree == 1 || which_tree == 2) {
    return 1;
    }

    return 0;

// -----------------------------------------------------------------------------------------------------------------------------------
};
// ===================================================================================================================================    
// Test the p4est version to check if it includes corner neighbor tracking 
// through edge-hanging nodes
// =================================================================================================================================== 
void p4_check_corner_neighbor_through_edge_hanging( )
{
// -----------------------------------------------------------------------------------------------------------------------------------

    sc_MPI_Comm mpicomm = MPI_COMM_WORLD;
    p4est_connectivity_t *conn = NULL;   
    p4est_ghost_t        *ghost;
    p4est_mesh_t         *mesh;
    p4est_t              *p4est;

    p4est_locidx_t       *quad_to_corner;
    p4est_locidx_t       *corner_offset;
    p4est_locidx_t       *corner_quad;
    int8_t               *corner_corner;

    p4est_locidx_t     local_num_quadrants;
    p4est_locidx_t     ghost_num_quadrants;
    p4est_locidx_t     local_num_corners;

    int                 q_id;
    int                 c_id;
    int                 q_id_opp;
    int                 c_id_opp;    
    int                 q_id_test;
    int                 c_id_test;  

    // create a communicator and initialize p4est
    sc_init ( mpicomm, 1, 1, NULL, SC_LP_ESSENTIAL );
    p4est_init ( NULL, SC_LP_ESSENTIAL );

    // create a 2x2 brick connectivity
    conn = p8est_connectivity_new_brick ( 2, 2, 1, 0, 0, 0 );

   // create the p4est object
    p4est = p4est_new ( mpicomm, conn, 0, NULL, NULL );

    // check that we run serial
    int mpisize = p4est->mpisize;
    SC_CHECK_ABORT ( mpisize == 1 , "this test requires a serial run");

    // refine trees 1 and 2. The result forest is corner-balanced
    p4est_refine ( p4est, 0, refine_12, NULL );

    // create a ghost layer and mesh. We should not need the tree index or level list
    int compute_tree_index   = 1;
    int compute_level_lists  = 1;
    ghost = p4est_ghost_new (p4est, 
                                P4EST_CONNECT_FULL);
    p8est_mesh_params_t params;
    p8est_mesh_params_init (&params);
    params.compute_tree_index  = 0;
    params.compute_level_lists = 1;
    params.btype               = P4EST_CONNECT_FULL;
    params.edgehanging_corners = 1; 
    mesh = p8est_mesh_new_params( p4est, ghost, &params);

    local_num_quadrants    = mesh->local_num_quadrants;
    ghost_num_quadrants    = mesh->ghost_num_quadrants;
    local_num_corners      = mesh->local_num_corners;

    SC_CHECK_ABORT ( local_num_corners > 0, "local_num_corners should be non-zero" );

    // get the corner neighborhood
    quad_to_corner         = mesh->quad_to_corner;
    corner_offset          = (p4est_locidx_t *) mesh->corner_offset->array;
    corner_quad            = (p4est_locidx_t *) mesh->corner_quad->array;
    corner_corner          = (int8_t *) mesh->corner_corner->array;

    // ---------------------
    // perform a specific check for which we know the desired behavior
    // the node being tested is edge-hanging in quad 0 in tree 0 and quad 17 in tree 3
    // ---------------------

    int index;
    int offset;

    // quad 3 in tree 2 should have corner 6 neighbor to quad 14 in tree 3 through corner 1
    // ---------------------

    q_id = 3;
    c_id = 6;
    q_id_opp = 14;
    c_id_opp = 1;

    // index into quad_to_corner
    index = quad_to_corner[ q_id*8 + c_id ];

    // cannot be -1 or -3
    SC_CHECK_ABORT ( index >= 0 , "quad_to_corner should have a valid entry (case 1)" );

    // index into corner_offset
    index  = index - ( local_num_quadrants + ghost_num_quadrants );
    offset = corner_offset[ index ];

    // corresponding quadrant and corner numbers
    c_id_test = corner_corner[ offset ];
    q_id_test = corner_quad  [ offset ];

    SC_CHECK_ABORT ( c_id_test == c_id_opp, "incorrect c_id_test (case 1)" );
    SC_CHECK_ABORT ( q_id_test == q_id_opp, "incorrect q_id_test (case 1)" );

    // quad 7 in tree 2 should have corner 2 neighbor to quad 10 in tree 3 through corner 5    
    // ---------------------

    q_id = 7;
    c_id = 2;
    q_id_opp = 10;
    c_id_opp = 5;

    // index into quad_to_corner
    index = quad_to_corner[ q_id*8 + c_id ];

    // cannot be -1 or -3
    SC_CHECK_ABORT ( index >= 0, "quad_to_corner should have a valid entry (case 2)" );

    // index into corner_offset
    index  = index - ( local_num_quadrants + ghost_num_quadrants );
    offset = corner_offset[ index ];

    // corresponding quadrant and corner numbers
    c_id_test = corner_corner[ offset ];
    q_id_test = corner_quad  [ offset ];

    SC_CHECK_ABORT ( c_id_test == c_id_opp, "incorrect c_id_test (case 2)" );
    SC_CHECK_ABORT ( q_id_test == q_id_opp, "incorrect q_id_test (case 2)" );

    // ---------------------
    // test complete, cleanup
    // ---------------------

    p4est_ghost_destroy ( ghost );
    p4est_mesh_destroy ( mesh );
    p4est_destroy ( p4est );
    p4est_connectivity_destroy ( conn );

// -----------------------------------------------------------------------------------------------------------------------------------
};
a-colaitis commented 11 months ago

Hi,

I have tested the branch feature hanging-corners from @hannesbrandt and the fix works as intended in my case.

I updated the test above with the correct "params" keywords and tested it.

Arnaud

cburstedde commented 11 months ago

Thanks for checking! @hannesbrandt 's PR is now merged. You'd be very welcome to create a PR with a test/test_mesh_hedges3.c program to double-check proper operation.

hannesbrandt commented 11 months ago

Nice to see the feature works as intended for your test case, thanks for checking!

You'd be very welcome to create a PR with a test/test_mesh_hedges3.c program to double-check proper operation.

During my work on https://github.com/cburstedde/p4est/pull/262 I implemented a test case that checks edgehanging corner neighbors across tree edges and faces. So, parts of this test are similar to the test case described in this issue (although they are based on a different refinement of the trees). What would be the best way to proceed?

a-colaitis commented 11 months ago

It seems like your test is performing the same check in a more general way. I would think it is then not needed to test for my special case, but it is as you wish.

cburstedde commented 11 months ago

The relevant code is merged in the develop branch. Please keep us posted if anything doesn't seem right. Thanks for the report!