payload: {"action":"created","comment":{"url":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls/comments/271624794","pull_request_review_id":222058241,"id":271624794,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDI3MTYyNDc5NA==","diff_hunk":"@@ -0,0 +1,410 @@\n+//    |  /           |\n+//    ' /   __| _` | __|  _ \\   __|\n+//    . \\  |   (   | |   (   |\\__ `\n+//   _|\\_\\_|  \\__,_|\\__|\\___/ ____/\n+//                   Multi-Physics\n+//\n+//  License:\t\t BSD License\n+//\t\t\t\t\t Kratos default license: kratos/license.txt\n+//\n+//\n+//  Main authors:    Marc Nuñez, based on A. Geiser, M. Fusseder, I. Lopez and R. Rossi work\n+//\n+\n+#include \"incompressible_adjoint_potential_flow_element.h\"\n+\n+namespace Kratos\n+{\n+    template <class TPrimalElement>\n+    Element::Pointer AdjointIncompressiblePotentialFlowElement<TPrimalElement>::Create(IndexType NewId, NodesArrayType const& ThisNodes, PropertiesType::Pointer pProperties) const \n+    {\n+        KRATOS_TRY\n+          return Kratos::make_shared<AdjointIncompressiblePotentialFlowElement<TPrimalElement>>(NewId, GetGeometry().Create(ThisNodes), pProperties);\n+        KRATOS_CATCH(\"\");\n+    }\n+\n+    template <class TPrimalElement>\n+    Element::Pointer AdjointIncompressiblePotentialFlowElement<TPrimalElement>::Create(IndexType NewId, GeometryType::Pointer pGeom, PropertiesType::Pointer pProperties) const \n+    {\n+        KRATOS_TRY\n+            return Kratos::make_shared<AdjointIncompressiblePotentialFlowElement<TPrimalElement>>(NewId, pGeom, pProperties);\n+        KRATOS_CATCH(\"\");\n+    }\n+\n+    template <class TPrimalElement>\n+    Element::Pointer AdjointIncompressiblePotentialFlowElement<TPrimalElement>::Clone(IndexType NewId, NodesArrayType const& ThisNodes) const \n+    {\n+        KRATOS_TRY\n+        return Element::Pointer(new AdjointIncompressiblePotentialFlowElement(NewId, GetGeometry().Create(ThisNodes), pGetProperties()));\n+        KRATOS_CATCH(\"\");\n+    }\n+\n+    template <class TPrimalElement>\n+    Element::IntegrationMethod AdjointIncompressiblePotentialFlowElement<TPrimalElement>::GetIntegrationMethod() const \n+    {\n+        return mpPrimalElement->GetIntegrationMethod();\n+    }\n+  \n+    template <class TPrimalElement>\n+    void AdjointIncompressiblePotentialFlowElement<TPrimalElement>::Initialize() \n+    {   \n+        mpPrimalElement->Initialize();\n+    }\n+\n+    template <class TPrimalElement>\n+    void AdjointIncompressiblePotentialFlowElement<TPrimalElement>::InitializeSolutionStep(ProcessInfo& rCurrentProcessInfo) \n+    {\n+        mpPrimalElement->Data() = this->Data();\n+        mpPrimalElement->Set(Flags(*this));\n+        mpPrimalElement->InitializeSolutionStep(rCurrentProcessInfo);\n+    }\n+\n+    template <class TPrimalElement>\n+    void AdjointIncompressiblePotentialFlowElement<TPrimalElement>::FinalizeSolutionStep(ProcessInfo& rCurrentProcessInfo) \n+    {\n+\n+    }\n+\n+    template <class TPrimalElement>\n+    void AdjointIncompressiblePotentialFlowElement<TPrimalElement>::CalculateLocalSystem(MatrixType& rLeftHandSideMatrix,\n+                                      VectorType& rRightHandSideVector,\n+                                      ProcessInfo& rCurrentProcessInfo) \n+    {\n+        mpPrimalElement->CalculateLocalSystem(rLeftHandSideMatrix,\n+                                              rRightHandSideVector,\n+                                              rCurrentProcessInfo);\n+    }\n+\n+    template <class TPrimalElement>\n+    void AdjointIncompressiblePotentialFlowElement<TPrimalElement>::CalculateLeftHandSide(MatrixType& rLeftHandSideMatrix,\n+                                       ProcessInfo& rCurrentProcessInfo) \n+    {\n+        MatrixType tmp;\n+        mpPrimalElement->CalculateLeftHandSide(tmp, rCurrentProcessInfo);\n+        rLeftHandSideMatrix = trans(tmp);                                    \n+    }\n+\n+    template <class TPrimalElement>\n+    void AdjointIncompressiblePotentialFlowElement<TPrimalElement>::CalculateRightHandSide(VectorType& rRightHandSideVector,\n+                                        ProcessInfo& rCurrentProcessInfo) \n+    {\n+        mpPrimalElement->CalculateRightHandSide(rRightHandSideVector,\n+                                                rCurrentProcessInfo);\n+    }\n+\n+    template <class TPrimalElement>\n+    void AdjointIncompressiblePotentialFlowElement<TPrimalElement>::GetValueOnIntegrationPoints(const Variable<double>& rVariable,\n+            std::vector<double>& rValues,\n+            const ProcessInfo& rCurrentProcessInfo) \n+    {\n+        mpPrimalElement->GetValueOnIntegrationPoints(rVariable,rValues,rCurrentProcessInfo);\n+    }\n+    \n+    template <class TPrimalElement>\n+    void AdjointIncompressiblePotentialFlowElement<TPrimalElement>::GetValueOnIntegrationPoints(const Variable<array_1d<double,3> >& rVariable,\n+            std::vector< array_1d<double,3> >& rValues,\n+            const ProcessInfo& rCurrentProcessInfo) \n+    {\n+        mpPrimalElement->GetValueOnIntegrationPoints(rVariable,rValues,rCurrentProcessInfo);\n+    }\n+\n+    template <class TPrimalElement>\n+    void AdjointIncompressiblePotentialFlowElement<TPrimalElement>::GetValuesVector(Vector& rValues, int Step) \n+    {\n+        KRATOS_TRY\n+        const AdjointIncompressiblePotentialFlowElement& r_this = *this;\n+        const int wake = r_this.GetValue(WAKE);\n+        const int kutta = r_this.GetValue(KUTTA);\n+\n+        if (wake == 1) // wake element\n+        {\n+            if(rValues.size() != 2*NumNodes)\n+                rValues.resize(2*NumNodes, false);\n+\n+            array_1d<double,NumNodes> distances;\n+            GetWakeDistances(distances);\n+            GetValuesOnSplitElement(rValues,distances);\n+            \n+        }else{ // normal element\n+            if(rValues.size() != NumNodes)\n+                rValues.resize(NumNodes, false);\n+\n+            if(kutta == 0){\n+                for(unsigned int i=0; i<NumNodes; i++)\n+                    rValues[i] =GetGeometry()[i].FastGetSolutionStepValue(ADJOINT_VELOCITY_POTENTIAL);\n+            }else{\n+                for(unsigned int i=0; i<NumNodes; i++){\n+                    if (!GetGeometry()[i].GetValue(TRAILING_EDGE))\n+                        rValues[i] = GetGeometry()[i].FastGetSolutionStepValue(ADJOINT_VELOCITY_POTENTIAL);\n+                    else\n+                        rValues[i] = GetGeometry()[i].FastGetSolutionStepValue(ADJOINT_AUXILIARY_VELOCITY_POTENTIAL);\n+                }\n+            }\n+        }\n+        KRATOS_CATCH(\"\")\n+    }\n+\n+    template <class TPrimalElement>\n+    void AdjointIncompressiblePotentialFlowElement<TPrimalElement>::CalculateSensitivityMatrix(const Variable<array_1d<double,3> >& rDesignVariable,\n+                                            Matrix& rOutput,\n+                                            const ProcessInfo& rCurrentProcessInfo) \n+    {\n+        KRATOS_TRY;\n+        const double delta = this->GetPerturbationSize();\n+        ProcessInfo process_info = rCurrentProcessInfo;\n+\n+        Vector RHS;\n+        Vector RHS_perturbed;\n+\n+        pGetPrimalElement()->CalculateRightHandSide(RHS, process_info);\n+\n+        if (rOutput.size1() != NumNodes)\n+            rOutput.resize(Dim*NumNodes, RHS.size(), false);\n+\n+        for(unsigned int i_node = 0; i_node<NumNodes; i_node++){\n+            for(unsigned int i_dim = 0; i_dim<Dim; i_dim++){\n+                if ((GetGeometry()[i_node].Is(SOLID)) && (!GetGeometry()[i_node].GetValue(TRAILING_EDGE))){\n+                    pGetPrimalElement()->GetGeometry()[i_node].GetInitialPosition()[i_dim] += delta;\n+                    pGetPrimalElement()->GetGeometry()[i_node].Coordinates()[i_dim] += delta;\n+\n+                    // compute LHS after perturbation\n+                    pGetPrimalElement()->CalculateRightHandSide(RHS_perturbed, process_info);\n+\n+                    //compute derivative of RHS w.r.t. design variable with finite differences\n+                    for(unsigned int i = 0; i < RHS.size(); ++i)\n+                        rOutput( (i_dim + i_node*Dim), i) = (RHS_perturbed[i] - RHS[i]) / delta;\n+\n+                    // unperturb the design variable\n+                    pGetPrimalElement()->GetGeometry()[i_node].GetInitialPosition()[i_dim] -= delta;\n+                    pGetPrimalElement()->GetGeometry()[i_node].Coordinates()[i_dim] -= delta;\n+                }else{\n+                    for(unsigned int i = 0; i < RHS.size(); ++i)\n+                        rOutput( (i_dim + i_node*Dim), i) = 0.0;\n+                }\n+            }\n+        }\n+\n+        KRATOS_CATCH(\"\")\n+    }\n+\n+    template <class TPrimalElement>\n+    void AdjointIncompressiblePotentialFlowElement<TPrimalElement>::EquationIdVector(EquationIdVectorType& rResult, ProcessInfo& CurrentProcessInfo) \n+    {\n+        const AdjointIncompressiblePotentialFlowElement& r_this = *this;\n+        const int wake = r_this.GetValue(WAKE);\n+        const int kutta = r_this.GetValue(KUTTA);\n+\n+        if(wake == 0)//normal element\n+        {\n+            if (rResult.size() != NumNodes)\n+                rResult.resize(NumNodes, false);\n+\n+            if(kutta == 0){\n+                for (unsigned int i = 0; i < NumNodes; i++)\n+                    rResult[i] = GetGeometry()[i].GetDof(ADJOINT_VELOCITY_POTENTIAL).EquationId();\n+            }\n+            else{\n+                for (unsigned int i = 0; i < NumNodes; i++){\n+                    if (!GetGeometry()[i].GetValue(TRAILING_EDGE))\n+                        rResult[i] = GetGeometry()[i].GetDof(ADJOINT_VELOCITY_POTENTIAL).EquationId();\n+                    else\n+                        rResult[i] = GetGeometry()[i].GetDof(ADJOINT_AUXILIARY_VELOCITY_POTENTIAL).EquationId();\n+                }\n+            }\n+        }\n+        else//wake element\n+        {\n+            if (rResult.size() != 2*NumNodes)\n+                rResult.resize(2*NumNodes, false);\n+\n+            array_1d<double,NumNodes> distances;\n+            GetWakeDistances(distances);\n+\n+            //positive part\n+            for (unsigned int i = 0; i < NumNodes; i++)\n+            {\n+                if(distances[i] > 0)\n+                    rResult[i] = GetGeometry()[i].GetDof(ADJOINT_VELOCITY_POTENTIAL).EquationId();\n+                else\n+                    rResult[i] = GetGeometry()[i].GetDof(ADJOINT_AUXILIARY_VELOCITY_POTENTIAL,0).EquationId();\n+            }\n+\n+            //negative part - sign is opposite to the previous case\n+            for (unsigned int i = 0; i < NumNodes; i++)\n+            {\n+                if(distances[i] < 0)\n+                    rResult[NumNodes+i] = GetGeometry()[i].GetDof(ADJOINT_VELOCITY_POTENTIAL).EquationId();\n+                else\n+                    rResult[NumNodes+i] = GetGeometry()[i].GetDof(ADJOINT_AUXILIARY_VELOCITY_POTENTIAL,0).EquationId();\n+            }\n+        }\n+\n+\n+    }\n+\n+    template <class TPrimalElement>\n+    void AdjointIncompressiblePotentialFlowElement<TPrimalElement>::GetDofList(DofsVectorType& rElementalDofList, ProcessInfo& CurrentProcessInfo) \n+    {\n+        const AdjointIncompressiblePotentialFlowElement& r_this = *this;\n+        const int wake = r_this.GetValue(WAKE);\n+        const int kutta = r_this.GetValue(KUTTA);\n+\n+        if(wake == 0) //normal element\n+        {\n+            if (rElementalDofList.size() != NumNodes)\n+                rElementalDofList.resize(NumNodes);\n+\n+            if(kutta == 0){\n+                for (unsigned int i = 0; i < NumNodes; i++)\n+                    rElementalDofList[i] = GetGeometry()[i].pGetDof(ADJOINT_VELOCITY_POTENTIAL);\n+            }\n+            else{\n+                for (unsigned int i = 0; i < NumNodes; i++){\n+                    if (!GetGeometry()[i].GetValue(TRAILING_EDGE))\n+                        rElementalDofList[i] = GetGeometry()[i].pGetDof(ADJOINT_VELOCITY_POTENTIAL);\n+                    else\n+                        rElementalDofList[i] = GetGeometry()[i].pGetDof(ADJOINT_AUXILIARY_VELOCITY_POTENTIAL);\n+                }\n+            }\n+        }\n+        else//wake element\n+        {\n+            if (rElementalDofList.size() != 2*NumNodes)\n+                rElementalDofList.resize(2*NumNodes);\n+\n+            array_1d<double,NumNodes> distances;\n+            GetWakeDistances(distances);\n+\n+            //positive part\n+            for (unsigned int i = 0; i < NumNodes; i++)\n+            {\n+                if(distances[i] > 0)\n+                    rElementalDofList[i] = GetGeometry()[i].pGetDof(ADJOINT_VELOCITY_POTENTIAL);\n+                else\n+                    rElementalDofList[i] = GetGeometry()[i].pGetDof(ADJOINT_AUXILIARY_VELOCITY_POTENTIAL);\n+            }\n+\n+            //negative part - sign is opposite to the previous case\n+            for (unsigned int i = 0; i < NumNodes; i++)\n+            {\n+                if(distances[i] < 0)\n+                    rElementalDofList[NumNodes+i] = GetGeometry()[i].pGetDof(ADJOINT_VELOCITY_POTENTIAL);\n+                else\n+                    rElementalDofList[NumNodes+i] = GetGeometry()[i].pGetDof(ADJOINT_AUXILIARY_VELOCITY_POTENTIAL);\n+            }\n+        }\n+    }\n+\n+    template <class TPrimalElement>\n+    int AdjointIncompressiblePotentialFlowElement<TPrimalElement>::Check(const ProcessInfo& rCurrentProcessInfo) \n+    {\n+\n+        KRATOS_TRY\n+\n+        if (this->Id() < 1)\n+        {\n+            KRATOS_THROW_ERROR(std::logic_error, \"AdjointIncompressiblePotentialFlowElement found with Id 0 or negative\", \"\")","path":"applications/CompressiblePotentialFlowApplication/custom_elements/incompressible_adjoint_potential_flow_element.cpp","position":306,"original_position":306,"commit_id":"2d67e0927cbc748f192bc80b92ed2adf65ee8bb0","original_commit_id":"2d67e0927cbc748f192bc80b92ed2adf65ee8bb0","user":{"login":"philbucher","id":25484702,"node_id":"MDQ6VXNlcjI1NDg0NzAy","avatar_url":"https://avatars3.githubusercontent.com/u/25484702?v=4","gravatar_id":"","url":"https://api.github.com/users/philbucher","html_url":"https://github.com/philbucher","followers_url":"https://api.github.com/users/philbucher/followers","following_url":"https://api.github.com/users/philbucher/following{/other_user}","gists_url":"https://api.github.com/users/philbucher/gists{/gist_id}","starred_url":"https://api.github.com/users/philbucher/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/philbucher/subscriptions","organizations_url":"https://api.github.com/users/philbucher/orgs","repos_url":"https://api.github.com/users/philbucher/repos","events_url":"https://api.github.com/users/philbucher/events{/privacy}","received_events_url":"https://api.github.com/users/philbucher/received_events","type":"User","site_admin":false},"body":"I think this check is already done in the baseclass => better to call that method","created_at":"2019-04-03T08:13:55Z","updated_at":"2019-04-03T08:19:29Z","html_url":"https://github.com/KratosMultiphysics/Kratos/pull/4258#discussion_r271624794","pull_request_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls/4258","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls/comments/271624794"},"html":{"href":"https://github.com/KratosMultiphysics/Kratos/pull/4258#discussion_r271624794"},"pull_request":{"href":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls/4258"}}},"pull_request":{"url":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls/4258","id":255884656,"node_id":"MDExOlB1bGxSZXF1ZXN0MjU1ODg0NjU2","html_url":"https://github.com/KratosMultiphysics/Kratos/pull/4258","diff_url":"https://github.com/KratosMultiphysics/Kratos/pull/4258.diff","patch_url":"https://github.com/KratosMultiphysics/Kratos/pull/4258.patch","issue_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/issues/4258","number":4258,"state":"open","locked":false,"title":"[PotentialFlow][Discussion] Adding the adjoint analysis and a lift response function.","user":{"login":"marcnunezc","id":32136457,"node_id":"MDQ6VXNlcjMyMTM2NDU3","avatar_url":"https://avatars1.githubusercontent.com/u/32136457?v=4","gravatar_id":"","url":"https://api.github.com/users/marcnunezc","html_url":"https://github.com/marcnunezc","followers_url":"https://api.github.com/users/marcnunezc/followers","following_url":"https://api.github.com/users/marcnunezc/following{/other_user}","gists_url":"https://api.github.com/users/marcnunezc/gists{/gist_id}","starred_url":"https://api.github.com/users/marcnunezc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/marcnunezc/subscriptions","organizations_url":"https://api.github.com/users/marcnunezc/orgs","repos_url":"https://api.github.com/users/marcnunezc/repos","events_url":"https://api.github.com/users/marcnunezc/events{/privacy}","received_events_url":"https://api.github.com/users/marcnunezc/received_events","type":"User","site_admin":false},"body":"Dear all, \r\n\r\nIn this PR, I add the adjoint analysis scheme from the StructuralMechanicsApplication to the CompressiblePotentialFlowApplication. On one hand, I would like to check with you if my general implementation (code-wise) is correct and what can be improved. On the other hand, I would like to know if you have any general comment about the validation process or some issues that I may have not taken into account.\r\n\r\n\r\n## IMPLEMENTATION ##\r\n\r\nAs for the implementation, I have followed the adjoint analysis implemented in the StructuralMechApp. My concerns in this aspect is that in some cases I had to copy the same code as in the StructuralMechApp,  only changing either variables names or element names in order for it to work in the PotentalFlowApp. **Is this accepted or is there any possible workaround?** This is the case for:\r\n* replace_elements_and_conditions_for_adjoint_problem_process.cpp - Where I have to define my custom adjoint elements.\r\n* adjoint_potential_response_function.cpp - Which is an adapted version of the adjoint_structural_response_function.cpp only changing the perturbation variable name.\r\n* adjoint_potential_static_scheme.h - Where I have my custom adjoint variables and I also compute the transpose of the LHS (since we have non-symmetric LHS in our case).\r\n\r\nRegarding the adjoint_postprocess.cpp, I use the one available in the StructuralMechancisApp by importing it in the adjoint solver.\r\n* Is this the common practice? Or is it preferred to implement a custom one in the same application to avoid having to compile StructuralMechacnicsApp? \r\n\r\n## VALIDATION ##\r\nThe aim is to obtain the gradient of the lift with respect to the position of the nodes of the geometry (an airfoil in our case). In order to do so, I have implemented a response function based on the computation of the lift from the potential jump across the wake, which is expressed as:\r\n\r\n![image](https://user-images.githubusercontent.com/32136457/53333338-dc8ecd80-38f5-11e9-8aa1-9adcbf5dd3e7.png)\r\nThis allows to compute the gradient of the **response function** as 2.0/v_inf in a single traced node. Also, the partial sensitivity in this case is zero.\r\n\r\nIn order to validate the adjoint gradient, I compared it with the gradient that results from the Finite difference method. Here are three examples with the distribution of the norm of the gradient along the airfoil and its relative error. As you may see, I forced to 0 values higher than 2 to be able to see the distribution comparison at the airfoil correctly.\r\n![naca0012_ds_200_aoa_5_far_field_mesh_size_20 0_case_0_comparisonbranchjump2](https://user-images.githubusercontent.com/32136457/53339260-2384bf00-3906-11e9-88d7-253f5ab63429.png)\r\n![naca0012_ds_200_aoa_5_far_field_mesh_size_20 0_case_1_comparisonbranchjump2](https://user-images.githubusercontent.com/32136457/53339259-2384bf00-3906-11e9-9a4a-ecaa6e80d005.png)\r\n![naca0012_ds_200_aoa_5_far_field_mesh_size_20 0_case_2_comparisonbranchjump2](https://user-images.githubusercontent.com/32136457/53339258-22ec2880-3906-11e9-93c1-15a2f345a58a.png)\r\n\r\n\r\n\r\nHere is an example for the same case as the last picture unmodified:\r\n\r\n![naca0012_ds_200_aoa_5_far_field_mesh_size_20 0_case_2_comparisonbranchjump2](https://user-images.githubusercontent.com/32136457/53339312-4d3de600-3906-11e9-8973-904c219131dc.png)\r\n\r\n\r\n## OTHER COMMENTS - Not included in this PR ##\r\nPrior to the jump_potential response function, I tried to implement the adjoint analysis by computing the lift as the sum that results from integrating the pressure distribution around the airfoil (not added in this PR). The response function for this case is implemented [in this branch.](https://github.com/KratosMultiphysics/Kratos/blob/CompressiblePotentialFlow/feature-embedded-mesh-implementation/applications/CompressiblePotentialFlowApplication/custom_response_functions/adjoint_lift_response_function_coordinates.cpp)\r\n\r\nHere is a picture comparing the gradient that comes from the adjoint potential jump, the finite difference using the potential jump, the gradient using the adjoint pressure distribution, and the finite difference gradient using the pressure distribution.\r\n\r\n![naca0012_ds_200_aoa_5_far_field_mesh_size_20 0_case_2_comparisonbranchalladjoint](https://user-images.githubusercontent.com/32136457/53335815-c6384000-38fc-11e9-9668-29f464623b2c.png)\r\n\r\nAs you can see, the adjoint gradient using the pressure distribution is different from the other three cases.\r\nThe tricky part about the pressure-distribution gradient is that apart from the gradient of the response function (df/du), the partial sensitivity (df/dx) is also needed. This requires to perturbate the nodes, and recalculate the pressure and the normal of the geometry to compute the lift.\r\n\r\nWhile I am still trying to get a validated pressure-distribution gradient, I showed you what I'm currently getting just in case you have any comments or idea on what could be the problem. The interest on computing the sensitivities like this is that it would work for other formulations apart from the potential flow.\r\n\r\nLastly, I also added a test for the adjoint analysis!\r\n\r\nPlease let me know any issue or comment that you may have! \r\n\r\nThank you!","created_at":"2019-02-25T13:14:34Z","updated_at":"2019-04-03T08:19:28Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6660dd3401ac32cc0c7279835d6839c89f4f5230","assignee":null,"assignees":[],"requested_reviewers":[{"login":"RiccardoRossi","id":8059665,"node_id":"MDQ6VXNlcjgwNTk2NjU=","avatar_url":"https://avatars1.githubusercontent.com/u/8059665?v=4","gravatar_id":"","url":"https://api.github.com/users/RiccardoRossi","html_url":"https://github.com/RiccardoRossi","followers_url":"https://api.github.com/users/RiccardoRossi/followers","following_url":"https://api.github.com/users/RiccardoRossi/following{/other_user}","gists_url":"https://api.github.com/users/RiccardoRossi/gists{/gist_id}","starred_url":"https://api.github.com/users/RiccardoRossi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RiccardoRossi/subscriptions","organizations_url":"https://api.github.com/users/RiccardoRossi/orgs","repos_url":"https://api.github.com/users/RiccardoRossi/repos","events_url":"https://api.github.com/users/RiccardoRossi/events{/privacy}","received_events_url":"https://api.github.com/users/RiccardoRossi/received_events","type":"User","site_admin":false},{"login":"msandre","id":22290001,"node_id":"MDQ6VXNlcjIyMjkwMDAx","avatar_url":"https://avatars0.githubusercontent.com/u/22290001?v=4","gravatar_id":"","url":"https://api.github.com/users/msandre","html_url":"https://github.com/msandre","followers_url":"https://api.github.com/users/msandre/followers","following_url":"https://api.github.com/users/msandre/following{/other_user}","gists_url":"https://api.github.com/users/msandre/gists{/gist_id}","starred_url":"https://api.github.com/users/msandre/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/msandre/subscriptions","organizations_url":"https://api.github.com/users/msandre/orgs","repos_url":"https://api.github.com/users/msandre/repos","events_url":"https://api.github.com/users/msandre/events{/privacy}","received_events_url":"https://api.github.com/users/msandre/received_events","type":"User","site_admin":false},{"login":"dbaumgaertner","id":25897162,"node_id":"MDQ6VXNlcjI1ODk3MTYy","avatar_url":"https://avatars2.githubusercontent.com/u/25897162?v=4","gravatar_id":"","url":"https://api.github.com/users/dbaumgaertner","html_url":"https://github.com/dbaumgaertner","followers_url":"https://api.github.com/users/dbaumgaertner/followers","following_url":"https://api.github.com/users/dbaumgaertner/following{/other_user}","gists_url":"https://api.github.com/users/dbaumgaertner/gists{/gist_id}","starred_url":"https://api.github.com/users/dbaumgaertner/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dbaumgaertner/subscriptions","organizations_url":"https://api.github.com/users/dbaumgaertner/orgs","repos_url":"https://api.github.com/users/dbaumgaertner/repos","events_url":"https://api.github.com/users/dbaumgaertner/events{/privacy}","received_events_url":"https://api.github.com/users/dbaumgaertner/received_events","type":"User","site_admin":false},{"login":"inigolcanalejo","id":28628414,"node_id":"MDQ6VXNlcjI4NjI4NDE0","avatar_url":"https://avatars0.githubusercontent.com/u/28628414?v=4","gravatar_id":"","url":"https://api.github.com/users/inigolcanalejo","html_url":"https://github.com/inigolcanalejo","followers_url":"https://api.github.com/users/inigolcanalejo/followers","following_url":"https://api.github.com/users/inigolcanalejo/following{/other_user}","gists_url":"https://api.github.com/users/inigolcanalejo/gists{/gist_id}","starred_url":"https://api.github.com/users/inigolcanalejo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/inigolcanalejo/subscriptions","organizations_url":"https://api.github.com/users/inigolcanalejo/orgs","repos_url":"https://api.github.com/users/inigolcanalejo/repos","events_url":"https://api.github.com/users/inigolcanalejo/events{/privacy}","received_events_url":"https://api.github.com/users/inigolcanalejo/received_events","type":"User","site_admin":false},{"login":"MFusseder","id":30687316,"node_id":"MDQ6VXNlcjMwNjg3MzE2","avatar_url":"https://avatars2.githubusercontent.com/u/30687316?v=4","gravatar_id":"","url":"https://api.github.com/users/MFusseder","html_url":"https://github.com/MFusseder","followers_url":"https://api.github.com/users/MFusseder/followers","following_url":"https://api.github.com/users/MFusseder/following{/other_user}","gists_url":"https://api.github.com/users/MFusseder/gists{/gist_id}","starred_url":"https://api.github.com/users/MFusseder/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MFusseder/subscriptions","organizations_url":"https://api.github.com/users/MFusseder/orgs","repos_url":"https://api.github.com/users/MFusseder/repos","events_url":"https://api.github.com/users/MFusseder/events{/privacy}","received_events_url":"https://api.github.com/users/MFusseder/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[{"id":538609887,"node_id":"MDU6TGFiZWw1Mzg2MDk4ODc=","url":"https://api.github.com/repos/KratosMultiphysics/Kratos/labels/Discussion","name":"Discussion","color":"cc317c","default":false},{"id":538395922,"node_id":"MDU6TGFiZWw1MzgzOTU5MjI=","url":"https://api.github.com/repos/KratosMultiphysics/Kratos/labels/Help%20Wanted","name":"Help Wanted","color":"cc317c","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls/4258/commits","review_comments_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls/4258/comments","review_comment_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls/comments{/number}","comments_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/issues/4258/comments","statuses_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/statuses/2d67e0927cbc748f192bc80b92ed2adf65ee8bb0","head":{"label":"KratosMultiphysics:cps/feature-adjoint-analysis","ref":"cps/feature-adjoint-analysis","sha":"2d67e0927cbc748f192bc80b92ed2adf65ee8bb0","user":{"login":"KratosMultiphysics","id":24840449,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI0ODQwNDQ5","avatar_url":"https://avatars1.githubusercontent.com/u/24840449?v=4","gravatar_id":"","url":"https://api.github.com/users/KratosMultiphysics","html_url":"https://github.com/KratosMultiphysics","followers_url":"https://api.github.com/users/KratosMultiphysics/followers","following_url":"https://api.github.com/users/KratosMultiphysics/following{/other_user}","gists_url":"https://api.github.com/users/KratosMultiphysics/gists{/gist_id}","starred_url":"https://api.github.com/users/KratosMultiphysics/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/KratosMultiphysics/subscriptions","organizations_url":"https://api.github.com/users/KratosMultiphysics/orgs","repos_url":"https://api.github.com/users/KratosMultiphysics/repos","events_url":"https://api.github.com/users/KratosMultiphysics/events{/privacy}","received_events_url":"https://api.github.com/users/KratosMultiphysics/received_events","type":"Organization","site_admin":false},"repo":{"id":81815495,"node_id":"MDEwOlJlcG9zaXRvcnk4MTgxNTQ5NQ==","name":"Kratos","full_name":"KratosMultiphysics/Kratos","private":false,"owner":{"login":"KratosMultiphysics","id":24840449,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI0ODQwNDQ5","avatar_url":"https://avatars1.githubusercontent.com/u/24840449?v=4","gravatar_id":"","url":"https://api.github.com/users/KratosMultiphysics","html_url":"https://github.com/KratosMultiphysics","followers_url":"https://api.github.com/users/KratosMultiphysics/followers","following_url":"https://api.github.com/users/KratosMultiphysics/following{/other_user}","gists_url":"https://api.github.com/users/KratosMultiphysics/gists{/gist_id}","starred_url":"https://api.github.com/users/KratosMultiphysics/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/KratosMultiphysics/subscriptions","organizations_url":"https://api.github.com/users/KratosMultiphysics/orgs","repos_url":"https://api.github.com/users/KratosMultiphysics/repos","events_url":"https://api.github.com/users/KratosMultiphysics/events{/privacy}","received_events_url":"https://api.github.com/users/KratosMultiphysics/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/KratosMultiphysics/Kratos","description":"Kratos Multiphysics (A.K.A Kratos) is a framework for building parallel multi-disciplinary simulation software. Modularity, extensibility and HPC are the main objectives. Kratos has BSD license and is written in C++ with extensive Python interface.","fork":false,"url":"https://api.github.com/repos/KratosMultiphysics/Kratos","forks_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/forks","keys_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/keys{/key_id}","collaborators_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/teams","hooks_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/hooks","issue_events_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/issues/events{/number}","events_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/events","assignees_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/assignees{/user}","branches_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/branches{/branch}","tags_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/tags","blobs_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/git/refs{/sha}","trees_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/git/trees{/sha}","statuses_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/statuses/{sha}","languages_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/languages","stargazers_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/stargazers","contributors_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/contributors","subscribers_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/subscribers","subscription_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/subscription","commits_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/commits{/sha}","git_commits_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/git/commits{/sha}","comments_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/comments{/number}","issue_comment_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/issues/comments{/number}","contents_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/contents/{+path}","compare_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/compare/{base}...{head}","merges_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/merges","archive_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/downloads","issues_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/issues{/number}","pulls_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls{/number}","milestones_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/milestones{/number}","notifications_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/labels{/name}","releases_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/releases{/id}","deployments_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/deployments","created_at":"2017-02-13T10:58:24Z","updated_at":"2019-04-03T07:33:58Z","pushed_at":"2019-04-03T08:08:53Z","git_url":"git://github.com/KratosMultiphysics/Kratos.git","ssh_url":"git@github.com:KratosMultiphysics/Kratos.git","clone_url":"https://github.com/KratosMultiphysics/Kratos.git","svn_url":"https://github.com/KratosMultiphysics/Kratos","homepage":"http://www.cimne.com/kratos/","size":1420421,"stargazers_count":305,"watchers_count":305,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":56,"mirror_url":null,"archived":false,"open_issues_count":210,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":56,"open_issues":210,"watchers":305,"default_branch":"master"}},"base":{"label":"KratosMultiphysics:master","ref":"master","sha":"4cbc8e771d406563a24ba40f7bcaa9b6f4eefa37","user":{"login":"KratosMultiphysics","id":24840449,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI0ODQwNDQ5","avatar_url":"https://avatars1.githubusercontent.com/u/24840449?v=4","gravatar_id":"","url":"https://api.github.com/users/KratosMultiphysics","html_url":"https://github.com/KratosMultiphysics","followers_url":"https://api.github.com/users/KratosMultiphysics/followers","following_url":"https://api.github.com/users/KratosMultiphysics/following{/other_user}","gists_url":"https://api.github.com/users/KratosMultiphysics/gists{/gist_id}","starred_url":"https://api.github.com/users/KratosMultiphysics/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/KratosMultiphysics/subscriptions","organizations_url":"https://api.github.com/users/KratosMultiphysics/orgs","repos_url":"https://api.github.com/users/KratosMultiphysics/repos","events_url":"https://api.github.com/users/KratosMultiphysics/events{/privacy}","received_events_url":"https://api.github.com/users/KratosMultiphysics/received_events","type":"Organization","site_admin":false},"repo":{"id":81815495,"node_id":"MDEwOlJlcG9zaXRvcnk4MTgxNTQ5NQ==","name":"Kratos","full_name":"KratosMultiphysics/Kratos","private":false,"owner":{"login":"KratosMultiphysics","id":24840449,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI0ODQwNDQ5","avatar_url":"https://avatars1.githubusercontent.com/u/24840449?v=4","gravatar_id":"","url":"https://api.github.com/users/KratosMultiphysics","html_url":"https://github.com/KratosMultiphysics","followers_url":"https://api.github.com/users/KratosMultiphysics/followers","following_url":"https://api.github.com/users/KratosMultiphysics/following{/other_user}","gists_url":"https://api.github.com/users/KratosMultiphysics/gists{/gist_id}","starred_url":"https://api.github.com/users/KratosMultiphysics/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/KratosMultiphysics/subscriptions","organizations_url":"https://api.github.com/users/KratosMultiphysics/orgs","repos_url":"https://api.github.com/users/KratosMultiphysics/repos","events_url":"https://api.github.com/users/KratosMultiphysics/events{/privacy}","received_events_url":"https://api.github.com/users/KratosMultiphysics/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/KratosMultiphysics/Kratos","description":"Kratos Multiphysics (A.K.A Kratos) is a framework for building parallel multi-disciplinary simulation software. Modularity, extensibility and HPC are the main objectives. Kratos has BSD license and is written in C++ with extensive Python interface.","fork":false,"url":"https://api.github.com/repos/KratosMultiphysics/Kratos","forks_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/forks","keys_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/keys{/key_id}","collaborators_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/teams","hooks_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/hooks","issue_events_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/issues/events{/number}","events_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/events","assignees_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/assignees{/user}","branches_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/branches{/branch}","tags_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/tags","blobs_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/git/refs{/sha}","trees_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/git/trees{/sha}","statuses_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/statuses/{sha}","languages_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/languages","stargazers_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/stargazers","contributors_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/contributors","subscribers_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/subscribers","subscription_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/subscription","commits_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/commits{/sha}","git_commits_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/git/commits{/sha}","comments_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/comments{/number}","issue_comment_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/issues/comments{/number}","contents_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/contents/{+path}","compare_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/compare/{base}...{head}","merges_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/merges","archive_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/downloads","issues_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/issues{/number}","pulls_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls{/number}","milestones_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/milestones{/number}","notifications_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/labels{/name}","releases_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/releases{/id}","deployments_url":"https://api.github.com/repos/KratosMultiphysics/Kratos/deployments","created_at":"2017-02-13T10:58:24Z","updated_at":"2019-04-03T07:33:58Z","pushed_at":"2019-04-03T08:08:53Z","git_url":"git://github.com/KratosMultiphysics/Kratos.git","ssh_url":"git@github.com:KratosMultiphysics/Kratos.git","clone_url":"https://github.com/KratosMultiphysics/Kratos.git","svn_url":"https://github.com/KratosMultiphysics/Kratos","homepage":"http://www.cimne.com/kratos/","size":1420421,"stargazers_count":305,"watchers_count":305,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":56,"mirror_url":null,"archived":false,"open_issues_count":210,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":56,"open_issues":210,"watchers":305,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls/4258"},"html":{"href":"https://github.com/KratosMultiphysics/Kratos/pull/4258"},"issue":{"href":"https://api.github.com/repos/KratosMultiphysics/Kratos/issues/4258"},"comments":{"href":"https://api.github.com/repos/KratosMultiphysics/Kratos/issues/4258/comments"},"review_comments":{"href":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls/4258/comments"},"review_comment":{"href":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/KratosMultiphysics/Kratos/pulls/4258/commits"},"statuses":{"href":"https://api.github.com/repos/KratosMultiphysics/Kratos/statuses/2d67e0927cbc748f192bc80b92ed2adf65ee8bb0"}},"author_association":"CONTRIBUTOR"}}
other: {"actor":{"display_login":"philbucher"}}