deal2lkit: A ToolKit library for Deal.II
imex_stepper.cc
Go to the documentation of this file.
1 //-----------------------------------------------------------
2 //
3 // Copyright (C) 2015 - 2016 by the deal2lkit authors
4 //
5 // This file is part of the deal2lkit library.
6 //
7 // The deal2lkit library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE at
12 // the top level of the deal2lkit distribution.
13 //
14 //-----------------------------------------------------------
15 
16 
17 #include <deal2lkit/imex_stepper.h>
18 #ifdef D2K_WITH_SUNDIALS
19 
20 #include <deal.II/base/utilities.h>
23 #ifdef DEAL_II_WITH_TRILINOS
27 #endif
28 #ifdef DEAL_II_WITH_PETSC
32 #endif
33 #include <deal.II/base/utilities.h>
34 
35 #include <iostream>
36 #include <iomanip>
37 
38 #include <math.h>
39 
40 #ifdef DEAL_II_WITH_MPI
41 #include <nvector/nvector_parallel.h>
42 #endif
43 
44 using namespace dealii;
45 
46 
47 D2K_NAMESPACE_OPEN
48 
49 #ifdef DEAL_II_WITH_MPI
50 template <typename VEC>
51 IMEXStepper<VEC>::IMEXStepper(std::string name,
52  MPI_Comm comm) :
53  ParameterAcceptor(name),
54  communicator(Utilities::MPI::duplicate_communicator(comm)),
55  kinsol("KINSOL for IMEX",comm),
56  pcout(std::cout,
57  Utilities::MPI::this_mpi_process(communicator)==0)
58 {
60 }
61 #else
62 template <typename VEC>
63 IMEXStepper<VEC>::IMEXStepper(std::string &name) :
64  ParameterAcceptor(name),
65  kinsol("KINSOL for IMEX",communicator),
66  pcout(std::cout)
67 {
69 }
70 #endif
71 
72 template <typename VEC>
74 {
75  double alpha;
76  if (initial_time == final_time || step_size == 0.0)
77  alpha = 0.0;
78  else
79  alpha = 1./step_size;
80 
81  return alpha;
82 
83 }
84 template <typename VEC>
86 {
87  initial_time = t;
88 }
89 
90 template <typename VEC>
92 {
94  "Step size", "1e-2", Patterns::Anything(),
95  "Conditional statement can be used as well:\n"
96  "(t<0.5?1e-2:1e-3)");
97 
98  add_parameter(prm, &abs_tol,
99  "Absolute error tolerance", "1e-6",
100  Patterns::Double());
101 
102  add_parameter(prm, &rel_tol,
103  "Relative error tolerance", "1e-5",
104  Patterns::Double());
105 
107  "Initial time", "0.0",
108  Patterns::Double());
109 
111  "Final time", "1.0",
112  Patterns::Double());
113 
115  "Intervals between outputs", "1",
117 
119  "Maximum number of outer nonlinear iterations", "5",
121  "At each outer iteration the Jacobian is updated if it is set that the \n"
122  "Jacobian is continuously updated and a cycle of inner iterations is \n"
123  "perfomed.");
124 
126  "Maximum number of inner nonlinear iterations", "3",
128  "At each inner iteration the Jacobian is NOT updated.");
129 
131  "Newton relaxation parameter", "1",
132  Patterns::Double());
133 
135  "Update continuously Jacobian", "true",
136  Patterns::Bool());
137 
139  "Number of elements in backtracking sequence", "5",
141  "In the line seach method with backtracking the following alphas are\n"
142  "tested: 1, 1/2, 1/4,..., 2^-i. This parameter sets the maximum i.");
143  add_parameter(prm, &method,
144  "Method used", "fixed_alpha",
145  Patterns::Selection("fixed_alpha|LS_backtracking"),
146  "Fixed alpha means that the parsed alpha is used in each Newton iteration\n"
147  "LS_backtracking is the line search with backtracking method.");
148 
149  add_parameter(prm, &verbose,
150  "Print useful informations", "false",
151  Patterns::Bool());
152 
154  "Use the KINSOL solver", "true",
155  Patterns::Bool());
156 
157 }
158 
159 template <typename VEC>
160 void IMEXStepper<VEC>::compute_y_dot(const VEC &y, const VEC &prev, const double alpha, VEC &y_dot)
161 {
162  y_dot = y;
163  y_dot -= prev;
164  y_dot *= alpha;
165 }
166 
167 template <typename VEC>
168 unsigned int IMEXStepper<VEC>::solve_dae(VEC &solution, VEC &solution_dot)
169 {
170  unsigned int step_number = 0;
171 
172  auto previous_solution = create_new_vector();
173  auto residual_ = create_new_vector();
174  auto rhs = create_new_vector();
175 
176  double t = initial_time;
177  double alpha;
179  alpha = get_alpha();
180  // check if it is a stationary problem
181  if (initial_time == final_time)
182  alpha = 0.0;
183  else
184  alpha = 1./step_size;
185 
186  compute_previous_solution(solution,solution_dot,alpha, *previous_solution);
187 
188 
189 
190  std::function<int(const VEC &, VEC &)> my_residual = [&] (const VEC &y, VEC &res)
191  {
192  double a = this->get_alpha();
193  compute_y_dot(y,*previous_solution,a,solution_dot);
194  int ret = this->residual(t,y,solution_dot,res);
195  *residual_ = res;
196  AssertThrow(!std::isnan(residual_->l2_norm()),ExcMessage("Residual contains one or more NaNs."));
197  return ret;
198  };
199 
200  std::function<int(const VEC &)> my_jac = [&] (const VEC &y)
201  {
202  double a = this->get_alpha();
203  compute_y_dot(y,*previous_solution,a,solution_dot);
204  return this->setup_jacobian(t,y,solution_dot,a);
205  };
206 
207  std::function<int(const VEC &, VEC &)> my_solve = [&] (const VEC &, VEC &dst)
208  {
209  *rhs = *residual_;
210  *rhs *= -1.0;
211  return this->solve_jacobian_system(*rhs,dst);
212  };
213 
214 
215  kinsol.create_new_vector = create_new_vector;
216  kinsol.residual = my_residual;
217  kinsol.setup_jacobian = my_jac;
218  kinsol.solve_linear_system = my_solve;
219  kinsol.jacobian_vmult = jacobian_vmult;
220 
221  // Initialization of the state of the boolean variable
222  // responsible to keep track of the requirement that the
223  // system's Jacobian be updated.
224  bool update_Jacobian = true;
225 
226  // silence a warning when using kinsol
227  (void) update_Jacobian;
228 
229 
230 // store initial conditions
231  output_step(t, solution, solution_dot, step_number);
232 
233  bool restart=false;
234 
235  auto L2 = create_new_vector();
236  if (use_kinsol)
237  {
238  // call kinsol initialization. this is mandatory if I am doing multiple cycle in pi-DoMUS
239  kinsol.initialize_solver(solution);
241  kinsol.set_scaling_vectors(*L2, *L2);
242  }
243 
245  solution,
246  solution_dot);
247  // The overall cycle over time begins here.
248  while (t<=final_time+1e-15)
249  {
250  pcout << "Solving for t = " << t
251  << " (step size = "<< step_size<<")"
252  << std::endl;
253 
254  if (use_kinsol)
255  {
256  kinsol.solve(solution);
257  compute_y_dot(solution,*previous_solution,alpha,solution_dot);
258  }
259  else
260  do_newton(t,alpha,update_Jacobian,*previous_solution,solution,solution_dot);
261 
262  restart = solver_should_restart(t,solution,solution_dot);
263 
264  while (restart)
265  {
266 
267  previous_solution = create_new_vector();
268  residual_ = create_new_vector();
269  rhs = create_new_vector();
270  L2 = create_new_vector();
271 
272 
273  if (use_kinsol)
274  {
275 
276 
277  kinsol.initialize_solver(solution);
279  kinsol.set_scaling_vectors(*L2, *L2);
280  }
282  solution,
283  solution_dot);
284 
285  compute_previous_solution(solution,solution_dot,alpha,*previous_solution);
286 
287  restart = solver_should_restart(t,solution,solution_dot);
288 
289  }
290 
291 
292  step_number += 1;
293 
294  if ((step_number % output_period) == 0)
295  output_step(t, solution, solution_dot, step_number);
297  t += step_size;
298 
299  if (initial_time == final_time)
300  alpha = 0.0;
301  else
302  alpha = 1./step_size;
303 
304  *previous_solution = solution;
305  update_Jacobian = update_jacobian_continuously;
306 
307  } // End of the cycle over time.
308  return 0;
309 }
310 
311 
312 
313 template <typename VEC>
314 double IMEXStepper<VEC>::
316  const VEC &previous_solution,
317  const double &alpha,
318  const double &t,
319  VEC &solution,
320  VEC &solution_dot,
321  VEC &residual)
322 {
323  auto first_trial = create_new_vector();
324  auto first_residual = create_new_vector();
325 
326  *first_trial = solution;
327  double n_alpha = 1.0;
328 
329  first_trial->sadd(1.0, n_alpha, update);
330 
331  solution_dot = *first_trial;
332  solution_dot -= previous_solution;
333  solution_dot *= alpha;
334 
335  this->residual(t, *first_trial, solution_dot, *first_residual);
336 
337  double first_res_norm = vector_norm(*first_residual);
338 
339  auto second_trial = create_new_vector();
340  auto second_residual = create_new_vector();
341 
342  for (unsigned int i=1; i<=n_max_backtracking; ++i)
343  {
344  *second_trial = solution;
345 
346  n_alpha = 1.0/(std::pow(2.0,i));
347 
348  second_trial->sadd(1.0, n_alpha, update);
349 
350  solution_dot = *second_trial;
351  solution_dot -= previous_solution;
352  solution_dot *= alpha;
353 
354  this->residual(t, *second_trial, solution_dot, *second_residual);
355  double second_res_norm = vector_norm(*second_residual);
356  if (first_res_norm < second_res_norm)
357  {
358  solution = *first_trial;
359  residual = *first_residual;
360 
361  solution_dot = *first_trial;
362  solution_dot -= previous_solution;
363  solution_dot *= alpha;
364  n_alpha = 1.0/(std::pow(2.0,i-1));
365  return n_alpha;
366 
367  }
368  else if (i<(n_max_backtracking))
369  {
370  *first_trial = *second_trial;
371  *first_residual = *second_residual;
372  first_res_norm = second_res_norm;
373  }
374  else
375  {
376  solution = *second_trial;
377  residual = *second_residual;
378  /* solution dot is ok */
379  return n_alpha;
380  }
381  }
382  return n_alpha;
383 }
384 
385 
386 template <typename VEC>
388 do_newton (const double t,
389  const double alpha,
390  const bool update_Jacobian,
391  const VEC &previous_solution,
392  VEC &solution,
393  VEC &solution_dot)
394 {
395  auto solution_update = create_new_vector();
396  auto res = create_new_vector();
397  auto rhs = create_new_vector();
398 
399 
400 
401  // Initialization of two counters for the monitoring of
402  // progress of the nonlinear solver.
403  unsigned int inner_iter = 0;
404  unsigned int outer_iter = 0;
405  unsigned int nonlin_iter = 0;
406  this->residual(t, solution, solution_dot, *res);
407  double res_norm = 0.0;
408  double solution_norm = 0.0;
409 
410  if (abs_tol>0.0||rel_tol>0.0)
411  res_norm = this->vector_norm(*res);
412  // if (rel_tol>0.0)
413  // solution_norm = interface.vector_norm(solution);
414 
415  // The nonlinear solver iteration cycle begins here.
416  // using a do while approach, we ensure that the system
417  // is solved at least once
418  do
419  {
420  outer_iter += 1;
421  if (update_Jacobian == true)
422  {
423  setup_jacobian(t,
424  solution,
425  solution_dot,
426  alpha);
427  }
428 
429  inner_iter = 0;
430  while (inner_iter < max_inner_non_linear_iterations &&
431  res_norm > abs_tol &&
432  res_norm > rel_tol*solution_norm)
433  {
434 
435 
436  inner_iter += 1;
437 
438  *rhs = *res;
439  *rhs *= -1.0;
440 
441  solve_jacobian_system(*rhs, *solution_update);
442 
443 
444  if (method == "LS_backtracking")
445  {
446  newton_alpha = line_search_with_backtracking(*solution_update,
447  previous_solution,
448  alpha,
449  t,
450  solution,
451  solution_dot,
452  *res);
453  }
454  else if (method == "fixed_alpha")
455  {
456  solution.sadd(1.0,
457  newton_alpha, *solution_update);
458 
459  }
460 
461  compute_y_dot(solution,previous_solution,alpha, solution_dot);
462 
463  res_norm = vector_norm(*solution_update);
464 
465  AssertThrow(!std::isnan(res->l2_norm()),ExcMessage("Residual contains one or more NaNs."));
466 
467  if (rel_tol>0.0)
468  {
469  solution_norm = vector_norm(solution);
470 
471  if (verbose)
472  {
473  pcout << std::endl
474  << " "
475  << " iteration "
476  << nonlin_iter + inner_iter
477  << ":\n"
478  << std::setw(19) << std::scientific << res_norm
479  << " update norm\n"
480  << std::setw(19) << std::scientific << solution_norm
481  << " solution norm\n"
482  << std::setw(19) << newton_alpha
483  << " newton alpha\n\n"
484  << std::endl;
485  }
486  }
487  else if (verbose)
488  {
489  pcout << std::endl
490  << " "
491  << " iteration "
492  << nonlin_iter + inner_iter
493  << ":\n"
494  << std::setw(19) << std::scientific << res_norm
495  << " update norm\n"
496  << std::setw(19) << newton_alpha
497  << " newton alpha\n\n"
498  << std::endl;
499  }
500 
501  this->residual(t,solution,solution_dot,*res);
502  }
503 
504  nonlin_iter += inner_iter;
505 
506  if (std::fabs(res_norm) < abs_tol ||
507  std::fabs(res_norm) < rel_tol*solution_norm)
508  {
509  pcout << std::endl
510  << " "
511  << std::setw(19) << std::scientific << res_norm
512  << " (converged in "
513  << nonlin_iter
514  << " iterations)\n\n"
515  << std::endl;
516  break; // Break of the while cycle
517  }
518  else if (outer_iter == max_outer_non_linear_iterations)
519  {
520  pcout << std::endl
521  << " "
522  << std::setw(19) << std::scientific << res_norm
523  << " (not converged in "
524  << std::setw(3) << nonlin_iter
525  << " iterations)\n\n"
526  << std::endl;
527  AssertThrow(false,
528  ExcMessage ("No convergence in nonlinear solver"));
529  }
530 
531  } // The nonlinear solver iteration cycle ends here.
532  while (outer_iter < max_outer_non_linear_iterations &&
533  res_norm > abs_tol &&
534  res_norm > rel_tol*solution_norm);
535 }
536 
537 template <typename VEC>
540  const VEC &sol_dot,
541  const double &alpha,
542  VEC &prev)
543 {
544  if (alpha > 0.0)
545  {
546  prev = sol_dot;
547  prev /= (-1.0*alpha);
548  prev += sol;
549  }
550  else
551  {
552  prev = sol;
553  (void)sol_dot;
554  (void)alpha;
555  }
556 }
557 
558 template <typename VEC>
560 {
561  std::string variables = "t";
562  std::map<std::string,double> constants;
563  // FunctionParser with 1 variables and 1 component:
564  FunctionParser<1> fp(1);
565  fp.initialize(variables,
566  _step_size,
567  constants);
568  // Point at which we want to evaluate the function
569  Point<1> time(t);
570  // evaluate the expression at 'time':
571  double result = fp.value(time);
572  return result;
573 }
574 
575 template <typename VEC>
577  VEC &y,
578  VEC &y_dot)
579 {
580  auto previous_solution = create_new_vector();
581  auto first_guess = create_new_vector();
582  auto first_guess_dot = create_new_vector();
583  auto update = create_new_vector();
584 
585  *first_guess = y;
586  *first_guess_dot = y_dot;
587 
588 
589 
590  step_size = 0;
591  if (use_kinsol)
592  {
593  kinsol.solve(y);
594  }
595  else
596  do_newton(t,0.0,true,*previous_solution,y,y_dot);
597 
598  *update = y;
599  *update -= *first_guess;
601  *update /= step_size;
602  *first_guess_dot += *update;
603  y_dot = *first_guess_dot;
604 
605 
606 }
607 
608 
609 template<typename VEC>
611 {
612 
613  create_new_vector = []() ->shared_ptr<VEC>
614  {
615  shared_ptr<VEC> p;
616  AssertThrow(false, ExcPureFunctionCalled("Please implement create_new_vector function."));
617  return p;
618  };
619 
620  residual = [](const double,
621  const VEC &,
622  const VEC &,
623  VEC &) ->int
624  {
625  int ret=0;
626  AssertThrow(false, ExcPureFunctionCalled("Please implement residual function."));
627  return ret;
628  };
629 
630  setup_jacobian = [](const double,
631  const VEC &,
632  const VEC &,
633  const double) ->int
634  {
635  int ret=0;
636  AssertThrow(false, ExcPureFunctionCalled("Please implement setup_jacobian function."));
637  return ret;
638  };
639 
640  solve_jacobian_system = [](const VEC &,
641  VEC &) ->int
642  {
643  int ret=0;
644  AssertThrow(false, ExcPureFunctionCalled("Please implement solve_jacobian_system function."));
645  return ret;
646  };
647 
648  output_step = [](const double,
649  const VEC &,
650  const VEC &,
651  const unsigned int)
652  {
653  AssertThrow(false, ExcPureFunctionCalled("Please implement output_step function."));
654  };
655 
656  solver_should_restart = [](const double,
657  VEC &,
658  VEC &) ->bool
659  {
660  bool ret=false;
661  AssertThrow(false, ExcPureFunctionCalled("Please implement solver_should_restart function."));
662  return ret;
663  };
664 
665  get_lumped_mass_matrix = []() ->VEC &
666  {
667  shared_ptr<VEC> y;
668  AssertThrow(false, ExcPureFunctionCalled("Please implement get_lumped_mass_matrix function."));
669  return *y;
670  };
671 
672  jacobian_vmult = [](const VEC &,
673  VEC &) ->int
674  {
675  int ret=0;
676  AssertThrow(false, ExcPureFunctionCalled("Please implement jacobian_vmult function."));
677  return ret;
678  };
679 
680  vector_norm = [](const VEC &vector) ->double
681  {
682  return vector.l2_norm();
683  };
684 }
685 
686 D2K_NAMESPACE_CLOSE
687 
688 template class deal2lkit::IMEXStepper<BlockVector<double> >;
689 
690 #ifdef DEAL_II_WITH_MPI
691 
692 #ifdef DEAL_II_WITH_TRILINOS
693 template class deal2lkit::IMEXStepper<TrilinosWrappers::MPI::Vector>;
694 template class deal2lkit::IMEXStepper<TrilinosWrappers::MPI::BlockVector>;
695 #endif
696 
697 #ifdef DEAL_II_WITH_PETSC
698 template class deal2lkit::IMEXStepper<PETScWrappers::MPI::Vector>;
699 template class deal2lkit::IMEXStepper<PETScWrappers::MPI::BlockVector>;
700 #endif
701 
702 #endif
703 
704 #endif
double line_search_with_backtracking(const VEC &update, const VEC &prev_sol, const double &alpha, const double &t, VEC &sol, VEC &sol_dot, VEC &residual)
line search algorithm with backtracking.The following sequence of Newton relaxation parameters is tes...
unsigned int max_outer_non_linear_iterations
Maximum number of outer iterations for Newton method.
Definition: imex_stepper.h:148
static::ExceptionBase & ExcPureFunctionCalled()
bool update_jacobian_continuously
Jacobian is updated at each outer iteration and time step.
Definition: imex_stepper.h:154
std::function< int(const VEC &src, VEC &dst)> jacobian_vmult
Compute the matrix-vector product Jacobian times src, and the result is put in dst.
Definition: imex_stepper.h:277
double abs_tol
Absolute error tolerance for non linear iterations.
Definition: imex_stepper.h:136
A parameter acceptor base class.
ConditionalOStream pcout
Output stream.
Definition: imex_stepper.h:162
virtual void declare_parameters(ParameterHandler &prm)
Declare parameters for this class to function properly.
Definition: imex_stepper.cc:91
void set_functions_to_trigger_an_assert()
Set the std::functions above to trigger an assert if they are not implemented.
void do_newton(const double t, const double alpha, const bool update_Jacobian, const VEC &previous_solution, VEC &solution, VEC &solution_dot)
find solution applying the newton method with given
KINSOLInterface< VEC > kinsol
kinsol solver
Definition: imex_stepper.h:111
void set_initial_time(const double &t)
Set initial time equal to t disregarding what is written in the parameter file.
Definition: imex_stepper.cc:85
std::string method
method used for alpha selection
Definition: imex_stepper.h:171
static ParameterHandler prm
Static parameter.
bool verbose
print useful informations
Definition: imex_stepper.h:165
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
bool use_kinsol
use kinsol solver true or false
Definition: imex_stepper.h:159
unsigned int max_inner_non_linear_iterations
Maximum number of inner iterations for Newton method.
Definition: imex_stepper.h:151
std::function< VEC &()> get_lumped_mass_matrix
Return the lumped mass matrix vector.
Definition: imex_stepper.h:270
STL namespace.
unsigned int solve_dae(VEC &solution, VEC &solution_dot)
Evolve.
#define AssertThrow(cond, exc)
void compute_y_dot(const VEC &y, const VEC &prev, const double alpha, VEC &y_dot)
double step_size
Step size.
Definition: imex_stepper.h:116
std::function< void(const double t, const VEC &sol, const VEC &sol_dot, const unsigned int step_number)> output_step
Store solutions to file.
Definition: imex_stepper.h:254
std::function< int(const VEC &rhs, VEC &dst)> solve_jacobian_system
Solve linear system.
Definition: imex_stepper.h:246
std::string _step_size
user defined step_size
Definition: imex_stepper.h:121
static::ExceptionBase & ExcMessage(std::string arg1)
Epetra_Comm * duplicate_communicator(const Epetra_Comm &communicator)
ConditionalOStream pcout
Output stream.
unsigned int output_period
Seconds between each output.
Definition: imex_stepper.h:142
double final_time
Final time.
Definition: imex_stepper.h:133
double rel_tol
Relative error tolerance for non linear iterations.
Definition: imex_stepper.h:139
void compute_consistent_initial_conditions(const double &t, VEC &y, VEC &y_dot)
compute_consistent_initial_conditions
virtual double value(const Point< dim > &p, const unsigned int component=0) const
void compute_previous_solution(const VEC &sol, const VEC &sol_dot, const double &alpha, VEC &prev)
compute previous solution from given
IMEXStepper(std::string &name="")
Constructor for the IMEXStepper class.
Definition: imex_stepper.cc:63
std::function< bool(const double t, VEC &sol, VEC &sol_dot)> solver_should_restart
Evaluate wether the mesh should be refined or not.
Definition: imex_stepper.h:263
std::function< int(const double t, const VEC &y, const VEC &y_dot, VEC &res)> residual
Compute residual.
Definition: imex_stepper.h:233
double get_alpha() const
if initial time is different from final time (i.e., we are solving a time-dep problem and not a stati...
Definition: imex_stepper.cc:73
void initialize(const std::string &vars, const std::vector< std::string > &expressions, const ConstMap &constants, const bool time_dependent=false)
std::function< int(const double t, const VEC &y, const VEC &y_dot, const double alpha)> setup_jacobian
Compute Jacobian.
Definition: imex_stepper.h:241
double evaluate_step_size(const double &t)
evaluate step size at time t according to the expression stored in _step_size
unsigned int n_max_backtracking
i max
Definition: imex_stepper.h:168
void set_functions_to_trigger_an_assert()
This function is executed at construction time to set the std::function above to trigger an assert if...
unsigned int this_mpi_process(const MPI_Comm &mpi_communicator)
MPI_Comm communicator
double initial_time
Initial time for the ode.
Definition: imex_stepper.h:130
void add_parameter(ParameterHandler &prm, T *parameter, const std::string &entry, const std::string &default_value, const Patterns::PatternBase &pattern=Patterns::Anything(), const std::string &documentation=std::string())
Add a parameter the given parameter list.
std::function< shared_ptr< VEC >)> create_new_vector
Return a shared_ptr<VEC>.
Definition: imex_stepper.h:225
std::function< double(const VEC &vector)> vector_norm
Return the norm of vector.
Definition: imex_stepper.h:283
double newton_alpha
Alpha to use in Newton method for the update of the solution.
Definition: imex_stepper.h:145