5
#include "config_code.h"
6
#include "global_config.h"
11
// =============================================================================
13
// =============================================================================
16
PDE::PDE(Mesh *mesh, const std::string& id) :
18
decl_finished_ (false),
19
defs_finished_ (false),
20
BCs_finished_ (false),
21
eqs_finished_ (false),
25
nonlinear_solver_ (DNEWTON),
27
no_jacobian_code (false),
28
boundary_discretization_method (INWARD_DIFF3)
30
if (!mesh_->finalized())
31
throw _S"PDE constructor: mesh definition is not complete.";
39
void PDE::require_stage_finished(bool stage_finished, const std::string& msg)
44
void PDE::require_stage_not_finished(const bool& stage)
46
const bool *p = static_cast<const bool *>(&stage);
48
if (p == &decl_finished_) msg = "";
49
if (stage_finished) throw msg;
53
// -----------------------------------------------------------------------------
55
// -----------------------------------------------------------------------------
57
void PDE::set_field(const Field &fc)
59
field_.set_component(fc);
62
void PDE::set_fields(std::initializer_list<const Field> l)
64
for (const Field& fc : l)
65
field_.set_component(fc);
68
int PDE::get_component_index(const Field &fc) const
70
return field_.get_component_index(fc);
73
int PDE::get_component_index(int cid) const
75
return field_.get_component_index(cid);
78
int PDE::get_component(int index) const
80
return field_.get_component(index);
83
bool PDE::find_component(const Field &fc) const
85
return field_.find_component(fc);
88
int PDE::get_component_subindex(const Field &fc) const
90
return field_.get_component_subindex(fc);
93
// -----------------------------------------------------------------------------
95
// -----------------------------------------------------------------------------
97
void PDE::set_BC(const BoundaryCondition &bc, const Field& u)
99
const int index = get_component_index(u);
100
if (index == -1) throw u.format_name() + " is not a field of " + name + ".";
101
if (find_BC(bc, u)) throw "A boundary condition was already defined "
102
"for field " + u.format_name() + _S" " +
103
"on boundary " + fd::to_string(mesh_->get_boundary_id(bc.boundary())) + ".";
107
case BoundaryCondition::DIRICHLET:
108
set_Dirichlet_BC(bc, u);
110
case BoundaryCondition::NEUMANN:
111
set_Neumann_BC(bc, u);
113
case BoundaryCondition::CUSTOM:
114
set_custom_BC(bc, u);
117
throw "Incorrect BC specification for field " + u.format_name() +
118
" on boundary " + fd::to_string(mesh_->get_boundary_id(bc.boundary())) +
123
void PDE::set_BC(const BoundaryCondition &bc)
125
THROW_IF_NOT(field_.size() == 1, "PDE must have exactly one field.");
126
set_BC(bc, field_[0]);
129
void PDE::set_BCs(std::initializer_list<const BoundaryCondition::Ref> l)
131
for (auto& bcref : l) set_BC(bcref.self);
134
void PDE::set_BCs(std::initializer_list<const BoundaryCondition::Ref> l,
137
for (auto& bcref : l) set_BC(bcref.self, u);
140
bool PDE::find_BC(const BoundaryCondition& bc, const Field& u) const
142
for (BoundaryCondition *p : mesh_->BCs())
143
if (p->boundary() == bc.boundary() && p->component() == u.id) return true;
147
void PDE::set_Dirichlet_BC(const BoundaryCondition &bc, const Field& u)
149
const int index = get_component_index(u);
150
DirichletBC *dbc = new DirichletBC(static_cast<const DirichletBC&>(bc));
151
dbc->set_component(u.id);
152
mesh_->BCs().push_back(dbc);
153
const int bid = mesh_->get_boundary_id(bc.boundary());
156
case Mesh::R: field_[index].i_end = mesh_->nx; break;
157
case Mesh::L: field_[index].i_start = 0; break;
158
default: throw _S"Internal error 0104. Please report this error.";
162
void PDE::set_Neumann_BC(const BoundaryCondition &bc, const Field& u)
164
const int index = get_component_index(u);
165
NeumannBC *nbc = new NeumannBC(static_cast<const NeumannBC&>(bc));
166
nbc->set_component(u.id);
167
mesh_->BCs().push_back(nbc);
168
const bool bdm2 = boundary_discretization_method == SYM_DIFF2;
169
const int bid = mesh_->get_boundary_id(bc.boundary());
172
case Mesh::R: field_[index].i_end = bdm2 ? mesh_->nx + 1 : mesh_->nx; break;
173
case Mesh::L: field_[index].i_start = bdm2 ? -1 : 0; break;
174
default: throw _S"Internal error 0105. Please report this error.";
178
void PDE::set_custom_BC(const BoundaryCondition &bc, const Field& u)
180
ASSERT_MSG(bc.type == BoundaryCondition::CUSTOM, "Expected custom BC.");
181
if (boundary_discretization_method == SYM_DIFF2)
182
throw _S"Custom (user defined) boundary conditions cannot be "
183
"discretized with the traditional symmetric differences method. "
184
"Use an inward differences method.";
185
if (static_cast<const CustomBC&>(bc).expression == nullptr)
186
throw _S"Custom (user defined) boundary conditions which are undefined "
187
"cannot be selected into a PDE.";
189
const int index = get_component_index(u);
190
CustomBC *cbc = new CustomBC(static_cast<const CustomBC&>(bc));
191
cbc->set_component(u.id);
192
set_equation_for_BC(cbc, cbc->expression);
193
mesh_->BCs().push_back(cbc);
194
const int bid = mesh_->get_boundary_id(bc.boundary());
197
case Mesh::R: field_[index].i_end = mesh_->nx; break;
198
case Mesh::L: field_[index].i_start = 0; break;
199
default: throw _S"Internal error 0106. Please report this error.";
203
// -----------------------------------------------------------------------------
204
// Set equations, functions etc
205
// -----------------------------------------------------------------------------
207
PDE& PDE::Ref::operator=(const Expression& expr)
211
if (eqnum < 0) throw _S"Equation numbers cannot be negative.";
212
eqname = fd::to_string(eqnum);
214
pde->set_differential_expression(eqname, expr);
218
PDE::EquationReplacement::EquationReplacement(int fi, int ic, double val,
219
const std::string& rv, const std::string& jv, PDE *pde) :
226
initialized_ (false) {}
228
void PDE::EquationReplacement::init()
230
if (initialized_) return;
231
THROW_IF(pde_ == nullptr, "null PDE object!");
233
if (!rhs.empty()) return;
234
const string fi = fd::to_string_empty_if(pde_->components() == 1, index);
235
const string eq = code_elem_lhs("e", fd::to_string(i), fi);
236
rhs = "u[" + eq + "]";
237
if (!fd::is_zero(value))
241
rhs += fd::to_string(value);
243
rhs += "("+fd::to_string(value)+")";
245
jac = "du[" + eq + "]";
248
void PDE::replace_equation(const Field &u, int i, const string& rhs,
251
const int index = get_component_index(u);
252
THROW_IF(index == -1, u.format_name() + " is not a field of " + name + ".");
253
replace_equation(index, i, 0, rhs, jac);
256
void PDE::replace_equation(const Field &u, int i, double rhs)
258
const int index = get_component_index(u);
259
THROW_IF(index == -1, u.format_name() + " is not a field of " + name + ".");
260
replace_equation(index, i, rhs, "", "");
263
void PDE::replace_equation(int i, const string& rhs, const string& jac)
265
THROW_IF(field_.empty(), "There is no field in the PDE.");
266
THROW_IF(field_.size() > 1, "For multidimensional fields use "
267
"the overload that accepts a Field as its first argument.");
268
replace_equation(0, i, 0, rhs, jac);
271
void PDE::replace_equation(int i, double rhs)
273
THROW_IF(field_.empty(), "There is no field in the PDE.");
274
THROW_IF(field_.size() > 1, "For multidimensional fields use "
275
"the overload that accepts a Field as its first argument.");
276
replace_equation(0, i, rhs, "", "");
279
void PDE::replace_equation(int index, int i, double rhs_value,
281
const string& rhs_jac)
283
const EquationReplacement eqrepl(index, i, rhs_value, rhs, rhs_jac, this);
284
const bool present = [this, &eqrepl]() {
285
for (const auto& r : replacements_)
286
if (r.equation_is_identical_to(eqrepl)) return true;
289
if (present) throw _S"The equation for field "
290
+ field_[index].format_name() + " was already replaced.";
291
replacements_.push_back(eqrepl);
294
void PDE::set_function(Function& f, const std::string& imp)
296
const std::string& fname = f.fname();
298
// Check function name
300
throw _S"Function has no name.";
301
if (!fd::regex_match_funcname(fname))
302
throw fname + " is not a valid function name.";
304
// Check if function name is reserved
305
if (fd::find(reserved_function_names_, fname))
306
throw "The function name \'" + fname + "\' is reserved.";
308
// Check if function is already defined:
309
if (find_fname_in_field_functions(fname) || find_fname_in_functions(fname))
310
throw "Function " + fname + " is already selected in the PDE.";
313
f.set_implementation(imp);
315
functions_.push_back(&f);
318
void PDE::set_function(FieldFunction& f, const std::vector<std::string>& imp)
320
const std::string& fname = f.fname();
322
// Check function name
324
throw _S"Field function has no name.";
325
if (!std::regex_match(fname, std::regex("[_a-zA-Z][_a-zA-Z0-9]*")))
326
throw fname + " is not a valid function name.";
328
// Check if function name is reserved
329
if (fd::find(reserved_function_names_, fname))
330
throw "The function name \'" + fname + "\' is reserved.";
332
// Check if function is already defined:
333
if (find_fname_in_field_functions(fname) || find_fname_in_functions(fname))
334
throw "Function " + fname + " is already selected in the PDE.";
337
f.set_implementation(imp);
339
field_functions_.push_back(&f);
342
void PDE::set_functions(std::initializer_list<Function::Ref> l)
344
for (const Function::Ref& ref : l)
345
set_function(ref.self);
348
void PDE::set_functions(std::initializer_list<FieldFunction::Ref> l)
350
for (const FieldFunction::Ref& ref : l)
351
set_function(ref.self);
354
fd1::Function& PDE::get_function(const string& func_name) const
356
for (Function *f : functions_)
357
if (f->fname() == func_name) return *f;
358
throw "Unable to find function " + func_name + " in PDE " +
359
(name.empty() ? _S"noname" : name) + ".";
362
fd1::FieldFunction& PDE::get_field_function(const string& func_name) const
364
for (FieldFunction *f : field_functions_)
365
if (f->fname() == func_name) return *f;
366
throw "Unable to find field function " + func_name + " in PDE " +
367
(name.empty() ? _S"noname" : name) + ".";
370
// -----------------------------------------------------------------------------
372
// -----------------------------------------------------------------------------
374
bool PDE::linear_BCs() const
376
for (BoundaryCondition *bc : mesh_->BCs())
377
if (bc->type == BoundaryCondition::CUSTOM &&
378
!static_cast<CustomBC *>(bc)->codeinfo.linear) return false;
382
bool PDE::linear_eqs() const
384
for (const CodeFragment& cf : equation_codes_)
385
if (!cf.linear) return false;
389
// -----------------------------------------------------------------------------
390
// Code generation utilities
391
// -----------------------------------------------------------------------------
393
void PDE::link(string& code) const
395
if (code.empty()) return;
397
bool single_component = components() == 1;
400
fd::replace_all(code, "$COMMA$", single_component ? "" : ",");
402
// Build list if referenced fields ids
403
std::vector<int> refs;
405
const string rex("\\$c\\_[1-9][0-9]*\\$");
406
auto it = code.cbegin();
407
while (std::regex_search(it, code.cend(), sm, std::regex(rex)))
410
try {id = std::stoi(sm.str(0).substr(3, sm.length(0) - 4));}
411
catch (std::invalid_argument& err) {
412
throw "Incorrectly formatted link: " + sm.str(0); }
413
if (!fd::find(refs, id))
415
it += sm.position(0) + sm.length(0);
420
// Check if ref is a valid field id selected in the PDE:
421
const int cindex = get_component_index(id);
424
const string global_field_name = Field::find_global_id(id);
425
if (global_field_name.empty())
426
throw _S"Internal error 0112. An unregistered field (#" +
427
fd::to_string(id) + ") was encountered. Please report this error.";
428
throw "The field " + global_field_name + " does not belong to the PDE " +
429
_S(components() > 1 ? "system " : "") + name + ".";
432
const string sym = "$c_" + fd::to_string(id) + "$";
433
const string val = fd::to_string_empty_if(single_component, cindex);
434
fd::replace_all(code, sym, val);
438
if (code.find('$') != string::npos) {
439
DEBUG_LOG("Generated code is possibly incorrect!");
440
} // Do not remove braces!
443
void PDE::link(CodeFragment& cf) const
447
link(cf.text_bx_y[0]);
448
link(cf.text_bx_y[1]);
449
link(cf.text_bx_j[0]);
450
link(cf.text_bx_j[1]);
453
// -----------------------------------------------------------------------------
455
// -----------------------------------------------------------------------------
463
void PDE::check() const
465
if (components() != equations())
466
throw _S"Number of equations does not match number of components.";
467
if (!finalized_) throw _S"PDE system definition is not complete.";
470
void PDE::check_function_implementation(bool empty, const string& name)
474
switch (fd::global_config().missing_function_impl)
476
case fd::NOT_AN_ERROR:
478
case fd::NOTE: log_msg("NOTE: The function "+name+
479
" has no implementation. Modify the generated code file(s)"
480
" to add it manually.");
482
case fd::WARNING: log_msg("WARNING: The function "+name+
483
" has no implementation.");
485
case fd::EXCEPTION: throw "The function "+name+
486
" has no implementation.";
492
void PDE::check_function_implementations()
494
for (const auto f: functions_)
495
check_function_implementation(f->implementation.empty(), f->fname());
498
void PDE::check_field_function_implementations()
500
for (const auto f: field_functions_)
501
check_function_implementation(f->implementation.empty(), f->fname());
504
bool PDE::boundary_discretization_method_changed() const
506
bool first_time = true;
508
for (BoundaryCondition *bc : mesh_->BCs())
510
auto msg = [this, bc]() -> string
512
const int cid = bc->component();
513
const int cindex = get_component_index(cid);
514
const string field_name = cindex == -1 ? Field::find_global_id(cid) :
515
field_[cindex].format_name(true);
516
return _S" discretization method was used for the discretization "
517
+ (components() == 1 ? _S"" : "of field " + field_name + " ")
519
+ fd::to_string(mesh_->get_boundary_id(bc->boundary())) + ".";
522
if (bc->type == BoundaryCondition::CUSTOM)
524
CustomBC *cbc = (CustomBC *) bc;
525
const int this_dm = cbc->codeinfo.boundary_discretization_method;
526
if (this_dm == -1) throw "An unknown " + msg();
527
if (this_dm == SYM_DIFF2) throw _S"Custom (user defined) boundary "
528
"conditions cannot be discretized with the traditional symmetric "
529
"differences method. Use an inward differences method.";
535
else if (dm != this_dm)
537
throw "A different (" + fd::to_string(this_dm) + ") " + msg()
538
+ " Method previously used: " + fd::to_string(dm) + ".";
543
return first_time ? false : (dm != boundary_discretization_method);
546
bool PDE::dirichlet_neumann_BCs_on_same_boundary() const
548
for (const int bid : {Mesh::R, Mesh::L})
550
int dirichler_bcs = 0;
552
for (const auto& field: field_)
554
BoundaryCondition *bc = mesh_->get_BC_for_component(field.id, bid);
555
if (bc == nullptr) continue;
556
if (bc->type == BoundaryCondition::DIRICHLET) ++dirichler_bcs;
557
else if (bc->type == BoundaryCondition::NEUMANN) ++neumann_bcs;
559
if (dirichler_bcs > 0 && neumann_bcs > 0) return true;
564
bool PDE::has_neumann_BCs() const
566
for (BoundaryCondition *bc : mesh_->BCs())
567
if (bc->type == BoundaryCondition::NEUMANN) return true;
571
bool PDE::component_has_neumann_BC_on(int comp_index, int bid) const
573
const auto bc = mesh_->get_BC_for_component(get_component(comp_index), bid);
574
THROW_IF(bc == nullptr, "no BC for component "+
575
field_[comp_index].format_name()+" on boundary "+fd::to_string(bid)+".");
576
return bc->type == BoundaryCondition::NEUMANN;
579
bool PDE::has_neumann_BC_on(int bid) const
581
for (int comp_index = 0; comp_index < components(); ++comp_index)
582
if (component_has_neumann_BC_on(comp_index, bid)) return true;
586
bool PDE::find_fname_in_functions(const std::string& fname) const
588
for (auto f : functions_)
589
if (fname == (f->fname())) return true;
593
bool PDE::find_fname_in_field_functions(const std::string& fname) const
595
for (auto f : field_functions_)
596
if (fname == (f->fname())) return true;
600
bool PDE::has_default_field_function_implementations() const
602
for (FieldFunction *f : field_functions_)
603
for (int n = 1; n < f->argc() + 1; ++n)
604
if ((int) f->implementation.size() > n && f->implementation[n] == "default")
609
bool PDE::has_exact_solution() const
611
for (auto f : functions_)
612
if (f->fname() == "exact_solution") return true;
616
// -----------------------------------------------------------------------------
618
// -----------------------------------------------------------------------------
620
void PDE::set_library(const std::string& library)
622
for (auto lib : libs_)
623
if (lib.second == library)
624
{ library_ = lib.first; return; }
625
throw "Unknown library: " + library;
628
void PDE::set_matrix_method(const std::string& matrix_method)
630
for (auto mm : matrix_methods_)
631
if (mm.second == matrix_method)
632
{ matrix_method_ = mm.first; return; }
633
throw "Unknown matrix method: " + matrix_method;
636
void PDE::set_nonlinear_solver(const std::string& nonlinear_solver)
638
for (auto nls : nonlinear_solvers_)
639
if (nls.second == nonlinear_solver)
640
{ nonlinear_solver_ = nls.first; return; }
641
throw "Unknown nonlinear solver: " + nonlinear_solver;
644
// -----------------------------------------------------------------------------
645
// Customization and options
646
// -----------------------------------------------------------------------------
647
void PDE::set_initial_estimate(const string& impl)
649
init_estimate_imp_ = impl;
650
fd::global_config().format_function_implementation(init_estimate_imp_);
653
bool PDE::has_initial_estimate() const
654
{ return !init_estimate_imp_.empty(); }
656
// -----------------------------------------------------------------------------
658
// -----------------------------------------------------------------------------
660
std::map<const std::string, int>& PDE::get_dict_for_source(int which)
662
if (which == HEADER_FILE) return extra_headers_def_;
663
if (which == IMP_FILE) return extra_headers_imp_;
664
return extra_headers_main_;
667
void PDE::set_headers(const std::string& src, const std::string& pos,
668
const std::string& headers)
670
const int which = [src] {
671
if (src == "header" || src == "interface" || src == "int" || src == "h" )
673
if (src == "implementation" || src == "imp" || src == "i") return IMP_FILE;
674
if (src == "main" || src == "m" ) return MAIN_FILE;
675
throw "Unknown output file type: " + src +".";
678
const int where = [src, pos] {
679
if (pos == "top" || pos == "begin" || pos == "start" || pos == "t" )
680
return PLACEMENT_TOP;
681
if (pos == "before library" || pos == "before interface" ||
682
pos == "before header" || pos == "b" || pos == "bh" )
683
return PLACEMENT_BEFORE_LIB;
684
if (pos == "bottom" || pos == "end" ||pos == "e" ) return PLACEMENT_LAST;
685
throw "Unknown placement \"" + pos + "\" for header(s): " + src +".";
688
/*std::map<const string, int>*/auto& d = get_dict_for_source(which);
689
const char delim = (headers.find(':') == string::npos) ? ';' : ':';
690
const string msg = " header specification.";
691
std::stringstream ss(headers);
694
while (std::getline(ss, item, delim))
696
if (item.empty()) throw "Incorrect" + msg;
697
if (d.find(item) != d.end()) throw "Duplicate" + msg;
702
void PDE::set_extra_includes(const std::string& s)
704
const char delim = (s.find(':') == std::string::npos) ? ';' : ':';
705
const std::string msg = " include file specification.";
706
std::stringstream ss(s);
708
while (std::getline(ss, item, delim))
710
if (item.empty()) throw "Incorrect" + msg;
711
if (fd::find(extra_includes_, item)) throw "Duplicate" + msg;
712
extra_includes_.push_back(item);
715
// -----------------------------------------------------------------------------
717
// -----------------------------------------------------------------------------
719
std::string PDE::get_implem_filename() const {return code_file_;}
720
std::string PDE::get_header_filename() const {return header_file_;}
721
std::string PDE::get_main_filename() const {return program_;}
723
void PDE::set_output(const std::string& filename, const std::string& progname)
725
auto make_filename = [](const std::string& name, bool header)->std::string {
726
static const char *ext[] = {".cpp", ".cxx", ".c++", ".cc", ".C",
727
".hpp", ".hxx", ".h++", ".hh", ".H"};
728
static const int sz[] = {4, 4, 4, 3, 2};
729
static const int n_ext = sizeof(ext)/(2*sizeof(ext[0]));
730
int w = header ? n_ext : 0;
731
if (name.rfind(ext[w + 0]) == name.size() - sz[0] ||
732
name.rfind(ext[w + 1]) == name.size() - sz[1] ||
733
name.rfind(ext[w + 2]) == name.size() - sz[2] ||
734
name.rfind(ext[w + 3]) == name.size() - sz[3] ||
735
name.rfind(ext[w + 4]) == name.size() - sz[4])
737
return name + ext[w];
739
// Open file makining necessary adjustments
740
auto open_file = [this, make_filename](const std::string& filename,
741
std::string& ofname, std::ofstream& of, bool hdr) {
742
ofname = make_filename(filename, hdr);
743
fd::backup_old_file(ofname);
745
if (!of) throw "Could not open file " + ofname;
747
open_file(filename, code_file_, code_, false);
748
open_file(filename, header_file_, code_h_, true);
749
// Decide which file to use to output main function
750
program_ = [this, progname, make_filename]() -> std::string {
751
if (progname.empty())
752
return code_file_.substr(0, code_file_.rfind('.')) + "_main.cpp";
753
if (progname == "*") return code_file_;
754
return make_filename(progname, false);
758
void PDE::switch_to_main()
760
if (program_ == code_file_) return;
762
std::string fname = get_main_filename();
763
fd::backup_old_file(fname);
765
if (!code_) throw "Could not open file " + fname;
768
// TODO finalize PDE just before code generation
769
// TODO check that number of field components == number of eqs (in check() ?)