oomph::HopfHandler Class Reference

#include <assembly_handler.h>

+ Inheritance diagram for oomph::HopfHandler:

Public Member Functions

 HopfHandler (Problem *const &problem_pt, double *const &parameter_pt)
 Constructor. More...
 
 HopfHandler (Problem *const &problem_pt, double *const &paramter_pt, const double &omega, const DoubleVector &phi, const DoubleVector &psi)
 
 ~HopfHandler ()
 Destructor return the problem to its original (non-augmented) state. More...
 
unsigned ndof (GeneralisedElement *const &elem_pt)
 Get the number of elemental degrees of freedom. More...
 
unsigned long eqn_number (GeneralisedElement *const &elem_pt, const unsigned &ieqn_local)
 Get the global equation number of the local unknown. More...
 
void get_residuals (GeneralisedElement *const &elem_pt, Vector< double > &residuals)
 Get the residuals. More...
 
void get_jacobian (GeneralisedElement *const &elem_pt, Vector< double > &residuals, DenseMatrix< double > &jacobian)
 
void get_dresiduals_dparameter (GeneralisedElement *const &elem_pt, double *const &parameter_pt, Vector< double > &dres_dparam)
 
void get_djacobian_dparameter (GeneralisedElement *const &elem_pt, double *const &parameter_pt, Vector< double > &dres_dparam, DenseMatrix< double > &djac_dparam)
 
void get_hessian_vector_products (GeneralisedElement *const &elem_pt, Vector< double > const &Y, DenseMatrix< double > const &C, DenseMatrix< double > &product)
 
int bifurcation_type () const
 
doublebifurcation_parameter_pt () const
 
void get_eigenfunction (Vector< DoubleVector > &eigenfunction)
 
const doubleomega () const
 Return the frequency of the bifurcation. More...
 
void solve_standard_system ()
 Set to solve the standard system. More...
 
void solve_complex_system ()
 Set to solve the complex system. More...
 
void solve_full_system ()
 Solve non-block system. More...
 
- Public Member Functions inherited from oomph::AssemblyHandler
 AssemblyHandler ()
 Empty constructor. More...
 
virtual void dof_vector (GeneralisedElement *const &elem_pt, const unsigned &t, Vector< double > &dof)
 Return vector of dofs at time level t in the element elem_pt. More...
 
virtual void dof_pt_vector (GeneralisedElement *const &elem_pt, Vector< double * > &dof_pt)
 Return vector of pointers to dofs in the element elem_pt. More...
 
virtual doublelocal_problem_dof (Problem *const &problem_pt, const unsigned &t, const unsigned &i)
 
virtual void get_all_vectors_and_matrices (GeneralisedElement *const &elem_pt, Vector< Vector< double >> &vec, Vector< DenseMatrix< double >> &matrix)
 
virtual void get_inner_products (GeneralisedElement *const &elem_pt, Vector< std::pair< unsigned, unsigned >> const &history_index, Vector< double > &inner_product)
 
virtual void get_inner_product_vectors (GeneralisedElement *const &elem_pt, Vector< unsigned > const &history_index, Vector< Vector< double >> &inner_product_vector)
 
virtual ~AssemblyHandler ()
 Empty virtual destructor. More...
 

Private Attributes

unsigned Solve_which_system
 
ProblemProblem_pt
 Pointer to the problem. More...
 
doubleParameter_pt
 Pointer to the parameter. More...
 
unsigned Ndof
 
double Omega
 The critical frequency of the bifurcation. More...
 
Vector< doublePhi
 The real part of the null vector. More...
 
Vector< doublePsi
 The imaginary part of the null vector. More...
 
Vector< doubleC
 
Vector< intCount
 

Friends

class BlockHopfLinearSolver
 

Detailed Description

A class that is used to assemble the augmented system that defines a Hopf bifurcation. The "standard" problem must be a function of a global parameter \( \lambda \) and a solution is \( R(u,\lambda) = 0 \), where \( u \) are the unknowns in the problem. A Hopf bifurcation may be specified by the augmented system of size \( 3N+2 \).

\[ R(u,\lambda) = 0, \]

\[ J\phi + \omega M \psi = 0, \]

\[ J\psi - \omega M \phi = 0, \]

\[ c \cdot \phi = 1. \]

\[ c \cdot \psi = 0. \]

In the above \( J \) is the usual Jacobian matrix, \( dR/du \) and \( M \) is the mass matrix that multiplies the time derivative terms. \( \phi + i\psi \) is the (complex) null vector of the complex matrix \( J - i\omega M \), where \( \omega \) is the critical frequency. \( c \) is a constant vector that is used to ensure that the null vector is non-trivial.

Constructor & Destructor Documentation

◆ HopfHandler() [1/2]

oomph::HopfHandler::HopfHandler ( Problem *const &  problem_pt,
double *const &  parameter_pt 
)

Constructor.

Constructor: Initialise the hopf handler, by setting initial guesses for Phi, Psi and calculating Count. If the system changes, a new handler must be constructed.

