Material Inversion Example: Nonlinear Diffusion Reaction

The following illustrates the capability of the MOOSE Optimization module by applying the module to a nonlinear, material-inversion optimization problem:

where is the number of measurement locations and and are the simulated and measured state variables (e.g. temperature and displacement fields), respectively, at location . The parameter being optimized is the spatially dependent reaction rate (). The domain is meshed using a grid of quadrilateral elements, and time is discretized using implict Euler over ten uniform time steps. The measurement data is generated by evaluating the PDE with and sampling the resulting solution at 22 locations—shown in top left plot of Figure 1—at every time step, resulting in .

The reaction rate is parameterized using the mesh shown in the top right plot of Figure 1, where parameter values set the reaction rate at the 19 nodes and linearly interpolating between them. The initial condition for the optimization sets , emulating a diffusion-only system. TAO's bounded quasi-Newton line search (TAOBQNLS) was the chosen optimization algorithm. The following listings show the optimization input driving the optimization solve and the physics sub-application input used to calculate the forward and adjoint simulations.

Listing 1: Optimization input

[Optimization<<<{"href": "../../../syntax/Optimization/index.html"}>>>]
[]

[OptimizationReporter<<<{"href": "../../../syntax/OptimizationReporter/index.html"}>>>]
  type = ParameterMeshOptimization
  objective_name = objective_value
  parameter_names = 'reaction_rate'
  parameter_meshes = 'parameter_mesh_out.e'

  initial_condition = 0
  lower_bounds = 0

[]

[Reporters<<<{"href": "../../../syntax/Reporters/index.html"}>>>]
  [main]
    type = OptimizationData<<<{"description": "Reporter to hold measurement and simulation data for optimization problems", "href": "../../../source/reporters/OptimizationData.html"}>>>
    measurement_file<<<{"description": "CSV file with measurement value and coordinates (value, x, y, z)."}>>> = forward_exact_csv_sample_0011.csv
    file_xcoord<<<{"description": "x coordinate column name from measurement_file csv being read in."}>>> = measurement_xcoord
    file_ycoord<<<{"description": "y coordinate column name from csv file being read in."}>>> = measurement_ycoord
    file_zcoord<<<{"description": "z coordinate column name from csv file being read in."}>>> = measurement_zcoord
    file_time<<<{"description": "time column name from csv file being read in."}>>> = measurement_time
    file_value<<<{"description": "measurement value column name from csv file being read in."}>>> = simulation_values
  []
[]

[MultiApps<<<{"href": "../../../syntax/MultiApps/index.html"}>>>]
  [forward]
    type = FullSolveMultiApp<<<{"description": "Performs a complete simulation during each execution.", "href": "../../../source/multiapps/FullSolveMultiApp.html"}>>>
    input_files<<<{"description": "The input file for each App.  If this parameter only contains one input file it will be used for all of the Apps.  When using 'positions_from_file' it is also admissable to provide one input_file per file."}>>> = forward_and_adjoint.i
    execute_on<<<{"description": "The list of flag(s) indicating when this object should be executed. For a description of each flag, see https://mooseframework.inl.gov/source/interfaces/SetupInterface.html."}>>> = FORWARD
  []
[]

[Transfers<<<{"href": "../../../syntax/Transfers/index.html"}>>>]
  [to_forward]
    type = MultiAppReporterTransfer<<<{"description": "Transfers reporter data between two applications.", "href": "../../../source/transfers/MultiAppReporterTransfer.html"}>>>
    to_multi_app<<<{"description": "The name of the MultiApp to transfer the data to"}>>> = forward
    from_reporters<<<{"description": "List of the reporter names (object_name/value_name) to transfer the value from."}>>> = 'main/measurement_xcoord
                      main/measurement_ycoord
                      main/measurement_zcoord
                      main/measurement_time
                      main/measurement_values
                      OptimizationReporter/reaction_rate'
    to_reporters<<<{"description": "List of the reporter names (object_name/value_name) to transfer the value to."}>>> = 'data/measurement_xcoord
                    data/measurement_ycoord
                    data/measurement_zcoord
                    data/measurement_time
                    data/measurement_values
                    params/reaction_rate'
  []
  [from_forward]
    type = MultiAppReporterTransfer<<<{"description": "Transfers reporter data between two applications.", "href": "../../../source/transfers/MultiAppReporterTransfer.html"}>>>
    from_multi_app<<<{"description": "The name of the MultiApp to receive data from"}>>> = forward
    from_reporters<<<{"description": "List of the reporter names (object_name/value_name) to transfer the value from."}>>> = 'adjoint/inner_product data/objective_value'
    to_reporters<<<{"description": "List of the reporter names (object_name/value_name) to transfer the value to."}>>> = 'OptimizationReporter/grad_reaction_rate OptimizationReporter/objective_value'
  []
