// boost1.67-1.67.0/libs/graph/example/r_c_shortest_paths_example.cpp

// Copyright Michael Drexl 2005, 2006.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://boost.org/LICENSE_1_0.txt)

// Example use of the resource-constrained shortest paths algorithm.
#include <boost/config.hpp>

#ifdef BOOST_MSVC
#pragma warning(disable : 4267)
#endif

#include <boost/graph/adjacency_list.hpp>

#include <boost/graph/r_c_shortest_paths.hpp>
#include <iostream>

using namespace boost;

struct SPPRC_Example_Graph_Vert_Prop
{
  SPPRC_Example_Graph_Vert_Prop(int n = 0, int e = 0, int l = 0)
      : num(n), eat(e), lat(l) {}
  int num;
  // earliest arrival time
  int eat;
  // latest arrival time
  int lat;
};

struct SPPRC_Example_Graph_Arc_Prop
{
  SPPRC_Example_Graph_Arc_Prop(int n = 0, int c = 0, int t = 0)
      : num(n), cost(c), time(t) {}
  int num;
  // traversal cost
  int cost;
  // traversal time
  int time;
};

typedef adjacency_list<vecS,
                       vecS,
                       directedS,
                       SPPRC_Example_Graph_Vert_Prop,
                       SPPRC_Example_Graph_Arc_Prop>
    SPPRC_Example_Graph;

// data structures for spp without resource constraints:
// ResourceContainer model
struct spp_no_rc_res_cont
{
  spp_no_rc_res_cont(int c = 0) : cost(c){};
  spp_no_rc_res_cont &operator=(const spp_no_rc_res_cont &other)
  {
    if (this == &other)
      return *this;
    this->~spp_no_rc_res_cont();
    new (this) spp_no_rc_res_cont(other);
    return *this;
  }
  int cost;
};

bool operator==(const spp_no_rc_res_cont &res_cont_1,
                const spp_no_rc_res_cont &res_cont_2)
{
  return (res_cont_1.cost == res_cont_2.cost);
}

bool operator<(const spp_no_rc_res_cont &res_cont_1,
               const spp_no_rc_res_cont &res_cont_2)
{
  return (res_cont_1.cost < res_cont_2.cost);
}

// ResourceExtensionFunction model
class ref_no_res_cont
{
public:
  inline bool operator()(const SPPRC_Example_Graph &g,
                         spp_no_rc_res_cont &new_cont,
                         const spp_no_rc_res_cont &old_cont,
                         graph_traits<SPPRC_Example_Graph>::edge_descriptor ed) const
  {
    new_cont.cost = old_cont.cost + g[ed].cost;
    return true;
  }
};

// DominanceFunction model
class dominance_no_res_cont
{
public:
  inline bool operator()(const spp_no_rc_res_cont &res_cont_1,
                         const spp_no_rc_res_cont &res_cont_2) const
  {
    // must be "<=" here!!!
    // must NOT be "<"!!!
    return res_cont_1.cost <= res_cont_2.cost;
    // this is not a contradiction to the documentation
    // the documentation says:
    // "A label $l_1$ dominates a label $l_2$ if and only if both are resident
    // at the same vertex, and if, for each resource, the resource consumption
    // of $l_1$ is less than or equal to the resource consumption of $l_2$,
    // and if there is at least one resource where $l_1$ has a lower resource
    // consumption than $l_2$."
    // one can think of a new label with a resource consumption equal to that
    // of an old label as being dominated by that old label, because the new
    // one will have a higher number and is created at a later point in time,
    // so one can implicitly use the number or the creation time as a resource
    // for tie-breaking
  }
};
// end data structures for spp without resource constraints:

// data structures for shortest path problem with time windows (spptw)
// ResourceContainer model
struct spp_spptw_res_cont
{
  spp_spptw_res_cont(int c = 0, int t = 0) : cost(c), time(t) {}
  spp_spptw_res_cont &operator=(const spp_spptw_res_cont &other)
  {
    if (this == &other)
      return *this;
    this->~spp_spptw_res_cont();
    new (this) spp_spptw_res_cont(other);
    return *this;
  }
  int cost;
  int time;
};

bool operator==(const spp_spptw_res_cont &res_cont_1,
                const spp_spptw_res_cont &res_cont_2)
{
  return (res_cont_1.cost == res_cont_2.cost && res_cont_1.time == res_cont_2.time);
}

bool operator<(const spp_spptw_res_cont &res_cont_1,
               const spp_spptw_res_cont &res_cont_2)
{
  if (res_cont_1.cost > res_cont_2.cost)
    return false;
  if (res_cont_1.cost == res_cont_2.cost)
    return res_cont_1.time < res_cont_2.time;
  return true;
}

// ResourceExtensionFunction model
class ref_spptw
{
public:
  inline bool operator()(const SPPRC_Example_Graph &g,
                         spp_spptw_res_cont &new_cont,
                         const spp_spptw_res_cont &old_cont,
                         graph_traits<SPPRC_Example_Graph>::edge_descriptor ed) const
  {
    const SPPRC_Example_Graph_Arc_Prop &arc_prop =
        get(edge_bundle, g)[ed];
    const SPPRC_Example_Graph_Vert_Prop &vert_prop =
        get(vertex_bundle, g)[target(ed, g)];
    new_cont.cost = old_cont.cost + arc_prop.cost;
    int &i_time = new_cont.time;
    i_time = old_cont.time + arc_prop.time;
    i_time < vert_prop.eat ? i_time = vert_prop.eat : 0;
    return i_time <= vert_prop.lat ? true : false;
  }
};

// DominanceFunction model
class dominance_spptw
{
public:
  inline bool operator()(const spp_spptw_res_cont &res_cont_1,
                         const spp_spptw_res_cont &res_cont_2) const
  {
    // must be "<=" here!!!
    // must NOT be "<"!!!
    return res_cont_1.cost <= res_cont_2.cost && res_cont_1.time <= res_cont_2.time;
    // this is not a contradiction to the documentation
    // the documentation says:
    // "A label $l_1$ dominates a label $l_2$ if and only if both are resident
    // at the same vertex, and if, for each resource, the resource consumption
    // of $l_1$ is less than or equal to the resource consumption of $l_2$,
    // and if there is at least one resource where $l_1$ has a lower resource
    // consumption than $l_2$."
    // one can think of a new label with a resource consumption equal to that
    // of an old label as being dominated by that old label, because the new
    // one will have a higher number and is created at a later point in time,
    // so one can implicitly use the number or the creation time as a resource
    // for tie-breaking
  }
};
// end data structures for shortest path problem with time windows (spptw)

// example graph structure and cost from
// http://www.boost.org/libs/graph/example/dijkstra-example.cpp
enum nodes
{
  A,
  B,
  C,
  D,
  E
};
char name[] = "ABCDE";

