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

Meso-Micro Coupling with no precursor for Terrain #1426

Merged
merged 39 commits into from
Jan 30, 2025

Conversation

hgopalan
Copy link
Contributor

@hgopalan hgopalan commented Dec 27, 2024

The current workflow in AMR - Wind requires a precursor simulation to generate boundary plane for running complex terrain simulations. However, this is not a requirement. This PR proposes an alternative path for running simulations over complex terrain.

  1. Based on met-mast measurement, a 1-D vertical wind profile is created along with the required geostrophic wind.
  2. The simulation is initialized with the 1-D profile.
  3. All the horizontal boundary conditions are replaced with pressure-outflow boundary condition with a sponge layer, forcing the 1-D profile.
  4. A forcing zone is added on the upper portion of the domain to drive the wind speed and temperature to be consistent with the 1-D vertical profile.

Future Work:

  1. Adding the nudging terms within domain for met-mast or Lidar data
  2. Replacing pressure-outflow with WRF-mesoscale boundary plane data and removing the flat terrain restrictions at the boundary.

Please check the type of change introduced:

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

SunZia Wind Farm LES - 150 kms

SunZia_chopped

Flatirons - RANS - 150 kms

Flaitrons_chopped

@hgopalan hgopalan added the enhancement New feature or request label Dec 27, 2024
@lawrenceccheung
Copy link
Contributor

Hi @hgopalan,

Thanks for documenting the process. One question I have about the MMC coupling with terrain is how we account for the fact that the turbulence will take time to spin-up, and how a time-varying MMC profile can be accounted for. One way to handle it is to include a large, flat inflow region where the turbulence can develop. Depending on the ABL stability state, it might require a shorter/longer region for the turbulence profile to adjust.

Once the 1D MMC profile is time-varying, though, that introduces a time delay between when the velocity/temperature changes and when that turbulence enters the domain. @mchurchf and @ewquon might have some ideas on what to do too.

Lawrence

@hgopalan
Copy link
Contributor Author

hgopalan commented Dec 27, 2024

Hi @hgopalan,

Thanks for documenting the process. One question I have about the MMC coupling with terrain is how we account for the fact that the turbulence will take time to spin-up, and how a time-varying MMC profile can be accounted for. One way to handle it is to include a large, flat inflow region where the turbulence can develop. Depending on the ABL stability state, it might require a shorter/longer region for the turbulence profile to adjust.

Once the 1D MMC profile is time-varying, though, that introduces a time delay between when the velocity/temperature changes and when that turbulence enters the domain. @mchurchf and @ewquon might have some ideas on what to do too.

Lawrence

The purpose of this workflow is mainly related to providing a simple method for AEP calculation for the RANS model similar to the method used in commercial RANS tools. The usability of the method and adoption for LES needs some investigation. If a terrain is complex, it does not need a lot of time to spin-up as the heterogeneity generate the flow structures. The terrain is smoothed in the horizontal boundaries and when they encounter a change in elevation it generated the structures for the cases I tested with mountains.

There is no restriction on specifying a time-varying 1-D profile. We just need to move the profile reader to be time-dependent. Will require some code changes but is doable.

There is some difference with the method proposed here. Our aim is to match the met-mast data while providing a reasonable boundary condition which will not cause divergence of the pressure poisson solver. We are not worried about flow spin-up and other issues and expect that the presence of complex terrain will generate the structure.

We may have to tweak the workflow in future if there are discrepancies with measured data. However, this method should be acceptable for RANS.

@hgopalan hgopalan requested review from mchurchf and marchdf January 7, 2025 02:21
@hgopalan hgopalan marked this pull request as ready for review January 7, 2025 02:22
@ewquon
Copy link
Contributor

ewquon commented Jan 7, 2025

Hi @hgopalan,

Thanks for documenting the process. One question I have about the MMC coupling with terrain is how we account for the fact that the turbulence will take time to spin-up, and how a time-varying MMC profile can be accounted for. One way to handle it is to include a large, flat inflow region where the turbulence can develop. Depending on the ABL stability state, it might require a shorter/longer region for the turbulence profile to adjust.

Once the 1D MMC profile is time-varying, though, that introduces a time delay between when the velocity/temperature changes and when that turbulence enters the domain. @mchurchf and @ewquon might have some ideas on what to do too.

Lawrence

What we could do for terrain is instead of a planar average, use a temporal average at a specified location (e.g., at the met mast). There is a question of whether you make the forcing constant with altitude or constant with height AGL; you can also blend from the latter to the former. Definitely should be explored further.

@hgopalan
Copy link
Contributor Author

hgopalan commented Jan 15, 2025

LES structures are not generated when the terrain is smooth. Added thermal perturbations to generate the flow structures. Sample result are shown for a flat surface without precursor and LES (dx=dy=60m). The fringe zone is able to absorb the outgoing waves. This is still preliminary and need further analysis on the strength of the structure on numerical stability for complex terrains.

Horizontal Plane

Horizontal

Vertical Plane

Vertical

@hgopalan
Copy link
Contributor Author

Added regression tests with and without perturbation forcing