[]

[Reporters]
  [optInfo]
    type = OptimizationInfo<<<{"description": "Reports Optimization Output", "href": "../../../source/reporters/OptimizationInfo.html"}>>>
    items<<<{"description": "The information to output, if nothing is provided everything will be output."}>>> = 'current_iterate function_value gnorm'
  []
[]

[Executioner<<<{"href": "../../../syntax/Executioner/index.html"}>>>]
  type = Optimize
  tao_solver = taobqnls
  petsc_options_iname = '-tao_gttol -tao_max_it'
  #petsc_options_value = '1e-5 100' #use this to get results for paper
  petsc_options_value = '1e-5 5'
  solve_on = 'NONE'
  verbose = true
[]
[Outputs<<<{"href": "../../../syntax/Outputs/index.html"}>>>]
  csv<<<{"description": "Output the scalar variable and postprocessors to a *.csv file using the default CSV output."}>>> = true
[]
(modules/optimization/examples/diffusion_reaction/optimize.i)

Listing 2: Physics sub-application

[Mesh<<<{"href": "../../../syntax/Mesh/index.html"}>>>]
  [square]
    type = GeneratedMeshGenerator<<<{"description": "Create a line, square, or cube mesh with uniformly spaced or biased elements.", "href": "../../../source/meshgenerators/GeneratedMeshGenerator.html"}>>>
    dim<<<{"description": "The dimension of the mesh to be generated"}>>> = 2
    nx<<<{"description": "Number of elements in the X direction"}>>> = 16
    ny<<<{"description": "Number of elements in the Y direction"}>>> = 16
    xmin<<<{"description": "Lower X Coordinate of the generated mesh"}>>> = 0
    xmax<<<{"description": "Upper X Coordinate of the generated mesh"}>>> = 1
    ymin<<<{"description": "Lower Y Coordinate of the generated mesh"}>>> = 0
    ymax<<<{"description": "Upper Y Coordinate of the generated mesh"}>>> = 1
  []
[]

[Variables<<<{"href": "../../../syntax/Variables/index.html"}>>>/u]
[]

[Reporters<<<{"href": "../../../syntax/Reporters/index.html"}>>>]
  [params]
    type = ConstantReporter<<<{"description": "Reporter with constant values to be accessed by other objects, can be modified using transfers.", "href": "../../../source/reporters/ConstantReporter.html"}>>>
    real_vector_names<<<{"description": "Names for each vector of reals value."}>>> = 'reaction_rate'
    real_vector_values<<<{"description": "Values for vectors of reals."}>>> = '0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0' # Dummy
    outputs<<<{"description": "Vector of output names where you would like to restrict the output of variables(s) associated with this object"}>>> = none
  []
  [data]
    type = OptimizationData<<<{"description": "Reporter to hold measurement and simulation data for optimization problems", "href": "../../../source/reporters/OptimizationData.html"}>>>
    variable<<<{"description": "Vector of variable names to sample at measurement points."}>>> = u
    objective_name<<<{"description": "Name of reporter value defining the objective."}>>> = objective_value
    measurement_file<<<{"description": "CSV file with measurement value and coordinates (value, x, y, z)."}>>> = forward_exact_csv_sample_0011.csv
    file_xcoord<<<{"description": "x coordinate column name from measurement_file csv being read in."}>>> = measurement_xcoord
    file_ycoord<<<{"description": "y coordinate column name from csv file being read in."}>>> = measurement_ycoord
    file_zcoord<<<{"description": "z coordinate column name from csv file being read in."}>>> = measurement_zcoord
    file_time<<<{"description": "time column name from csv file being read in."}>>> = measurement_time
    file_value<<<{"description": "measurement value column name from csv file being read in."}>>> = simulation_values
    outputs<<<{"description": "Vector of output names where you would like to restrict the output of variables(s) associated with this object"}>>> = none
  []
[]

[Functions<<<{"href": "../../../syntax/Functions/index.html"}>>>]
  [rxn_func]
    type = ParameterMeshFunction<<<{"description": "Optimization function with parameters represented by a mesh and finite-element shape functions.", "href": "../../../source/functions/ParameterMeshFunction.html"}>>>
    exodus_mesh<<<{"description": "File containing parameter mesh."}>>> = parameter_mesh_out.e
    parameter_name<<<{"description": "Reporter or VectorPostprocessor vector containing parameter values."}>>> = params/reaction_rate
  []
[]

[Materials<<<{"href": "../../../syntax/Materials/index.html"}>>>]
  [ad_dc_prop]
    type = ADParsedMaterial<<<{"description": "Parsed expression Material.", "href": "../../../source/materials/ParsedMaterial.html"}>>>
    expression<<<{"description": "Parsed function (see FParser) expression for the parsed material"}>>> = '1 + u'
    coupled_variables<<<{"description": "Vector of variables used in the parsed function"}>>> = 'u'
    property_name<<<{"description": "Name of the parsed material property"}>>> = dc_prop
  []
  [ad_rxn_prop]
    type = ADGenericFunctionMaterial<<<{"description": "Material object for declaring properties that are populated by evaluation of Function object.", "href": "../../../source/materials/GenericFunctionMaterial.html"}>>>
    prop_values<<<{"description": "The corresponding names of the functions that are going to provide the values for the variables"}>>> = 'rxn_func'
    prop_names<<<{"description": "The names of the properties this material will have"}>>> = rxn_prop
  []
  #ADMatReaction includes a negative sign in residual evaluation, so we need to
  #reverse this with a negative reaction rate. However, we wanted the parameter
  #to remain positive, which is why there is one object to evaluate function
  #and another to flip it's sign for the kernel
  [ad_neg_rxn_prop]
    type = ADParsedMaterial<<<{"description": "Parsed expression Material.", "href": "../../../source/materials/ParsedMaterial.html"}>>>
    expression<<<{"description": "Parsed function (see FParser) expression for the parsed material"}>>> = '-rxn_prop'
    material_property_names<<<{"description": "Vector of material properties used in the parsed function"}>>> = 'rxn_prop'
    property_name<<<{"description": "Name of the parsed material property"}>>> = 'neg_rxn_prop'
  []
[]

[Kernels<<<{"href": "../../../syntax/Kernels/index.html"}>>>]
  [udot]
    type = ADTimeDerivative<<<{"description": "The time derivative operator with the weak form of $(\\psi_i, \\frac{\\partial u_h}{\\partial t})$.", "href": "../../../source/kernels/ADTimeDerivative.html"}>>>
    variable<<<{"description": "The name of the variable that this residual object operates on"}>>> = u
  []
  [diff]
    type = ADMatDiffusion<<<{"description": "Diffusion equation kernel that takes an isotropic diffusivity from a material property", "href": "../../../source/kernels/ADMatDiffusion.html"}>>>
    variable<<<{"description": "The name of the variable that this residual object operates on"}>>> = u
    diffusivity<<<{"description": "The diffusivity value or material property"}>>> = dc_prop
  []
  [reaction]
    type = ADMatReaction<<<{"description": "Kernel representing the contribution of the PDE term $-L*v$, where $L$ is a reaction rate material property, $v$ is a scalar variable (nonlinear or coupled), and whose Jacobian contribution is calculated using automatic differentiation.", "href": "../../../source/kernels/ADMatReaction.html"}>>>
    variable<<<{"description": "The name of the variable that this residual object operates on"}>>> = u
    reaction_rate<<<{"description": "The reaction_rate used with the kernel."}>>> = neg_rxn_prop
  []
  [src]
    type = ADBodyForce<<<{"description": "Demonstrates the multiple ways that scalar values can be introduced into kernels, e.g. (controllable) constants, functions, and postprocessors. Implements the weak form $(\\psi_i, -f)$.", "href": "../../../source/kernels/BodyForce.html"}>>>
    variable<<<{"description": "The name of the variable that this residual object operates on"}>>> = u
    value<<<{"description": "Coefficient to multiply by the body force term"}>>> = 1
  []
