translating a mesh

158 views
Skip to first unread message

hoover.a...@gmail.com

unread,
Dec 7, 2020, 5:27:20 PM12/7/20
to IBAMR Users
Hello,

If I was using IBFE and wanted to have multiple copies of a mesh translated by some amount for their initial position (say +1.0 in the x dimension), is there an easy was of doing so? As opposed to inputting copies of the mesh in the different positions? This is the current snippet of code I'm trying to adjust:


        Mesh mesh1(init.comm(), NDIM);
        Mesh mesh2(init.comm(), NDIM);
        Mesh mesh3(init.comm(), NDIM);        
        Mesh mesh4(init.comm(), NDIM);
        const double dx = input_db->getDouble("DX");
        const double ds = input_db->getDouble("MFAC")*dx;
        const string mesh1_filename = input_db->getStringWithDefault("MESH_FILENAME","Plate.e");
        const string mesh2_filename = input_db->getStringWithDefault("MESH_FILENAME","Plate.e");
        const string mesh3_filename = input_db->getStringWithDefault("MESH_FILENAME","Plate.e");
        const string mesh4_filename = input_db->getStringWithDefault("MESH_FILENAME","Plate.e");
        mesh1.read(mesh1_filename);
        mesh1.prepare_for_use();
        mesh2.read(mesh2_filename);
        mesh2.prepare_for_use();
        mesh3.read(mesh2_filename);
        mesh3.prepare_for_use();
        mesh4.read(mesh2_filename);
        mesh4.prepare_for_use();
        vector<Mesh*>meshes(4);
        meshes[0]=&mesh1;
        meshes[1]=&mesh2;
        meshes[2]=&mesh3;
        meshes[3]=&mesh4;


Related, If multiple copies of the mesh were acted on by the same stress function, albeit with slight differences in phase, would there be a smart way to adjust the stress function to account for multiple structures? Is this better accomplished with a geometry file that includes all of the same structures in their initial position?