@marchdf marchdf requested a review from moprak-nrel January 24, 2025 16:50
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.

Thanks for adding this in! I made a first pass. LMK when I can do a second one.

temperature field to generate flow structures for LES when the inflow data is coarse or uniform flow condition. Not
recommended for use with RANS models.

.. input_param:: PertForcing.xstart
Copy link
Contributor

Choose a reason for hiding this comment

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

To match other input file styles for specifying boxes, can we switch all these to

PerturbationForcing.start = x0 y0 z0
PerturbationForcing.end = x1 y1 z1

then parse these into a real box and we can use a .contain method on them. This will remove a lot of lines of code.

@@ -116,6 +116,8 @@ Flow physics

* Arbitrarily spatially and time varying boundary conditions using Python tools [:ref:`inp <inputs_native_boundary_plane>`]

* Sponge layer driven ABL simulations
Copy link
Contributor

Choose a reason for hiding this comment

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

I am a bit confused. Why is this in the boundary condition section? This feels like just another element in the bullet * Atmospheric boundary layer (ABL):

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a different way of specifying the boundary conditions through a fringe or sponge layer to force the simulations.

@hgopalan hgopalan requested a review from marchdf January 24, 2025 19:01
private:
const SimTime& m_time;
const amrex::AmrCore& m_mesh;
std::string m_1d_rans;
Copy link
Contributor

Choose a reason for hiding this comment

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

rename: m_1d_rans_filename or something like that.

const auto* v_values_d = m_v_values_d.data();
const auto* w_values_d = m_w_values_d.data();
const bool has_terrain = this->m_sim.repo().field_exists("terrain_height");
if (has_terrain) {
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 consolidate these 2 branches since they look so similar. I think the only difference is the calculation of z? I would move the if has_terrain into the parallel for. It's not ideal but I would rather than then the code duplication.

Copy link
Contributor

Choose a reason for hiding this comment

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

Or do it like you do below with the perturbation forcing

prob_lo[2] + (k + 0.5) * dx[2] - height_arr_cell,
0.5 * dx[2]);
const amrex::RealVect point{x, y, z};
if (pert_box.contains(point)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nicely done

auto* const m_terrain_height =
&this->m_sim.repo().get_field("terrain_height");
const auto& terrain_height = (*m_terrain_height)(lev).const_array(mfi);
amrex::ParallelFor(
Copy link
Contributor

Choose a reason for hiding this comment

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

this also looks like the one on L144. Think you can consolidate that?

@marchdf
Copy link
Contributor

marchdf commented Jan 28, 2025

Good work, just had some thoughts for avoiding some duplicate code. I am running the tests on GPU now.

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.

Good work! Have a few quick questions on the actual model.

const amrex::Real m = std::sqrt(ux * ux + uy * uy);
const amrex::Real ustar = m * kappa / std::log(z / z0);
const amrex::Real ustar = m * kappa / std::log(3 * z / z0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the 3 a model specific coefficient? Looks different from the standard ustar calculation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because we use the forcing term to set the boundary condition, cannot use the value of the cell in which the forcing term is set and we use something above it.

const amrex::Real sponge_forcing =
zi * zi * (tke_arr(i, j, k) - ref_tke);
1.0 / dt * (tke_arr(i, j, k) - ref_tke);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the 1/dt fixing a previous bug? The dimensions of the sponge_forcing array should now be different right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changing to 1/dt helped in speeding the convergence and reducing oscillation in the free atmosphere

Copy link
Contributor

Choose a reason for hiding this comment

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

Does the dimensional change not matter? It changed from [No-units] * tke to [1/T] * tke? or is the 1 some type of dimensional constant?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The original zi also had a model constant which basically returns 1/s. It was never explicitly mentioned.

amrex::Real m_sponge_distance_east{1000};
amrex::Real m_sponge_distance_south{-1000};
amrex::Real m_sponge_distance_north{1000};
int m_sponge_west{0};
Copy link
Contributor

Choose a reason for hiding this comment

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

Are these always just 0/1? Should they be bools?

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 kept them as ints for future modifications when the different boundaries can have other non-zero positive values when we use different driving winds

const amrex::Real start_west = problo[0] - m_sponge_distance_west;
const amrex::Real start_north = probhi[1] - m_sponge_distance_north;
const amrex::Real start_south = problo[1] - m_sponge_distance_south;
const int sponge_east = m_sponge_east;
Copy link
Contributor

Choose a reason for hiding this comment

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

Same question as before, are the ints or bools?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same as above. Currently it is 1/0 but there will be future changes when they can be positive integers

Copy link
Contributor

Choose a reason for hiding this comment

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

one last question: would that int work with

                    amrex::Real xi_start =
                        (start_west - x) / (start_west - problo[0]);
                    xi_start = sponge_west * std::max(xi_start, 0.0);
                    xstart_damping =
                        sponge_west * sponge_strength * xi_start * xi_start;

That int seems to be multiplied as a cubed power in the damping calculation

@moprak-nrel moprak-nrel enabled auto-merge (squash) January 30, 2025 20:09
@moprak-nrel moprak-nrel merged commit 82f64d2 into Exawind:main Jan 30, 2025
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants