Line data Source code
1 : // Initialize a perturbed interface using Fourier Modes
2 : ///
3 : // Notes:
4 : // 1. \todo Extend this class to allow for 3D perturbations, currently only 2D are allowed
5 : // 2. \todo Allow for cosine (or complex exponential) expansions rather than just sin.
6 : // 3. \note This is a **two grain only** initial condition.
7 : // 4. \note This replaces the depricated "perturbed_bar" initial condition from previous versions
8 : //
9 : // The interface is defined as the :math:`x=0` plane (2D), or the :math:`x=0,z=0` plane (3D).
10 : // The equation for the interface is given by
11 : // :math:`y(x,z) = \sum_{n\in \{n_1,\ldots,n_N\}} A_n \sin(n\pi x/L_x)`
12 : // where :math:`A_n` are the amplitudes (stored in #wave_amplitudes),
13 : // :math:`n_1,\ldots,n_N\subset\mathbb{Z}_+` are wave numbers (stored in #wave_numbers),
14 : // and :math:`L_x` is the length in the x direction (obtained using the #geom object).
15 : //
16 : // Grain 1 is defined as being above :math:`y(x,z)`, Grain 2 is defined as being below.
17 :
18 : #ifndef IC_PERTURBEDINTERFACE_H_
19 : #define IC_PERTURBEDINTERFACE_H_
20 :
21 : #include "IC/IC.H"
22 : #include "Util/Util.H"
23 : #include "IO/ParmParse.H"
24 :
25 : namespace IC
26 : {
27 : class PerturbedInterface : public IC
28 : {
29 : public:
30 : static constexpr const char* name = "perturbedinterface";
31 :
32 : enum Mollifier {Dirac, Gaussian};
33 0 : PerturbedInterface (amrex::Vector<amrex::Geometry> &_geom) :
34 0 : IC(_geom)
35 0 : { }
36 1 : PerturbedInterface(amrex::Vector<amrex::Geometry> &_geom,IO::ParmParse &pp, std::string name) : IC(_geom)
37 1 : {pp_queryclass(name,*this);}
38 : PerturbedInterface(amrex::Vector<amrex::Geometry> &_geom,IO::ParmParse &pp) : IC(_geom)
39 : {pp_queryclass(*this);}
40 :
41 5 : void Add(const int &lev, Set::Field<Set::Scalar> &a_field, Set::Scalar)
42 : {
43 5 : Set::Scalar AMREX_D_DECL(L1 = geom[lev].ProbHi()[0] - geom[lev].ProbLo()[0],
44 : L2 = geom[lev].ProbHi()[1] - geom[lev].ProbLo()[1],
45 : L3 = geom[lev].ProbHi()[2] - geom[lev].ProbLo()[2]);
46 :
47 301 : for (amrex::MFIter mfi(*a_field[lev],true); mfi.isValid(); ++mfi)
48 : {
49 296 : amrex::Box bx = mfi.tilebox();
50 296 : bx.grow(a_field[lev]->nGrow());
51 296 : amrex::Array4<Set::Scalar> const& field = a_field[lev]->array(mfi);
52 296 : amrex::IndexType type = a_field[lev]->ixType();
53 :
54 296 : amrex::ParallelFor (bx,[=] AMREX_GPU_DEVICE(int i, int j, int k) {
55 42624 : amrex::IntVect m(AMREX_D_DECL(i,j,k));
56 :
57 42624 : Set::Vector x;
58 : // NODE
59 85248 : if (type == amrex::IndexType::TheNodeType())
60 : {
61 0 : AMREX_D_TERM(x(0) = geom[lev].ProbLo()[0] + ((amrex::Real)(i)) * geom[lev].CellSize()[0];,
62 : x(1) = geom[lev].ProbLo()[1] + ((amrex::Real)(j)) * geom[lev].CellSize()[1];,
63 : x(2) = geom[lev].ProbLo()[2] + ((amrex::Real)(k)) * geom[lev].CellSize()[2];);
64 : }
65 85248 : else if (type == amrex::IndexType::TheCellType())
66 : {
67 42624 : AMREX_D_TERM(x(0) = geom[lev].ProbLo()[0] + ((amrex::Real)(i) + 0.5) * geom[lev].CellSize()[0];,
68 : x(1) = geom[lev].ProbLo()[1] + ((amrex::Real)(j) + 0.5) * geom[lev].CellSize()[1];,
69 : x(2) = geom[lev].ProbLo()[2] + ((amrex::Real)(k) + 0.5) * geom[lev].CellSize()[2];);
70 : }
71 :
72 42624 : Set::Scalar bdry = 0.;
73 : #if AMREX_SPACEDIM == 2
74 42624 : Set::Scalar s1=NAN, l1=NAN;
75 42624 : if (normal==Direction::X) {s1 = x(1); l1 = L2;}
76 42624 : if (normal==Direction::Y) {s1 = x(0); l1 = L1;}
77 85248 : for (int n = 0; n < wave_numbers.size(); n++)
78 42624 : bdry += wave_amplitudes[n]
79 42624 : * ( fabs(std::cos(phis[n]))*std::cos(wave_numbers[n].real()*Set::Constant::Pi*s1 / l1) +
80 42624 : fabs(std::sin(phis[n]))*std::sin(wave_numbers[n].imag()*Set::Constant::Pi*s1 / l1));
81 : #elif AMREX_SPACEDIM == 3
82 0 : Set::Scalar s1=NAN, s2=NAN, l1=NAN, l2=NAN;
83 0 : if (normal==Direction::X) {s1 = x(1); s2 = x(2); l1 = L2; l2 = L3;}
84 0 : if (normal==Direction::Y) {s1 = x(2); s2 = x(0); l1 = L3; l2 = L1;}
85 0 : if (normal==Direction::Z) {s1 = x(0); s2 = x(0); l1 = L1; l2 = L2;}
86 0 : for (int n = 0; n < wave_numbers.size(); n++)
87 0 : bdry += wave_amplitudes[n]
88 0 : * ( fabs(std::cos(phis[n]))*std::cos(wave_numbers[n].real()*Set::Constant::Pi*s1 / l1) +
89 0 : fabs(std::sin(phis[n]))*std::sin(wave_numbers[n].imag()*Set::Constant::Pi*s1 / l1))
90 0 : * ( fabs(std::cos(phis[n]))*std::cos(wave_numbers[n].real()*Set::Constant::Pi*s2 / l2) +
91 0 : fabs(std::sin(phis[n]))*std::sin(wave_numbers[n].imag()*Set::Constant::Pi*s2 / l2))
92 : ;
93 : #endif
94 42624 : if (mol == Mollifier::Dirac)
95 : {
96 : // Util::Message(INFO);
97 0 : if ((normal == Direction::X && x(0) < bdry + offset)||
98 63936 : (normal == Direction::Y && x(1) < bdry + offset)||
99 21312 : (normal == Direction::Z && x(2) < bdry + offset))
100 : {
101 21312 : field(i,j,k,reverse) = 1.;
102 42624 : field(i,j,k,1-reverse) = 0.;
103 : }
104 : else
105 : {
106 21312 : field(i,j,k,reverse) = 0.;
107 42624 : field(i,j,k,1-reverse) = 1.;
108 : }
109 : }
110 : else
111 : {
112 0 : Set::Scalar t = 0.0;
113 0 : if (normal == Direction::X) t = x(0) - bdry - offset;
114 0 : else if (normal == Direction::Y) t = x(1) - bdry - offset;
115 0 : else if (normal == Direction::Z) t = x(2) - bdry - offset;
116 :
117 0 : Set::Scalar value = 0.5 + 0.5*std::erf(t/eps);
118 : // Util::Message(INFO, "value = ", value);
119 0 : field(i,j,k,reverse) = value;
120 0 : field(i,j,k,1-reverse) = 1. - value;
121 :
122 0 : if (field(i,j,k,0) < 0.0) field(i,j,k,reverse) = 0.0;
123 0 : if (field(i,j,k,0) > 1.0) field(i,j,k,reverse) = 1.0;
124 0 : if (field(i,j,k,1) < 0.0) field(i,j,k,1-reverse) = 0.0;
125 0 : if (field(i,j,k,1) > 1.0) field(i,j,k,1-reverse) = 1.0;
126 : }
127 42624 : });
128 : }
129 :
130 5 : };
131 :
132 : private:
133 : enum Direction {X,Y,Z};
134 : Direction normal = Direction::Y;
135 : Set::Scalar offset = 0.0;
136 : amrex::Vector<std::complex<int> > wave_numbers; ///< Store mode amplitudes \f$A_n\f$
137 : amrex::Vector<Set::Scalar> wave_amplitudes;
138 : std::vector<Set::Scalar> phis;
139 : Mollifier mol = Mollifier::Gaussian;
140 : Set::Scalar eps;
141 : int reverse = 0;
142 :
143 : public:
144 1 : static void Parse(PerturbedInterface & value, IO::ParmParse & pp)
145 : {
146 2 : std::vector<std::string> wave_numbers_str;
147 1 : pp_queryarr("wave_numbers",wave_numbers_str); // Wave numbers
148 2 : for (unsigned int i = 0; i<wave_numbers_str.size(); ++i)
149 : {
150 1 : value.wave_numbers.push_back(Util::String::Parse<std::complex<int> >(wave_numbers_str[i]));
151 1 : value.phis.push_back(std::atan2(value.wave_numbers[i].imag(),value.wave_numbers[i].real()));
152 : }
153 1 : pp_queryarr("wave_amplitudes",value.wave_amplitudes); // Wave amplitudes
154 :
155 2 : std::string normal_str;
156 1 : pp_query("normal",normal_str); // Which axis is normal to the interface (x,y,z)
157 1 : if (normal_str == "x") value.normal = Direction::X;
158 1 : if (normal_str == "y") value.normal = Direction::Y;
159 1 : if (normal_str == "z") value.normal = Direction::Z;
160 2 : std::string offset_str;
161 1 : pp_query("offset",value.offset); // Interface offset from origin
162 :
163 1 : pp_query("reverse",value.reverse); // If true, flip the interface (default:false)
164 2 : Util::Assert(INFO,TEST(value.reverse==0 || value.reverse==1));
165 :
166 1 : if (value.wave_numbers.size() != value.wave_amplitudes.size())
167 0 : Util::Abort(INFO, "Number of wave numbers and amplitudes must match");
168 :
169 2 : std::string mollifier = "dirac";
170 1 : pp_query("mollifier", mollifier); // Mollifier (options: dirac, [gaussian])
171 :
172 1 : if(mollifier == "dirac")
173 : {
174 1 : value.mol = Mollifier::Dirac;
175 1 : if (pp.contains("eps")) Util::Warning(INFO,"eps defined but not needed for dirac mollifier");
176 : }
177 : else
178 : {
179 0 : value.mol = Mollifier::Gaussian;
180 0 : pp_query("eps",value.eps); // Magnitude of mollifier
181 0 : Util::Message(INFO, "eps = ", value.eps);
182 : }
183 1 : }
184 :
185 : };
186 : }
187 : #endif
|