REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_NCTimeSeriesRiver.cpp
Go to the documentation of this file.
2#include "REMORA_NCFile.H"
3
4#include "AMReX_ParallelDescriptor.H"
5
6#include <string>
7
8#ifdef REMORA_USE_NETCDF
9
10/**
11 * @param[in ] a_file_names vector of file name(s) to read from
12 * @param[in ] a_field_name name of field to read in
13 * @param[in ] a_time_name name of time variable in NetCDF file
14 * @param[in ] a_nz number of vertical levels in domain
15 * @param[in ] a_time_ref remora.time_ref, the reference date of the model clock
16 * @param[in ] a_use_vert_integ whether the data in the file is vertically integrated
17 * @param[in ] a_is_transport whether the field is the river transport rather than a tracer
18 */
19NCTimeSeriesRiver::NCTimeSeriesRiver (const amrex::Vector<std::string>& a_file_names, const std::string a_field_name,
20 const std::string a_time_name,
21 const int a_nz, double a_time_ref,
22 const int a_use_vert_integ,
23 const int a_is_transport) {
24 file_names.assign(a_file_names.begin(), a_file_names.end());
27 nz = a_nz;
31}
32
34 // open file
35 amrex::Print() << "Loading " << field_name << " from rivers NetCDF file(s)" << std::endl;
36
37 // The time field can have any number of names, depending on the field.
38 // If not specified in input file (time_name.empty()) then set it by default
39 if (time_name.empty())
40 {
41 time_name = "river_time";
42 }
43
44 for (int ifile = 0; ifile < file_names.size(); ++ifile) {
45 const std::string& file_name = file_names[ifile];
46
47 // The time stamps must count days. Their reference date, if the units
48 // name one, is honored: this is the shift that puts them on the model clock.
50 true, true);
51
52 // get times and put in array
53 amrex::Vector<NDArray<double>> array_ts(1);
54 ReadNetCDFFile(file_name, {time_name}, array_ts); // filled only on proc 0
55 if (amrex::ParallelDescriptor::IOProcessor())
56 {
57 int ntimes_io = array_ts[0].get_vshape()[0];
58 for (int nt(0); nt < ntimes_io; nt++)
59 {
60 // Onto the model clock, then from days to seconds
61 river_times.push_back((*(array_ts[0].get_data() + nt) + offset_days) * 60.0 * 60.0 * 24.0);
62 file_for_time.push_back(ifile);
63 file_itime_offset.push_back(nt);
64 }
65 }
66 }
67 int ntimes = river_times.size();
68 // Only do checks on IO processors since river_times isn't populated on other ranks yet
69 if (amrex::ParallelDescriptor::IOProcessor()) {
70 AMREX_ASSERT(std::is_sorted(river_times.begin(), river_times.end()));
71 if (ntimes <= 1) {
72 amrex::Error("River data must be given at at least two times");
73 }
74 }
75 int ioproc = amrex::ParallelDescriptor::IOProcessorNumber();
76 amrex::ParallelDescriptor::Bcast(&ntimes,1,ioproc);
77 if (!(amrex::ParallelDescriptor::IOProcessor())) {
78 river_times.resize(ntimes);
79 file_for_time.resize(ntimes);
81 }
82 amrex::ParallelDescriptor::Bcast(river_times.data(), river_times.size(), ioproc);
83 amrex::ParallelDescriptor::Bcast(file_for_time.data(), file_for_time.size(), ioproc);
84 amrex::ParallelDescriptor::Bcast(file_itime_offset.data(), file_itime_offset.size(), ioproc);
85
87 if (amrex::ParallelDescriptor::IOProcessor())
88 {
89 std::vector<MPI_Offset> shape = ncf.var(field_name).shape();
90 if (shape.size() == 2) {
91 has_z = 0;
92 nriv = shape[1];
93 } else if (shape.size() == 3) {
94 has_z = 1;
95 nriv = shape[2];
96 } else {
97 amrex::Abort("River field shape not 2 or 3");
98 }
99 }
100 amrex::ParallelDescriptor::Bcast(&has_z, 1, ioproc);
101 amrex::ParallelDescriptor::Bcast(&nriv, 1, ioproc);
102
103 // river_Vshape distributes the total transport in the vertical (ROMS Qsrc = Qbar*Qshape),
104 // so it applies only to the transport. A tracer given as (river_time, river) is a
105 // concentration, and is used unscaled at every level.
106 if (!has_z && !is_transport) {
107 amrex::Print() << "Warning: " << field_name << " has no s_rho dimension in "
108 << file_names[0] << "; the same value will be used at every "
109 << "vertical level. ROMS expects river tracers to be given as "
110 << "(river_time, s_rho, river)." << std::endl;
111 }
112
113 amrex::Box vshape_box(amrex::IntVect(0,0,0),amrex::IntVect(nriv,0,nz));
114 if (!has_z && !use_vert_integ && is_transport) {
115 amrex::Vector<amrex::FArrayBox*> NC_fabs;
116 amrex::Vector<std::string> NC_names;
117 amrex::Vector<enum NC_Data_Dims_Type> NC_dim_types;
118 amrex::Print() << "Reading in river_Vshape from " << file_names[0] << std::endl;
119
120 fab_vshape = new amrex::FArrayBox();
121 NC_fabs.push_back(fab_vshape); NC_names.push_back("river_Vshape");
124 }
125
126 nzbox = (use_vert_integ) ? 1 : nz;
127 amrex::Box riv_box(amrex::IntVect(0,0,0),amrex::IntVect(nriv,0,nzbox));
128#ifdef AMREX_USE_GPU
129 // It's possible there should be a different arena
130 fab_before = new amrex::FArrayBox(riv_box,1,amrex::The_Pinned_Arena());
131 fab_after = new amrex::FArrayBox(riv_box,1,amrex::The_Pinned_Arena());
132 fab_interp = new amrex::FArrayBox(riv_box,1,amrex::The_Pinned_Arena());
133#else
134 fab_before = new amrex::FArrayBox(riv_box,1);
135 fab_after = new amrex::FArrayBox(riv_box,1);
136 fab_interp = new amrex::FArrayBox(riv_box,1);
137#endif
138
139 // dummy initialization
140 i_time_before = -100;
141}
142
144 // Figure out time index:
146 int i_time_new = -1;
147 for (int nt=0; nt < river_times.size()-1; nt++) {
148 if ((river_times[nt] <= time) and (river_times[nt+1] >= time)) {
149 i_time_new = nt;
150 break;
151 }
152 }
153 // Bracketing must succeed: falling through would read the series at a stale or
154 // uninitialized index. A runtime check rather than an assert, because the failure
155 // is a mismatch between the file and the run.
156 if (i_time_new < 0) {
157 amrex::Abort("Time " + std::to_string(time) + " is not spanned by the river time"
158 + " series for '" + field_name + "', which covers ["
159 + std::to_string(river_times[0]) + ", "
160 + std::to_string(river_times[river_times.size()-1])
161 + "]. Extend the river file to cover the run. The file's times were"
162 + " placed on the model clock from the reference date in their units"
163 + " attribute (the mapping is printed when the series is loaded), so"
164 + " also check remora.time_ref and remora.start_time.");
165 }
169
170 int i_time_after = i_time_before + 1;
171 if (i_time_before_old + 1 == i_time_before) {
172 // swap data vectors so we only have to read in one MultiFab
173 std::swap(fab_before, fab_after);
175 } else if (i_time_before_old != i_time_before) {
178 }
179
180 // Both differences are small, so Real holds them even when the times do not.
181 const amrex::Real dt = static_cast<amrex::Real>(time_after - time_before);
182 const amrex::Real time_since_before = static_cast<amrex::Real>(time - time_before);
183
184 amrex::Box fab_domain(amrex::IntVect(0,0,0), amrex::IntVect(nriv-1,0,nzbox-1));
185 auto interp_array = fab_interp->array();
186 auto before_array = fab_before->array();
187 auto after_array = fab_after->array();
188 amrex::ParallelFor(fab_domain, [=] AMREX_GPU_DEVICE (int r, int , int k) {
189 interp_array(r,0,k) = before_array(r,0,k) + time_since_before *(after_array(r,0,k) - before_array(r,0,k)) / dt;
190 });
191}
192
193void NCTimeSeriesRiver::read_in_at_time (amrex::FArrayBox* fab_dat, int itime) {
194 amrex::FArrayBox NC_fab;
195 amrex::Vector<amrex::FArrayBox*> NC_fabs;
196 amrex::Vector<std::string> NC_names;
197 amrex::Vector<enum NC_Data_Dims_Type> NC_dim_types;
198 // actual dims don't really matter here; only lower is used in call
199
200 const std::string& file_name = file_names[file_for_time[itime]];
202
203 amrex::Print() << "Reading in " << field_name << " at time index " << itime
204 << " from " << file_name << std::endl;
205
206 NC_fabs.push_back(&NC_fab) ; NC_names.push_back(field_name);
207
208 if (has_z) {
210 } else {
212 }
213
214 amrex::Box riv_domain(amrex::IntVect(0,0,0), amrex::IntVect(nriv-1,0,nz-1));
215 amrex::Box fab_domain(amrex::IntVect(0,0,0), amrex::IntVect(nriv-1,0,nzbox-1));
216
218
219 auto dat_array = fab_dat->array();
220 auto tmp_array = NC_fabs[0]->const_array();
221 if (has_z || use_vert_integ) {
222 amrex::ParallelFor(fab_domain, [=] AMREX_GPU_DEVICE (int r, int , int k) {
223 dat_array(r,0,k) = tmp_array(r,0,k);
224 });
225 } else if (is_transport) {
226 // Distribute the vertically integrated transport over the levels
227 auto array_vshape = fab_vshape->const_array();
228 amrex::ParallelFor(fab_domain, [=] AMREX_GPU_DEVICE (int r, int , int k) {
229 dat_array(r,0,k) = tmp_array(r,0,0) * array_vshape(r,0,k);
230 });
231 } else {
232 // Concentration given only as (river_time, river); use it at every level
233 amrex::ParallelFor(fab_domain, [=] AMREX_GPU_DEVICE (int r, int , int k) {
234 dat_array(r,0,k) = tmp_array(r,0,0);
235 });
236 }
237}
238#endif // REMORA_USE_NETCDF
mf_h setVal(geomdata.ProbHi(2))
void ReadNetCDFFile(const std::string &fname, amrex::Vector< std::string > names, amrex::Vector< NDArray< DType > > &arrays, bool one_time=false, int fill_time=0)
Read in data from netcdf file and save to data arrays.
double ReadNetCDFTimeOffsetDays(const std::string &fname, const std::string &time_name, double time_ref, bool units_required, bool announce)
Offset, in days, that puts a file's time variable onto the model clock.
NCTimeSeriesRiver(const amrex::Vector< std::string > &a_file_names, const std::string a_field_name, const std::string a_time_name, const int a_nz, double a_time_ref, const int a_use_vert_integ=0, const int a_is_transport=0)
int nriv
Number of rivers.
int nzbox
How many vertical cells there are in the data fabs.
amrex::Vector< int > file_itime_offset
Offset to access a particular time within its file.
amrex::Vector< double > river_times
Time points in netcdf file, in seconds on the model clock.
amrex::Vector< std::string > file_names
File names to read from.
std::string time_name
Field name for time series in netcdf file.
double time_before
Time in ocean_time immediately before the last time interpolated to.
std::string field_name
Field name in netcdf file.
amrex::FArrayBox * fab_vshape
Vshape data if needed.
amrex::FArrayBox * fab_after
double time_after
Time in ocean_time immediately after the last time interpolated to.
int i_time_before
Time index immediately before the last time interpolated to.
void update_interpolated_to_time(double time)
Calculate interpolated values at time, in seconds on the model clock.
void read_in_at_time(amrex::FArrayBox *vec, int itime)
amrex::FArrayBox * fab_before
FABs to pointers of river data.
int nz
Number of vertical points.
amrex::Vector< int > file_for_time
File index to access a particular time.
amrex::FArrayBox * fab_interp
Container for interpolated data; Only used if save_interpolated == true.
int has_z
Whether the field is specified in the z-dimension.
static NCFile open(const std::string &name, const int cmode=NC_NOWRITE, MPI_Comm comm=MPI_COMM_WORLD, MPI_Info info=MPI_INFO_NULL)
Open an existing file.