4437  : Solve_which_system(0), Parameter_pt(parameter_pt), Omega(0.0)
4438  {
4439  // Set the problem pointer
4440  Problem_pt = problem_pt;
4441  // Set the number of non-augmented degrees of freedom
4442  Ndof = problem_pt->ndof();
4443 
4444  // create the linear algebra distribution for this solver
4445  // currently only global (non-distributed) distributions are allowed
4446  LinearAlgebraDistribution* dist_pt =
4447  new LinearAlgebraDistribution(problem_pt->communicator_pt(), Ndof, false);
4448 
4449  // Resize the vectors of additional dofs
4450  Phi.resize(Ndof);
4451  Psi.resize(Ndof);
4452  C.resize(Ndof);
4453  Count.resize(Ndof, 0);
4454 
4455  // Loop over all the elements in the problem
4456  unsigned n_element = problem_pt->mesh_pt()->nelement();
4457  for (unsigned e = 0; e < n_element; e++)
4458  {
4459  GeneralisedElement* elem_pt = problem_pt->mesh_pt()->element_pt(e);
4460  // Loop over the local freedoms in an element
4461  unsigned n_var = elem_pt->ndof();
4462  for (unsigned n = 0; n < n_var; n++)
4463  {
4464  // Increase the associated global equation number counter
4465  ++Count[elem_pt->eqn_number(n)];
4466  }
4467  }
4468 
4469  // Calculate the value Phi by
4470  // solving the system JPhi = dF/dlambda
4471 
4472  // Locally cache the linear solver
4473  LinearSolver* const linear_solver_pt = problem_pt->linear_solver_pt();
4474 
4475  // Save the status before entry to this routine
4476  bool enable_resolve = linear_solver_pt->is_resolve_enabled();
4477 
4478  // We need to do a resolve
4479  linear_solver_pt->enable_resolve();
4480 
4481  // Storage for the solution
4482  DoubleVector x(dist_pt, 0.0);
4483 
4484  // Solve the standard problem, we only want to make sure that
4485  // we factorise the matrix, if it has not been factorised. We shall
4486  // ignore the return value of x.
4487  linear_solver_pt->solve(problem_pt, x);
4488 
4489  // Get the vector dresiduals/dparameter
4490  problem_pt->get_derivative_wrt_global_parameter(parameter_pt, x);
4491 
4492  // Copy rhs vector into local storage so it doesn't get overwritten
4493  // if the linear solver decides to initialise the solution vector, say,
4494  // which it's quite entitled to do!
4495  DoubleVector input_x(x);
4496 
4497  // Now resolve the system with the new RHS and overwrite the solution
4498  linear_solver_pt->resolve(input_x, x);
4499 
4500  // Restore the storage status of the linear solver
4501  if (enable_resolve)
4502  {
4503  linear_solver_pt->enable_resolve();
4504  }
4505  else
4506  {
4507  linear_solver_pt->disable_resolve();
4508  }
4509 
4510  // Normalise the solution x
4511  double length = 0.0;
4512  for (unsigned n = 0; n < Ndof; n++)
4513  {
4514  length += x[n] * x[n];
4515  }
4516  length = sqrt(length);
4517 
4518  // Now add the real part of the null space components to the problem
4519  // unknowns and initialise it all
4520  // This is dumb at the moment ... fix with eigensolver?
4521  for (unsigned n = 0; n < Ndof; n++)
4522  {
4523  problem_pt->Dof_pt.push_back(&Phi[n]);
4524  C[n] = Phi[n] = -x[n] / length;
4525  }
4526 
4527  // Set the imaginary part so that the appropriate residual is
4528  // zero initially (eigensolvers)
4529  for (unsigned n = 0; n < Ndof; n += 2)
4530  {
4531  // Make sure that we are not at the end of an array of odd length
4532  if (n != Ndof - 1)
4533  {
4534  Psi[n] = C[n + 1];
4535  Psi[n + 1] = -C[n];
4536  }
4537  // If it's odd set the final entry to zero
4538  else
4539  {
4540  Psi[n] = 0.0;
4541  }
4542  }
4543 
4544  // Next add the imaginary parts of the null space components to the problem
4545  for (unsigned n = 0; n < Ndof; n++)
4546  {
4547  problem_pt->Dof_pt.push_back(&Psi[n]);
4548  }
4549  // Now add the parameter
4550  problem_pt->Dof_pt.push_back(parameter_pt);
4551  // Finally add the frequency
4552  problem_pt->Dof_pt.push_back(&Omega);
4553 
4554  // rebuild the dof dist
4556  Problem_pt->communicator_pt(), Ndof * 3 + 2, false);
4557  // Remove all previous sparse storage used during Jacobian assembly
4559 
4560  // delete the dist_pt
4561  delete dist_pt;
4562  }
AnnoyingScalar sqrt(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:134
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
std::vector< double > DoubleVector
loads clump configuration
Definition: ClumpInput.h:26
Array< double, 1, 3 > e(1./3., 0.5, 2.)
Vector< double > Phi
The real part of the null vector.
Definition: assembly_handler.h:1074
Vector< int > Count
Definition: assembly_handler.h:1086
double Omega
The critical frequency of the bifurcation.
Definition: assembly_handler.h:1071
Vector< double > Psi
The imaginary part of the null vector.
Definition: assembly_handler.h:1077
unsigned Solve_which_system
Definition: assembly_handler.h:1058
Problem * Problem_pt
Pointer to the problem.
Definition: assembly_handler.h:1061
unsigned Ndof
Definition: assembly_handler.h:1068
double * Parameter_pt
Pointer to the parameter.
Definition: assembly_handler.h:1064
void build(const OomphCommunicator *const comm_pt, const unsigned &first_row, const unsigned &nrow_local, const unsigned &nrow=0)
Definition: linear_algebra_distribution.cc:35
Definition: matrices.h:74
Vector< Vector< unsigned > > Sparse_assemble_with_arrays_previous_allocation
Definition: problem.h:667
OomphCommunicator * communicator_pt()
access function to the oomph-lib communicator
Definition: problem.h:1246
LinearAlgebraDistribution * Dof_distribution_pt
Definition: problem.h:460
list x
Definition: plotDoE.py:28

References oomph::LinearAlgebraDistribution::build(), oomph::Problem::communicator_pt(), Count, oomph::LinearSolver::disable_resolve(), oomph::Problem::Dof_distribution_pt, oomph::Problem::Dof_pt, e(), oomph::Mesh::element_pt(), oomph::LinearSolver::enable_resolve(), oomph::GeneralisedElement::eqn_number(), oomph::Problem::get_derivative_wrt_global_parameter(), oomph::LinearSolver::is_resolve_enabled(), oomph::Problem::linear_solver_pt(), oomph::Problem::mesh_pt(), n, Ndof, oomph::GeneralisedElement::ndof(), oomph::Problem::ndof(), oomph::Mesh::nelement(), Omega, Phi, Problem_pt, Psi, oomph::LinearSolver::resolve(), oomph::LinearSolver::solve(), oomph::Problem::Sparse_assemble_with_arrays_previous_allocation, sqrt(), and plotDoE::x.

◆ HopfHandler() [2/2]

oomph::HopfHandler::HopfHandler ( Problem *const &  problem_pt,
double *const &  parameter_pt,
const double omega,
const DoubleVector phi,
const DoubleVector psi 
)

Constructor with initial guesses for the frequency and null vectors, such as might be provided by an eigensolver

Constructor: Initialise the hopf handler, by setting initial guesses for Phi, Psi, Omega and calculating Count. If the system changes, a new handler must be constructed.

4574  : Solve_which_system(0), Parameter_pt(parameter_pt), Omega(omega)
4575  {
4576  // Set the problem pointer
4577  Problem_pt = problem_pt;
4578  // Set the number of non-augmented degrees of freedom
4579  Ndof = problem_pt->ndof();
4580 
4581  // Resize the vectors of additional dofs
4582  Phi.resize(Ndof);
4583  Psi.resize(Ndof);
4584  C.resize(Ndof);
4585  Count.resize(Ndof, 0);
4586 
4587  // Loop over all the elements in the problem
4588  unsigned n_element = problem_pt->mesh_pt()->nelement();
4589  for (unsigned e = 0; e < n_element; e++)
4590  {
4591  GeneralisedElement* elem_pt = problem_pt->mesh_pt()->element_pt(e);
4592  // Loop over the local freedoms in an element
4593  unsigned n_var = elem_pt->ndof();
4594  for (unsigned n = 0; n < n_var; n++)
4595  {
4596  // Increase the associated global equation number counter
4597  ++Count[elem_pt->eqn_number(n)];
4598  }
4599  }
4600 
4601  // Normalise the guess for phi
4602  double length = 0.0;
4603  for (unsigned n = 0; n < Ndof; n++)
4604  {
4605  length += phi[n] * phi[n];
4606  }
4607  length = sqrt(length);
4608 
4609  // Now add the real part of the null space components to the problem
4610  // unknowns and initialise it all
4611  for (unsigned n = 0; n < Ndof; n++)
4612  {
4613  problem_pt->Dof_pt.push_back(&Phi[n]);
4614  C[n] = Phi[n] = phi[n] / length;
4615  Psi[n] = psi[n] / length;
4616  }
4617 
4618  // Next add the imaginary parts of the null space components to the problem
4619  for (unsigned n = 0; n < Ndof; n++)
4620  {
4621  problem_pt->Dof_pt.push_back(&Psi[n]);
4622  }
4623 
4624  // Now add the parameter
4625  problem_pt->Dof_pt.push_back(parameter_pt);
4626  // Finally add the frequency
4627  problem_pt->Dof_pt.push_back(&Omega);
4628 
4629  // rebuild the Dof_distribution_pt
4631  Problem_pt->communicator_pt(), Ndof * 3 + 2, false);
4632  // Remove all previous sparse storage used during Jacobian assembly
4634  }
const double & omega() const
Return the frequency of the bifurcation.
Definition: assembly_handler.h:1160