int main()
{
  SPPRC_Example_Graph g;

  add_vertex(SPPRC_Example_Graph_Vert_Prop(A, 0, 0), g);
  add_vertex(SPPRC_Example_Graph_Vert_Prop(B, 5, 20), g);
  add_vertex(SPPRC_Example_Graph_Vert_Prop(C, 6, 10), g);
  add_vertex(SPPRC_Example_Graph_Vert_Prop(D, 3, 12), g);
  add_vertex(SPPRC_Example_Graph_Vert_Prop(E, 0, 100), g);

  add_edge(A, C, SPPRC_Example_Graph_Arc_Prop(0, 1, 5), g);
  add_edge(B, B, SPPRC_Example_Graph_Arc_Prop(1, 2, 5), g);
  add_edge(B, D, SPPRC_Example_Graph_Arc_Prop(2, 1, 2), g);
  add_edge(B, E, SPPRC_Example_Graph_Arc_Prop(3, 2, 7), g);
  add_edge(C, B, SPPRC_Example_Graph_Arc_Prop(4, 7, 3), g);
  add_edge(C, D, SPPRC_Example_Graph_Arc_Prop(5, 3, 8), g);
  add_edge(D, E, SPPRC_Example_Graph_Arc_Prop(6, 1, 3), g);
  add_edge(E, A, SPPRC_Example_Graph_Arc_Prop(7, 1, 5), g);
  add_edge(E, B, SPPRC_Example_Graph_Arc_Prop(8, 1, 4), g);

  // the unique shortest path from A to E in the dijkstra-example.cpp is
  // A -> C -> D -> E
  // its length is 5
  // the following code also yields this result

  // with the above time windows, this path is infeasible
  // now, there are two shortest paths that are also feasible with respect to
  // the vertex time windows:
  // A -> C -> B -> D -> E and
  // A -> C -> B -> E
  // however, the latter has a longer total travel time and is therefore not
  // pareto-optimal, i.e., it is dominated by the former path
  // therefore, the code below returns only the former path

  // spp without resource constraints
  graph_traits<SPPRC_Example_Graph>::vertex_descriptor s = A;
  graph_traits<SPPRC_Example_Graph>::vertex_descriptor t = E;

  std::vector<std::vector<graph_traits<SPPRC_Example_Graph>::edge_descriptor>>
      opt_solutions;
  std::vector<spp_no_rc_res_cont> pareto_opt_rcs_no_rc;

  r_c_shortest_paths(g,
                     get(&SPPRC_Example_Graph_Vert_Prop::num, g),
                     get(&SPPRC_Example_Graph_Arc_Prop::num, g),
                     s,
                     t,
                     opt_solutions,
                     pareto_opt_rcs_no_rc,
                     spp_no_rc_res_cont(0),
                     ref_no_res_cont(),
                     dominance_no_res_cont(),
                     std::allocator<r_c_shortest_paths_label<SPPRC_Example_Graph, spp_no_rc_res_cont>>(),
                     default_r_c_shortest_paths_visitor());

  std::cout << "SPP without resource constraints:" << std::endl;
  std::cout << "Number of optimal solutions: ";
  std::cout << static_cast<int>(opt_solutions.size()) << std::endl;
  for (int i = 0; i < static_cast<int>(opt_solutions.size()); ++i)
  {
    std::cout << "The " << i << "th shortest path from A to E is: ";
    std::cout << std::endl;
    for (int j = static_cast<int>(opt_solutions[i].size()) - 1; j >= 0; --j)
      std::cout << name[source(opt_solutions[i][j], g)] << std::endl;
    std::cout << "E" << std::endl;
    std::cout << "Length: " << pareto_opt_rcs_no_rc[i].cost << std::endl;
  }
  std::cout << std::endl;

  // spptw
  std::vector<std::vector<graph_traits<SPPRC_Example_Graph>::edge_descriptor>>
      opt_solutions_spptw;
  std::vector<spp_spptw_res_cont> pareto_opt_rcs_spptw;

  r_c_shortest_paths(g,
                     get(&SPPRC_Example_Graph_Vert_Prop::num, g),
                     get(&SPPRC_Example_Graph_Arc_Prop::num, g),
                     s,
                     t,
                     opt_solutions_spptw,
                     pareto_opt_rcs_spptw,
                     spp_spptw_res_cont(0, 0),
                     ref_spptw(),
                     dominance_spptw(),
                     std::allocator<r_c_shortest_paths_label<SPPRC_Example_Graph, spp_spptw_res_cont>>(),
                     default_r_c_shortest_paths_visitor());

  std::cout << "SPP with time windows:" << std::endl;
  std::cout << "Number of optimal solutions: ";
  std::cout << static_cast<int>(opt_solutions.size()) << std::endl;
  for (int i = 0; i < static_cast<int>(opt_solutions.size()); ++i)
  {
    std::cout << "The " << i << "th shortest path from A to E is: ";
    std::cout << std::endl;
    for (int j = static_cast<int>(opt_solutions_spptw[i].size()) - 1;
         j >= 0;
         --j)
      std::cout << name[source(opt_solutions_spptw[i][j], g)] << std::endl;
    std::cout << "E" << std::endl;
    std::cout << "Length: " << pareto_opt_rcs_spptw[i].cost << std::endl;
    std::cout << "Time: " << pareto_opt_rcs_spptw[i].time << std::endl;
  }

  // utility function check_r_c_path example
  std::cout << std::endl;
  bool b_is_a_path_at_all = false;
  bool b_feasible = false;
  bool b_correctly_extended = false;
  spp_spptw_res_cont actual_final_resource_levels(0, 0);
  graph_traits<SPPRC_Example_Graph>::edge_descriptor ed_last_extended_arc;
  check_r_c_path(g,
                 opt_solutions_spptw[0],
                 spp_spptw_res_cont(0, 0),
                 true,
                 pareto_opt_rcs_spptw[0],
                 actual_final_resource_levels,
                 ref_spptw(),
                 b_is_a_path_at_all,
                 b_feasible,
                 b_correctly_extended,
                 ed_last_extended_arc);
  if (!b_is_a_path_at_all)
    std::cout << "Not a path." << std::endl;
  if (!b_feasible)
    std::cout << "Not a feasible path." << std::endl;
  if (!b_correctly_extended)
    std::cout << "Not correctly extended." << std::endl;
  if (b_is_a_path_at_all && b_feasible && b_correctly_extended)
  {
    std::cout << "Actual final resource levels:" << std::endl;
    std::cout << "Length: " << actual_final_resource_levels.cost << std::endl;
    std::cout << "Time: " << actual_final_resource_levels.time << std::endl;
    std::cout << "OK." << std::endl;
  }

  return 0;
}


// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "lu_lagrange.h"

// Cholesky LLT decomposition for symmetric positive definite
//#include <Eigen/SparseExtra>
// Bug in unsupported/Eigen/SparseExtra needs iostream first
#include <iostream>
#include <unsupported/Eigen/SparseExtra>
#include <cassert>
#include <cstdio>
#include "find.h"
#include "sparse.h"

template <typename T>
IGL_INLINE bool igl::lu_lagrange(
  const Eigen::SparseMatrix<T> & ATA,
  const Eigen::SparseMatrix<T> & C,
  Eigen::SparseMatrix<T> & L,
  Eigen::SparseMatrix<T> & U)
{
#if EIGEN_VERSION_AT_LEAST(3,0,92)
#  warning lu_lagrange has not yet been implemented for your Eigen Version
  return false;
#else
  // number of unknowns
  int n = ATA.rows();
  // number of lagrange multipliers
  int m = C.cols();

  assert(ATA.cols() == n);
  if(m != 0)
  {
    assert(C.rows() == n);
    if(C.nonZeros() == 0)
    {
      // See note above about empty columns in C
      fprintf(stderr,"Error: lu_lagrange() C has columns but no entries\n");
      return false;
    }
  }

  // Check that each column of C has at least one entry
  std::vector<bool> has_entry; has_entry.resize(C.cols(),false);
  // Iterate over outside
  for(int k=0; k<C.outerSize(); ++k)
  {
    // Iterate over inside
    for(typename Eigen::SparseMatrix<T>::InnerIterator it (C,k); it; ++it)
    {
      has_entry[it.col()] = true;
    }
  }
  for(int i=0;i<(int)has_entry.size();i++)
  {
    if(!has_entry[i])
    {
      // See note above about empty columns in C
      fprintf(stderr,"Error: lu_lagrange() C(:,%d) has no entries\n",i);
      return false;
    }
  }

  // Cholesky factorization of ATA
  //// Eigen fails if you give a full view of the matrix like this:
  //Eigen::SparseLLT<SparseMatrix<T> > ATA_LLT(ATA);
  Eigen::SparseMatrix<T> ATA_LT = ATA.template triangularView<Eigen::Lower>();
  Eigen::SparseLLT<Eigen::SparseMatrix<T> > ATA_LLT(ATA_LT);

  Eigen::SparseMatrix<T> J = ATA_LLT.matrixL();

  //if(!ATA_LLT.succeeded())
  if(!((J*0).eval().nonZeros() == 0))
  {
    fprintf(stderr,"Error: lu_lagrange() failed to factor ATA\n");
    return false;
  }

  if(m == 0)
  {
    // If there are no constraints (C is empty) then LU decomposition is just L
    // and L' from cholesky decomposition
    L = J;
    U = J.transpose();
  }else
  {
    // Construct helper matrix M
    Eigen::SparseMatrix<T> M = C;
    J.template triangularView<Eigen::Lower>().solveInPlace(M);

    // Compute cholesky factorizaiton of M'*M
    Eigen::SparseMatrix<T> MTM = M.transpose() * M;

    Eigen::SparseLLT<Eigen::SparseMatrix<T> > MTM_LLT(MTM.template triangularView<Eigen::Lower>());

    Eigen::SparseMatrix<T> K = MTM_LLT.matrixL();

    //if(!MTM_LLT.succeeded())
    if(!((K*0).eval().nonZeros() == 0))
    {
      fprintf(stderr,"Error: lu_lagrange() failed to factor MTM\n");
      return false;
    }

    // assemble LU decomposition of Q
    Eigen::Matrix<int,Eigen::Dynamic,1> MI;
    Eigen::Matrix<int,Eigen::Dynamic,1> MJ;
    Eigen::Matrix<T,Eigen::Dynamic,1> MV;
    igl::find(M,MI,MJ,MV);

    Eigen::Matrix<int,Eigen::Dynamic,1> KI;
    Eigen::Matrix<int,Eigen::Dynamic,1> KJ;
    Eigen::Matrix<T,Eigen::Dynamic,1> KV;
    igl::find(K,KI,KJ,KV);

    Eigen::Matrix<int,Eigen::Dynamic,1> JI;
    Eigen::Matrix<int,Eigen::Dynamic,1> JJ;
    Eigen::Matrix<T,Eigen::Dynamic,1> JV;
    igl::find(J,JI,JJ,JV);

    int nnz = JV.size()  + MV.size() + KV.size();

    Eigen::Matrix<int,Eigen::Dynamic,1> UI(nnz);
    Eigen::Matrix<int,Eigen::Dynamic,1> UJ(nnz);
    Eigen::Matrix<T,Eigen::Dynamic,1> UV(nnz);
    UI << JJ,                        MI, (KJ.array() + n).matrix();
    UJ << JI, (MJ.array() + n).matrix(), (KI.array() + n).matrix();
    UV << JV,                        MV,                     KV*-1;
    igl::sparse(UI,UJ,UV,U);

    Eigen::Matrix<int,Eigen::Dynamic,1> LI(nnz);
    Eigen::Matrix<int,Eigen::Dynamic,1> LJ(nnz);
    Eigen::Matrix<T,Eigen::Dynamic,1> LV(nnz);
    LI << JI, (MJ.array() + n).matrix(), (KI.array() + n).matrix();
    LJ << JJ,                        MI, (KJ.array() + n).matrix();
    LV << JV,                        MV,                        KV;
    igl::sparse(LI,LJ,LV,L);
  }

  return true;
  #endif
}

#ifdef IGL_STATIC_LIBRARY
// Explicit template specialization
template bool igl::lu_lagrange<double>(Eigen::SparseMatrix<double, 0, int> const&, Eigen::SparseMatrix<double, 0, int> const&, Eigen::SparseMatrix<double, 0, int>&, Eigen::SparseMatrix<double, 0, int>&);
#endif


