REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_NCTimeSeries.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 * @param[in ] a_file_name file name to read from
11 * @param[in ] a_field_name name of field to read in
12 * @param[in ] a_time_name name of time variable in NetCDF file
13 * @param[in ] a_domain simulation domain
14 * @param[inout] a_mf_var MultiFab of data to either store into or reference for dimensions
15 * @param[in ] a_is2d Whether the variable we're working with is 2D
16 * @param[in ] a_save_interpolated Whether the interpolated value should be saved internally
17 */
18NCTimeSeries::NCTimeSeries (const std::string a_file_name, const std::string a_field_name,
19 const std::string a_time_name,
20 const amrex::Box& a_domain,
21 amrex::MultiFab* a_mf_var, bool a_is2d, bool a_save_interpolated) {
22 file_name = a_file_name;
23 time_name = a_time_name;
24 field_name = a_field_name;
25 domain = a_domain;
26 mf_var = a_mf_var;
27 is2d = a_is2d;
28 save_interpolated = a_save_interpolated;
29}
30
32 // open file
33 amrex::Print() << "Loading " << field_name << " from NetCDF file " << file_name << std::endl;
34
35 // The time field can have any number of names, depending on the field.
36 // If not specified in input file (time_name.empty()) then set it by default
37 if (time_name.empty())
38 {
39 if (field_name.find("wind") != std::string::npos) {
40 time_name = "wind_time";
41 } else if ((field_name.find("str") != std::string::npos) and (field_name[0] == 's')) {
42 time_name = "sms_time";
43 } else if ((field_name.find("str") != std::string::npos) and (field_name[0] == 'b')) {
44 time_name = "bms_time";
45 } else {
46 time_name = "ocean_time";
47 }
48 }
49
50 // Check units of time stamps; should be days
51 std::string unit_str = ReadNetCDFVarAttrStr(file_name, time_name, "units"); // works on proc 0
52 if (amrex::ParallelDescriptor::IOProcessor())
53 {
54 if (unit_str.find("days") == std::string::npos) {
55 amrex::Print() << "Units of ocean_time given as: " << unit_str << std::endl;
56 amrex::Abort("Units must be in days.");
57 }
58 }
59 // get times and put in array
60 using RARRAY = NDArray<amrex::Real>;
61 amrex::Vector<RARRAY> array_ts(1);
62 ReadNetCDFFile(file_name, {time_name}, array_ts); // filled only on proc 0
63 if (amrex::ParallelDescriptor::IOProcessor())
64 {
65 int ntimes_io = array_ts[0].get_vshape()[0];
66 for (int nt(0); nt < ntimes_io; nt++)
67 {
68 // Convert ocean time from days to seconds
69 ocean_times.push_back((*(array_ts[0].get_data() + nt)) * amrex::Real(60.0) * amrex::Real(60.0) * amrex::Real(24.0));
70 // amrex::Print() << "TIMES " << ocean_times[nt] << std::endl;
71 }
72 }
73 int ntimes = ocean_times.size();
74 int ioproc = amrex::ParallelDescriptor::IOProcessorNumber();
75 amrex::ParallelDescriptor::Bcast(&ntimes,1,ioproc);
76 if (!(amrex::ParallelDescriptor::IOProcessor())) {
77 ocean_times.resize(ntimes);
78 }
79 amrex::ParallelDescriptor::Bcast(ocean_times.data(), ocean_times.size(), ioproc);
80
81 // Initialize MultiFabs
82 // This assumes that the level 0 distribution map and box array won't
83 mf_before = new amrex::MultiFab(mf_var->boxArray(), mf_var->DistributionMap(), 1, mf_var->nGrowVect());
84 mf_after = new amrex::MultiFab(mf_var->boxArray(), mf_var->DistributionMap(), 1, mf_var->nGrowVect());
86 mf_interpolated = new amrex::MultiFab(mf_var->boxArray(), mf_var->DistributionMap(), 1, mf_var->nGrowVect());
87 }
88
89 // dummy initialization
90 i_time_before = -100;
91}
92
93/**
94 * @param time time to interpolate to
95 */
97 // Figure out time index:
98 AMREX_ASSERT(time >= ocean_times[0]);
99 AMREX_ASSERT(time <= ocean_times[ocean_times.size()-1]);
100 int i_time_before_old = i_time_before;
101 for (int nt=0; nt < ocean_times.size()-1; nt++) {
102 if ((ocean_times[nt] <= time) and (ocean_times[nt+1] >= time)) {
103 i_time_before = nt;
105 time_after = ocean_times[nt+1];
106 break;
107 }
108 }
109
110 int i_time_after = i_time_before + 1;
111 if (i_time_before_old + 1 == i_time_before) {
112 // swap multifabs so we only have to read in one MultiFab
113 std::swap(mf_before, mf_after);
114 read_in_at_time(mf_after, i_time_after);
115 } else if (i_time_before_old != i_time_before) {
116 read_in_at_time(mf_after, i_time_after);
118 }
119
120 amrex::Real dt = time_after - time_before;
121
122 auto nodality = mf_var->ixType();
123
124#ifdef AMREX_USE_OMP
125#pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
126#endif
127 for (amrex::MFIter mfi(*mf_var,true); mfi.isValid(); ++mfi) {
128 // Adjust box to match ROMS grid
129 amrex::Box bx = mfi.growntilebox(amrex::IntVect(1-nodality[0],1-nodality[1],0));
130
131 amrex::Real time_before_copy = time_before;
132
133 // If we're saving the interpolated values, we fill mf_interpolated; otherwise, directly fill
134 // the multifab from the REMORA class
135 amrex::MultiFab* mf_to_fill = save_interpolated ? mf_interpolated : mf_var;
136 amrex::Array4<amrex::Real> to_fill = mf_to_fill->array(mfi);
137 amrex::Array4<const amrex::Real> before = mf_before->const_array(mfi);
138 amrex::Array4<const amrex::Real> after = mf_after->const_array(mfi);
139 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
140 {
141 to_fill(i,j,k) = before(i,j,k) + (time - time_before_copy) * (after(i,j,k) - before(i,j,k)) / dt;
142 });
143 }
144}
145
146/**
147 * @param[inout] mf multifab to store time step data into
148 * @param[in ] itime index of time step to read from file
149 */
150void NCTimeSeries::read_in_at_time (amrex::MultiFab* mf, int itime) {
151 // This all assumes that we're on level 0 with only one boxes_at_level
152 amrex::FArrayBox NC_fab;
153 amrex::Vector<amrex::FArrayBox*> NC_fabs;
154 amrex::Vector<std::string> NC_names;
155 amrex::Vector<enum NC_Data_Dims_Type> NC_dim_types;
156
157 amrex::Print() << "Reading in " << field_name << " at time index " << itime << " from " << file_name << std::endl;
158
159 NC_fabs.push_back(&NC_fab) ; NC_names.push_back(field_name);
160
161 if (is2d) {
162 NC_dim_types.push_back(NC_Data_Dims_Type::Time_SN_WE);
163 } else {
164 NC_dim_types.push_back(NC_Data_Dims_Type::Time_BT_SN_WE);
165 }
166
167 BuildFABsFromNetCDFFile<amrex::FArrayBox,amrex::Real>(domain, file_name, NC_names, NC_dim_types, NC_fabs, true, itime);
168
169#ifdef _OPENMP
170#pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
171#endif
172 {
173 // Don't tile this since we are operating on full FABs in this routine
174 for ( amrex::MFIter mfi(*mf, false); mfi.isValid(); ++mfi )
175 {
176 amrex::FArrayBox &fab = (*mf)[mfi];
177
178 //
179 // FArrayBox to FArrayBox copy does "copy on intersection"
180 // This only works here because we have broadcast the FArrayBox of data from the netcdf file to all ranks
181
182 fab.template copy<amrex::RunOn::Device>(NC_fab);
183 } // mf
184 } // omp
185}
186#endif // REMORA_USE_NETCDF
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.
std::string ReadNetCDFVarAttrStr(const std::string &fname, const std::string &var_name, const std::string &attr_name)
Helper function for reading a single variable attribute.
bool is2d
Whether the field we're reading in is 2d.
void read_in_at_time(amrex::MultiFab *mf, int itime)
Read in data from file at time index itime and fill into mf.
amrex::Real time_after
Time in ocean_times immediately after the last time interpolated to.
amrex::Real time_before
Time in ocean_times immediately before the last time interpolated to.
amrex::Box domain
Domain.
void Initialize()
Read in time array from file and allocate data arrays.
amrex::MultiFab * mf_before
Multifab to store data at time_before.
int i_time_before
Time index immediately before the last time interpolated to.
std::string field_name
Field name in netcdf file.
amrex::MultiFab * mf_after
Multifab to store data at time_after.
amrex::MultiFab * mf_interpolated
std::string time_name
Field name for time series in netcdf file.
std::string file_name
File name to read from.
void update_interpolated_to_time(amrex::Real time)
Calculate interpolated values at time, reading in data as necessary.
amrex::Vector< amrex::Real > ocean_times
Time points in netcdf file.
amrex::MultiFab * mf_var
NCTimeSeries(const std::string a_file_name, const std::string a_field_name, const std::string a_time_name, const amrex::Box &a_domain, amrex::MultiFab *a_mf_var, bool a_is2d, bool a_save_interpolated)
Constructor.
NDArray is the datatype designed to hold any data, including scalars, multidimensional arrays,...