References oomph::LinearAlgebraDistribution::build(), oomph::Problem::communicator_pt(), Count, oomph::Problem::Dof_distribution_pt, oomph::Problem::Dof_pt, e(), oomph::Mesh::element_pt(), oomph::GeneralisedElement::eqn_number(), oomph::Problem::mesh_pt(), n, Ndof, oomph::GeneralisedElement::ndof(), oomph::Problem::ndof(), oomph::Mesh::nelement(), Omega, Phi, Problem_pt, Psi, oomph::Problem::Sparse_assemble_with_arrays_previous_allocation, and sqrt().

◆ ~HopfHandler()

oomph::HopfHandler::~HopfHandler ( )

Destructor return the problem to its original (non-augmented) state.

Destructor, return the problem to its original state, before the augmented system was added

4641  {
4642  // If we are using the block solver reset the problem's linear solver
4643  // to the original one
4644  BlockHopfLinearSolver* block_hopf_solver_pt =
4646  if (block_hopf_solver_pt)
4647  {
4648  // Reset the problem's linear solver
4649  Problem_pt->linear_solver_pt() = block_hopf_solver_pt->linear_solver_pt();
4650  // Delete the block solver
4651  delete block_hopf_solver_pt;
4652  }
4653  // Now return the problem to its original size
4654  Problem_pt->Dof_pt.resize(Ndof);
4656  Problem_pt->communicator_pt(), Ndof, false);
4657  // Remove all previous sparse storage used during Jacobian assembly
4659  }
friend class BlockHopfLinearSolver
Definition: assembly_handler.h:1051
LinearSolver *& linear_solver_pt()
Return a pointer to the linear solver object.
Definition: problem.h:1466
Vector< double * > Dof_pt
Vector of pointers to dofs.
Definition: problem.h:554