/******************************************************
    Author : shipeng_liu
    Email : 1196075299@qq.com
    Description: Adaptive dynamic programming
******************************************************/
#include <cmath>
#include<iostream>
#include <fstream>
#include <Eigen/Dense>
#include <iomanip>

using namespace std;

/******************************************************************
    Critic Network
    Input: S, diff_s, steer_cmd
    number of hidden_layer: 1
    number of active_function: hidden_number
    output: J_cost
    parameter_layer1: parameters from input to hidden_layer
    parameter_layer2: parameters from hidden_layer to output
******************************************************************/

class critic_network {

  private:

    double learning_rate_basic = 0.1 ; //learning rate
    double reward_count = 0.95; //the reward count
    Eigen::MatrixXd parameter_layer1;
    Eigen::MatrixXd parameter_layer2;
    Eigen::MatrixXd input_variable;
    Eigen::MatrixXd hidden_layer;
    Eigen::MatrixXd p_hidden_layer;
    double output;
    double reference_signal;
    double last_reference_signal;
    int hidden_number;
    static double active_function(double input) {
        // 1 - exp( - qi(t) ) / 1 + exp( - qi(t) ) )
        double ret = std::exp(- input);
        double tmp = (2)/(1 + ret) - 1;
        return tmp;
    }

  public:
    static critic_network& Get_critic_network()
    {
        static critic_network singleton;
        return singleton;
    }

    critic_network()
    {
        hidden_number = 4;
        init();
    }
    double return_J_cost()
    {
        return output;
    }
    double return_reference()
    {
        return reference_signal;
    }
    Eigen::MatrixXd return_parameter_layer1()
    {
        return parameter_layer1;
    }
    Eigen::MatrixXd return_parameter_layer2()
    {
        return parameter_layer2;
    }
    Eigen::MatrixXd return_p_hidden_layer()
    {
        return p_hidden_layer;
    }
    void init()
    {
        parameter_layer1 = Eigen::MatrixXd::Random(3, hidden_number);
        parameter_layer2 = Eigen::MatrixXd::Random(hidden_number, 1);

        hidden_layer = Eigen::MatrixXd::Random(1, hidden_number);
        p_hidden_layer = Eigen::MatrixXd::Random(1, hidden_number);
        input_variable = Eigen::MatrixXd::Random(1,3);
        output = 0;

    }

    double output_J_cost(double s, double diff_s,double steer_cmd)
    {
        input_variable(0,0) = s;
        input_variable(0,1) = diff_s;
        input_variable(0,2) = steer_cmd;
        hidden_layer = input_variable * parameter_layer1;
        // limit hidden layer

        for (int i = 0; i < hidden_number; i++)
        {
            p_hidden_layer(0,i) = active_function(hidden_layer(0,i));
            // limit the p_Hidden_layer
            if (p_hidden_layer(0,i) < 0.000001 && p_hidden_layer(0,i) > 0 )
                p_hidden_layer(0,i) = 0.000001;
            else if (p_hidden_layer(0,i) > -0.000001 && p_hidden_layer(0,i) < 0)
                p_hidden_layer(0,i) = - 0.000001;
        }

        Eigen::MatrixXd test = p_hidden_layer * parameter_layer2;
        output = test(0,0);
        // limit the J_cost
        if (output < 0)
        {
            output = 0;
        }

        return output;
    }

    void update_weight(double J_cost, double Last_J_cost, double refer_s)
    {
        // update learning rate:
        last_reference_signal = reference_signal;
        reference_signal =  refer_s;
        //double learning_rate = learning_rate_basic * (1+2/(1+fabs(reference_signal - last_reference_signal)) );
        double learning_rate = learning_rate_basic * 4;
        double ect = reward_count*J_cost -  reference_signal;
        //double reference_signal = 0.5;
        cout << "\ncritic_network_information:\n";
        cout << left << setw(20) << "[ refer: " << right
             << setw(20) << reference_signal << "]" << endl;
        cout << left << setw(20) << "[ J_cost: " << right
             << setw(20) << J_cost << "]" << endl;
        cout << left << setw(20) << "[ ect: " << right
             << setw(20) << ect << "]" << endl;
        cout << "[ p_hidden_layer: " << endl;
        cout << p_hidden_layer << endl;
        // cout <<  "[ parameter_layer1: " << endl;
        // cout << parameter_layer1 << endl;

        // cout << "[ parameter_layer2: " << endl;
        // cout << parameter_layer2 << endl;

        for (int j = 0; j < hidden_number; j++)
        {

            for (int k = 0; k < 3; k++)
            {

                double derivative_1 = reward_count * ect * parameter_layer2(j,0) * 0.5 * (1 - pow(p_hidden_layer(0,j),2)) * input_variable(0,k);

                double delta_weight1 = learning_rate * (- derivative_1);

                parameter_layer1(k,j) += delta_weight1;
            }
            double derivative =  reward_count * ect * (p_hidden_layer(0,j));
            double delta_weight = learning_rate * (- derivative);
            parameter_layer2(j,0) += delta_weight;

        }
    }
};


/*
@copyright Louis Dionne 2015
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/assert.hpp>
#include <boost/hana/equal.hpp>
#include <boost/hana/integral_constant.hpp>
#include <boost/hana/less.hpp>
#include <boost/hana/negate.hpp>

#include <type_traits>
namespace hana = boost::hana;
using namespace hana::literals;

BOOST_HANA_CONSTANT_CHECK(0_c == hana::llong_c<0>);
BOOST_HANA_CONSTANT_CHECK(1_c == hana::llong_c<1>);
BOOST_HANA_CONSTANT_CHECK(12_c == hana::llong_c<12>);
BOOST_HANA_CONSTANT_CHECK(123_c == hana::llong_c<123>);
BOOST_HANA_CONSTANT_CHECK(1234567_c == hana::llong_c<1234567>);
BOOST_HANA_CONSTANT_CHECK(-34_c == hana::llong_c<-34>);

static_assert(std::is_same<
    decltype(-1234_c)::value_type,
    long long
>{}, "");
static_assert(-1234_c == -1234ll, "");
BOOST_HANA_CONSTANT_CHECK(-12_c < 0_c);

int main() { }


// Copyright 2019 Xanadu Quantum Technologies Inc.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
 * @file
 * Contains functions for calculating the multidimensional
 * Hermite polynomials, used for computation of batched hafnians.
 */

#pragma once
#include <stdafx.h>

#include <Eigen/Eigenvalues>
#include <Eigen/LU>

typedef unsigned long long int ullint;

/**
 * Returns the index of the one dimensional flattened vector corresponding to the multidimensional tensor
 *
 * @param pos
 * @param resolution
 *
 * @return index on flattened vector
 */
ullint vec2index(std::vector<int> &pos, int resolution) {
    int dim = pos.size();
    ullint nextCoordinate = 0;

    nextCoordinate = pos[0]-1;
    for(int ii = 0; ii < dim-1; ii++) {
        nextCoordinate = nextCoordinate*resolution + (pos[ii+1]-1);
    }

    return nextCoordinate;

}

/**
 * Returns the indices of the tensor corresponding to a given element
 *
 * @param val
 * @param base
 * @param n
 *
 * @return tensor index
 */
std::vector<int> find_rep(int val, int base, int n) {
    std::vector<int> x(n, 0);
    int local_val = val;

    x[0] = 1;

    for (int i = 1; i < n; i++)
        x[i] = x[i-1]*base;

    std::vector<int> digits(n, 0);

    for (int i = 0; i < n; i++) {
        digits[i] = local_val/x[n-i-1];
        local_val = local_val - digits[i] * x[n-i-1];
    }

    return digits;
}

/**
 * Returns the sqrt of the factorial of an integer.
 *
 * @param nn input integer
 *
 * @return Square root of the factorial of \f$n\f$.
 */
long double sqrtfactorial(int nn)
{
    long double n = static_cast<long double>(nn);

    if(n > 1)
        return std::sqrt(n) * sqrtfactorial(n - 1);
    else
        return 1;
}

/**
 * Renormalizes an unnormalized photon number statistics of a Gaussian state.
 * Based on the MATLAB code available at: https://github.com/clementsw/gaussian-optics
 *
 * @param tn unnormalized flattened vector of size \f$res**nmodes$ representing unnormalized photon number statistics
 *       \f$2n\times 2n\f$ row-ordered symmetric matrix.
 * @param nmodes number of modes
 * @param res highest number of photons to be resolved.
 *
 * @return Renormalized photon number statistics
 */
template <typename T>
inline std::vector<T> renormalization(std::vector<T> tn, int nmodes, int res) {
    std::vector<long double> invsqfacts(res, 0);
    std::vector<int> digits(nmodes, 0);

    ullint Hdim = pow(res, nmodes);

    for (int i = 0; i < res; i++)
        invsqfacts[i] = sqrtfactorial(i);

    for (ullint i = 0; i < Hdim; i++) {
        digits = find_rep(i, res, nmodes);
        long double pref = 1;
        for (int j = 0; j < nmodes; j++)
            pref *= 1.0L/invsqfacts[digits[j]];
        tn[i] = tn[i]*static_cast<double>(pref);
    }

    return tn;

}

