rom: Galerkin: General Info

This page explains the API for using the pressio Galerkin ROMs. After reading this, you should understand what a "pressio Galerkin problem" is, the variants we currently support, and how to use the problem after instantiating it.

If anything is unclear, and/or you have suggestions on how to improve this page, open an issue on github.


Everything starts with creating a problem!

The main entry point to use the pressio Galerkin ROMs is the problem class. You create an instance of one of the supported "Galerkin problems" as:

where <keyword> expresses the variant you want (more below), scheme is a value from the ode.stepscheme enum to set the desired stepping scheme, and the other arguments depend on the variant you choose. If you pass an invalid scheme, you get a runtime error.

We currently offer the following variants:


Explicit Problem API

An explicit Galerkin problem exposes the following API:

class GalerkinProblem

  def __call__(state, time, time_step_size, step_count):

  def fomStateReconstructor():
};

How do I solve an EXPLICIT problem?

The following snippets illustrate some things you can do.

Snippet 1:

scheme    = ode.stepscheme.RungeKutta4
problem   = galerkin.DefaultExplicitProblem(scheme, ...)

time, dt = 0., 0.5
for step in range(10):
  problem(romState, currTime, dt, step)
  time += dt

Snippet 2:

class MyObserver:
  def __call__(self, step, time, state):
    # this is called at every step allowing you to
    # monitor and/or use the Galerkin state
    print(state)

scheme    = ode.stepscheme.RungeKutta4
problem   = galerkin.DefaultExplicitProblem(scheme, ...)
time0, dt, nSteps = 0, 0.5, 2
obs = MyObserver()
ode.advance_n_steps_and_observe(problem, romState, time0, dt, nSteps, obs)




Implicit Problem API

If you create an implicit Galerkin problem, the problem exposes the following API:

class GalerkinProblem

  def fomStateReconstructor():

  def __call__(state, time, time_step_size, step_count, solver):

  def createResidual()
    return # a residual instance

  def createJacobian()
    return # a Jacobian instance

  def residual(state, R)
    # evaluates the residual for the given state

  def jacobian(state, J)
    # evaluates the Jacobian for the given state

};

How do I solve an IMPLICIT problem?

Recall that doing implicit time stepping it is not as simple as explicit. For implicit, in fact, you also need a solver to compute the solution at the next step. In the case of Galerkin, you can use a Newton-Raphson solver, because at eaach step, you are solving a (reduced) system of equations with as many equations as the number of unknowns. More specifically, the system you need to solve has as many equations as the dimensionality of your approximating subspace. See some sample snippets below:

Snippet 1:

class MyLinSolver:
  def solve(self, A,b,x):
    # solve Ax = b using your favority solver, like scipy

class MyObserver:
  def __call__(self, step, time, state):
    print(state)

if __name__ == "__main__":
  # ...
  # assuming romState and other things are already created

  scheme    = ode.stepscheme.BDF1
  problem   = galerkin.DefaultExplicitProblem(scheme, ...)

  lsO  = MyLinSolver()
  nlsO = solvers.create_newton_raphson(problem, romState, lsO)
  nlsO.setUpdatingCriterion(solvers.update.Standard)
  nlsO.setMaxIterations(5)
  nlsO.setStoppingCriterion(solvers.stop.AfterMaxIters)

  # use the call operator directly
  time, dt = 0., 0.5
  for step in range(10):
    problem(romState, currTime, dt, step, nlsO)
    time += dt

  # or use our own advance functions
  obs = MyObserver()
  t0, dt, nSteps = 0., 0.5, 5
  ode.advance_n_steps_and_observe(problem, t0, dt, Steps, obs, nlsO)

Snippet 2:

Here we show the scenario where you want to use your own nonlinear solver.

class MyNonLinSolver:
  def __init__(self, system):
    self.R = system.createResidual()
    self.J = system.createJacobian()

  def solve(self, system, x):
    # here you have the solve problem
    # you can compute the operators as follows:
    system.residual(x, self.R);
    system.jacobian(x, self.J);


class MyObserver:
  def __call__(self, step, time, state):
    print(state)

if __name__ == "__main__":
  # ...
  # assuming romState and other things are already created

  scheme    = ode.stepscheme.BDF1
  problem   = galerkin.DefaultExplicitProblem(scheme, ...)

  customNonLinSolver = MyNonLinSolver(problem)

  # use the call operator directly
  time, dt = 0., 0.5
  for step in range(10):
    problem(romState, currTime, dt, step, customNonLinSolver)
    time += dt

  # or use our own advance functions
  obs = MyObserver()
  t0, dt, nSteps = 0., 0.5, 5
  ode.advance_n_steps_and_observe(problem, t0, dt, Steps, obs, customNonLinSolver)

todo finish