References oomph::LinearAlgebraDistribution::build(), oomph::Problem::communicator_pt(), oomph::Problem::Dof_distribution_pt, oomph::Problem::Dof_pt, oomph::Problem::linear_solver_pt(), oomph::BlockHopfLinearSolver::linear_solver_pt(), Ndof, Problem_pt, and oomph::Problem::Sparse_assemble_with_arrays_previous_allocation.

Member Function Documentation

◆ bifurcation_parameter_pt()

double* oomph::HopfHandler::bifurcation_parameter_pt ( ) const
inlinevirtual

Return a pointer to the bifurcation parameter in bifurcation tracking problems

Reimplemented from oomph::AssemblyHandler.

1151  {
1152  return Parameter_pt;
1153  }

References Parameter_pt.

◆ bifurcation_type()

int oomph::HopfHandler::bifurcation_type ( ) const
inlinevirtual

Indicate that we are tracking a Hopf bifurcation by returning 3

Reimplemented from oomph::AssemblyHandler.

1144  {
1145  return 3;
1146  }

◆ eqn_number()

unsigned long oomph::HopfHandler::eqn_number ( GeneralisedElement *const &  elem_pt,
const unsigned ieqn_local 
)
virtual

Get the global equation number of the local unknown.

Reimplemented from oomph::AssemblyHandler.

4695  {
4696  // Get the raw value
4697  unsigned raw_ndof = elem_pt->ndof();
4698  unsigned long global_eqn;
4699  if (ieqn_local < raw_ndof)
4700  {
4701  global_eqn = elem_pt->eqn_number(ieqn_local);
4702  }
4703  else if (ieqn_local < 2 * raw_ndof)
4704  {
4705  global_eqn = Ndof + elem_pt->eqn_number(ieqn_local - raw_ndof);
4706  }
4707  else if (ieqn_local < 3 * raw_ndof)
4708  {
4709  global_eqn = 2 * Ndof + elem_pt->eqn_number(ieqn_local - 2 * raw_ndof);
4710  }
4711  else if (ieqn_local == 3 * raw_ndof)
4712  {
4713  global_eqn = 3 * Ndof;
4714  }
4715  else
4716  {
4717  global_eqn = 3 * Ndof + 1;
4718  }
4719  return global_eqn;
4720  }

References oomph::GeneralisedElement::eqn_number(), Ndof, and oomph::GeneralisedElement::ndof().

Referenced by get_jacobian().

◆ get_djacobian_dparameter()

void oomph::HopfHandler::get_djacobian_dparameter ( GeneralisedElement *const &  elem_pt,
double *const &  parameter_pt,
Vector< double > &  dres_dparam,
DenseMatrix< double > &  djac_dparam 
)
virtual

Overload the derivative of the residuals and jacobian with respect to a parameter so that it breaks

Overload the derivative of the residuals and jacobian with respect to a parameter so that it breaks because it should not be required

Reimplemented from oomph::AssemblyHandler.