namespace libwalrus {

/**
 * Returns photon number statistics of a Gaussian state for a given covariance matrix `mat`.
 * as described in *Multidimensional Hermite polynomials and photon distribution for polymode mixed light*
 * [arxiv:9308033](https://arxiv.org/abs/hep-th/9308033).
 *
 * This implementation is based on the MATLAB code available at
 * https://github.com/clementsw/gaussian-optics
 *
 * @param mat a flattened vector of size \f$2n^2\f$, representing an
 *       \f$2n\times 2n\f$ row-ordered symmetric matrix.
 * @param d a flattened vector of size \f$2n\f$, representing the first order moments.
 * @param resolution highest number of photons to be resolved.
 *
 */
template <typename T>
inline std::vector<T> hermite_multidimensional_cpp(std::vector<T> &R_mat, std::vector<T> &y_mat, int &resolution, int &renorm) {
    int dim = std::sqrt(static_cast<double>(R_mat.size()));

    namespace eg = Eigen;

    eg::Matrix<T, eg::Dynamic, eg::Dynamic> R = eg::Map<eg::Matrix<T, eg::Dynamic, eg::Dynamic>, eg::Unaligned>(R_mat.data(), dim, dim);
    eg::Matrix<T, eg::Dynamic, eg::Dynamic> y = eg::Map<eg::Matrix<T, eg::Dynamic, eg::Dynamic>, eg::Unaligned>(y_mat.data(), dim, dim);

    ullint Hdim = pow(resolution, dim);
    std::vector<T> H(Hdim, 0);
    std::vector<double> ren_factor(Hdim, 0);

    H[0] = 1;
    ren_factor[0] = 1;

    std::vector<int> nextPos(dim, 1);
    std::vector<int> jumpFrom(dim, 1);
    std::vector<int> ek(dim, 0);
    std::vector<double> factors(resolution+1, 0);
    int jump = 0;

    for (ullint jj = 0; jj < Hdim-1; jj++) {

        if (jump > 0) {
            jumpFrom[jump] += 1;
            jump = 0;
        }

        for (int ii = 0; ii < dim; ii++) {
            std::vector<int> forwardStep(dim, 0);
            forwardStep[ii] = 1;

            if ( forwardStep[ii] + nextPos[ii] > resolution) {
                nextPos[ii] = 1;
                jumpFrom[ii] = 1;
                jump = ii+1;
            }
            else {
                jumpFrom[ii] = nextPos[ii];
                nextPos[ii] = nextPos[ii] + 1;
                break;
            }
        }

        for (int ii = 0; ii < dim; ii++)
            ek[ii] = nextPos[ii] - jumpFrom[ii];

        int k = 0;
        for(; k < static_cast<int>(ek.size()); k++) {
            if(ek[k]) break;
        }

        ullint nextCoordinate = vec2index(nextPos, resolution);
        ullint fromCoordinate = vec2index(jumpFrom, resolution);

        for (int ii = 0; ii < dim; ii++) {
            H[nextCoordinate] = H[nextCoordinate] + R(k, ii) * y(ii, 0);
        }
        H[nextCoordinate] = H[nextCoordinate] * H[fromCoordinate];

        std::vector<int> tmpjump(dim, 0);

        for (int ii = 0; ii < dim; ii++) {
            if (jumpFrom[ii] > 1) {
                std::vector<int> prevJump(dim, 0);
                prevJump[ii] = 1;
                std::transform(jumpFrom.begin(), jumpFrom.end(), prevJump.begin(), tmpjump.begin(), std::minus<int>());
                ullint prevCoordinate = vec2index(tmpjump, resolution);
                H[nextCoordinate] = H[nextCoordinate] - (static_cast<T>(jumpFrom[ii]-1))*static_cast<T>(R(k,ii))*H[prevCoordinate];

            }
        }

    }

    if (renorm) {
        H = renormalization(H, dim, resolution);
    }

    return H;

}

}


//=======================================================================
// Copyright 2013 Maciej Piechotka
// Authors: Maciej Piechotka
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
#ifndef BOOST_GRAPH_EDGE_COLORING_HPP
#define BOOST_GRAPH_EDGE_COLORING_HPP

#include <boost/graph/graph_traits.hpp>
#include <boost/graph/iteration_macros.hpp>
#include <boost/graph/properties.hpp>
#include <algorithm>
#include <limits>
#include <vector>

/* This algorithm is to find coloring of an edges

   Reference:

   Misra, J., & Gries, D. (1992). A constructive proof of Vizing's
   theorem. In Information Processing Letters.
*/

namespace boost {
  namespace detail {
    template<typename Graph, typename ColorMap>
    bool
    is_free(const Graph &g,
            ColorMap color,
            typename boost::graph_traits<Graph>::vertex_descriptor u,
            typename boost::property_traits<ColorMap>::value_type free_color)
    {
      typedef typename boost::property_traits<ColorMap>::value_type color_t;
      if (free_color == (std::numeric_limits<color_t>::max)())
        return false;
      BGL_FORALL_OUTEDGES_T(u, e, g, Graph) {
        if (get(color, e) == free_color) {
          return false;
        }
      }
      return true;
    }

    template<typename Graph, typename ColorMap>
    std::vector<typename boost::graph_traits<Graph>::vertex_descriptor>
    maximal_fan(const Graph &g,
                ColorMap color,
                typename boost::graph_traits<Graph>::vertex_descriptor x,
                typename boost::graph_traits<Graph>::vertex_descriptor y)
    {
      typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex_t;
      std::vector<vertex_t> fan;
      fan.push_back(y);
      bool extended;
      do {
        extended = false;
        BGL_FORALL_OUTEDGES_T(x, e, g, Graph) {
          vertex_t v = target(e, g);
          if (is_free(g, color, fan.back(), get(color, e)) &&
              std::find(fan.begin(), fan.end(), v) == fan.end()) {
            fan.push_back(v);
            extended = true;
          }
        }
      } while(extended);
      return fan;
    }
    template<typename Graph, typename ColorMap>
    typename boost::property_traits<ColorMap>::value_type
    find_free_color(const Graph &g,
                    ColorMap color,
                    typename boost::graph_traits<Graph>::vertex_descriptor u)
    {
      typename boost::property_traits<ColorMap>::value_type c = 0;
      while (!is_free(g, color, u, c)) c++;
      return c;
    }

    template<typename Graph, typename ColorMap>
    void
    invert_cd_path(const Graph &g,
                   ColorMap color,
                   typename boost::graph_traits<Graph>::vertex_descriptor x,
                   typename boost::graph_traits<Graph>::edge_descriptor eold,
                   typename boost::property_traits<ColorMap>::value_type c,
                   typename boost::property_traits<ColorMap>::value_type d)
    {
      put(color, eold, d);
      BGL_FORALL_OUTEDGES_T(x, e, g, Graph) {
        if (get(color, e) == d && e != eold) {
          invert_cd_path(g, color, target(e, g), e, d, c);
          return;
        }
      }
    }

    template<typename Graph, typename ColorMap>
    void
    invert_cd_path(const Graph &g,
                   ColorMap color,
                   typename boost::graph_traits<Graph>::vertex_descriptor x,
                   typename boost::property_traits<ColorMap>::value_type c,
                   typename boost::property_traits<ColorMap>::value_type d)
    {
      BGL_FORALL_OUTEDGES_T(x, e, g, Graph) {
        if (get(color, e) == d) {
          invert_cd_path(g, color, target(e, g), e, d, c);
          return;
        }
      }
    }

    template<typename Graph, typename ColorMap, typename ForwardIterator>
    void
    rotate_fan(const Graph &g,
               ColorMap color,
               typename boost::graph_traits<Graph>::vertex_descriptor x,
               ForwardIterator begin,
               ForwardIterator end)
    {
      typedef typename boost::graph_traits<Graph>::edge_descriptor edge_t;
      if (begin == end) {
        return;
      }
      edge_t previous = edge(x, *begin, g).first;
      for (begin++; begin != end; begin++) {
        edge_t current = edge(x, *begin, g).first;
        put(color, previous, get(color, current));
        previous = current;
      }
    }

    template<typename Graph, typename ColorMap>
    class find_free_in_fan
    {
    public:
      find_free_in_fan(const Graph &graph,
                       const ColorMap color,
                       typename boost::property_traits<ColorMap>::value_type d)
        : graph(graph),
          color(color),
          d(d) {}
      bool operator()(const typename boost::graph_traits<Graph>::vertex_descriptor u) const {
        return is_free(graph, color, u, d);
      }
    private:
      const Graph &graph;
      const ColorMap color;
      const typename boost::property_traits<ColorMap>::value_type d;
    };
  }

  template<typename Graph, typename ColorMap>
  typename boost::property_traits<ColorMap>::value_type
  color_edge(const Graph &g,
             ColorMap color,
             typename boost::graph_traits<Graph>::edge_descriptor e)
  {
    typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex_t;
    typedef typename boost::property_traits<ColorMap>::value_type color_t;
    typedef typename std::vector<vertex_t>::iterator fan_iterator;
    using namespace detail;
    vertex_t x = source(e, g), y = target(e, g);
    std::vector<vertex_t> fan = maximal_fan(g, color, x, y);
    color_t c = find_free_color(g, color, x);
    color_t d = find_free_color(g, color, fan.back());
    invert_cd_path(g, color, x, c, d);
    fan_iterator w = std::find_if(fan.begin(),
                                  fan.end(),
                                  find_free_in_fan<Graph, ColorMap>(g, color, d));
    rotate_fan(g, color, x, fan.begin(), w + 1);
    put(color, edge(x, *w, g).first, d);
    return (std::max)(c, d);
  }

  template<typename Graph, typename ColorMap>
  typename boost::property_traits<ColorMap>::value_type
  edge_coloring(const Graph &g,
                ColorMap color)
  {
    typedef typename boost::property_traits<ColorMap>::value_type color_t;
    BGL_FORALL_EDGES_T(e, g, Graph) {
      put(color, e, (std::numeric_limits<color_t>::max)());
    }
    color_t colors = 0;
    BGL_FORALL_EDGES_T(e, g, Graph) {
      colors = (std::max)(colors, color_edge(g, color, e) + 1);
    }
    return colors;
  }
}

#endif


/* ---------------------------------------------------------------------
 *
 * Copyright (C) 2011 - 2021 by the deal.II authors
 *
 * This file is part of the deal.II library.
 *
 * The deal.II library is free software; you can use it, redistribute
 * it, and/or modify it under the terms of the GNU Lesser General
 * Public License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * The full text of the license can be found in the file LICENSE.md at
 * the top level directory of deal.II.
 *
 * ---------------------------------------------------------------------
 *
 * Author: Wolfgang Bangerth, Texas A&M University, 2011
 */

// @sect3{Include files}

// 这个程序的包含文件与之前许多其他程序的包含文件是一样的。唯一的新文件是在介绍中讨论的声明FE_Nothing的文件。hp目录下的文件已经在  step-27  中讨论过了。

#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/logstream.h>
#include <deal.II/base/function.h>
#include <deal.II/base/utilities.h>

#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/lac/sparse_direct.h>
#include <deal.II/lac/affine_constraints.h>

#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_refinement.h>

#include <deal.II/dofs/dof_tools.h>

#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_nothing.h>
#include <deal.II/fe/fe_system.h>
#include <deal.II/fe/fe_values.h>

#include <deal.II/hp/fe_collection.h>
#include <deal.II/hp/fe_values.h>

#include <deal.II/numerics/vector_tools.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/error_estimator.h>

#include <iostream>
#include <fstream>

namespace Step46
{
  using namespace dealii;
// @sect3{The FluidStructureProblem class template}

// 这是主类。如果你想的话，它是 step-8 和 step-22 的组合，因为它的成员变量要么针对全局问题（Triangulation和DoFHandler对象，以及 hp::FECollection 和各种线性代数对象），要么与弹性或斯托克斯子问题有关。然而，该类的一般结构与其他大多数实现静止问题的程序一样。

// 有几个不言自明的辅助函数（cell_is_in_fluid_domain, cell_is_in_solid_domain）（对两个子域的符号名称进行操作，这些名称将被用作属于子域的单元的 material_ids。正如介绍中所解释的那样）和几个函数（make_grid, set_active_fe_indices, assemble_interface_terms），这些函数已经从其他的函数中分离出来，可以在其他的教程程序中找到，我们将在实现它们的时候讨论。

// 最后一组变量 (  viscosity, lambda, eta  ) 描述了用于两个物理模型的材料属性。

  template <int dim>
  class FluidStructureProblem
  {
  public:
    FluidStructureProblem(const unsigned int stokes_degree,
                          const unsigned int elasticity_degree);
    void run();

  private:
    enum
    {
      fluid_domain_id,
      solid_domain_id
    };

    static bool cell_is_in_fluid_domain(
      const typename DoFHandler<dim>::cell_iterator &cell);

    static bool cell_is_in_solid_domain(
      const typename DoFHandler<dim>::cell_iterator &cell);

    void make_grid();
    void set_active_fe_indices();
    void setup_dofs();
    void assemble_system();
    void assemble_interface_term(
      const FEFaceValuesBase<dim> &         elasticity_fe_face_values,
      const FEFaceValuesBase<dim> &         stokes_fe_face_values,
      std::vector<Tensor<1, dim>> &         elasticity_phi,
      std::vector<SymmetricTensor<2, dim>> &stokes_symgrad_phi_u,
      std::vector<double> &                 stokes_phi_p,
      FullMatrix<double> &                  local_interface_matrix) const;
    void solve();
    void output_results(const unsigned int refinement_cycle) const;
    void refine_mesh();

    const unsigned int stokes_degree;
    const unsigned int elasticity_degree;

    Triangulation<dim>    triangulation;
    FESystem<dim>         stokes_fe;
    FESystem<dim>         elasticity_fe;
    hp::FECollection<dim> fe_collection;
    DoFHandler<dim>       dof_handler;

    AffineConstraints<double> constraints;

    SparsityPattern      sparsity_pattern;
    SparseMatrix<double> system_matrix;

    Vector<double> solution;
    Vector<double> system_rhs;

    const double viscosity;
    const double lambda;
    const double mu;
  };
// @sect3{Boundary values and right hand side}

// 下面这个类如其名。速度的边界值分别为2d的
//  $\mathbf u=(0, \sin(\pi x))^T$ 和3d的 $\mathbf u=(0,
//  0, \sin(\pi x)\sin(\pi y))^T$ 。
//  这个问题的其余边界条件都是同质的，在介绍中已经讨论过。右边的强迫项对于流体和固体都是零，所以我们不需要为它设置额外的类。

  template <int dim>
  class StokesBoundaryValues : public Function<dim>
  {
  public:
    StokesBoundaryValues()
      : Function<dim>(dim + 1 + dim)
    {}

    virtual double value(const Point<dim> & p,
                         const unsigned int component = 0) const override;

    virtual void vector_value(const Point<dim> &p,
                              Vector<double> &  value) const override;
  };

  template <int dim>
  double StokesBoundaryValues<dim>::value(const Point<dim> & p,
                                          const unsigned int component) const
  {
    Assert(component < this->n_components,
           ExcIndexRange(component, 0, this->n_components));

    if (component == dim - 1)
      switch (dim)
        {
          case 2:
            return std::sin(numbers::PI * p[0]);
          case 3:
            return std::sin(numbers::PI * p[0]) * std::sin(numbers::PI * p[1]);
          default:
            Assert(false, ExcNotImplemented());
        }

    return 0;
  }

  template <int dim>
  void StokesBoundaryValues<dim>::vector_value(const Point<dim> &p,
                                               Vector<double> &  values) const
  {
    for (unsigned int c = 0; c < this->n_components; ++c)
      values(c) = StokesBoundaryValues<dim>::value(p, c);
  }

//  @sect3{The FluidStructureProblem implementation}
// @sect4{Constructors and helper functions}

// 现在我们来谈谈这个程序的主类的实现。最初的几个函数是构造函数和辅助函数，可以用来确定一个单元格在域的哪个部分。鉴于介绍中对这些主题的讨论，它们的实现是相当明显的。在构造函数中，注意我们必须从斯托克斯和弹性的基本元素中构造 hp::FECollection 对象；使用 hp::FECollection::push_back 函数在这个集合中为它们分配了0和1的位置，我们必须记住这个顺序，并在程序的其余部分一致使用。

  template <int dim>
  FluidStructureProblem<dim>::FluidStructureProblem(
    const unsigned int stokes_degree,
    const unsigned int elasticity_degree)
    : stokes_degree(stokes_degree)
    , elasticity_degree(elasticity_degree)
    , triangulation(Triangulation<dim>::maximum_smoothing)
    , stokes_fe(FE_Q<dim>(stokes_degree + 1),
                dim,
                FE_Q<dim>(stokes_degree),
                1,
                FE_Nothing<dim>(),
                dim)
    , elasticity_fe(FE_Nothing<dim>(),
                    dim,
                    FE_Nothing<dim>(),
                    1,
                    FE_Q<dim>(elasticity_degree),
                    dim)
    , dof_handler(triangulation)
    , viscosity(2)
    , lambda(1)
    , mu(1)
  {
    fe_collection.push_back(stokes_fe);
    fe_collection.push_back(elasticity_fe);
  }

  template <int dim>
  bool FluidStructureProblem<dim>::cell_is_in_fluid_domain(
    const typename DoFHandler<dim>::cell_iterator &cell)
  {
    return (cell->material_id() == fluid_domain_id);
  }

  template <int dim>
  bool FluidStructureProblem<dim>::cell_is_in_solid_domain(
    const typename DoFHandler<dim>::cell_iterator &cell)
  {
    return (cell->material_id() == solid_domain_id);
  }
// @sect4{Meshes and assigning subdomains}

// 接下来的一对函数是处理生成网格，并确保所有表示子域的标志都是正确的。  make_grid  ，正如在介绍中所讨论的，生成一个 $8\times 8$ 的网格（或者一个 $8\times 8\times 8$ 的三维网格）以确保每个粗略的网格单元完全在一个子域内。生成这个网格后，我们在其边界上循环，并在顶部边界设置边界指标为1，这是我们设置非零迪里希特边界条件的唯一地方。在这之后，我们再次在所有单元上循环，设置材料指标—用来表示我们处于域的哪一部分，是流体指标还是固体指标。

  template <int dim>
  void FluidStructureProblem<dim>::make_grid()
  {
    GridGenerator::subdivided_hyper_cube(triangulation, 8, -1, 1);

    for (const auto &cell : triangulation.active_cell_iterators())
      for (const auto &face : cell->face_iterators())
        if (face->at_boundary() && (face->center()[dim - 1] == 1))
          face->set_all_boundary_ids(1);

    for (const auto &cell : dof_handler.active_cell_iterators())
      if (((std::fabs(cell->center()[0]) < 0.25) &&
           (cell->center()[dim - 1] > 0.5)) ||
          ((std::fabs(cell->center()[0]) >= 0.25) &&
           (cell->center()[dim - 1] > -0.5)))
        cell->set_material_id(fluid_domain_id);
      else
        cell->set_material_id(solid_domain_id);
  }

// 这对函数的第二部分决定在每个单元上使用哪个有限元。上面我们设置了每个粗略网格单元的材料指标，正如在介绍中提到的，这个信息在网格细化时将从母单元继承到子单元。

// 换句话说，只要我们细化（或创建）了网格，我们就可以依靠材料指示器来正确描述一个单元所处的域的哪一部分。然后我们利用这一点将单元的活动FE索引设置为该类的 hp::FECollection 成员变量中的相应元素：流体单元为0，固体单元为1。

