Project: s41467-025-58566-z | Function: CustomHexagonalNagaiHondaForce::AddForceContribution (cpp)

system: You are an expert C++ programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow C++ best practices and maintain consistent code style.
Return **only** the complete C++ function code.

user: Complete this C++ function:

**DESCRIPTION:** Implements the Nagai-Honda vertex model force calculations for epithelial tissue mechanics, computing forces from cell deformation, membrane tension, and cell-cell adhesion energies

**KEY TERMS:** {
  "deformation_contribution": "force from area conservation",
  "membrane_surface_tension_contribution": "force from perimeter elasticity",
  "adhesion_contribution": "force from cell-cell adhesion",
  "element_areas": "cell areas",
  "element_perimeters": "cell perimeters",
  "target_areas": "equilibrium cell areas",
  "AS_eq_surf": "amnioserosa equilibrium surface",
  "node_is_on_leading_edge": "vertex at tissue boundary"
}

**CONTEXT CODE:**  * Neither the name of the University of Oxford nor the names of its
   contributors may be used to endorse or promote products derived from this
   software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#include "CustomHexagonalNagaiHondaForce.hpp"

template<unsigned DIM>
CustomHexagonalNagaiHondaForce<DIM>::CustomHexagonalNagaiHondaForce()
   : AbstractForce<DIM>(),
     mNagaiHondaDeformationEnergyParameter(100.0), // This is 1.0 in the Nagai & Honda paper.
     mNagaiHonda_A_DeformationEnergyParameter(100.0), // This is 1.0 in the Nagai & Honda paper.
     mNagaiHondaMembraneSurfaceEnergyParameter(10.0), // This is 0.1 in the Nagai & Honda paper.
     mNagaiHonda_A_A_AdhesionEnergyParameter(0.5), // This corresponds to a value of 1.0 for
                                                      // the sigma parameter in the Nagai & Honda
                                                      // paper. In the paper, the sigma value is
                                                      // set to 0.01.
     mNagaiHonda_A_Boundary_AdhesionEnergyParameter(1.0), // This is 0.01 in the Nagai & Honda paper.
     mNagaiHonda_B_B_AdhesionEnergyParameter(1.0),
     mNagaiHonda_A_B_AdhesionEnergyParameter(1.0),
     mNagaiHonda_B_Boundary_AdhesionEnergyParameter(1.0),
     mNagaiHonda_A_C_AdhesionEnergyParameter(1.0),
     mNagaiHonda_B_C_AdhesionEnergyParameter(1.0),
     mNagaiHonda_C_C_AdhesionEnergyParameter(1.0),
     mNagaiHonda_C_Boundary_AdhesionEnergyParameter(1.0),
     mNagaiHonda_A_equilibrium_surface_Parameter(1.0),
     mNoiseContribution(0.0)

{
}

template<unsigned DIM>
CustomHexagonalNagaiHondaForce<DIM>::~CustomHexagonalNagaiHondaForce()
{
}

