Skip to content
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

Terrain-Aware Immersed Forcing Method #1115

Open
wants to merge 66 commits into
base: main
Choose a base branch
from

Conversation

hgopalan
Copy link
Contributor

@hgopalan hgopalan commented Jul 2, 2024

Summary

This draft PR introduces an immersed forcing method for representing terrain within AMR-Wind. The terrain is generated outside amr-wind and this method does not rely on the existing immersed boundary method. The terrain runs are performed in steps:

  1. Run precursor
  2. Generate the terrain using the included utility from the mmctools project and SRTM data
  3. Generate roughness using NLCD database (under development and not fully integrated)
  4. Run the terrain simulation with the terrain-related forcing terms included in the amp-wind input file and a terrain file created using the utilities.

The method can also be used to run cases without terrain such as an array of bluff bodies. The user has to create a XYZ file with the relevant information to do perform these simulations. The user can also use the stl2XYZ.py in tools/terrain folder to convert a STL file to XYZ file for AMR-Wind. It is recommended to have a smooth boundary near the inflow and outflow to reduce the number of iterations of the nodal and Mac projections.

The terrain modules have been designed to run with the Kosovic turbulence model. To use other turbulence models, the terrainBlanking information should be used to set the turbulent viscosity to zero in the solid region.

Pull request type

This is a draft PR and will be updated over the next weeks.

Please check the type of change introduced:

  • Bugfix
  • Feature
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • Documentation content changes
  • Other (please describe):

Checklist

The following is included:

  • new unit-test(s)
  • new regression test(s)
  • documentation for new capability

This PR was tested by running:

  • the unit tests
    • on GPU
    • on CPU (Kestrel and MacOS)
  • the regression tests
    • on GPU
    • on CPU (Kestrel and MacOS)

Additional background

Issue Number:

Copy link
Contributor

@moprak-nrel moprak-nrel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's some quick comments from a first pass

const amrex::Real gpu_startY = (m_spongeDistanceY > 0)
? prob_hi[1] - m_spongeDistanceY
: prob_lo[1] - m_spongeDistanceY;
const amrex::Real gpu_spongeX = (m_spongeDistanceX > 0) ? 1 : 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these need to be amrex::Real? Looks like the value is just 0 or 1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not have to be real. Can change it to int.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or bool?

amrex::Real gpu_spongeVelZ = 0.0;
amrex::Real residual = 1000;
amrex::Real height_error = 0.0;
for (unsigned ii = 0; ii < vsize; ++ii) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There might be a better way to find the first closest index to the reference height x3 using std::lower_bound and std::upper_bound or some such instead of this loop?