[]

[BCs<<<{"href": "../../../syntax/BCs/index.html"}>>>]
  [dirichlet]
    type = DirichletBC<<<{"description": "Imposes the essential boundary condition $u=g$, where $g$ is a constant, controllable value.", "href": "../../../source/bcs/DirichletBC.html"}>>>
    variable<<<{"description": "The name of the variable that this residual object operates on"}>>> = u
    boundary<<<{"description": "The list of boundary IDs from the mesh where this object applies"}>>> = 'left bottom'
    value<<<{"description": "Value of the BC"}>>> = 0
  []
[]

[Preconditioning<<<{"href": "../../../syntax/Preconditioning/index.html"}>>>]
  [nl0]
    type = SMP<<<{"description": "Single matrix preconditioner (SMP) builds a preconditioner using user defined off-diagonal parts of the Jacobian.", "href": "../../../source/preconditioners/SingleMatrixPreconditioner.html"}>>>
    nl_sys<<<{"description": "The nonlinear system whose linearization this preconditioner should be applied to."}>>> = 'nl0'
    petsc_options_iname<<<{"description": "Names of PETSc name/value pairs"}>>> = '-pc_type'
    petsc_options_value<<<{"description": "Values of PETSc name/value pairs (must correspond with \"petsc_options_iname\""}>>> = 'lu'
  []
  [adjoint]
    type = SMP<<<{"description": "Single matrix preconditioner (SMP) builds a preconditioner using user defined off-diagonal parts of the Jacobian.", "href": "../../../source/preconditioners/SingleMatrixPreconditioner.html"}>>>
    nl_sys<<<{"description": "The nonlinear system whose linearization this preconditioner should be applied to."}>>> = 'adjoint'
    petsc_options_iname<<<{"description": "Names of PETSc name/value pairs"}>>> = '-pc_type'
    petsc_options_value<<<{"description": "Values of PETSc name/value pairs (must correspond with \"petsc_options_iname\""}>>> = 'lu'
  []
[]

[Executioner<<<{"href": "../../../syntax/Executioner/index.html"}>>>]
  type = TransientAndAdjoint
  forward_system = nl0
  adjoint_system = adjoint
  dt = 0.1
  end_time = 1

  nl_rel_tol = 1e-12
[]

[Problem<<<{"href": "../../../syntax/Problem/index.html"}>>>]
  nl_sys_names = 'nl0 adjoint'
  kernel_coverage_check = false
  skip_nl_system_check = true
[]

[Variables]
  [u_adjoint]
    initial_condition<<<{"description": "Specifies a constant initial condition for this variable"}>>> = 0
    solver_sys = adjoint
    outputs = none
  []
[]

[DiracKernels<<<{"href": "../../../syntax/DiracKernels/index.html"}>>>]
  [misfit]
    type = ReporterTimePointSource<<<{"description": "Apply a time dependent point load defined by Reporters.", "href": "../../../source/dirackernels/ReporterTimePointSource.html"}>>>
    variable<<<{"description": "The name of the variable that this residual object operates on"}>>> = u_adjoint
    value_name<<<{"description": "reporter value name.  This uses the reporter syntax <reporter>/<name>."}>>> = data/misfit_values
    x_coord_name<<<{"description": "reporter x-coordinate name.  This uses the reporter syntax <reporter>/<name>."}>>> = data/measurement_xcoord
    y_coord_name<<<{"description": "reporter y-coordinate name.  This uses the reporter syntax <reporter>/<name>."}>>> = data/measurement_ycoord
    z_coord_name<<<{"description": "reporter z-coordinate name.  This uses the reporter syntax <reporter>/<name>."}>>> = data/measurement_zcoord
    time_name<<<{"description": "Name of vector-postprocessor or reporter vector containing time, default is assumed to be all 0s."}>>> = data/measurement_time
  []
