-
Notifications
You must be signed in to change notification settings - Fork 97
Added terrain refinement option in tagging #1633
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ews-ffarella
wants to merge
34
commits into
Exawind:main
Choose a base branch
from
ews-ffarella:1-terrain-based-refinement
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 7 commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
27f85d7
Added terrain refiner
ews-ffarella 42d53ea
Added possibility to specify a polygon as input
ews-ffarella 8e479b5
Inner rings
ews-ffarella 70b09e4
Added checks for inner rings
ews-ffarella c863448
Real example
ews-ffarella 8013e4b
fmt
ews-ffarella 00331b5
Documentation
ews-ffarella 05de011
Really?
ews-ffarella 87c95ba
Big refractor, added unit test and integration test. Made Polygon reu…
ews-ffarella a3298c6
Merge pull request #3 from Exawind/main
ews-ffarella bcd0030
clang
ews-ffarella e29f820
Relase mode
ews-ffarella 1f120d4
clang tidy
ews-ffarella 3aaad45
clang-tidy
ews-ffarella b7a1d49
More advanced tests and edge fix
ews-ffarella dc0fcae
Restore terrain_box_amr
ews-ffarella d51529a
Do not print out the polygons
ews-ffarella 4aaeda5
Update test/CMakeLists.txt: remove missing reg test
mbkuhn 11cda56
add to documentation
mbkuhn 10927be
Merge branch 'main' into 1-terrain-based-refinement
ews-ffarella b5a40e2
[ci skip] Max level actually apply to the current grid level.
ews-ffarella 01913ce
Merge branch '1-terrain-based-refinement' of github.com:ews-ffarella/…
ews-ffarella 9ff4b82
Added more static methods to polygon
ews-ffarella 169b12e
Optimize tagging
ews-ffarella 94e3eef
Update docs
ews-ffarella d445abb
Fmt
ews-ffarella 0829baf
tidy
ews-ffarella 0310ba6
Merge branch 'main' into 1-terrain-based-refinement
ews-ffarella 671375e
Merge branch '1-terrain-based-refinement' of github.com:ews-ffarella/…
ews-ffarella 4da98d0
Added optimization
ews-ffarella e77ebdb
Remove bad macros for GPU
ews-ffarella 6cae8df
Always initialize terrain drag
ews-ffarella 75f9b45
Refactor TerrainRefinement: per-level buffer ratios and verbosity, im…
ews-ffarella f02a28a
Merge branch 'main' into 1-terrain-based-refinement
ews-ffarella File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
#ifndef FIELDREFINEMENT_H | ||
#define FIELDREFINEMENT_H | ||
|
||
#include "amr-wind/CFDSim.H" | ||
#include "amr-wind/utilities/tagging/poly_ops.H" | ||
#include "amr-wind/utilities/tagging/RefinementCriteria.H" | ||
|
||
namespace amr_wind { | ||
// class CFDSim; | ||
|
||
class Field; | ||
// class IntField; | ||
|
||
|
||
/** AMR refinement using a given distance to terrain | ||
* \ingroup amr_utils | ||
* | ||
* ``` | ||
* tagging.labels = terrain_lvl1 terrain_lvl2 | ||
* tagging.terrain_lvl1.type = TerrainRefinement | ||
* tagging.terrain_lvl1.vertical_distance = 100 | ||
* tagging.terrain_lvl1.level = 1 | ||
* tagging.terrain_lvl1.poly_outer = ... | ||
* tagging.terrain_lvl1.poly_inners = ... | ||
* tagging.terrain_lvl1.box_lo = ... | ||
* tagging.terrain_lvl1.box_hi = ... | ||
* tagging.terrain_lvl2.type = TerrainRefinement | ||
* tagging.terrain_lvl2.vertical_distance = 40 | ||
* tagging.terrain_lvl2.level = 2 | ||
* tagging.terrain_lvl2.poly_outer = ... | ||
* | ||
* poly_outer should be define as: number_of_points pt0_x pt0_y ... ptN_x ptN_y | ||
* poly_outer poly_inners be define as: number_of_inner_rings | ||
* number_of_points_in_ring0 r0_pt0_x r0_pt0_x ... r0_ptN_x r0_ptN_x | ||
* number_of_points_in_ringN rN_pt0_x rN_pt0_x ... rN_ptN_x rN_ptN_x | ||
* ``` | ||
*/ | ||
|
||
class TerrainRefinement : public RefinementCriteria::Register<TerrainRefinement> | ||
{ | ||
public: | ||
static std::string identifier() { return "TerrainRefinement"; } | ||
|
||
explicit TerrainRefinement(const CFDSim& sim); | ||
|
||
~TerrainRefinement() override = default; | ||
|
||
//! Read input file and initialize boxarray used to refine each level | ||
void initialize(const std::string& key) override; | ||
|
||
void operator()( | ||
const int level, | ||
amrex::TagBoxArray& tags, | ||
const amrex::Real time, | ||
const int ngrow) override; | ||
|
||
private: | ||
const CFDSim& m_sim; | ||
|
||
// Pointer to the terrain_height field. | ||
Field* m_terrain_height{nullptr}; | ||
// Pointer to the terrain_blank field so that we try not to refine under the | ||
// terrain | ||
IntField* m_terrain_blank{nullptr}; | ||
// Target grid refinement level | ||
int m_max_lev{0}; | ||
// Distance above the terrain to refine | ||
amrex::Real m_vertical_distance{0.0}; | ||
// The usual tagging bbox | ||
amrex::RealBox m_tagging_box; | ||
|
||
// Optional: The coordinates of the polygon's exterior ring | ||
amrex::Vector<amr_wind::polygon_utils::Point> m_poly_outer; | ||
// Optional: The coordinates of the polygon's interior ring(s) | ||
amrex::Vector<amrex::Vector<amr_wind::polygon_utils::Point>> m_poly_rings; | ||
}; | ||
|
||
} // namespace amr_wind | ||
|
||
#endif /* TERRAINREFINEMENT_H */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
#include "amr-wind/utilities/tagging/TerrainRefinement.H" | ||
#include "amr-wind/CFDSim.H" | ||
|
||
#include "AMReX.H" | ||
#include "AMReX_ParmParse.H" | ||
|
||
namespace amr_wind { | ||
|
||
TerrainRefinement::TerrainRefinement(const CFDSim& sim) | ||
: m_sim(sim), m_tagging_box(m_sim.repo().mesh().Geom(0).ProbDomain()) | ||
{} | ||
|
||
void TerrainRefinement::initialize(const std::string& key) | ||
{ | ||
amrex::ParmParse pp(key); | ||
|
||
const auto& repo = m_sim.repo(); | ||
|
||
const bool is_terrain = repo.field_exists("terrain_height"); | ||
mbkuhn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!is_terrain) { | ||
amrex::Abort("Need terrain blanking variable to use this refinement"); | ||
} | ||
m_terrain_height = &(m_sim.repo().get_field("terrain_height")); | ||
m_terrain_blank = &(m_sim.repo().get_int_field("terrain_blank")); | ||
|
||
// Outer radial extent of the cylinder, always read in from input file | ||
pp.get("vertical_distance", m_vertical_distance); | ||
pp.get("level", m_max_lev); | ||
if (m_max_lev <= 0) { | ||
amrex::Abort("TerrainRefinement: level should be strictly above 0"); | ||
} | ||
|
||
amrex::Vector<amrex::Real> poly_outer; | ||
amrex::Vector<amrex::Real> poly_inners; | ||
pp.queryarr("poly_outer", poly_outer); | ||
if (poly_outer.size() > 0) { | ||
pp.getarr("poly_outer", poly_outer); | ||
const int n = poly_outer.size(); | ||
const int n_points = static_cast<int>(poly_outer[0]); | ||
const int n_expected = n_points * 2 + 1; | ||
if (n_expected != n) { | ||
amrex::Abort( | ||
"Expected a list of " + std::to_string(n_expected) + | ||
" numbers, found " + std::to_string(n - 1) + "!"); | ||
} | ||
m_poly_outer.resize(n_points); | ||
for (int i = 0; i < n_points; ++i) { | ||
const auto pt_x = poly_outer[1 + 2 * i]; | ||
const auto pt_y = poly_outer[1 + 2 * i + 1]; | ||
m_poly_outer[i] = amr_wind::polygon_utils::Point({pt_x, pt_y}); | ||
} | ||
pp.queryarr("poly_inners", poly_inners); | ||
if (poly_inners.size() > 0) { | ||
const int n_rings = static_cast<int>(poly_inners[0]); | ||
m_poly_rings.resize(n_rings); | ||
int offset = 1; | ||
for (int ring_i = 0; ring_i < n_rings; ++ring_i) { | ||
const int n_pts = static_cast<int>(poly_inners[offset]); | ||
m_poly_rings[ring_i].resize(n_pts); | ||
offset += 1; | ||
for (int pt_i = 0; pt_i < n_pts; ++pt_i) { | ||
const auto pt_x = poly_inners[offset + 2 * pt_i]; | ||
const auto pt_y = poly_inners[offset + 2 * pt_i + 1]; | ||
m_poly_rings[ring_i][pt_i] = | ||
amr_wind::polygon_utils::Point({pt_x, pt_y}); | ||
} | ||
offset += 2 * n_pts; | ||
} | ||
if ((n_rings > 0) && (offset != poly_inners.size())) { | ||
amrex::Abort( | ||
"Expected a list of " + std::to_string(offset) + | ||
" numbers, found " + std::to_string(poly_inners.size()) + | ||
"!"); | ||
} | ||
} | ||
} | ||
|
||
amrex::Vector<amrex::Real> box_lo(AMREX_SPACEDIM, 0); | ||
amrex::Vector<amrex::Real> box_hi(AMREX_SPACEDIM, 0); | ||
if (pp.queryarr("box_lo", box_lo, 0, static_cast<int>(box_lo.size())) == | ||
1) { | ||
m_tagging_box.setLo(box_lo); | ||
} | ||
if (pp.queryarr("box_hi", box_hi, 0, static_cast<int>(box_hi.size())) == | ||
1) { | ||
m_tagging_box.setHi(box_hi); | ||
} | ||
|
||
amrex::Print() << "Created terrain refinement with level " << m_max_lev | ||
<< " and vertical distance " << m_vertical_distance | ||
<< std::endl; | ||
} | ||
|
||
void TerrainRefinement::operator()( | ||
int level, amrex::TagBoxArray& tags, amrex::Real time, int /*ngrow*/) | ||
{ | ||
const bool do_tag = level < m_max_lev; | ||
if (!do_tag) { | ||
return; | ||
} | ||
const auto& repo = m_sim.repo(); | ||
const auto& geom = repo.mesh().Geom(level); | ||
const auto& prob_lo = geom.ProbLoArray(); | ||
const auto& dx = geom.CellSizeArray(); | ||
const auto tagging_box = m_tagging_box; | ||
|
||
const auto& tag_arrs = tags.arrays(); | ||
const auto& mfab = (*m_terrain_height)(level); | ||
const auto& mterrain_h_arrs = mfab.const_arrays(); | ||
const auto& mterrain_b_arrs = (*m_terrain_blank)(level).const_arrays(); | ||
|
||
amrex::ParallelFor( | ||
mfab, [=] AMREX_GPU_DEVICE(int nbx, int i, int j, int k) noexcept { | ||
const amrex::Real z = prob_lo[2] + (k + 0.5) * dx[2]; | ||
const amrex::Real terrainHt = mterrain_h_arrs[nbx](i, j, k); | ||
const amrex::Real cellHt = z - terrainHt; | ||
|
||
const amrex::RealVect coord = {AMREX_D_DECL( | ||
prob_lo[0] + (i + 0.5) * dx[0], prob_lo[1] + (j + 0.5) * dx[1], | ||
prob_lo[2] + (k + 0.5) * dx[2])}; | ||
|
||
const auto testPt = | ||
amr_wind::polygon_utils::Point({coord[0], coord[1]}); | ||
bool in_poly = m_poly_outer.size() | ||
? amr_wind::polygon_utils::is_point_in_polygon( | ||
m_poly_outer, testPt) | ||
: true; | ||
if (in_poly) { | ||
for (int ring_i = 0; ring_i < m_poly_rings.size(); ++ring_i) { | ||
const bool in_ring = | ||
amr_wind::polygon_utils::is_point_in_polygon( | ||
m_poly_rings[ring_i], testPt); | ||
if (in_ring) { | ||
in_poly = false; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
if (((cellHt >= -0.5) * dx[2] && (cellHt <= m_vertical_distance)) && | ||
(mterrain_b_arrs[nbx](i, j, k) < 1) && in_poly && | ||
(tagging_box.contains(coord))) { | ||
tag_arrs[nbx](i, j, k) = amrex::TagBox::SET; | ||
} | ||
}); | ||
} | ||
|
||
} // namespace amr_wind |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know this is ugly as ***. This was a quick proof of concept, taken from internet. I had used this mehod before in Openfoam. As we probably won't have super complex polygons, I think this is fast enough ... AMReX probably already has a class for this, but I haven't found it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
#ifndef POLY_OPS_H | ||
#define POLY_OPS_H | ||
|
||
#include "AMReX.H" | ||
#include <cmath> | ||
#include "AMReX_REAL.H" | ||
#include "AMReX_Gpu.H" | ||
|
||
// Taken from https://www.geeksforgeeks.org/point-in-polygon-in-cpp/ | ||
namespace amr_wind::polygon_utils { | ||
|
||
struct Point | ||
{ | ||
amrex::Real x, y; | ||
}; | ||
|
||
// Function to compute the cross product of vectors (p1p2) | ||
// and (p1p3) | ||
|
||
AMREX_FORCE_INLINE amrex::Real | ||
cross_product(const Point& p1, const Point& p2, const Point& p3) | ||
{ | ||
return (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x); | ||
} | ||
|
||
// Function to check if point p lies on segment p1p2 | ||
AMREX_FORCE_INLINE bool | ||
is_point_on_segment(const Point& p, const Point& p1, const Point& p2) | ||
{ | ||
// Check if point p lies on the line segment p1p2 and | ||
// within the bounding box of p1p2 | ||
return cross_product(p1, p2, p) == 0 && p.x >= std::min(p1.x, p2.x) && | ||
p.x <= std::max(p1.x, p2.x) && p.y >= std::min(p1.y, p2.y) && | ||
p.y <= std::max(p1.y, p2.y); | ||
} | ||
|
||
// Function to compute the winding number of a point with | ||
// respect to a polygon | ||
int winding_number(const amrex::Vector<Point>& polygon, const Point& point) | ||
{ | ||
int n = polygon.size(); | ||
int windingNumber = 0; | ||
|
||
// Iterate through each edge of the polygon | ||
for (int i = 0; i < n; i++) { | ||
Point p1 = polygon[i]; | ||
// Next vertex in the polygon | ||
Point p2 = polygon[(i + 1) % n]; | ||
|
||
// Check if the point lies on the current edge | ||
if (is_point_on_segment(point, p1, p2)) { | ||
// Point is on the polygon boundary | ||
return 0; | ||
} | ||
|
||
// Calculate the cross product to determine winding | ||
// direction | ||
if (p1.y <= point.y) { | ||
if (p2.y > point.y && cross_product(p1, p2, point) > 0) { | ||
windingNumber++; | ||
} | ||
} else { | ||
if (p2.y <= point.y && cross_product(p1, p2, point) < 0) { | ||
windingNumber--; | ||
} | ||
} | ||
} | ||
// Return the winding number | ||
return windingNumber; | ||
} | ||
|
||
// Function to check if a point is inside a polygon using | ||
// the winding number algorithm | ||
bool is_point_in_polygon( | ||
const amrex::Vector<Point>& polygon, const Point& point) | ||
{ | ||
// Compute the winding number for the point with respect | ||
// to the polygon | ||
return winding_number(polygon, point) != 0; | ||
} | ||
|
||
} // namespace amr_wind::polygon_utils | ||
|
||
#endif /* POLY_OPS_H */ |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.