4995  {
4996  std::ostringstream error_stream;
4997  error_stream
4998  << "This function has not been implemented because it is not required\n";
4999  error_stream << "in standard problems.\n";
5000  error_stream
5001  << "If you find that you need it, you will have to implement it!\n\n";
5002 
5003  throw OomphLibError(
5004  error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5005  }
#define OOMPH_EXCEPTION_LOCATION
Definition: oomph_definitions.h:61
#define OOMPH_CURRENT_FUNCTION
Definition: oomph_definitions.h:86

References OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ get_dresiduals_dparameter()

void oomph::HopfHandler::get_dresiduals_dparameter ( GeneralisedElement *const &  elem_pt,
double *const &  parameter_pt,
Vector< double > &  dres_dparam 
)
virtual

Overload the derivatives of the residuals with respect to a parameter to apply to the augmented system

Get the derivatives of the augmented residuals with respect to a parameter

Reimplemented from oomph::AssemblyHandler.

4941  {
4942  // Should only call get residuals for the full system
4943  if (Solve_which_system == 0)
4944  {
4945  // Need to get raw residuals and jacobian
4946  unsigned raw_ndof = elem_pt->ndof();
4947 
4948  DenseMatrix<double> djac_dparam(raw_ndof), dM_dparam(raw_ndof);
4949  // Get the basic residuals, jacobian and mass matrix
4950  elem_pt->get_djacobian_and_dmass_matrix_dparameter(
4951  parameter_pt, dres_dparam, djac_dparam, dM_dparam);
4952 
4953  // Initialise the pen-ultimate residual, which does not
4954  // depend on the parameter
4955  dres_dparam[3 * raw_ndof] = 0.0;
4956  dres_dparam[3 * raw_ndof + 1] = 0.0;
4957 
4958  // Now multiply to fill in the residuals
4959  for (unsigned i = 0; i < raw_ndof; i++)
4960  {
4961  dres_dparam[raw_ndof + i] = 0.0;
4962  dres_dparam[2 * raw_ndof + i] = 0.0;
4963  for (unsigned j = 0; j < raw_ndof; j++)
4964  {
4965  unsigned global_unknown = elem_pt->eqn_number(j);
4966  // Real part
4967  dres_dparam[raw_ndof + i] +=
4968  djac_dparam(i, j) * Phi[global_unknown] +
4969  Omega * dM_dparam(i, j) * Psi[global_unknown];
4970  // Imaginary part
4971  dres_dparam[2 * raw_ndof + i] +=
4972  djac_dparam(i, j) * Psi[global_unknown] -
4973  Omega * dM_dparam(i, j) * Phi[global_unknown];
4974  }
4975  }
4976  }
4977  else
4978  {
4979  throw OomphLibError("Solve_which_system can only be 0",
4982  }
4983  }
int i
Definition: BiCGSTAB_step_by_step.cpp:9
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2

References oomph::GeneralisedElement::eqn_number(), oomph::GeneralisedElement::get_djacobian_and_dmass_matrix_dparameter(), i, j, oomph::GeneralisedElement::ndof(), Omega, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, Phi, Psi, and Solve_which_system.

◆ get_eigenfunction()

void oomph::HopfHandler::get_eigenfunction ( Vector< DoubleVector > &  eigenfunction)
virtual

Return the eigenfunction(s) associated with the bifurcation that has been detected in bifurcation tracking problems

Reimplemented from oomph::AssemblyHandler.

5035  {
5036  // There is a real and imaginary part of the null vector
5037  eigenfunction.resize(2);
5038  LinearAlgebraDistribution dist(Problem_pt->communicator_pt(), Ndof, false);
5039  // Rebuild the vector
5040  eigenfunction[0].build(&dist, 0.0);
5041  eigenfunction[1].build(&dist, 0.0);
5042  // Set the value to be the null vector
5043  for (unsigned n = 0; n < Ndof; n++)
5044  {
5045  eigenfunction[0][n] = Phi[n];
5046  eigenfunction[1][n] = Psi[n];
5047  }
5048  }

References oomph::Problem::communicator_pt(), n, Ndof, Phi, Problem_pt, and Psi.

◆ get_hessian_vector_products()

void oomph::HopfHandler::get_hessian_vector_products ( GeneralisedElement *const &  elem_pt,
Vector< double > const &  Y,
DenseMatrix< double > const &  C,
DenseMatrix< double > &  product 
)
virtual

Overload the hessian vector product function so that it breaks

Overload the hessian vector product function so that it breaks because it should not be required

Reimplemented from oomph::AssemblyHandler.

5017  {
5018  std::ostringstream error_stream;
5019  error_stream
5020  << "This function has not been implemented because it is not required\n";
5021  error_stream << "in standard problems.\n";
5022  error_stream
5023  << "If you find that you need it, you will have to implement it!\n\n";
5024 
5025  throw OomphLibError(
5026  error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
5027  }

References OOMPH_CURRENT_FUNCTION, and OOMPH_EXCEPTION_LOCATION.

◆ get_jacobian()

void oomph::HopfHandler::get_jacobian ( GeneralisedElement *const &  elem_pt,
Vector< double > &  residuals,
DenseMatrix< double > &  jacobian 
)
virtual

Calculate the elemental Jacobian matrix "d equation / d variable".

Reimplemented from oomph::AssemblyHandler.

4785  {
4786  // The standard case
4787  if (Solve_which_system == 0)
4788  {
4789  unsigned augmented_ndof = ndof(elem_pt);
4790  unsigned raw_ndof = elem_pt->ndof();
4791 
4792  // Get the basic residuals and jacobian
4793  DenseMatrix<double> M(raw_ndof);
4794  elem_pt->get_jacobian_and_mass_matrix(residuals, jacobian, M);
4795  // Now fill in the actual residuals
4796  get_residuals(elem_pt, residuals);
4797 
4798  // Now the jacobian appears in other entries
4799  for (unsigned n = 0; n < raw_ndof; ++n)
4800  {
4801  for (unsigned m = 0; m < raw_ndof; ++m)
4802  {
4803  jacobian(raw_ndof + n, raw_ndof + m) = jacobian(n, m);
4804  jacobian(raw_ndof + n, 2 * raw_ndof + m) = Omega * M(n, m);
4805  jacobian(2 * raw_ndof + n, 2 * raw_ndof + m) = jacobian(n, m);
4806  jacobian(2 * raw_ndof + n, raw_ndof + m) = -Omega * M(n, m);
4807  unsigned global_eqn = elem_pt->eqn_number(m);
4808  jacobian(raw_ndof + n, 3 * raw_ndof + 1) += M(n, m) * Psi[global_eqn];
4809  jacobian(2 * raw_ndof + n, 3 * raw_ndof + 1) -=
4810  M(n, m) * Phi[global_eqn];
4811  }
4812 
4813  unsigned local_eqn = elem_pt->eqn_number(n);
4814  jacobian(3 * raw_ndof, raw_ndof + n) = C[local_eqn] / Count[local_eqn];
4815  jacobian(3 * raw_ndof + 1, 2 * raw_ndof + n) =
4816  C[local_eqn] / Count[local_eqn];
4817  }
4818 
4819  const double FD_step = 1.0e-8;
4820 
4821  Vector<double> newres_p(augmented_ndof), newres_m(augmented_ndof);
4822 
4823  // Loop over the dofs
4824  for (unsigned n = 0; n < raw_ndof; n++)
4825  {
4826  // Just do the x's
4827  unsigned long global_eqn = eqn_number(elem_pt, n);
4828  double* unknown_pt = Problem_pt->Dof_pt[global_eqn];
4829  double init = *unknown_pt;
4830  *unknown_pt += FD_step;
4831 
4832  // Get the new residuals
4833  get_residuals(elem_pt, newres_p);
4834 
4835  // Reset
4836  *unknown_pt = init;
4837 
4838  // Subtract
4839  *unknown_pt -= FD_step;
4840  get_residuals(elem_pt, newres_m);
4841 
4842  for (unsigned m = 0; m < raw_ndof; m++)
4843  {
4844  jacobian(raw_ndof + m, n) =
4845  (newres_p[raw_ndof + m] - residuals[raw_ndof + m]) / (FD_step);
4846  jacobian(2 * raw_ndof + m, n) =
4847  (newres_p[2 * raw_ndof + m] - residuals[2 * raw_ndof + m]) /
4848  (FD_step);
4849  }
4850  // Reset the unknown
4851  *unknown_pt = init;
4852  }
4853 
4854  {
4855  // Now do the global parameter
4856  double* unknown_pt = Problem_pt->Dof_pt[3 * Ndof];
4857  double init = *unknown_pt;
4858  *unknown_pt += FD_step;
4859 
4861  // Get the new residuals
4862  get_residuals(elem_pt, newres_p);
4863 
4864  // Reset
4865  *unknown_pt = init;
4866 
4867  // Subtract
4868  *unknown_pt -= FD_step;
4869  get_residuals(elem_pt, newres_m);
4870 
4871  for (unsigned m = 0; m < augmented_ndof - 2; m++)
4872  {
4873  jacobian(m, 3 * raw_ndof) = (newres_p[m] - residuals[m]) / FD_step;
4874  }
4875  // Reset the unknown
4876  *unknown_pt = init;
4878  }
4879  } // End of standard case
4880  // Normal case
4881  else if (Solve_which_system == 1)
4882  {
4883  // Just get the normal jacobian and residuals
4884  elem_pt->get_jacobian(residuals, jacobian);
4885  }
4886  // Otherwise the augmented complex case
4887  else if (Solve_which_system == 2)
4888  {
4889  unsigned raw_ndof = elem_pt->ndof();
4890 
4891  // Get the basic residuals and jacobian
4892  DenseMatrix<double> M(raw_ndof);
4893  elem_pt->get_jacobian_and_mass_matrix(residuals, jacobian, M);
4894 
4895  // We now need to fill in the other blocks
4896  for (unsigned n = 0; n < raw_ndof; n++)
4897  {
4898  for (unsigned m = 0; m < raw_ndof; m++)
4899  {
4900  jacobian(n, raw_ndof + m) = Omega * M(n, m);
4901  jacobian(raw_ndof + n, m) = -Omega * M(n, m);
4902  jacobian(raw_ndof + n, raw_ndof + m) = jacobian(n, m);
4903  }
4904  }
4905 
4906  // Now overwrite to fill in the residuals
4907  // The decision take is to solve for the mass matrix multiplied
4908  // terms in the residuals because they require no additional
4909  // information to assemble.
4910  for (unsigned n = 0; n < raw_ndof; n++)
4911  {
4912  residuals[n] = 0.0;
4913  residuals[raw_ndof + n] = 0.0;
4914  for (unsigned m = 0; m < raw_ndof; m++)
4915  {
4916  unsigned global_unknown = elem_pt->eqn_number(m);
4917  // Real part
4918  residuals[n] += M(n, m) * Psi[global_unknown];
4919  // Imaginary part
4920  residuals[raw_ndof + n] -= M(n, m) * Phi[global_unknown];
4921  }
4922  }
4923  } // End of complex augmented case
4924  else
4925  {
4926  throw OomphLibError("Solve_which_system can only be 0,1 or 2",
4929  }
4930  }
Matrix< RealScalar, Dynamic, Dynamic > M
Definition: bench_gemm.cpp:50
void get_residuals(GeneralisedElement *const &elem_pt, Vector< double > &residuals)
Get the residuals.
Definition: assembly_handler.cc:4725
unsigned ndof(GeneralisedElement *const &elem_pt)
Get the number of elemental degrees of freedom.
Definition: assembly_handler.cc:4665
unsigned long eqn_number(GeneralisedElement *const &elem_pt, const unsigned &ieqn_local)
Get the global equation number of the local unknown.
Definition: assembly_handler.cc:4693
virtual void actions_after_change_in_bifurcation_parameter()
Definition: problem.h:1151
int * m
Definition: level2_cplx_impl.h:294
double FD_step
FD step.
Definition: black_box_newton_solver.cc:54
Definition: TutorialInplaceLU.cpp:2

References oomph::Problem::actions_after_change_in_bifurcation_parameter(), Count, oomph::Problem::Dof_pt, oomph::GeneralisedElement::eqn_number(), eqn_number(), oomph::BlackBoxFDNewtonSolver::FD_step, oomph::GeneralisedElement::get_jacobian(), oomph::GeneralisedElement::get_jacobian_and_mass_matrix(), get_residuals(), m, n, Ndof, oomph::GeneralisedElement::ndof(), ndof(), Omega, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, Phi, Problem_pt, Psi, and Solve_which_system.

◆ get_residuals()

void oomph::HopfHandler::get_residuals ( GeneralisedElement *const &  elem_pt,
Vector< double > &  residuals 
)
virtual

Get the residuals.

Reimplemented from oomph::AssemblyHandler.

4727  {
4728  // Should only call get residuals for the full system
4729  if (Solve_which_system == 0)
4730  {
4731  // Need to get raw residuals and jacobian
4732  unsigned raw_ndof = elem_pt->ndof();
4733 
4734  DenseMatrix<double> jacobian(raw_ndof), M(raw_ndof);
4735  // Get the basic residuals, jacobian and mass matrix
4736  elem_pt->get_jacobian_and_mass_matrix(residuals, jacobian, M);
4737 
4738  // Initialise the pen-ultimate residual
4739  residuals[3 * raw_ndof] =
4740  -1.0 / (double)(Problem_pt->mesh_pt()->nelement());
4741  residuals[3 * raw_ndof + 1] = 0.0;
4742 
4743  // Now multiply to fill in the residuals
4744  for (unsigned i = 0; i < raw_ndof; i++)
4745  {
4746  residuals[raw_ndof + i] = 0.0;
4747  residuals[2 * raw_ndof + i] = 0.0;
4748  for (unsigned j = 0; j < raw_ndof; j++)
4749  {
4750  unsigned global_unknown = elem_pt->eqn_number(j);
4751  // Real part
4752  residuals[raw_ndof + i] += jacobian(i, j) * Phi[global_unknown] +
4753  Omega * M(i, j) * Psi[global_unknown];
4754  // Imaginary part
4755  residuals[2 * raw_ndof + i] += jacobian(i, j) * Psi[global_unknown] -
4756  Omega * M(i, j) * Phi[global_unknown];
4757  }
4758  // Get the global equation number
4759  unsigned global_eqn = elem_pt->eqn_number(i);
4760 
4761  // Real part
4762  residuals[3 * raw_ndof] +=
4763  (Phi[global_eqn] * C[global_eqn]) / Count[global_eqn];
4764  // Imaginary part
4765  residuals[3 * raw_ndof + 1] +=
4766  (Psi[global_eqn] * C[global_eqn]) / Count[global_eqn];
4767  }
4768  }
4769  else
4770  {
4771  throw OomphLibError("Solve_which_system can only be 0",
4774  }
4775  }
unsigned long nelement() const
Return number of elements in the mesh.
Definition: mesh.h:590
Mesh *& mesh_pt()
Return a pointer to the global mesh.
Definition: problem.h:1280

References Count, oomph::GeneralisedElement::eqn_number(), oomph::GeneralisedElement::get_jacobian_and_mass_matrix(), i, j, oomph::Problem::mesh_pt(), oomph::GeneralisedElement::ndof(), oomph::Mesh::nelement(), Omega, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, Phi, Problem_pt, Psi, and Solve_which_system.

Referenced by get_jacobian().

◆ ndof()

unsigned oomph::HopfHandler::ndof ( GeneralisedElement *const &  elem_pt)
virtual

Get the number of elemental degrees of freedom.

Reimplemented from oomph::AssemblyHandler.

4666  {
4667  unsigned raw_ndof = elem_pt->ndof();
4668  switch (Solve_which_system)
4669  {
4670  // Full augmented system
4671  case 0:
4672  return (3 * raw_ndof + 2);
4673  break;
4674  // Standard non-augmented system
4675  case 1:
4676  return raw_ndof;
4677  break;
4678  // Complex system
4679  case 2:
4680  return (2 * raw_ndof);
4681  break;
4682 
4683  default:
4684  throw OomphLibError("Solve_which_system can only be 0,1 or 2",
4687  }
4688  }

References oomph::GeneralisedElement::ndof(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION, and Solve_which_system.

Referenced by get_jacobian().

◆ omega()

const double& oomph::HopfHandler::omega ( ) const
inline

Return the frequency of the bifurcation.

1161  {
1162  return Omega;
1163  }

References Omega.

◆ solve_complex_system()

void oomph::HopfHandler::solve_complex_system ( )

Set to solve the complex system.

Set to solve the complex (jacobian and mass matrix) system.

5072  {
5073  // If we were not solving the complex system resize the unknowns
5074  // accordingly
5075  if (Solve_which_system != 2)
5076  {
5077  Solve_which_system = 2;
5078  // Resize to the first Ndofs (will work whichever system we were
5079  // solving before)
5080  Problem_pt->Dof_pt.resize(Ndof);
5081  // Add the first (real) part of the eigenfunction back into the problem
5082  for (unsigned n = 0; n < Ndof; n++)
5083  {
5084  Problem_pt->Dof_pt.push_back(&Phi[n]);
5085  }
5087  Problem_pt->communicator_pt(), Ndof * 2, false);
5088  // Remove all previous sparse storage used during Jacobian assembly
5090  }
5091  }

References oomph::LinearAlgebraDistribution::build(), oomph::Problem::communicator_pt(), oomph::Problem::Dof_distribution_pt, oomph::Problem::Dof_pt, n, Ndof, Phi, Problem_pt, Solve_which_system, and oomph::Problem::Sparse_assemble_with_arrays_previous_allocation.

Referenced by oomph::BlockHopfLinearSolver::solve(), and oomph::BlockHopfLinearSolver::solve_for_two_rhs().

◆ solve_full_system()

void oomph::HopfHandler::solve_full_system ( )

Solve non-block system.

Set to Solve full system system.

5098  {
5099  // If we are starting from another system
5100  if (Solve_which_system)
5101  {
5102  Solve_which_system = 0;
5103 
5104  // Resize to the first Ndofs (will work whichever system we were
5105  // solving before)
5106  Problem_pt->Dof_pt.resize(Ndof);
5107  // Add the additional unknowns back into the problem
5108  for (unsigned n = 0; n < Ndof; n++)
5109  {
5110  Problem_pt->Dof_pt.push_back(&Phi[n]);
5111  }
5112  for (unsigned n = 0; n < Ndof; n++)
5113  {
5114  Problem_pt->Dof_pt.push_back(&Psi[n]);
5115  }
5116  // Now add the parameter
5117  Problem_pt->Dof_pt.push_back(Parameter_pt);
5118  // Finally add the frequency
5119  Problem_pt->Dof_pt.push_back(&Omega);
5120 
5121  //
5123  Problem_pt->communicator_pt(), 3 * Ndof + 2, false);
5124  // Remove all previous sparse storage used during Jacobian assembly
5126  }
5127  }

References oomph::LinearAlgebraDistribution::build(), oomph::Problem::communicator_pt(), oomph::Problem::Dof_distribution_pt, oomph::Problem::Dof_pt, n, Ndof, Omega, Parameter_pt, Phi, Problem_pt, Psi, Solve_which_system, and oomph::Problem::Sparse_assemble_with_arrays_previous_allocation.

Referenced by oomph::BlockHopfLinearSolver::solve(), and oomph::BlockHopfLinearSolver::solve_for_two_rhs().

◆ solve_standard_system()

void oomph::HopfHandler::solve_standard_system ( )

Set to solve the standard system.

Set to solve the standard (underlying jacobian) system.

5055  {
5056  if (Solve_which_system != 1)
5057  {
5058  Solve_which_system = 1;
5059  // Restrict the problem to the standard variables only
5060  Problem_pt->Dof_pt.resize(Ndof);
5062  Problem_pt->communicator_pt(), Ndof, false);
5063  // Remove all previous sparse storage used during Jacobian assembly
5065  }
5066  }

References oomph::LinearAlgebraDistribution::build(), oomph::Problem::communicator_pt(), oomph::Problem::Dof_distribution_pt, oomph::Problem::Dof_pt, Ndof, Problem_pt, Solve_which_system, and oomph::Problem::Sparse_assemble_with_arrays_previous_allocation.

Referenced by oomph::BlockHopfLinearSolver::solve(), and oomph::BlockHopfLinearSolver::solve_for_two_rhs().

Friends And Related Function Documentation

◆ BlockHopfLinearSolver

friend class BlockHopfLinearSolver
friend

Member Data Documentation

◆ C

Vector<double> oomph::HopfHandler::C
private

A constant vector used to ensure that the null vector is not trivial

Referenced by oomph::BlockHopfLinearSolver::solve(), and oomph::BlockHopfLinearSolver::solve_for_two_rhs().

◆ Count

Vector<int> oomph::HopfHandler::Count
private

A vector that is used to determine how many elements contribute to a particular equation. It is used to ensure that the global system is correctly formulated.

Referenced by get_jacobian(), get_residuals(), and HopfHandler().

◆ Ndof

unsigned oomph::HopfHandler::Ndof
private

Store the number of degrees of freedom in the non-augmented problem

Referenced by eqn_number(), get_eigenfunction(), get_jacobian(), HopfHandler(), solve_complex_system(), solve_full_system(), solve_standard_system(), and ~HopfHandler().

◆ Omega

◆ Parameter_pt

double* oomph::HopfHandler::Parameter_pt
private

Pointer to the parameter.

Referenced by bifurcation_parameter_pt(), and solve_full_system().

◆ Phi

◆ Problem_pt

Problem* oomph::HopfHandler::Problem_pt
private

◆ Psi

◆ Solve_which_system

unsigned oomph::HopfHandler::Solve_which_system
private

Integer flag to indicate which system should be assembled. There are three possibilities. The full augmented system (0), the non-augmented jacobian system (1), and complex system (2), where the matrix is a combination of the jacobian and mass matrices.

Referenced by get_dresiduals_dparameter(), get_jacobian(), get_residuals(), ndof(), solve_complex_system(), solve_full_system(), and solve_standard_system().


The documentation for this class was generated from the following files: