Skip to content

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
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
27f85d7
Added terrain refiner
ews-ffarella Jun 2, 2025
42d53ea
Added possibility to specify a polygon as input
ews-ffarella Jun 2, 2025
8e479b5
Inner rings
ews-ffarella Jun 2, 2025
70b09e4
Added checks for inner rings
ews-ffarella Jun 2, 2025
c863448
Real example
ews-ffarella Jun 2, 2025
8013e4b
fmt
ews-ffarella Jun 2, 2025
00331b5
Documentation
ews-ffarella Jun 2, 2025
05de011
Really?
ews-ffarella Jun 2, 2025
87c95ba
Big refractor, added unit test and integration test. Made Polygon reu…
ews-ffarella Jun 3, 2025
a3298c6
Merge pull request #3 from Exawind/main
ews-ffarella Jun 3, 2025
bcd0030
clang
ews-ffarella Jun 3, 2025
e29f820
Relase mode
ews-ffarella Jun 3, 2025
1f120d4
clang tidy
ews-ffarella Jun 3, 2025
3aaad45
clang-tidy
ews-ffarella Jun 3, 2025
b7a1d49
More advanced tests and edge fix
ews-ffarella Jun 3, 2025
dc0fcae
Restore terrain_box_amr
ews-ffarella Jun 3, 2025
d51529a
Do not print out the polygons
ews-ffarella Jun 3, 2025
4aaeda5
Update test/CMakeLists.txt: remove missing reg test
mbkuhn Jun 3, 2025
11cda56
add to documentation
mbkuhn Jun 3, 2025
10927be
Merge branch 'main' into 1-terrain-based-refinement
ews-ffarella Jun 3, 2025
b5a40e2
[ci skip] Max level actually apply to the current grid level.
ews-ffarella Jun 4, 2025
01913ce
Merge branch '1-terrain-based-refinement' of github.com:ews-ffarella/…
ews-ffarella Jun 4, 2025
9ff4b82
Added more static methods to polygon
ews-ffarella Jun 4, 2025
169b12e
Optimize tagging
ews-ffarella Jun 4, 2025
94e3eef
Update docs
ews-ffarella Jun 4, 2025
d445abb
Fmt
ews-ffarella Jun 4, 2025
0829baf
tidy
ews-ffarella Jun 4, 2025
0310ba6
Merge branch 'main' into 1-terrain-based-refinement
ews-ffarella Jun 5, 2025
671375e
Merge branch '1-terrain-based-refinement' of github.com:ews-ffarella/…
ews-ffarella Jun 5, 2025
4da98d0
Added optimization
ews-ffarella Jun 5, 2025
e77ebdb
Remove bad macros for GPU
ews-ffarella Jun 12, 2025
6cae8df
Always initialize terrain drag
ews-ffarella Jun 13, 2025
75f9b45
Refactor TerrainRefinement: per-level buffer ratios and verbosity, im…
ews-ffarella Jun 14, 2025
f02a28a
Merge branch 'main' into 1-terrain-based-refinement
ews-ffarella Jun 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions amr-wind/utilities/tagging/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ target_sources(${amr_wind_lib_name} PRIVATE
VorticityMagRefinement.cpp
OversetRefinement.cpp
GeometryRefinement.cpp
TerrainRefinement.cpp

# Geometry based refinement options
BoxRefiner.cpp
Expand Down
57 changes: 57 additions & 0 deletions amr-wind/utilities/tagging/TerrainRefinement.H
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#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;

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.
class Field;
// class IntField;

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.

/** AMR refinement using a given field (e.g., density)
* \ingroup amr_utils
*
* ```
* tagging.labels = t1
* tagging/t1.type = TerrainRefinement
* tagging/t1.level = 3
* tagging/t1.vertical_distance = 1´500
* ```
*/
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;

Field* m_terrain_height{nullptr};
IntField* m_terrain_blank{nullptr};
int m_max_lev{0};
amrex::Real m_vertical_distance{0.0};
amrex::RealBox m_tagging_box;

//! Boxarrays for each level in AMR hierarchy
amrex::Vector<amr_wind::polygon_utils::Point> m_poly_outer;
amrex::Vector<amrex::Vector<amr_wind::polygon_utils::Point>> m_poly_rings;
};

} // namespace amr_wind

#endif /* TERRAINREFINEMENT_H */
159 changes: 159 additions & 0 deletions amr-wind/utilities/tagging/TerrainRefinement.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#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");
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]);
// amrex::Print() << "Number of points:" << n_points << std::endl;

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.
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});
// amrex::Print() << pt_x << " " << pt_y << std::endl;

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.
}
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);
// amrex::Print() << "Number of inner rings:" << n_rings <<
// std::endl;
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);
// amrex::Print() << "Number of inner points:" << n_pts <<
// std::endl;
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});
// amrex::Print() << m_poly_rings[ring_i][pt_i].x <<
// std::endl;
}
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*/)

Check warning on line 104 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / CPU (ubuntu-24.04, Release, NoOpenMP)

unused parameter ‘time’ [-Wunused-parameter]

Check warning on line 104 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / CPU (ubuntu-24.04, Release, NoOpenMP)

unused parameter ‘time’ [-Wunused-parameter]

Check warning on line 104 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / CPU (macos-latest, Release, NoOpenMP)

unused parameter 'time' [-Wunused-parameter]

Check warning on line 104 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / Lint-clang-tidy

unused parameter 'time' [clang-diagnostic-unused-parameter]

Check warning on line 104 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / Lint-clang-tidy

unused parameter 'time' [-Wunused-parameter]
{
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& farrs = mfab.const_arrays();

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.
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()

Check warning on line 135 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / Lint-clang-tidy

the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]

Check warning on line 135 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / Lint-clang-tidy

implicit conversion 'Long' (aka 'long') -> 'bool' [readability-implicit-bool-conversion]
? 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) {

Check warning on line 140 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / Lint-clang-tidy

use range-based for loop instead [modernize-loop-convert]
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)) &&

Check warning on line 151 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / CPU (ubuntu-24.04, Release, NoOpenMP)

‘*’ in boolean context, suggest ‘&&’ instead [-Wint-in-bool-context]

Check warning on line 151 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / CPU (ubuntu-24.04, Release, NoOpenMP)

‘*’ in boolean context, suggest ‘&&’ instead [-Wint-in-bool-context]

Check warning on line 151 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / CPU (macos-latest, Release, NoOpenMP)

implicit conversion turns floating-point number into integer: 'double' to 'bool' [-Wfloat-conversion]

Check warning on line 151 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / Lint-clang-tidy

implicit conversion turns floating-point number into integer: 'double' to 'bool' [clang-diagnostic-float-conversion]

Check warning on line 151 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / Lint-clang-tidy

implicit conversion turns floating-point number into integer: 'double' to 'bool' [-Wfloat-conversion]

Check warning on line 151 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / Lint-clang-tidy

narrowing conversion from 'double' to 'bool' [bugprone-narrowing-conversions]

Check warning on line 151 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / Lint-clang-tidy

implicit conversion 'double' -> 'bool' [readability-implicit-bool-conversion]

Check warning on line 151 in amr-wind/utilities/tagging/TerrainRefinement.cpp

View workflow job for this annotation

GitHub Actions / Lint-clang-tidy

implicit conversion 'bool' -> 'double' [readability-implicit-bool-conversion]
(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
84 changes: 84 additions & 0 deletions amr-wind/utilities/tagging/poly_ops.H
Copy link
Author

Choose a reason for hiding this comment

The 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.

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 */
Loading
Loading