[]

[VectorPostprocessors<<<{"href": "../../../syntax/VectorPostprocessors/index.html"}>>>]
  [adjoint]
    type = ElementOptimizationReactionFunctionInnerProduct<<<{"description": "Compute the gradient for reaction material inversion by taking the inner product of the forward and adjoint variables multiplied by the derivative of the optimization function with respect to the controllable parameters.", "href": "../../../source/vectorpostprocessors/ElementOptimizationReactionFunctionInnerProduct.html"}>>>
    variable<<<{"description": "The names of the variables that this VectorPostprocessor operates on"}>>> = u_adjoint
    forward_variable<<<{"description": "Variable from the forward solution"}>>> = u
    function<<<{"description": "Optimization function."}>>> = rxn_func
    execute_on<<<{"description": "The list of flag(s) indicating when this object should be executed. For a description of each flag, see https://mooseframework.inl.gov/source/interfaces/SetupInterface.html."}>>> = ADJOINT_TIMESTEP_END
    outputs<<<{"description": "Vector of output names where you would like to restrict the output of variables(s) associated with this object"}>>> = none
  []
[]

[AuxVariables<<<{"href": "../../../syntax/AuxVariables/index.html"}>>>]
  [reaction_rate]
  []
[]

[AuxKernels<<<{"href": "../../../syntax/AuxKernels/index.html"}>>>]
  [reaction_rate_aux]
    type = FunctionAux<<<{"description": "Auxiliary Kernel that creates and updates a field variable by sampling a function through space and time.", "href": "../../../source/auxkernels/FunctionAux.html"}>>>
    variable<<<{"description": "The name of the variable that this object applies to"}>>> = reaction_rate
    function<<<{"description": "The function to use as the value"}>>> = rxn_func
    execute_on<<<{"description": "The list of flag(s) indicating when this object should be executed. For a description of each flag, see https://mooseframework.inl.gov/source/interfaces/SetupInterface.html."}>>> = TIMESTEP_END
  []
[]

[Postprocessors<<<{"href": "../../../syntax/Postprocessors/index.html"}>>>]
  [u1]
    type = PointValue<<<{"description": "Compute the value of a variable at a specified location", "href": "../../../source/postprocessors/PointValue.html"}>>>
    variable<<<{"description": "The name of the variable that this postprocessor operates on."}>>> = u
    point<<<{"description": "The physical point where the solution will be evaluated."}>>> = '0.25 0.25 0'
  []
  [u2]
    type = PointValue<<<{"description": "Compute the value of a variable at a specified location", "href": "../../../source/postprocessors/PointValue.html"}>>>
    variable<<<{"description": "The name of the variable that this postprocessor operates on."}>>> = u
    point<<<{"description": "The physical point where the solution will be evaluated."}>>> = '0.75 0.75 0'
  []
  [u3]
    type = PointValue<<<{"description": "Compute the value of a variable at a specified location", "href": "../../../source/postprocessors/PointValue.html"}>>>
    variable<<<{"description": "The name of the variable that this postprocessor operates on."}>>> = u
    point<<<{"description": "The physical point where the solution will be evaluated."}>>> = '1 1 0'
  []
[]

[Outputs<<<{"href": "../../../syntax/Outputs/index.html"}>>>]
  exodus<<<{"description": "Output the results using the default settings for Exodus output."}>>> = true
  console<<<{"description": "Output the results using the default settings for Console output"}>>> = false
  csv<<<{"description": "Output the scalar variable and postprocessors to a *.csv file using the default CSV output."}>>> = true
[]
(modules/optimization/examples/diffusion_reaction/forward_and_adjoint.i)

The top right figure in Figure 1 shows the rate found from the optimization process. Figure 2 shows how close the solution from the optimized reaction rate is from the synthetic measurement data. The diffusion-only initial guess is shown by the square data points.

Figure 1: Left: Exact reaction rate, simulation mesh, and measurement locations. Right: Optimized rection rate and parameter mesh.

Figure 2: Comparing simulated transient solution with exact, initial optimization guess, and optimized reaction rate at several measurement locations (measurement and optimized data are visually identical).