amr-wind/physics/TerrainDrag.cpp Show resolved Hide resolved
const amrex::Real uy1 = vel(i, j, k, 1);
const amrex::Real uz1 = vel(i, j, k, 2);
const amrex::Real m = std::sqrt(ux1 * ux1 + uy1 * uy1 + uz1 * uz1);
if (drag(i, j, k) == 1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to Michael's comments, is drag an integer/bool field? Also, perhaps call this something more descriptive than drag to not confuse this with m_drag?

amr-wind/physics/TerrainDrag.cpp Show resolved Hide resolved
@@ -18,7 +18,7 @@ Kosovic<Transport>::Kosovic(CFDSim& sim)
, m_vel(sim.repo().get_field("velocity"))
, m_rho(sim.repo().get_field("density"))
, m_Nij(sim.repo().declare_field("Nij", 9, 1, 1))
, m_divNij(sim.repo().declare_field("divNij", 3))
, m_divNij(sim.repo().declare_field("divNij", 3, 1, 1))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought @marchdf changed this line as a bugfix in #1030, is there a reason to change this back?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated to latest repo updated and included the changes. Not sure why it changed back.

Copy link
Contributor

@marchdf marchdf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made a first pass at the non code stuff. My main comment has to do with how we want to present this work.

@@ -242,6 +242,9 @@ add_test_re(burggraf_flow)
add_test_re(abl_godunov_rayleigh_damping)
add_test_re(rankine)
add_test_re(rankine-sym)
add_test_re(terrainbox)
add_test_re(nrel_precursor)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to test the whole chain you can add test dependencies. The input-output runs do this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified

@@ -0,0 +1,6 @@
This is the sample terrain case. You need to run "python3.XX backendinterface.py NREL.yaml" in the tools/terrain folder.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this readme (kinda strange to have that in a reg test folder) and move it to proper documentation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, remove/move the png from here.

@@ -0,0 +1,6 @@
This is a simple test case to verify the terrain setup. A box is created and a freestream velocity
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about the readme here.

@@ -0,0 +1,13 @@
from netCDF4 import Dataset
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this file or follow the MMS test template (which has a python call). Still my preference would be to remove/move to documentation.

@@ -0,0 +1,15 @@
import numpy as np
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here. I guess these regtests are really "tutorials". I like the regtest and think the tutorial part should be moved to documenation.

@@ -0,0 +1,414 @@
def SRTM_Converter(outputDir,refLat,refLon,refHeight,left,right,bottom,top):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks copy-pasted from jupyter. Can we make this a proper script?

# In[26]:


if getWRFdata is True:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't need to test like this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

pp.addarr("physics", physics);
amr_wind::terraindrag::TerrainDrag terrain_drag(sim());
terrain_drag.post_init_actions();
int value = 100;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const int

amrex::Gpu::DeviceVector<amrex::Real> gpu_vel_ht;
amrex::Gpu::DeviceVector<amrex::Real> gpu_vel_vals;
amrex::Real m_drag{100.0};
amrex::Real m_spongeStrength{1.0};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's maintain the consistent naming case. These should be m_sponge_strength etc.

{
amrex::ParmParse pp("DragForcing");
pp.query("dragCoefficient", m_drag);
pp.query("spongeStrength", m_spongeStrength);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same remark about case here. I think we typically use snake case in the input files

const auto& phy_mgr = m_sim.physics_manager();
if (phy_mgr.contains("ABL")) {
const auto& abl = m_sim.physics_manager().get<amr_wind::ABL>();
const VelPlaneAveraging& fa_velocity =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const auto&

const CFDSim& m_sim;
const amrex::AmrCore& m_mesh;
const Field& m_velocity;
amrex::Gpu::DeviceVector<amrex::Real> gpu_vel_ht;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename gpu_ to device_

{
const auto& vel =
m_velocity.state(field_impl::dof_state(fstate))(lev).const_array(mfi);
bool is_terrain = this->m_sim.repo().field_exists("terrainBlank");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const bool

// Terrain Drag Force Term
Field& m_terrainDrag;
// Reading the Terrain Coordinates from file
amrex::Vector<amrex::Real> m_xterrain, m_yterrain, m_zterrain;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to be consistent with other things, can you declare on 1 line? Even better an amrex::Array of size AMREX_SPACEDIM. Same comment for line 56

}
amrex::Real value1, value2, value3;
while (file >> value1 >> value2 >> value3) {
m_xterrain.push_back(value1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so if you use Array, these become nice loops.

auto& terrainz0 = m_terrainz0(level);
auto& drag = m_terrainDrag(level);
// copy terrain data to gpu
amrex::Gpu::DeviceVector<amrex::Real> gpu_xterrain(m_xterrain.size());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gpu -> device

amrex::ParallelFor(
vbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
// compute the source term
const amrex::Real x1 = prob_lo[0] + (i + 0.5) * dx[0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call these x/y/z? or number from 0? Or stuff into gpuarray.

break;
}
}
int turnOn = (x3 <= terrainHt) ? 1 : 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just do levelBlanking(i, j, k, 0) = static_cast(x3 <= terrainHt)

@hgopalan
Copy link
Contributor Author

hgopalan commented Jul 9, 2024

Have addressed the comments

@marchdf
Copy link
Contributor

marchdf commented Jul 9, 2024

There's a stray update to the amrex submods that shouldn't be there. Can you remove that?

const CFDSim& m_sim;
const amrex::AmrCore& m_mesh;
const Field& m_velocity;
amrex::Gpu::DeviceVector<amrex::Real> device_vel_ht;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need the m_ prefix on these 2

const auto& dx = geom.CellSize();
const auto& prob_lo = geom.ProbLoArray();
const auto& prob_hi = geom.ProbHiArray();
const amrex::Real device_drag = m_drag;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry to be a pain. But you can remove all the device_ prefix here. That's the nice thing about using the m_ pfx in the first place. There's a bunch of places like that here.

const auto& geom_vec = m_mesh.Geom();
const auto& geom = geom_vec[lev];
const auto& dx = geom.CellSize();
const amrex::Real gpu_drag = m_drag;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to above. Just call this drag etc.

m_z0rough.push_back(value3);
}
file1.close();
m_sim.io_manager().register_output_int_var("terrainDrag");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use snake case for the output variables. It keeps things consistent.

@@ -0,0 +1,63 @@
#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this case/file should be named terrain_box

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that this should be named terrain_box. But more importantly, the input file must have the same name as the directory in order for it to run with ctest. When you update the directory and input file names, make sure you update the CMakeLists.txt too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@@ -0,0 +1,29 @@
solver: "amrWind"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should remove these python scripts. There are no tests for these and we don't support python in the CI so these are liable to drift. We need to think of a better place to put these things. Right now it feels like they would belong to the amr-wind-frontend. Any thoughts on where this could live?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure of the best way. I can create a separate toolkit GitHub repo for terrain.

The setting up of a terrain case by hand is not feasible and this was the reason I had to create the python codes. The user typically creates the terrain and then zooms in to see that the region of interest is sufficient for him and again reruns the script to create the workflow. Also when we create the refinement boxes, the python script lets us keep it at the terrain height and reduces the marking of unnecessary cells.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sure sounds useful. I would recommend a bunch of hardening: linting, proper packaging (with clear dependencies, python versions, etc), scripting (users don't edit the scripts, they pass arguments to the scripts).

The amr-wind-frontend could be a good place since that's what a lot of people are using to set up complex cases. I would hesitate to create yet another repo...

hgopalan and others added 2 commits July 17, 2024 11:14
… GPU errors. Added a test case to show that IB does not affect the MOST BC.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants