Create an eigenvalue problem from an initial value problem. Parameters ---------- eigenvalue : Field, optional Eigenvalue field. backgrounds : list of Fields, optional Background fields for linearizing the RHS. Default: the IVP variab
(self, eigenvalue=None, backgrounds=None, perturbations=None, **kw)
| 364 | logger.debug(f" F: {F}") |
| 365 | |
| 366 | def build_EVP(self, eigenvalue=None, backgrounds=None, perturbations=None, **kw): |
| 367 | """ |
| 368 | Create an eigenvalue problem from an initial value problem. |
| 369 | |
| 370 | Parameters |
| 371 | ---------- |
| 372 | eigenvalue : Field, optional |
| 373 | Eigenvalue field. |
| 374 | backgrounds : list of Fields, optional |
| 375 | Background fields for linearizing the RHS. Default: the IVP variables. |
| 376 | perturbations : list of Fields, optional |
| 377 | Perturbation fields for the EVP. Default: copies of IVP variables. |
| 378 | |
| 379 | Notes |
| 380 | ----- |
| 381 | This method converts time-independent IVP equations of the form |
| 382 | M.dt(X) + L.X = F(X) |
| 383 | to EVP equations as |
| 384 | λ*M.X1 + L.X1 - F'(X0).X1 = 0. |
| 385 | If backgrounds (X0) are not specified, the IVP variables (X) are used. |
| 386 | """ |
| 387 | # Create eigenvalue problem for perturbations |
| 388 | variables = self.variables |
| 389 | if eigenvalue is None: |
| 390 | eigenvalue = self.dist.Field(name='λ') |
| 391 | if perturbations is None: |
| 392 | perturbations = [var.copy() for var in variables] |
| 393 | for pert, var in zip(perturbations, variables): |
| 394 | if var.name: |
| 395 | pert.name = 'δ'+var.name |
| 396 | for pert, var in zip(perturbations, variables): |
| 397 | pert.valid_modes[:] = var.valid_modes |
| 398 | EVP = EigenvalueProblem(perturbations, eigenvalue, **kw) |
| 399 | # Convert equations from IVP |
| 400 | for eqn in self.equations: |
| 401 | # Extract IVP expressions |
| 402 | M, L = eqn['LHS'].split(operators.TimeDerivative) |
| 403 | F = eqn['RHS'] |
| 404 | # Convert M@dt(X) to λ*M@Y |
| 405 | if M: |
| 406 | M = M.replace(operators.TimeDerivative, lambda x: eigenvalue*x) |
| 407 | for var, pert in zip(variables, perturbations): |
| 408 | M = M.replace(var, pert) |
| 409 | # Convert L@X to L@Y |
| 410 | if L: |
| 411 | for var, pert in zip(variables, perturbations): |
| 412 | L = L.replace(var, pert) |
| 413 | # Take Frechet differential of F(X) |
| 414 | if F: |
| 415 | if F.has(self.time): |
| 416 | raise UnsupportedEquationError("Cannot convert time-dependent IVP to EVP.") |
| 417 | dF = F.frechet_differential(variables=variables, perturbations=perturbations, backgrounds=backgrounds) |
| 418 | else: |
| 419 | dF = 0 |
| 420 | # Add linearized equation and copy valid modes |
| 421 | evp_eqn = EVP.add_equation((M + L - dF, 0)) |
| 422 | evp_eqn['valid_modes'][:] = eqn['valid_modes'] |
| 423 | # Add backgrounds to EVP namespace |
nothing calls this directly
no test coverage detected