  template <int dim>
  void FluidStructureProblem<dim>::set_active_fe_indices()
  {
    for (const auto &cell : dof_handler.active_cell_iterators())
      {
        if (cell_is_in_fluid_domain(cell))
          cell->set_active_fe_index(0);
        else if (cell_is_in_solid_domain(cell))
          cell->set_active_fe_index(1);
        else
          Assert(false, ExcNotImplemented());
      }
  }
// @sect4{FluidStructureProblem::setup_dofs}

// 下一步是为线性系统设置数据结构。为此，我们首先要用上面的函数设置活动FE指数，然后分配自由度，再确定线性系统的约束。后者包括像往常一样的悬挂节点约束，但也包括顶部流体边界的不均匀边界值，以及沿固体子域周边的零边界值。

  template <int dim>
  void FluidStructureProblem<dim>::setup_dofs()
  {
    set_active_fe_indices();
    dof_handler.distribute_dofs(fe_collection);

    {
      constraints.clear();
      DoFTools::make_hanging_node_constraints(dof_handler, constraints);

      const FEValuesExtractors::Vector velocities(0);
      VectorTools::interpolate_boundary_values(dof_handler,
                                               1,
                                               StokesBoundaryValues<dim>(),
                                               constraints,
                                               fe_collection.component_mask(
                                                 velocities));

      const FEValuesExtractors::Vector displacements(dim + 1);
      VectorTools::interpolate_boundary_values(
        dof_handler,
        0,
        Functions::ZeroFunction<dim>(dim + 1 + dim),
        constraints,
        fe_collection.component_mask(displacements));
    }

// 不过，我们还需要处理更多的约束条件：我们必须确保在流体和固体的界面上速度为零。下面这段代码已经在介绍中介绍过了。

    {
      std::vector<types::global_dof_index> local_face_dof_indices(
        stokes_fe.n_dofs_per_face());
      for (const auto &cell : dof_handler.active_cell_iterators())
        if (cell_is_in_fluid_domain(cell))
          for (const auto face_no : cell->face_indices())
            if (cell->face(face_no)->at_boundary() == false)
              {
                bool face_is_on_interface = false;

                if ((cell->neighbor(face_no)->has_children() == false) &&
                    (cell_is_in_solid_domain(cell->neighbor(face_no))))
                  face_is_on_interface = true;
                else if (cell->neighbor(face_no)->has_children() == true)
                  {
                    for (unsigned int sf = 0;
                         sf < cell->face(face_no)->n_children();
                         ++sf)
                      if (cell_is_in_solid_domain(
                            cell->neighbor_child_on_subface(face_no, sf)))
                        {
                          face_is_on_interface = true;
                          break;
                        }
                  }

                if (face_is_on_interface)
                  {
                    cell->face(face_no)->get_dof_indices(local_face_dof_indices,
                                                         0);
                    for (unsigned int i = 0; i < local_face_dof_indices.size();
                         ++i)
                      if (stokes_fe.face_system_to_component_index(i).first <
                          dim)
                        constraints.add_line(local_face_dof_indices[i]);
                  }
              }
    }

// 在这一切结束后，我们可以向约束对象声明，我们现在已经准备好了所有的约束，并且该对象可以重建其内部数据结构以提高效率。

    constraints.close();

    std::cout << "   Number of active cells: " << triangulation.n_active_cells()
              << std::endl
              << "   Number of degrees of freedom: " << dof_handler.n_dofs()
              << std::endl;

// 在这个函数的其余部分，我们创建了一个在介绍中广泛讨论的稀疏模式，并使用它来初始化矩阵；然后还将向量设置为正确的大小。

    {
      DynamicSparsityPattern dsp(dof_handler.n_dofs(), dof_handler.n_dofs());

      Table<2, DoFTools::Coupling> cell_coupling(fe_collection.n_components(),
                                                 fe_collection.n_components());
      Table<2, DoFTools::Coupling> face_coupling(fe_collection.n_components(),
                                                 fe_collection.n_components());

      for (unsigned int c = 0; c < fe_collection.n_components(); ++c)
        for (unsigned int d = 0; d < fe_collection.n_components(); ++d)
          {
            if (((c < dim + 1) && (d < dim + 1) &&
                 !((c == dim) && (d == dim))) ||
                ((c >= dim + 1) && (d >= dim + 1)))
              cell_coupling[c][d] = DoFTools::always;

            if ((c >= dim + 1) && (d < dim + 1))
              face_coupling[c][d] = DoFTools::always;
          }

      DoFTools::make_flux_sparsity_pattern(dof_handler,
                                           dsp,
                                           cell_coupling,
                                           face_coupling);
      constraints.condense(dsp);
      sparsity_pattern.copy_from(dsp);
    }

    system_matrix.reinit(sparsity_pattern);

    solution.reinit(dof_handler.n_dofs());
    system_rhs.reinit(dof_handler.n_dofs());
  }

//  @sect4{FluidStructureProblem::assemble_system}

// 下面是这个程序的中心函数：组装线性系统的函数。它在开始时有一长段设置辅助函数的内容：从创建正交公式到设置FEValues、FEFaceValues和FESubfaceValues对象，这些都是整合单元项以及界面项所必需的，以应对界面上的单元以相同大小或不同细化程度聚集在一起的情况...

  template <int dim>
  void FluidStructureProblem<dim>::assemble_system()
  {
    system_matrix = 0;
    system_rhs    = 0;

    const QGauss<dim> stokes_quadrature(stokes_degree + 2);
    const QGauss<dim> elasticity_quadrature(elasticity_degree + 2);

    hp::QCollection<dim> q_collection;
    q_collection.push_back(stokes_quadrature);
    q_collection.push_back(elasticity_quadrature);

    hp::FEValues<dim> hp_fe_values(fe_collection,
                                   q_collection,
                                   update_values | update_quadrature_points |
                                     update_JxW_values | update_gradients);

    const QGauss<dim - 1> common_face_quadrature(
      std::max(stokes_degree + 2, elasticity_degree + 2));

    FEFaceValues<dim>    stokes_fe_face_values(stokes_fe,
                                            common_face_quadrature,
                                            update_JxW_values |
                                              update_gradients | update_values);
    FEFaceValues<dim>    elasticity_fe_face_values(elasticity_fe,
                                                common_face_quadrature,
                                                update_normal_vectors |
                                                  update_values);
    FESubfaceValues<dim> stokes_fe_subface_values(stokes_fe,
                                                  common_face_quadrature,
                                                  update_JxW_values |
                                                    update_gradients |
                                                    update_values);
    FESubfaceValues<dim> elasticity_fe_subface_values(elasticity_fe,
                                                      common_face_quadrature,
                                                      update_normal_vectors |
                                                        update_values);

// ...描述局部对全局线性系统贡献所需的对象...

    const unsigned int stokes_dofs_per_cell = stokes_fe.n_dofs_per_cell();
    const unsigned int elasticity_dofs_per_cell =
      elasticity_fe.n_dofs_per_cell();

    FullMatrix<double> local_matrix;
    FullMatrix<double> local_interface_matrix(elasticity_dofs_per_cell,
                                              stokes_dofs_per_cell);
    Vector<double>     local_rhs;

    std::vector<types::global_dof_index> local_dof_indices;
    std::vector<types::global_dof_index> neighbor_dof_indices(
      stokes_dofs_per_cell);

    const Functions::ZeroFunction<dim> right_hand_side(dim + 1);

// ...到变量，允许我们提取形状函数的某些成分并缓存它们的值，而不是在每个正交点重新计算它们。

    const FEValuesExtractors::Vector velocities(0);
    const FEValuesExtractors::Scalar pressure(dim);
    const FEValuesExtractors::Vector displacements(dim + 1);

    std::vector<SymmetricTensor<2, dim>> stokes_symgrad_phi_u(
      stokes_dofs_per_cell);
    std::vector<double> stokes_div_phi_u(stokes_dofs_per_cell);
    std::vector<double> stokes_phi_p(stokes_dofs_per_cell);

    std::vector<Tensor<2, dim>> elasticity_grad_phi(elasticity_dofs_per_cell);
    std::vector<double>         elasticity_div_phi(elasticity_dofs_per_cell);
    std::vector<Tensor<1, dim>> elasticity_phi(elasticity_dofs_per_cell);

// 然后是所有单元格的主循环，和 step-27 一样，初始化当前单元格的 hp::FEValues 对象，提取适合当前单元格的FEValues对象。

    for (const auto &cell : dof_handler.active_cell_iterators())
      {
        hp_fe_values.reinit(cell);

        const FEValues<dim> &fe_values = hp_fe_values.get_present_fe_values();

        local_matrix.reinit(cell->get_fe().n_dofs_per_cell(),
                            cell->get_fe().n_dofs_per_cell());
        local_rhs.reinit(cell->get_fe().n_dofs_per_cell());

// 做完这些后，我们继续为属于斯托克斯和弹性区域的单元组装单元项。虽然我们原则上可以在一个公式中完成，实际上就是实现了介绍中所说的双线性形式，但我们意识到，我们的有限元空间的选择方式是，在每个单元上，有一组变量（速度和压力，或者位移）总是为零，因此，计算局部积分的更有效的方法是，根据测试我们处于域的哪一部分的 if 条款，只做必要的事情。

// 局部矩阵的实际计算与 step-22 以及 @ref vector_valued 文件模块中给出的弹性方程的计算相同。

        if (cell_is_in_fluid_domain(cell))
          {
            const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
            Assert(dofs_per_cell == stokes_dofs_per_cell, ExcInternalError());

            for (unsigned int q = 0; q < fe_values.n_quadrature_points; ++q)
              {
                for (unsigned int k = 0; k < dofs_per_cell; ++k)
                  {
                    stokes_symgrad_phi_u[k] =
                      fe_values[velocities].symmetric_gradient(k, q);
                    stokes_div_phi_u[k] =
                      fe_values[velocities].divergence(k, q);
                    stokes_phi_p[k] = fe_values[pressure].value(k, q);
                  }

                for (unsigned int i = 0; i < dofs_per_cell; ++i)
                  for (unsigned int j = 0; j < dofs_per_cell; ++j)
                    local_matrix(i, j) +=
                      (2 * viscosity * stokes_symgrad_phi_u[i] *
                         stokes_symgrad_phi_u[j] -
                       stokes_div_phi_u[i] * stokes_phi_p[j] -
                       stokes_phi_p[i] * stokes_div_phi_u[j]) *
                      fe_values.JxW(q);
              }
          }
        else
          {
            const unsigned int dofs_per_cell = cell->get_fe().n_dofs_per_cell();
            Assert(dofs_per_cell == elasticity_dofs_per_cell,
                   ExcInternalError());

            for (unsigned int q = 0; q < fe_values.n_quadrature_points; ++q)
              {
                for (unsigned int k = 0; k < dofs_per_cell; ++k)
                  {
                    elasticity_grad_phi[k] =
                      fe_values[displacements].gradient(k, q);
                    elasticity_div_phi[k] =
                      fe_values[displacements].divergence(k, q);
                  }

                for (unsigned int i = 0; i < dofs_per_cell; ++i)
                  for (unsigned int j = 0; j < dofs_per_cell; ++j)
                    {
                      local_matrix(i, j) +=
                        (lambda * elasticity_div_phi[i] *
                           elasticity_div_phi[j] +
                         mu * scalar_product(elasticity_grad_phi[i],
                                             elasticity_grad_phi[j]) +
                         mu *
                           scalar_product(elasticity_grad_phi[i],
                                          transpose(elasticity_grad_phi[j]))) *
                        fe_values.JxW(q);
                    }
              }
          }

// 一旦我们得到了单元积分的贡献，我们就把它们复制到全局矩阵中（通过 AffineConstraints::distribute_local_to_global 函数，立即处理约束）。请注意，我们没有向 local_rhs 变量中写入任何东西，尽管我们仍然需要传递它，因为消除非零边界值需要修改局部，因此也需要修改全局的右手值。

        local_dof_indices.resize(cell->get_fe().n_dofs_per_cell());
        cell->get_dof_indices(local_dof_indices);
        constraints.distribute_local_to_global(local_matrix,
                                               local_rhs,
                                               local_dof_indices,
                                               system_matrix,
                                               system_rhs);

// 这个函数更有趣的部分是我们看到关于两个子域之间的界面上的脸部条款。为此，我们首先要确保我们只组装一次，即使在所有单元的所有面的循环中会遇到界面的每一部分两次。我们武断地决定，只有当当前单元是固体子域的一部分，并且因此一个面不在边界上，并且它后面的潜在邻居是流体域的一部分时，我们才会评估界面条款。让我们从这些条件开始。

        if (cell_is_in_solid_domain(cell))
          for (const auto f : cell->face_indices())
            if (cell->face(f)->at_boundary() == false)
              {

// 在这一点上，我们知道当前的单元格是一个候选的整合对象，并且面 f 后面存在一个邻居。现在有三种可能性。

// - 邻居处于同一细化水平，并且没有孩子。

// - 邻居有子女。

// - 邻居比较粗糙。            在所有这三种情况下，我们只对它感兴趣，如果它是流体子域的一部分。因此，让我们从第一种最简单的情况开始：如果邻居处于同一层次，没有子女，并且是一个流体单元，那么这两个单元共享一个边界，这个边界是界面的一部分，我们想沿着这个边界整合界面项。我们所要做的就是用当前面和邻接单元的面初始化两个FEFaceValues对象（注意我们是如何找出邻接单元的哪个面与当前单元接壤的），然后把东西传给评估界面项的函数（这个函数的第三个到第五个参数为它提供了抓取数组）。然后，结果再次被复制到全局矩阵中，使用一个知道本地矩阵的行和列的DoF指数来自不同单元的函数。

                if ((cell->neighbor(f)->level() == cell->level()) &&
                    (cell->neighbor(f)->has_children() == false) &&
                    cell_is_in_fluid_domain(cell->neighbor(f)))
                  {
                    elasticity_fe_face_values.reinit(cell, f);
                    stokes_fe_face_values.reinit(cell->neighbor(f),
                                                 cell->neighbor_of_neighbor(f));

                    assemble_interface_term(elasticity_fe_face_values,
                                            stokes_fe_face_values,
                                            elasticity_phi,
                                            stokes_symgrad_phi_u,
                                            stokes_phi_p,
                                            local_interface_matrix);

                    cell->neighbor(f)->get_dof_indices(neighbor_dof_indices);
                    constraints.distribute_local_to_global(
                      local_interface_matrix,
                      local_dof_indices,
                      neighbor_dof_indices,
                      system_matrix);
                  }

// 第二种情况是，如果邻居还有更多的孩子。在这种情况下，我们必须在邻居的所有子女中进行循环，看他们是否属于流体子域的一部分。如果它们是，那么我们就在共同界面上进行整合，这个界面是邻居的一个面和当前单元的一个子面，要求我们对邻居使用FEFaceValues，对当前单元使用FESubfaceValues。

                else if ((cell->neighbor(f)->level() == cell->level()) &&
                         (cell->neighbor(f)->has_children() == true))
                  {
                    for (unsigned int subface = 0;
                         subface < cell->face(f)->n_children();
                         ++subface)
                      if (cell_is_in_fluid_domain(
                            cell->neighbor_child_on_subface(f, subface)))
                        {
                          elasticity_fe_subface_values.reinit(cell, f, subface);
                          stokes_fe_face_values.reinit(
                            cell->neighbor_child_on_subface(f, subface),
                            cell->neighbor_of_neighbor(f));

                          assemble_interface_term(elasticity_fe_subface_values,
                                                  stokes_fe_face_values,
                                                  elasticity_phi,
                                                  stokes_symgrad_phi_u,
                                                  stokes_phi_p,
                                                  local_interface_matrix);

                          cell->neighbor_child_on_subface(f, subface)
                            ->get_dof_indices(neighbor_dof_indices);
                          constraints.distribute_local_to_global(
                            local_interface_matrix,
                            local_dof_indices,
                            neighbor_dof_indices,
                            system_matrix);
                        }
                  }

// 最后一个选项是，邻居比较粗大。在这种情况下，我们必须为邻居使用一个FESubfaceValues对象，为当前单元使用一个FEFaceValues；其余部分与之前相同。

                else if (cell->neighbor_is_coarser(f) &&
                         cell_is_in_fluid_domain(cell->neighbor(f)))
                  {
                    elasticity_fe_face_values.reinit(cell, f);
                    stokes_fe_subface_values.reinit(
                      cell->neighbor(f),
                      cell->neighbor_of_coarser_neighbor(f).first,
                      cell->neighbor_of_coarser_neighbor(f).second);

                    assemble_interface_term(elasticity_fe_face_values,
                                            stokes_fe_subface_values,
                                            elasticity_phi,
                                            stokes_symgrad_phi_u,
                                            stokes_phi_p,
                                            local_interface_matrix);

                    cell->neighbor(f)->get_dof_indices(neighbor_dof_indices);
                    constraints.distribute_local_to_global(
                      local_interface_matrix,
                      local_dof_indices,
                      neighbor_dof_indices,
                      system_matrix);
                  }
              }
      }
  }

// 在组装全局系统的函数中，我们将计算接口条款传递给我们在此讨论的一个单独的函数。关键是，尽管我们无法预测FEFaceValues和FESubfaceValues对象的组合，但它们都是从FEFaceValuesBase类派生出来的，因此我们不必在意：该函数被简单地调用，有两个这样的对象表示面的两边的正交点上的形状函数值。然后我们做我们一直在做的事情：我们用形状函数的值和它们的导数来填充从头数组，然后循环计算矩阵的所有条目来计算局部积分。我们在这里评估的双线性形式的细节在介绍中给出。

  template <int dim>
  void FluidStructureProblem<dim>::assemble_interface_term(
    const FEFaceValuesBase<dim> &         elasticity_fe_face_values,
    const FEFaceValuesBase<dim> &         stokes_fe_face_values,
    std::vector<Tensor<1, dim>> &         elasticity_phi,
    std::vector<SymmetricTensor<2, dim>> &stokes_symgrad_phi_u,
    std::vector<double> &                 stokes_phi_p,
    FullMatrix<double> &                  local_interface_matrix) const
  {
    Assert(stokes_fe_face_values.n_quadrature_points ==
             elasticity_fe_face_values.n_quadrature_points,
           ExcInternalError());
    const unsigned int n_face_quadrature_points =
      elasticity_fe_face_values.n_quadrature_points;

    const FEValuesExtractors::Vector velocities(0);
    const FEValuesExtractors::Scalar pressure(dim);
    const FEValuesExtractors::Vector displacements(dim + 1);

    local_interface_matrix = 0;
    for (unsigned int q = 0; q < n_face_quadrature_points; ++q)
      {
        const Tensor<1, dim> normal_vector =
          elasticity_fe_face_values.normal_vector(q);

        for (unsigned int k = 0; k < stokes_fe_face_values.dofs_per_cell; ++k)
          {
            stokes_symgrad_phi_u[k] =
              stokes_fe_face_values[velocities].symmetric_gradient(k, q);
            stokes_phi_p[k] = stokes_fe_face_values[pressure].value(k, q);
          }
        for (unsigned int k = 0; k < elasticity_fe_face_values.dofs_per_cell;
             ++k)
          elasticity_phi[k] =
            elasticity_fe_face_values[displacements].value(k, q);

        for (unsigned int i = 0; i < elasticity_fe_face_values.dofs_per_cell;
             ++i)
          for (unsigned int j = 0; j < stokes_fe_face_values.dofs_per_cell; ++j)
            local_interface_matrix(i, j) +=
              -((2 * viscosity * (stokes_symgrad_phi_u[j] * normal_vector) -
                 stokes_phi_p[j] * normal_vector) *
                elasticity_phi[i] * stokes_fe_face_values.JxW(q));
      }
  }
// @sect4{FluidStructureProblem::solve}

// 正如介绍中所讨论的，我们在这里使用了一个相当琐碎的求解器：我们只是将线性系统传递给SparseDirectUMFPACK直接求解器（例如，见 step-29  ）。在求解之后，我们唯一要做的是确保悬挂的节点和边界值约束是正确的。

  template <int dim>
  void FluidStructureProblem<dim>::solve()
  {
    SparseDirectUMFPACK direct_solver;
    direct_solver.initialize(system_matrix);
    direct_solver.vmult(solution, system_rhs);

    constraints.distribute(solution);
  }

//  @sect4{FluidStructureProblem::output_results}

// 生成图形输出在这里相当简单：我们所要做的就是确定解向量的哪些成分属于标量和/或向量（例如，见 step-22 之前的例子），然后把它全部传递给DataOut类。

  template <int dim>
  void FluidStructureProblem<dim>::output_results(
    const unsigned int refinement_cycle) const
  {
    std::vector<std::string> solution_names(dim, "velocity");
    solution_names.emplace_back("pressure");
    for (unsigned int d = 0; d < dim; ++d)
      solution_names.emplace_back("displacement");

    std::vector<DataComponentInterpretation::DataComponentInterpretation>
      data_component_interpretation(
        dim, DataComponentInterpretation::component_is_part_of_vector);
    data_component_interpretation.push_back(
      DataComponentInterpretation::component_is_scalar);
    for (unsigned int d = 0; d < dim; ++d)
      data_component_interpretation.push_back(
        DataComponentInterpretation::component_is_part_of_vector);

    DataOut<dim> data_out;
    data_out.attach_dof_handler(dof_handler);

    data_out.add_data_vector(solution,
                             solution_names,
                             DataOut<dim>::type_dof_data,
                             data_component_interpretation);
    data_out.build_patches();

    std::ofstream output(
      "solution-" + Utilities::int_to_string(refinement_cycle, 2) + ".vtk");
    data_out.write_vtk(output);
  }
// @sect4{FluidStructureProblem::refine_mesh}

// 下一步是细化网格。正如在介绍中所讨论的，这有点棘手，主要是因为流体和固体子域使用的变量具有不同的物理尺寸，因此，误差估计的绝对大小不能直接比较。因此，我们将不得不对它们进行缩放。因此，在函数的顶部，我们首先分别计算不同变量的误差估计值（在流体域中使用速度而不是压力，在固体域中使用位移）。

  template <int dim>
  void FluidStructureProblem<dim>::refine_mesh()
  {
    Vector<float> stokes_estimated_error_per_cell(
      triangulation.n_active_cells());
    Vector<float> elasticity_estimated_error_per_cell(
      triangulation.n_active_cells());

    const QGauss<dim - 1> stokes_face_quadrature(stokes_degree + 2);
    const QGauss<dim - 1> elasticity_face_quadrature(elasticity_degree + 2);

    hp::QCollection<dim - 1> face_q_collection;
    face_q_collection.push_back(stokes_face_quadrature);
    face_q_collection.push_back(elasticity_face_quadrature);

    const FEValuesExtractors::Vector velocities(0);
    KellyErrorEstimator<dim>::estimate(
      dof_handler,
      face_q_collection,
      std::map<types::boundary_id, const Function<dim> *>(),
      solution,
      stokes_estimated_error_per_cell,
      fe_collection.component_mask(velocities));

    const FEValuesExtractors::Vector displacements(dim + 1);
    KellyErrorEstimator<dim>::estimate(
      dof_handler,
      face_q_collection,
      std::map<types::boundary_id, const Function<dim> *>(),
      solution,
      elasticity_estimated_error_per_cell,
      fe_collection.component_mask(displacements));

// 然后，我们通过除以误差估计值的法线对其进行归一化处理，并按照介绍中所讨论的那样，将流体误差指标按4的系数进行缩放。然后将这些结果加在一起，形成一个包含所有单元的误差指标的向量。

    stokes_estimated_error_per_cell *=
      4. / stokes_estimated_error_per_cell.l2_norm();
    elasticity_estimated_error_per_cell *=
      1. / elasticity_estimated_error_per_cell.l2_norm();

    Vector<float> estimated_error_per_cell(triangulation.n_active_cells());

    estimated_error_per_cell += stokes_estimated_error_per_cell;
    estimated_error_per_cell += elasticity_estimated_error_per_cell;

// 在实际细化网格之前，函数的倒数第二部分涉及到我们在介绍中已经提到的启发式方法：由于解是不连续的，KellyErrorEstimator类对位于子域之间边界的单元感到困惑：它认为那里的误差很大，因为梯度的跳跃很大，尽管这完全是预期的，事实上在精确解中也存在这一特征，因此不表明任何数值错误。

// 因此，我们将界面上的所有单元的误差指标设置为零；决定影响哪些单元的条件略显尴尬，因为我们必须考虑到自适应细化网格的可能性，这意味着邻近的单元可能比当前的单元更粗，或者事实上可能被细化一些。这些嵌套条件的结构与我们在 assemble_system 中组装接口条款时遇到的情况基本相同。

    for (const auto &cell : dof_handler.active_cell_iterators())
      for (const auto f : cell->face_indices())
        if (cell_is_in_solid_domain(cell))
          {
            if ((cell->at_boundary(f) == false) &&
                (((cell->neighbor(f)->level() == cell->level()) &&
                  (cell->neighbor(f)->has_children() == false) &&
                  cell_is_in_fluid_domain(cell->neighbor(f))) ||
                 ((cell->neighbor(f)->level() == cell->level()) &&
                  (cell->neighbor(f)->has_children() == true) &&
                  (cell_is_in_fluid_domain(
                    cell->neighbor_child_on_subface(f, 0)))) ||
                 (cell->neighbor_is_coarser(f) &&
                  cell_is_in_fluid_domain(cell->neighbor(f)))))
              estimated_error_per_cell(cell->active_cell_index()) = 0;
          }
        else
          {
            if ((cell->at_boundary(f) == false) &&
                (((cell->neighbor(f)->level() == cell->level()) &&
                  (cell->neighbor(f)->has_children() == false) &&
                  cell_is_in_solid_domain(cell->neighbor(f))) ||
                 ((cell->neighbor(f)->level() == cell->level()) &&
                  (cell->neighbor(f)->has_children() == true) &&
                  (cell_is_in_solid_domain(
                    cell->neighbor_child_on_subface(f, 0)))) ||
                 (cell->neighbor_is_coarser(f) &&
                  cell_is_in_solid_domain(cell->neighbor(f)))))
              estimated_error_per_cell(cell->active_cell_index()) = 0;
          }

    GridRefinement::refine_and_coarsen_fixed_number(triangulation,
                                                    estimated_error_per_cell,
                                                    0.3,
                                                    0.0);
    triangulation.execute_coarsening_and_refinement();
  }

//  @sect4{FluidStructureProblem::run}

// 像往常一样，这是控制整个操作流程的函数。如果你读过教程程序  step-1  到  step-6  ，例如，那么你已经对以下结构相当熟悉。

  template <int dim>
  void FluidStructureProblem<dim>::run()
  {
    make_grid();

    for (unsigned int refinement_cycle = 0; refinement_cycle < 10 - 2 * dim;
         ++refinement_cycle)
      {
        std::cout << "Refinement cycle " << refinement_cycle << std::endl;

        if (refinement_cycle > 0)
          refine_mesh();

        setup_dofs();

        std::cout << "   Assembling..." << std::endl;
        assemble_system();

        std::cout << "   Solving..." << std::endl;
        solve();

        std::cout << "   Writing output..." << std::endl;
        output_results(refinement_cycle);

        std::cout << std::endl;
      }
  }
} // namespace Step46

//  @sect4{The main() function}

// 这个，最后的，函数所包含的内容几乎与其他大多数教程程序的内容完全一样。

int main()
{
  try
    {
      using namespace Step46;

      FluidStructureProblem<2> flow_problem(1, 1);
      flow_problem.run();
    }
  catch (std::exception &exc)
    {
      std::cerr << std::endl
                << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;

      return 1;
    }
  catch (...)
    {
      std::cerr << std::endl
                << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Unknown exception!" << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }

  return 0;
}