template<unsigned DIM>
template<unsigned DIM>
void CustomHexagonalNagaiHondaForce<DIM>::AddForceContribution(AbstractCellPopulation<DIM>& rCellPopulation)
{
    if (dynamic_cast<VertexBasedCellPopulation<DIM>*>(&rCellPopulation) == nullptr)
    {
        throw std::invalid_argument("Vertex-based cell population expected.");
    }

    VertexBasedCellPopulation<DIM>& rVertexCellPopulation = static_cast<VertexBasedCellPopulation<DIM>&>(rCellPopulation);

    // Get the mesh and nodes
    typename AbstractMesh<DIM, DIM>::EdgeIterator edge_iter = rVertexCellPopulation.rGetMesh().GetEdgeIteratorBegin();
    typename AbstractMesh<DIM, DIM>::EdgeIterator edge_end = rVertexCellPopulation.rGetMesh().GetEdgeIteratorEnd();

    for (; edge_iter != edge_end; ++edge_iter)
    {
        c_vector<double, DIM> force = zero_vector<double>(DIM);

        unsigned nodeAIndex = edge_iter->GetNodeA()->GetIndex();
        unsigned nodeBIndex = edge_iter->GetNodeB()->GetIndex();
        Node<DIM>* pNodeA = rVertexCellPopulation.GetNode(nodeAIndex);
        Node<DIM>* pNodeB = rVertexCellPopulation.GetNode(nodeBIndex);

        // Calculate deformation contribution
        double elementArea = rVertexCellPopulation.GetVolumeOfElement(rVertexCellPopulation.GetContainingElementIndex(*pNodeA));
        double targetArea = rVertexCellPopulation.GetElement(nodeAIndex)->GetCell()->GetTargetArea();
        double areaDifference = elementArea - targetArea;

        double deformationContrib = mNagaiHondaDeformationEnergyParameter * areaDifference / rVertexCellPopulation.GetElement(nodeAIndex)->GetCell()->GetRadius();

        c_vector<double, DIM> edgeVector = pNodeA->rGetLocation() - pNodeB->rGetLocation();
        double edgeLength = norm_2(edgeVector);

        force += deformationContrib * (pNodeA->rGetLocation() - pNodeB->rGetLocation()) / edgeLength;

        // Calculate membrane surface tension contribution
        double perimeterDifference = edgeLength - rVertexCellPopulation.GetElement(nodeAIndex)->GetCell()->GetTargetPerimeter();

        double membraneSurfaceTensionContrib = mNagaiHondaMembraneSurfaceEnergyParameter * perimeterDifference / edgeLength;

        force -= membraneSurfaceTensionContrib * (pNodeA->rGetLocation() - pNodeB->rGetLocation()) / edgeLength;

        // Calculate adhesion contribution
        double adhesionParameter = GetAdhesionParameter(pNodeA, pNodeB, rVertexCellPopulation);

        double adhesionContrib = adhesionParameter / edgeLength;

        force -= adhesionContrib * (pNodeA->rGetLocation() - pNodeB->rGetLocation()) / edgeLength;

        // Apply forces
        c_vector<double, DIM> currentForceOnA = pNodeA->GetAppliedForce();
        c_vector<double, DIM> currentForceOnB = pNodeB->GetAppliedForce();

        pNodeA->AddAppliedForce(-force);
        pNodeB->AddAppliedForce(force);
    }
}

template<unsigned DIM>
template<unsigned DIM>
double CustomHexagonalNagaiHondaForce<DIM>::GetAdhesionParameter(Node<DIM>* pNodeA,
                                                                      Node<DIM>* pNodeB,
                                                                      VertexBasedCellPopulation<DIM>& rVertexCellPopulation)
{
    CellPtr pCellA = rVertexCellPopulation.GetCellUsingLocationIndex(pNodeA->GetNodeGlobalIndex());
    CellPtr pCellB = rVertexCellPopulation.GetCellUsingLocationIndex(pNodeB->GetNodeGlobalIndex());

    CellMutationState* pStateA = pCellA->GetMutationState();
    CellMutationState* pStateB = pCellB->GetMutationState();

    // Assuming mutation states are named as "type_A", "type_B", "type_C", and "scar"
    if (pStateA->GetName() == "type_A" && pStateB->GetName() == "type_A") {
        return GetNagaiHonda_A_A_AdhesionEnergyParameter();
    } else if (pStateA->GetName() == "type_A" && pStateB->GetName() == "type_B") {
        return GetNagaiHonda_A_B_AdhesionEnergyParameter();
    } else if (pStateA->GetName() == "type_B" && pStateB->GetName() == "type_A") {
        return GetNagaiHonda_A_B_AdhesionEnergyParameter();
    } else if (pStateA->GetName() == "type_B" && pStateB->GetName() == "type_B") {
        return GetNagaiHonda_B_B_AdhesionEnergyParameter();
    } else if (pStateA->GetName() == "type_A" && pStateB->GetName() == "type_C") {
        return GetNagaiHonda_A_C_AdhesionEnergyParameter();
    } else if (pStateA->GetName() == "type_C" && pStateB->GetName() == "type_A") {
        return GetNagaiHonda_A_C_AdhesionEnergyParameter();
    } else if (pStateA->GetName() == "type_B" && pStateB->GetName() == "type_C") {
        return GetNagaiHonda_B_C_AdhesionEnergyParameter();
    } else if (pStateA->GetName() == "type_C" && pStateB->GetName() == "type_B") {
        return GetNagaiHonda_B_C_AdhesionEnergyParameter();
    } else if (pStateA->GetName() == "type_C" && pStateB->GetName() == "type_C") {
        return GetNagaiHonda_C_C_AdhesionEnergyParameter();
    } else if (pStateA->GetName() == "scar" || pStateB->GetName() == "scar") {
        return GetNagaiHonda_Scar_AdhesionEnergyParameter();
    } else {
        throw std::invalid_argument("Unknown cell type(s) encountered.");
    }
}


template<unsigned DIM>
double CustomHexagonalNagaiHondaForce<DIM>::GetNagaiHondaDeformationEnergyParameter()
{
    return mNagaiHondaDeformationEnergyParameter;


Complete the function implementation replacing "throw std::runtime_error("Not implemented");".