-
Notifications
You must be signed in to change notification settings - Fork 86
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
Conversation
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. |
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. |
Added regression tests with and without perturbation forcing |
There was a problem hiding this 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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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):
There was a problem hiding this comment.
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.
amr-wind/equation_systems/temperature/source_terms/TemperatureFreeAtmosphereForcing.H
Show resolved
Hide resolved
amr-wind/equation_systems/temperature/source_terms/PertForcing.H
Outdated
Show resolved
Hide resolved
private: | ||
const SimTime& m_time; | ||
const amrex::AmrCore& m_mesh; | ||
std::string m_1d_rans; |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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?
Good work, just had some thoughts for avoiding some duplicate code. I am running the tests on GPU now. |
There was a problem hiding this 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.
amr-wind/equation_systems/icns/source_terms/VelocityFreeAtmosphereForcing.H
Outdated
Show resolved
Hide resolved
amr-wind/equation_systems/temperature/source_terms/TemperatureFreeAtmosphereForcing.H
Show resolved
Hide resolved
amr-wind/equation_systems/temperature/source_terms/TemperatureFreeAtmosphereForcing.H
Show resolved
Hide resolved
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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}; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
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.
Future Work:
Please check the type of change introduced:
SunZia Wind Farm LES - 150 kms
Flatirons - RANS - 150 kms