Skip to content

Commit

Permalink
This is an incomplete commit. Establishes SymmetryTransformerGenerator.
Browse files Browse the repository at this point in the history
Beware, the option to optionally use StitchedMeshGenerator has not been tested. Tests have not been written.
I would appreciate it if the test currently in simple.i would end up in the documentation as an example of what
default parameters are ran when the stitch option is exercised.
  • Loading branch information
RocksSavage committed Jul 28, 2022
1 parent 04e909d commit 492bbba
Show file tree
Hide file tree
Showing 9 changed files with 334 additions and 0 deletions.
@@ -0,0 +1,24 @@
# SymmetryTransformerGenerator

!syntax description /Mesh/SymmetryTransformerGenerator

## Overview

The SymmetryTransformerGenerator makes a mirror reflection of a mesh across an arbitrary plane or line (supplied by the user). All input is expected to be three dimensional even if the mesh in question is two dimensional; in such a case, let the z component be 0 (e.g., `extrusion_vector = '1 1 0'`). The values of the components can be floating types, not just integers.

The user sets the plane that will be reflected over by giving two vectors: a vector that gives the position of any given point on the line/plane from the origin; and, a vector that is normal (aka perpendicular/ orthogonal) to said line/plane. The normal vector establishes the slope of the plane of reflection.

Internally, SymmetryTransformerGenerator applies a matrix operation to the vectors the user supplied. The matrix that is used assumed that the inputted normal vector is also a unit vector, too (meaning, the length of the vector is 1). However, it is impractical to ask that the user give the x,y, and z components of a perfect normal unit vector. The user would have to calculate the coordiantes to a high precision to prevent arithmetic rounding from calculating a norm (length) of not exactly 1.

For example, if the user wanted a give a normal unit vector at 45° angle, even if they entered x and y values to the 10th decimal place of precision (0.7071067811), the norm of the inputted vector would be calculated to be 0.99999999987760335. The maximum decimal places of precision the user can enter is limited by the operating system and compiler used to build MOOSE. The user would not be able to enter some extremely high precision number even if they wanted to (on the author’s machine, up to 17 decimals of precision are accepted).
For this reason, the user is only expected to enter a normal vector, which is automatically converted to a unit normal vector. While unexpected, it is theoretically possible if the user is dealing with very small, precise areas for arithmetic error to be introduced in the calculation of the unit normal vector. In this case, adjust the inputted normal vector.

!alert warning
No treatment is made on the internally-kept order of nodes in the mirrored mesh, so the mirrored mesh will have a negative connectivity/volume!


!syntax parameters /Mesh/SymmetryTransformerGenerator

!syntax inputs /Mesh/SymmetryTransformerGenerator

!syntax children /Mesh/SymmetryTransformerGenerator
35 changes: 35 additions & 0 deletions framework/include/meshgenerators/SymmetryTransformerGenerator.h
@@ -0,0 +1,35 @@
//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#pragma once

#include "MeshGenerator.h"

/*
* A mesh generator that applies a linear transformation (rotation, translation) to the mesh
*/
class SymmetryTransformerGenerator : public MeshGenerator
{
public:
static InputParameters validParams();

SymmetryTransformerGenerator(const InputParameters & parameters);

std::unique_ptr<MeshBase> generate() override;

protected:
/// the input mesh
std::unique_ptr<MeshBase> & _input;

/// the mirror around which to transform the mesh
RealEigenVector _mirror_point_vector;
RealEigenVector _mirror_normal_vector;

std::vector<std::vector<std::string>> _stitch_boundaries_pairs;
};
113 changes: 113 additions & 0 deletions framework/src/meshgenerators/SymmetryTransformerGenerator.C
@@ -0,0 +1,113 @@
//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#include "SymmetryTransformerGenerator.h"
#include "CastUniquePointer.h"


registerMooseObject("MooseApp", SymmetryTransformerGenerator);

InputParameters
SymmetryTransformerGenerator::validParams()
{
InputParameters params = MeshGenerator::validParams();

params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
params.addClassDescription("Applies a mirror transform to the entire mesh.");
params.addRequiredParam<RealEigenVector>(
"mirror_point",
"Any point on the plane/line over which the reflection operation will be done");
params.addRequiredParam<RealEigenVector>(
"mirror_normal_vector",
"A vector normal to (perpendicular/orthogonal to) the plane/line over which the "
"reflection operation will be done");
params.addParam<std::vector<std::vector<std::string>>>(
" ",
std::vector<std::vector<std::string>>(),
"Pairs of boundaries to be stitched together between the 1st mesh in inputs and each "
"consecutive mesh");
return params;
}

SymmetryTransformerGenerator::SymmetryTransformerGenerator(const InputParameters & parameters)
: MeshGenerator(parameters),
_input(getMesh("input")),
_mirror_point_vector(getParam<RealEigenVector>("mirror_point")),
_mirror_normal_vector(getParam<RealEigenVector>("mirror_normal_vector")),
_stitch_boundaries_pairs(
getParam<std::vector<std::vector<std::string>>>("stitch_boundaries_pairs"))
{
// enforce 3D coordinates
if (_mirror_point_vector.size() != 3)
mooseError("mirror_point should be 3d vectors, but a dimension of ",
_mirror_point_vector.size(),
"was entered instead, respectively. If you are working in 2D, let the 3rd component"
"of your vectors be zero.");

if (_mirror_normal_vector.size() != 3)
mooseError(" mirror_normal_vector should be 3d vectors, but a dimension of ",
_mirror_normal_vector.size(),
"was entered instead, respectively. If you are working in 2D, let the 3rd component"
"of your vectors be zero.");

// convert normal vector into a unit normal vector
double nrm = _mirror_normal_vector.norm();
_mirror_normal_vector << _mirror_normal_vector[0] / nrm, _mirror_normal_vector[1] / nrm,
_mirror_normal_vector[2] / nrm;
}

std::unique_ptr<MeshBase>
SymmetryTransformerGenerator::generate()
{
std::unique_ptr<MeshBase> mesh = std::move(_input);

// https://en.wikipedia.org/wiki/Transformation_matrix#Reflection_2
// variables renamed for readability and verification
double a = _mirror_normal_vector[0], b = _mirror_normal_vector[1], c = _mirror_normal_vector[2],
d = ((RealEigenVector)(-1 * _mirror_point_vector.transpose() * _mirror_normal_vector))(0);

RealEigenMatrix mirror_transformation(4, 4);
mirror_transformation << (1 - 2 * a * a), (-2 * a * b), (-2 * a * c), (-2 * a * d), (-2 * a * b),
(1 - 2 * b * b), (-2 * b * c), (-2 * b * d), (-2 * a * c), (-2 * b * c), (1 - 2 * c * c),
(-2 * c * d), (0), (0), (0), (1);

for (auto & node : mesh->node_ptr_range())
{
RealEigenVector location_vec(4);
location_vec << (*node)(0), (*node)(1), (*node)(2), 1;

location_vec = mirror_transformation * location_vec;

// MOOSE uses libmesh in a 3 dim only mode, therefore we assume 3D here
(*node)(0) = location_vec(0);
(*node)(1) = location_vec(1);
(*node)(2) = location_vec(2);
}

if (_stitch_boundaries_pairs.size() > 0)
{
auto params = _app.getFactory().getValidParams("StitchedMeshGenerator");

// order of vector elements matters for this generator
// here order by: original mesh first, our custom mesh second
params.set<std::vector<MeshGeneratorName>>("inputs") = {getParam<MeshGeneratorName>("input"),
name()};

// params.set<std::vector<std::vector<std::string>>>("stitch_boundaries_pairs") = {
// {_sideset_name, _SIDESET_TO_BE_STITCHED}};
params.set<std::vector<std::vector<std::string>>>("stitch_boundaries_pairs") =
_stitch_boundaries_pairs;

// stitch the newly made high-dimensional mesh back to the original mesh
return dynamic_pointer_cast<MeshBase>(
addMeshSubgenerator("StitchedMeshGenerator", name() + "_stitchedMeshGenerator", params));
}

return mesh;
}
@@ -0,0 +1,60 @@
[Mesh]
[./fmg]
type = FileMeshGenerator
file = cylinder.e
[]

[./rotate]
type = TransformGenerator
input = fmg
transform = ROTATE
vector_value = '0 90 0'
[]

[./scale]
type = TransformGenerator
input = rotate
transform = SCALE
vector_value ='1e2 1e2 1e2'
[]
[]

[Variables]
[./u]
order = FIRST
family = LAGRANGE
[../]
[]

[Kernels]
[./diff]
type = Diffusion
variable = u
[../]
[]

[BCs]
[./left]
type = DirichletBC
variable = u
boundary = 1
value = 0
[../]

[./right]
type = DirichletBC
variable = u
boundary = 2
value = 1
[../]
[]

[Executioner]
type = Steady

solve_type = 'PJFNK'
[]

[Outputs]
exodus = true
[]
21 changes: 21 additions & 0 deletions test/tests/meshgenerators/symmetry_transformer_generator/simple.i
@@ -0,0 +1,21 @@
[Mesh]
# [file]
# type = FileMeshGenerator
# file = reactor.e
# []
[file]
type = GeneratedMeshGenerator
dim = 2
[]
[mirror]
type = SymmetryTransformerGenerator
input = file
mirror_point = "0 1 0"
mirror_normal_vector = "0 1 0"
[]
[stitch]
type = StitchedMeshGenerator
inputs = 'file mirror'
stitch_boundaries_pairs = 'top top'
[]
[]
34 changes: 34 additions & 0 deletions test/tests/meshgenerators/symmetry_transformer_generator/tests
@@ -0,0 +1,34 @@
[Tests]
design = 'meshgenerators/SymmetryTransformerGenerator.md'
issues = ' #21578'

[rotate_and_scale]
type = 'Exodiff'
input = '.i'
exodiff = '_in.e'
requirement = "The system shall include the ability to mirror a finite element mesh."
cli_args = '--mesh-only'
recover = false
[]

[translate]
requirement = "The system shall include the ability to "

[user_set]
type = 'Exodiff'
input = 'translate.i'
exodiff = 'translate_in.e'
cli_args = '--mesh-only'
recover = false
detail = 'do this, '
[]
[center_origin]
type = 'Exodiff'
input = '.i'
exodiff = '_in.e'
cli_args = '--mesh-only'
recover = false
detail = 'or that'
[]
[]
[]
@@ -0,0 +1,15 @@
[Mesh]
[gmg]
type = GeneratedMeshGenerator
dim = 2
nx = 2
ny = 2
[]

[translate]
type = TransformGenerator
input = gmg
transform = translate
vector_value = '1 2 0'
[]
[]
@@ -0,0 +1,14 @@
[Mesh]
[gmg]
type = GeneratedMeshGenerator
dim = 2
nx = 2
ny = 2
[]

[translate]
type = TransformGenerator
input = gmg
transform = translate_center_origin
[]
[]
@@ -0,0 +1,18 @@
[Mesh]
[gmg]
type = GeneratedMeshGenerator
dim = 2
nx = 2
ny = 2
xmin = 1
xmax = 2
ymin = 3
ymax = 4
[]

[translate]
type = TransformGenerator
input = gmg
transform = translate_min_origin
[]
[]

0 comments on commit 492bbba

Please sign in to comment.