void
stress_function(
    TensorValue<double>& PP,
    const TensorValue<double>& FF,
    const libMesh::Point& X,
    const libMesh::Point& s,
Elem* const /*elem*/,
  const std::vector<const std::vector<double>*>& /*var_data*/,
  const std::vector<const std::vector<VectorValue<double> >*>& /*grad_var_data*/,
  double time,
  void* /*ctx*/)
{

Thank you for any and all help.

-Alex
Message has been deleted

Alexander Hoover

unread,
Dec 7, 2020, 7:08:14 PM12/7/20
to ibamr...@googlegroups.com
Thanks Hong! This is exactly what I was looking for.

On Mon, Dec 7, 2020 at 5:40 PM Hong Nguyen <hon...@gmail.com> wrote:
hi Alex,

I am not an expert or one of the developers of the IBFE, but as far as I know, there is a method called registerInitialCoordinateMappingFunction in the IBFEmethod class that do what exactly your first question asked for, you can take a look at example ex0 in the  ./examples/IBFE/explicit/ folder for more details.

For your 2nd question, If I were you, i would utilize the parameter *ctx, which essentially can be anything, to encode the difference between slightly different stress functions.

Best,
Hong
--
You received this message because you are subscribed to a topic in the Google Groups "IBAMR Users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/ibamr-users/ZrLq6NiB8NE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to ibamr-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ibamr-users/ed91c85b-214b-42c3-81b6-267ff033691dn%40googlegroups.com.

Alexander Hoover

unread,
Dec 7, 2020, 8:36:44 PM12/7/20
to ibamr...@googlegroups.com
Quick follow-up (in case anybody know), is there a way internally to set *.ctx for different meshes, as opposed to setting them in CAD/your mesher? 

Hong Nguyen

unread,
Dec 7, 2020, 9:39:10 PM12/7/20
to IBAMR Users
Alex,

I am thinking of this.
First, declare a structure that contains info for different meshes: something like:
struct StructureCtx
{
    std::string name;
    double xyz;
};


In the body,  declaring each 'struct' for each mesh.
StructureCtx struct[4];

Then, use the same stress function but different *ctx for each mesh.
IBFEMethod::PK1StressFcnData PK1_stress_data_0(stress_function);
PK1_stress_data_0.ctx = &struct[0];
ib_method_ops->registerPK1StressFunction(PK1_stress_data_0, _STRUCT_NO_);



Make sure in  your stress_function, add this line:
StructureCtx& struct_ctx = *static_cast<StructureCtx*>(ctx);

stress_function(
    TensorValue<double>& PP,
    const TensorValue<double>& FF,
    const libMesh::Point& X,
    const libMesh::Point& s,
    Elem* const /*elem*/,
      const std::vector<const std::vector<double>*>& /*var_data*/,
      const std::vector<const std::vector<VectorValue<double> >*>& /*grad_var_data*/,
      double time,
      void* /*ctx*/)
{
StructureCtx& struct_ctx = *static_cast<StructureCtx*>(ctx);
...
}


I did not try this myself. Maybe there is a better solution, but hope it can give you some ideas.

Best,
Hong

Boyce Griffith

unread,
Dec 8, 2020, 9:52:51 AM12/8/20
to IBAMR Users

On Dec 7, 2020, at 8:36 PM, Alexander Hoover <hoover.a...@gmail.com> wrote:

Quick follow-up (in case anybody know), is there a way internally to set *.ctx for different meshes, as opposed to setting them in CAD/your mesher? 

Alex, are you asking how to do different things in different “parts” (meshes)?

You could create different functions for each part, although this gets messy if you have a large number of parts or don’t want to prescribe the number of parts in advance.

You also should be able to set/reset the block IDs for each part during model setup. That information will then be accessible through the Elem pointer passed into the stress and/or force functions. I think this is the cleanest way to do things since it does not require either multiple function definitions or using context pointers.

Finally, you can communicate this information via the ctx pointer. Let me try to walk through that procedure in case you (or anyone else) wants to do this.

First, for any IBFE model with multiple parts, you will need to register stress and/or force functions for each part. You could do something like this:

vector<PK1StressFcnData> stress_fcn_data_vec = { … }; // set up stress functions for each part
for (int part = 0; part < num_part; ++part)
{
    ibfe_method->registerPK1StressFunction(stress_fcn_data_vec[part], part);
}

When setting up the stress function objects for each part, you could then do something to “tag” which part is associated with each stress function. Here is a possible way to do this.

First, use the context pointer to communicate to the stress function which part you are operating on by passing a pointer to the part number:

void my_PK1_stress_fcn(
    TensorValue<double>& F,
    const TensorValue<double>& FF,
    const Point& x,
    const Point& X,
    Elem* elem,
    const vector<const vector<double>*>& system_var_data,
    const vector<const vector<VectorValue<double>>*>& system_grad_var_data,
    double data_time,
    void* ctx)
{
    // interpret ctx as pointer to integer:
    int* part_num_ptr = static_cast<int*>(ctx);

    // dereference the pointer to get the part number:
    int part_num = *part_num_ptr;

    /// NOTE: the foregoing could be done in one line: int part_num = *static_cast<int*>(ctx);

    // rest of the implementation …
}

(The context could be a more complicated object that provides more specification information.)

Finally, when setting up the stress functions, you would do something like:

// first create a vector<int> containing the part numbers.
//
// because vector::push_back can move data around in memory if
// it resizes the vector, we need to do this in a separate loop, because
// we will ultimately pass in pointers to these data items to the
// PK1 stress function specifications. Those pointers can become
// invalidated if the vector is resized.
for (int part = 0; part < num_part; ++part)
{
    part_num_vec.push_back(part);
}

// then create a vector of PK1 stress function specifications and pass its
// contents to the IBFE method object.
vector<PK1StressFcnData> stress_fcn_data_vec;
for (int part = 0; part < num_part; ++part)
{
    // notice that we pass in a pointer to an integer that captures the part number
    // as the ctx here:
    stress_fcn_data_vec.push_back(PK1StressFcnData(my_PK1_stress_fcn, {}, &part_num_vec[part]));
    ibfe_method->registerPK1StressFunction(stress_fcn_data_vec[part], part);
}

Does this make sense?

You received this message because you are subscribed to the Google Groups "IBAMR Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ibamr-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ibamr-users/CAA5WRaUT0VnB7iwCAmQUSoBF1PrEjESsmXggA0DwH9YaG%2B-wSA%40mail.gmail.com.

Boyce Griffith

unread,
Dec 8, 2020, 9:54:52 AM12/8/20
to ibamr...@googlegroups.com
Something like this could work, but I would not recommend using strings to specify the different meshes/parts, because string comparison can be expensive, and these functions are evaluated at every quadrature point (typically many points per element) one or more times per time step. If you wanted to do something like this, I would suggest using integer tags rather than strings.

— Boyce

You received this message because you are subscribed to the Google Groups "IBAMR Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ibamr-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ibamr-users/1a435e2b-6da7-47ab-8bcd-c5a59f08b4c6n%40googlegroups.com.

Boyce Griffith

unread,
Dec 8, 2020, 10:18:25 AM12/8/20
to IBAMR Users
I think that Hong already answered that one approach to perturbing the initial positions is to use IBFEMethod::registerInitialCoordinateMappingFunction(). (Thanks, Hong!) Let me know if you have any questions about its use.

You also can modify the coordinates of the nodes of each mesh while you are reading in/setting up the meshes. libMesh provides functions to do basic manipulations in the MeshTools::Modification namespace (https://libmesh.github.io/doxygen/namespacelibMesh_1_1MeshTools_1_1Modification.html). If those do not do what you want, you also can manually loop over the nodes and apply the desired transformation. Note that all of these modifications should be done before initializing the IBFE model.

One comment is that you can make some of this code more compact now that we are using C++11. For instance you can use bracket initialization:

vector<Mesh*> meshes = {&mesh1, &mesh2, &mesh3, &mesh4};

Alternatively, here is a version of the above code that should work for an arbitrary number of structures (assuming that all of the basic configurations come from the same mesh):

const double dx = input_db->getDouble("DX");
const double ds = input_db->getDouble("MFAC")*dx;
const string mesh_filename = input_db->getStringWithDefault("MESH_FILENAME","Plate.e”);

int num_parts = … ;
vector<shared_ptr<Mesh>> mesh_shared_ptr_vec(num_parts);
vector<Mesh> mesh_ptr_vec(num_parts);
for (int part = 0; part < num_parts; ++part)
{
    mesh_shared_ptr_vec[part] = make_shared<Mesh>(init.comm(), NDIM));
    mesh_shared_ptr_vec[part]->read(mesh_filename);
    mesh_shared_ptr_vec[part]->prepare_for_use();

    // the IBFE interface still takes in raw pointers, so we need to get it from the
    // shared_ptr:
    mesh_ptr_vec[part] =  mesh_shared_ptr_vec[part].get();
}

Related, If multiple copies of the mesh were acted on by the same stress function, albeit with slight differences in phase, would there be a smart way to adjust the stress function to account for multiple structures? Is this better accomplished with a geometry file that includes all of the same structures in their initial position?

void
stress_function(
    TensorValue<double>& PP,
    const TensorValue<double>& FF,
    const libMesh::Point& X,
    const libMesh::Point& s,
Elem* const /*elem*/,
  const std::vector<const std::vector<double>*>& /*var_data*/,
  const std::vector<const std::vector<VectorValue<double> >*>& /*grad_var_data*/,
  double time,
  void* /*ctx*/)
{

Thank you for any and all help.

I tried to answer how to use the ctx pointer to accomplish this in a separate email; let me know if it is not clear how to do this. However, as I mentioned in that email, I think the easiest would be to setup different block IDs for each part. E.g. building off the above snippet, you could do something like:

for (int part = 0; part < num_parts; ++part)
{
    Mesh* mesh = mesh_ptr_vec[part];
    for (auto& elem : mesh->element_ptr_range())
    {
        elem->sudomain_id() = part;
    }
}

(If you know the “default” subdomain ID, you can use MeshTools::Modification::change_subdomain_id() to change the IDs. However, if you don’t know the ID, then it looks like you have to reset these “manually” by looping over the elements.)

Then in your stress and/or force functions, you can look up the subdomain ID directly from the element:

void my_PK1_stress_fcn(
    TensorValue<double>& F,
    const TensorValue<double>& FF,
    const Point& x,
    const Point& X,
    Elem* elem,
    const vector<const vector<double>*>& system_var_data,
    const vector<const vector<VectorValue<double>>*>& system_grad_var_data,
    double data_time,
    void* ctx)
{
    int part_num = elem->subdomain_id();

    // rest of the implementation …
}

— Boyce

-Alex

--
You received this message because you are subscribed to the Google Groups "IBAMR Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ibamr-users...@googlegroups.com.

Alexander Hoover

unread,
Dec 8, 2020, 10:45:07 AM12/8/20
to ibamr...@googlegroups.com
Thanks Boyce. This is all super helpful.

Just for context, my goal was to 
1. Upload a number of copies of an identical mesh
2. Space them out within the domain 
4. Apply a stress function that is identical for the meshes, with a slight difference in phase among them based on their location.

I think the best route may be to set the location of the copies in a mesher, and then account for the differences in phase with Elem. Is there an example that uses the subdomain ID? 


Boyce Griffith

unread,
Dec 8, 2020, 10:48:54 AM12/8/20
to IBAMR Users

On Dec 8, 2020, at 10:44 AM, Alexander Hoover <hoover.a...@gmail.com> wrote:

Thanks Boyce. This is all super helpful.

Just for context, my goal was to 
1. Upload a number of copies of an identical mesh
2. Space them out within the domain 
4. Apply a stress function that is identical for the meshes, with a slight difference in phase among them based on their location.

I think the best route may be to set the location of the copies in a mesher, and then account for the differences in phase with Elem. Is there an example that uses the subdomain ID? 

There is not currently an example, but I hope that the code snippets here should be most of what you need.

In essence, you just set the subdomain ID in each element when creating the meshes, and then look it up from the element when evaluating the stress.

The Elem pointer that is passed to the stress/force function tells you the element where the function is being evaluated in each call to the function.

— Boyce

Alexander Hoover

unread,
Dec 8, 2020, 11:01:25 AM12/8/20
to ibamr...@googlegroups.com
I should be able to figure it out from here Just to confirm, are elem ID the  integers starting from 0? So that I could do a call something along the lines: 

if(elem==0){
 phase=...
}



Boyce Griffith

unread,
Dec 8, 2020, 11:11:22 AM12/8/20
to IBAMR Users

On Dec 8, 2020, at 11:01 AM, Alexander Hoover <hoover.a...@gmail.com> wrote:

I should be able to figure it out from here Just to confirm, are elem ID the  integers starting from 0? So that I could do a call something along the lines: 

if(elem==0){
 phase=...
}

I think that you should treat it as an unsigned integer (i.e. no negative subdomain IDs). By default it is a 2-byte integer, although you can control that when you configure libMesh.

— Boyce

Alexander Hoover

unread,
Dec 11, 2020, 11:44:04 AM12/11/20
to ibamr...@googlegroups.com
Following up. I've been following the elem path detailed previously, but I ran into an error with libmesh (which may be an older version)

main.C:359:35: error: class libMesh::Mesh’ has no member named ‘element_ptr_range

           for (auto& elem : mesh->element_ptr_range())


Otherwise, I've had no issues.

Boyce Griffith

unread,
Dec 11, 2020, 11:45:09 AM12/11/20
to IBAMR Users

On Dec 11, 2020, at 11:43 AM, Alexander Hoover <hoover.a...@gmail.com> wrote:

Following up. I've been following the elem path detailed previously, but I ran into an error with libmesh (which may be an older version)

main.C:359:35: error: class libMesh::Mesh’ has no member named ‘element_ptr_range
           for (auto& elem : mesh->element_ptr_range())

Otherwise, I've had no issues.

I think those iterators are relatively new. You should update your libMesh! These range-based loops are much nicer to use. :-)

Alexander Hoover

unread,
Dec 11, 2020, 12:53:22 PM12/11/20
to ibamr...@googlegroups.com
Ah well, for the next install/update then ;).

Alexander Hoover

unread,
Dec 11, 2020, 3:26:05 PM12/11/20
to ibamr...@googlegroups.com
Seeing as I don't have an updated version, I did the brute force strategy of just uploading 4 meshes and defining functions for each. It seems to compile fine, but I get a  sig segv error.

vector<Mesh*>meshes(4);        
        meshes[0]=&mesh1;
        meshes[1]=&mesh2;
        meshes[2]=&mesh3;
        meshes[3]=&mesh4;
 ...
        Pointer<IBFEMethod> ib_method_ops = new IBFEMethod(
            "IBFEMethod", app_initializer->getComponentDatabase("IBFEMethod"), meshes, app_initializer->getComponentDatabase("GriddingAlgorithm")->getInteger("max_levels"),/*register_for_restart*/ true, restart_read_dirname, restart_restore_num);

Caught signal number 11 SEGV: Segmentation Violation, probably memory access out of range


Any insight about what's the source?



Alexander Hoover

unread,
Dec 11, 2020, 3:32:24 PM12/11/20
to ibamr...@googlegroups.com
Just to clarify, the SEGV occurs when ib_method_ops is set.

Alexander Hoover

unread,
Dec 11, 2020, 6:56:51 PM12/11/20
to ibamr...@googlegroups.com
Disregard my previous  two emails. I decided to upgrade to a more recent version (0.6.0). However, I'm seeking a little clarity following the path delineated previously, particularly with regards MeshBase and Mesh. I inputted the mesh in a manner similar to what had been suggested (though with some variance with respect to Mesh and MeshBase):

        const string mesh_filename = input_db->getStringWithDefault("MESH_FILENAME","Plate.e");
       
        int num_parts = 4;
        vector<shared_ptr<MeshBase>> mesh_shared_ptr_vec(num_parts);
        vector<MeshBase*> meshes(num_parts);

        for (int part = 0; part < num_parts; ++part)
        {
          mesh_shared_ptr_vec[part] = make_shared<Mesh>(init.comm(), NDIM);

          mesh_shared_ptr_vec[part]->read(mesh_filename);
          mesh_shared_ptr_vec[part]->prepare_for_use();

          // the IBFE interface still takes in raw pointers, so we need to get it from the
          // shared_ptr:
          meshes[part]=  mesh_shared_ptr_vec[part].get();

         
        }

        for (int part = 0; part < num_parts; ++part)
        {
          MeshBase* mesh = meshes[part];

          for (auto& elem : mesh->element_ptr_range())
          {
            elem->subdomain_id() = part;
          }
        }


However, I get two types of error, the first is in the IBFEMethod call

Pointer<IBFEMethod> ib_method_ops = new IBFEMethod("IBFEMethod", app_initializer->getComponentDatabase("IBFEMethod"),meshes, app_initializer->getComponentDatabase("GriddingAlgorithm")->getInteger("max_levels"),/*register_for_restart*/ true, restart_read_dirname, restart_restore_num);
        

main.C:393:291: error: no matching function for call to ‘IBAMR::IBFEMethod::IBFEMethod(const char [11], SAMRAI::tbox::Pointer<SAMRAI::tbox::Database>, std::vector<libMesh::Mesh*>&, int, bool, const string&, const int&)

         Pointer<IBFEMethod> ib_method_ops = new IBFEMethod("IBFEMethod", app_initializer->getComponentDatabase("IBFEMethod"),meshes, app_initializer->getComponentDatabase("GriddingAlgorithm")->getInteger("max_levels"),/*register_for_restart*/ true, restart_read_dirname, restart_restore_num);



The second is during the Exodus call


        std::unique_ptr<ExodusII_IO> mesh1_exodus_io(uses_exodus ? new ExodusII_IO(meshes[0]) : NULL);


main.C:512:93: error: no matching function for call to ‘libMesh::ExodusII_IO::ExodusII_IO(libMesh::Mesh*&)

         std::unique_ptr<ExodusII_IO> mesh1_exodus_io(uses_exodus ? new ExodusII_IO(meshes[0]) : NULL);



The second is during the Exodus call. Looking at the examples, I noted that MeshBase was used for vectors of meshes, while Mesh is used for cases where only one mesh is present.


Thank you for all the help so far.

Boyce Griffith

unread,
Dec 13, 2020, 11:28:46 PM12/13/20
to ibamr...@googlegroups.com

On Dec 11, 2020, at 6:56 PM, Alexander Hoover <hoover.a...@gmail.com> wrote:

Disregard my previous  two emails. I decided to upgrade to a more recent version (0.6.0). However, I'm seeking a little clarity following the path delineated previously, particularly with regards MeshBase and Mesh. I inputted the mesh in a manner similar to what had been suggested (though with some variance with respect to Mesh and MeshBase):

        const string mesh_filename = input_db->getStringWithDefault("MESH_FILENAME","Plate.e");
        
        int num_parts = 4;
        vector<shared_ptr<MeshBase>> mesh_shared_ptr_vec(num_parts);
        vector<MeshBase*> meshes(num_parts);
        for (int part = 0; part < num_parts; ++part)
        {
          mesh_shared_ptr_vec[part] = make_shared<Mesh>(init.comm(), NDIM);
          mesh_shared_ptr_vec[part]->read(mesh_filename);
          mesh_shared_ptr_vec[part]->prepare_for_use();

          // the IBFE interface still takes in raw pointers, so we need to get it from the
          // shared_ptr:
          meshes[part]=  mesh_shared_ptr_vec[part].get();
          
        }

        for (int part = 0; part < num_parts; ++part)
        {
          MeshBase* mesh = meshes[part];
          for (auto& elem : mesh->element_ptr_range())
          {
            elem->subdomain_id() = part;
          }
        }


However, I get two types of error, the first is in the IBFEMethod call

Pointer<IBFEMethod> ib_method_ops = new IBFEMethod("IBFEMethod", app_initializer->getComponentDatabase("IBFEMethod"),meshes, app_initializer->getComponentDatabase("GriddingAlgorithm")->getInteger("max_levels"),/*register_for_restart*/ true, restart_read_dirname, restart_restore_num);
        
main.C:393:291: error: no matching function for call to ‘IBAMR::IBFEMethod::IBFEMethod(const char [11], SAMRAI::tbox::Pointer<SAMRAI::tbox::Database>, std::vector<libMesh::Mesh*>&, int, bool, const string&, const int&)
         Pointer<IBFEMethod> ib_method_ops = new IBFEMethod("IBFEMethod", app_initializer->getComponentDatabase("IBFEMethod"),meshes, app_initializer->getComponentDatabase("GriddingAlgorithm")->getInteger("max_levels"),/*register_for_restart*/ true, restart_read_dirname, restart_restore_num);

Are you using vector<Mesh*> or vector<MeshBase*>? I think you need to be using vector<MeshBase*>.

The second is during the Exodus call

        std::unique_ptr<ExodusII_IO> mesh1_exodus_io(uses_exodus ? new ExodusII_IO(meshes[0]) : NULL);

main.C:512:93: error: no matching function for call to ‘libMesh::ExodusII_IO::ExodusII_IO(libMesh::Mesh*&)

         std::unique_ptr<ExodusII_IO> mesh1_exodus_io(uses_exodus ? new ExodusII_IO(meshes[0]) : NULL);

I think you need to write new ExodusII_IO(*meshes[0]). ExodusII_IO takes in a reference (MeshBase&) not a pointer (MeshBase*).

The second is during the Exodus call. Looking at the examples, I noted that MeshBase was used for vectors of meshes, while Mesh is used for cases where only one mesh is present.

For a single pointer, C++ is automatically upcasting from the derived pointer type (Mesh*) to the base pointer type (MeshBase*). You have to explicitly cast to go from base back to derived.

There is not a corresponding automatic conversion between vector<Mesh*> to vector<MeshBase*>. (See, e.g., http://www.cplusplus.com/forum/general/34099/.)

Alexander Hoover

unread,
Dec 17, 2020, 6:14:27 PM12/17/20
to ibamr...@googlegroups.com
Just wanted to confirm that this works (setting the element id, using it in the stress function). Thank you very much everyone for the help.

Boyce Griffith

unread,
Dec 17, 2020, 9:19:45 PM12/17/20
to noreply-spamdigest via IBAMR Users

On Dec 17, 2020, at 6:14 PM, Alexander Hoover <hoover.a...@gmail.com> wrote:

Just wanted to confirm that this works (setting the element id, using it in the stress function). Thank you very much everyone for the help.

Great!

Reply all
Reply to author
Forward
0 new messages