Entries for 2018

  1. Portrait of Onur Solmaz
    Onur Solmaz · Post · /2018/10/18

    The Anatomy of a Block Stuffing Attack

    Block stuffing is a type of attack in blockchains where an attacker submits transactions that deliberately fill up the block’s gas limit and stall other transactions. To ensure inclusion of their transactions by miners, the attacker can choose to pay higher transaction fees. By controlling the amount of gas spent by their transactions, the attacker can influence the number of transactions that get to be included in the block.

    To control the amount of gas spent by the transaction, the attacker utilizes a special contract. There is a function in the contract which takes as input the amount of gas that the attacker wants to burn. The function runs meaningless instructions in a loop, and either returns or throws an error when the desired amount is burned.

    For example let’s say that the average gas price has been 5 Gwei in the last 10 blocks. In order to exert influence over the next block, the attacker needs to submit transactions with gas prices higher than that, say 100 Gwei. The higher the gas price, the higher the chance of inclusion by miners. The attacker can choose to divide the task of using 8,000,000 gas—current gas limit for blocks—into as many transactions as they want. This could be 80 transactions with 100,000 gas expenditure, or 4 transactions with 2,000,000 gas expenditure.

    Deciding on how to divide the task is a matter of maximizing the chance of inclusion, and depends on the factors outline below.

    Miners’ strategy for selecting transactions

    Miners want to maximize their profit by including transactions with highest fees. In the current PoW implementation of Ethereum, mining the block takes significantly more time than executing the transactions. So let’s assume all transactions in the pool are trivially executed as soon as they arrive and miners know the amount of gas each one uses.

    For miners, maximizing profit is an optimum packing problem. Miners want to choose a subset of the transaction pool that gives them maximum profit per block. Since there are at least tens of thousands of transactions in the pool at any given time, the problem can’t be solved by brute-forcing every combination. Miners use algorithms that test a feasible number of combinations and select the one giving the highest reward.

    A block stuffer’s main goal is to target the selection process by crafting a set of transactions that has the highest chance of being picked up by miners in a way that will deplete blocks’ gas limits. They can’t devise a 100% guaranteed strategy since each miner can use a different algorithm, but they can find a sweet spot by testing out the whole network.

    (In a PoS system, our assumptions would be wrong since executing transactions is not trivial compared to validating blocks. Validators would need to develop more complex strategies depending on the PoS implementation.)

    The transactions the attacker wants to stall:

    It could be so that the attacker wants to stall transactions with a specific contract. If the function calls to that contract use a distinctively high amount of gas, say between 300,000 and 500,000, then the attacker has to stuff the block in a way that targets that range.

    For example, the attacker can periodically submit nn transactions {T1,T2,,Tn1,Tn}\{T_1, T_2,\dots, T_{n-1}, T_n\} with very high prices where

    i=1nTigas8,000,000.\sum\limits_{i=1}^{n} T_i^{\text{gas}} \approx 8,000,000.

    If the attacker is targeting transactions within a range of (Rlower,Rupper)(R_\text{lower}, R_\text{upper}), they can choose the first n1n-1 transactions to deplete 8,000,000Rupper8,000,000 - R_\text{upper} gas in short steps, and submit TnT_n to deplete the remaining RupperR_\text{upper} gas with a relatively higher price. Note that the revenue from including a single transaction is

    tx_fee=gas_price×gas_usage.\text{tx\_fee} = \text{gas\_price} \times \text{gas\_usage}.

    As gas usage decreases, the probability of being picked up by miners decreases, so prices should increase to compensate.

    Example: Fomo3D

    Fomo3D is a gambling game where players buy keys from a contract and their money goes into a pot. At the beginning of each round, a time counter is initiated which starts counting back from 24 hours. Each bought key adds 30 seconds to the counter. When the counter hits 0, the last player to have bought a key wins the majority of the pot and the rest is distributed to others. The way the pot is distributed depends on the team that the winner belongs to.

    Key price increases with increasing key supply, which makes it harder and harder to buy a key and ensures the round will end after some point. In time, the stakes increase and the counter reduces to a minimum, like 2 minutes. At this point, the players pay both high gas and key prices to be “it” and win the game. Players program bots to buy keys for them, and winning becomes a matter of coding the right strategy. As you can understand from the subject, the first round was won through a block stuffing attack.

    On August 22 2018, the address 0xa16…f85 won 10,469 ETH from the first round by following the strategy I outlined above. The winner managed to be the last buyer in block 6191896 and managed to stall transactions with Fomo3D until block 6191909 for 175 seconds, ending the round. Some details:

    The user addresses above were scraped from the Ethereum transaction graph as being linked to a primary account which supplied them with funds. The contract addresses were scraped from 0-valued transactions sent from user addresses. These have a distance of 1, there may be other addresses involved with greater distances.

    Below are details of the last 4 blocks preceding the end of the round. The rows highlighted with yellow are transactions submitted by the attacker. The crossed out rows are failed transactions. All transactions by the attacker were submitted with a 501 Gwei gas price, and stuffing a single block costed around 4 ETH. The calls to buy keys generally spend around 300,000~500,000 gas, depending on which function was called. Below, you see the successfully stuffed block 6191906.

    Block 6191906
    Idx From To Hash ETH sent Gas Price
    [Gwei]
    Gas Limit Gas Used ETH spent
    on gas
    0 0xF03…1f2 0x18e…801 0xb97…8e4 0 501.0 4,200,000 4,200,000 2.1042
    1 0x87C…4eF 0x18e…801 0x96f…1b0 0 501.0 3,600,000 3,600,000 1.8036
    2 0xf6E…059 0x18e…801 0x897…2b3 0 501.0 200,000 200,000 0.1002
    Sum 0 1503.01 8,000,000 8,000,000 4.0080

    Block 6191907 was a close call for the winner, because their transactions picked up for the block did not amount up to 8,000,000 and the other transaction was a call to Fomo3D by an opponent to buy keys. Note that it has a gas price of 5559 Gwei, which means either the bot or person who submitted the transaction was presumably aware of the attack. The transaction failed due to low gas limit, presumably due to a miscalculation by the bot or the person.

    Block 6191907
    Idx From To Hash ETH sent Gas Price
    [Gwei]
    Gas Limit Gas Used ETH spent
    on gas
    0 0x32A…370 0xA62…Da1 0x5e7…be1 0.0056 5559.7 379,000 379,000 2.1071
    1 0xC6A…3E2 0x18e…801 0xb8b…40c 0 501.0 3,900,000 3,900,000 1.9539
    2 0xD27…642 0x18e…801 0xbcf…c62 0 501.0 3,300,000 3,300,000 1.6533
    3 0x00c…776 0x18e…801 0xf30…337 0 501.0 400,000 400,000 0.2004
    Sum 0.0056 7062.71 7,979,000 7,979,000 5.9147

    Transactions in block 6191908 belonged to the attacker except for one irrelevant transfer. This block is also considered successfully stuffed, since the 7,970,000 gas usage by the attacker leaves no space for a call to buy keys.

    Block 6191908
    Idx From To Hash ETH sent Gas Price
    [Gwei]
    Gas Limit Gas Used ETH spent
    on gas
    0 0xD27…642 0x18e…801 0x74a…9b1 0 501.0 3,300,000 3,300,000 1.6533
    1 0x7Dd…c4c 0x18e…801 0x48c…222 0 501.0 2,700,000 2,700,000 1.3527
    2 0x3C3…f27 0x18e…801 0x01b…4aa 0 501.0 1,800,000 1,800,000 0.9018
    3 0xa94…eb8 0x18e…801 0x776…d43 0 501.0 170,000 170,000 0.0851
    4 0xbFd…1b4 0x663…d31 0x3a6…ba1 0.05 100.0 21,000 21,000 0.0021
    Sum 0.05 2104.01 7,991,000 7,991,000 3.9950

    By block 6191909, the counter has struck zero—more like current UTC time surpassed the round end variable stored in the contract—and any call to Fomo3D would be the one to end the round and distribute the pot. And the first transaction in the block is—wait for it—a call to Fomo3D to buy keys by the opponent whose transaction failed a few blocks earlier, submitted with 5562 Gwei. So the guy basically paid 1.7 ETH to declare the attacker the winner!

    Block 6191909
    Idx From To Hash ETH sent Gas Price
    [Gwei]
    Gas Limit Gas Used ETH spent
    on gas
    0 0x32A…370 0xA62…Da1 0xa14…012 0.0056 5562.2 379,000 304,750 1.6950
    1 0xC96…590 0x18e…801 0xf47…9ca 0 501.0 2,200,000 37,633 0.0188
    2 0xb1D…aEF 0x18e…801 0xe4c…edb 0 501.0 1,400,000 37,633 0.0188
    3 0x18D…A9A 0x18e…801 0xf3a…995 0 501.0 800,000 37,633 0.0188

    Another thing to note is that the attacker probably crafted the spender contract to stop the attack when the round has ended, presumably to cut costs. So the 37,633 gas used by the contract are probably to call the Fomo3D contract to check round status. All these point out to the fact that the attacker is an experienced programmer who knows their way around Ethereum.

    Here, you can see the details of the 100 blocks preceding the end of the round, with the additional information of ABI calls and events fired in transactions.

    Since the end of the first round, 2 more rounds ended with attacks similar to this one. I didn’t analyze all of them because it’s too much for this post, but here are some details if you want to do it yourselves.

    RoundAddress winning the potWinner’s last tx before the end of the roundBlock containing that txTx ending the roundBlock containing that txTx where winner withdraws prizeAmount won [ETH]Contract used for block stuffing
    10xa169df5ed3363cfc4c92ac96c6c5f2a42fccbf850x7a06d9f11e650fbb2061b320442e26b4a704e1277547e943d73e5b67eb49c34961918960xa143a1ee36e1065c3388440ef7e7b38ed41925ca4799c8a4d429fa3ee196601261919090xe08a519c03cb0aed0e04b33104112d65fa1d3a48cd3aeab65f047b2abce9d50810,4690x18e1b664c6a2e88b93c1b71f61cbf76a726b7801
    20x18a0451ea56fd4ff58f59837e9ec30f346ffdca50x0437885fa741f93acfdcda9f5a2e673bb16d26dd22dfc4890775efb8a94fb58363915370x87bf726bc60540c6b91cc013b48024a5b8c1431e0847aadecf0e92c56f8f46fd63915480x4da4052d2baffdc9c0b82d628b87d2c76368914e33799032c6966ee8a3c216a03,2640x705203fc06027379681AEf47c08fe679bc4A58e1
    30xaf492045962428a15903625B1a9ECF438890eF920x88452b56e9aa58b70321ee8d5c9ac762a62509c98d9a29a4d64d6caae49ae75765077610xe6a5a10ec91d12e3fec7e17b0dfbb983e00ffe93d61225735af2e1a8eabde00365077740xd7e70fdf58aca40139246a324e871c84d988cfaff673c9e5f384315c91afa5e43760xdcC655B2665A675B90ED2527354C18596276B0de

    A thing to note in the following rounds is that participation in the game and amount of pot gradually decreased, presumably owing to the fact that the way of beating the game has been systematized. Although anyone can attempt such an attack, knowing how it will be won takes the “fun” factor out of it.

    Credit: Although I’ve found previous instances of the term “block stuffing” online, Nic Carter is the first one to use it in this context.

  2. Portrait of Onur Solmaz
    Onur Solmaz · Post · /2018/08/03

    Mathematics of Bonding Curves

    A bonding curve is a financial instrument proposed by Simon de la Rouviere in his Medium articles. ETH is bonded in a smart contract to mint tokens, and unbonded to burn them. Every bonding and unbonding changes the price of the token according to a predefined formula. The “curves” represent the relationship between the price of a single token and the token supply. The result is an ETH-backed token that rewards early adopters.

    An example supply versus price graph. The area below the curve is equal to the amount of ETH EE that must be spent to increase the supply from S0S_0 to S1S_1, or that is going to be received when S1S0S_1-S_0 tokens are unbonded.

    Inside a transaction, the price paid/received per token is not constant and depends on the amount that is bonded or unbonded. This complicates the calculations.

    Let’s say for an initial supply of S0S_0, we want to bond TT tokens which are added to the new supply S1=S0+TS_1=S_0+T. The ETH EE that must be spent for this bonding is defined as

    E=S0S1PdSE = \int_{S_0}^{S_1} P\, dS

    which is illustrated in the figure above. If one wanted to unbond TT tokens, the upper limit for the integral would be S0S_0 and the lower S0TS_0-T, with E corresponding to the amount of ETH received for the unbonding.

    Linear Curves

    A linear relationship for the bonding curves are defined as

    P(S)=P0+SIpP(S) = P_0 + S I_p

    where P0P_0 is the initial price of the token and IpI_p is the price increment per token.

    Bonding Tokens

    Let us have EE ETH which we want to bond tokens with. Substituting PP into the integral above with the limits S0S0+TS_0\to S_0+T, we obtain EE in terms of the tokens TT that we want to bond:

    E(S,T)=TP0+TIpS+12T2IpE(S, T) = T P_0 + T I_p S + \frac{1}{2} T^2 I_p

    where SS is the supply before the bonding. Solving this for TT, we obtain the tokens received in a bonding as a function of the supply and ETH spent:

    T(S,E)=S2Ip2+2EIp+2SP0Ip+P02P0IpS.\boxed{T(S, E) = \frac{\sqrt{S^2I_p^2 + 2E I_p + 2 S P_0 I_p + P_0^2}-P_0}{I_p} - S.}

    Unbonding Tokens

    Let us have T tokens which we want to unbond for ETH. Unbonding TT tokens decreases the supply from S0S_0 to S0TS_0-T, which we apply as limits for the above integral and obtain:

    E(S,T)=TP0+TIpS12T2Ip.\boxed{E(S, T) = T P_0 + T I_p S - \frac{1}{2} T^2 I_p.}

    Breaking Even in PoWH3D

    PoWH3D is one of the applications of bonding curves with a twist: 1/10th of every transaction is distributed among token holders as dividends. When you bond tokens with EE ETH, you receive 9/10E9/10 E worth of tokens and 1/10E1/10 E is distributed to everybody else in proportion to the amount they hold.

    This means you are at a loss when you bond P3D (the token used by PoWH3D). If you were to unbond immediately, you would only receive 81% of your money. Given the situation, one wonders when exactly one can break even with their investment. The activity in PoWH3D isn’t deterministic; nonetheless we can deduce sufficient but not necessary conditions for breaking even in PoWH3D.

    Sufficient Bonding

    Let us spend E1E_1 ETH to bond tokens at supply S0S_0. The following calculations are done with the assumption that the tokens received

    T1=T(S0,9E1/10)T_1 = T(S_0, 9E_1/10)

    are small enough to be neglected, that is T1S0T_1 \ll S_0 and S1S0S_1 \approx S_0. In other words, this only holds for non-whale bondings.

    Then let others spend E2E_2 ETH to bond tokens and raise the supply to S2S_2. The objective is to find an E2E_2 large enough to earn us dividends and make us break even when we unbond our tokens at S2S_2. We have

    S2=S0+T(S0,E2).S_2 = S_0 + T(S_0, E_2).

    Our new share of the P3D pool is T1/S2T_1/S_2 and the dividends we earn from the bonding is equal to

    110T1S2E2.\frac{1}{10}\frac{T_1}{S_2}E_2.

    Then the condition for breaking even is

    910E(S2,T1)+110T1S2E2E1.\boxed{\frac{9}{10} E(S_2, T_1) + \frac{1}{10}\frac{T_1}{S_2}E_2 \geq E_1.}

    This inequality has a lengthy analytic solution which is impractical to typeset. The definition should be enough:

    E2suff(S0,E1):=solve for E_2{910E(S2,T1)+110T1S2E2=E1}E^{\text{suff}}_2(S_0, E_1) := \text{solve for E_2E\_2}\left\{\frac{9}{10} E(S_2, T_1) + \frac{1}{10}\frac{T_1}{S_2}E_2 = E_1\right\}

    and

    E2E2suff.E_2 \geq E^{\text{suff}}_2.

    E2suffE^{\text{suff}}_2 can be obtained from the source of this page in JavaScript from the function sufficient_bonding. The function involves many power and square operations and may yield inexact results for too high values of S0S_0 or too small values off E1E_1, due to insufficient precision of the underlying math functions. For this reason, the calculator is disabled for sensitive input.

    S0S_0 versus E2suffE^{\text{suff}}_2 for E1=100E_1 = 100.

    The relationship between the initial supply and sufficient bonding is roughly quadratic, as seen from the graph above. This means that the difficulty of breaking even increases quadratically as more people bond into P3D. As interest in PoWH3D saturates, dividends received from the supply increase decreases quadratically.

    Logarithmic plot of S0S_0 versus E2suffE^{\text{suff}}_2 for changing values of E1E_1.

    The relationship is not exactly quadratic, as seen from the graph above. The function is sensitive to E1E_1 for small values of S0S_0.

    Sufficient Unbonding

    Let us spend E1E_1 ETH to bond tokens at supply S0S_0 and receive T1T_1 tokens.

    Then let others unbond T2T_2 P3D to lower the supply to S2S_2. The objective is to find a T2T_2 large enough to earn us dividends and make us break even when we unbond our tokens at S2S_2. We have

    S2=S0T2.S_2 = S_0 - T_2.

    Our new share of the P3D pool is T1/S2T_1/S_2 and the dividends we earn from the bonding is equal to

    110T1S2E(S2,T2)\frac{1}{10}\frac{T_1}{S_2} E(S_2, T_2)

    Then the condition for breaking even is

    910E(S2,T1)+110T1S2E(S2,T2)E1.\boxed{\frac{9}{10} E(S_2, T_1) + \frac{1}{10}\frac{T_1}{S_2} E(S_2, T_2) \geq E_1.}

    Similar to the previous section, we have

    T2suff(S0,E1):=solve for T_2{910E(S2,T1)+110T1S2E(S2,T2)=E1}T^{\text{suff}}_2(S_0, E_1) := \text{solve for T_2T\_2}\left\{\frac{9}{10} E(S_2, T_1) + \frac{1}{10}\frac{T_1}{S_2} E(S_2, T_2) = E_1\right\}

    and

    T2T2suff.T_2 \geq T^{\text{suff}}_2.

    T2suffT^{\text{suff}}_2 can be obtained from the function sufficient_unbonding.

    S0S_0 versus T2suffT^{\text{suff}}_2 for E1=100E_1 = 100.

    The relationship between S0S_0 and T2suffT^{\text{suff}}_2 is linear and insensitive to E1E_1. Regardless of the ETH you invest, the amount of tokens that need to be unbonded to guarantee your break-even is roughly the same, depending on your entry point.

    Calculator

    Below is a calculator you can input S0S_0 and E1E_1 to calculate E2suffE^{\text{suff}}_2 and T2suffT^{\text{suff}}_2.

    S0S_0
    E1E_1
    E2suffE^{\text{suff}}_2
    T2suffT^{\text{suff}}_2

    For the default values above, we read this as:

    For 100 ETH worth of P3D bonded at 3,500,000 supply, either a bonding of ~31715 ETH or an unbonding of ~3336785 P3D made by other people is sufficient to break even.

    In order to follow these statistics, you can follow this site.

    Conclusion

    Bonding curve calculations can get complicated because the price paid per token depends on the amount of intended bonding/unbonding. With this work, I aimed to clarify the logic behind PoWH3D. Use the formulation and calculator at your own risk.

    The above conditions are only sufficient and not necessary to break even. As PoWH3D becomes more popular, it gets quadratically more difficult to break even from a supply increase. PoWH3D itself doesn’t generate any value or promise long-term returns for its holders. However every bond, unbond and transfer deliver dividends. According to its creators, P3D is intended to become the base token for a number of games that will be built upon PoWH3D, like FOMO3D.

  3. Portrait of Onur Solmaz
    Onur Solmaz · Post · /2018/04/24

    Lumped L2 Projection

    $ \newcommand{\rowsum}{\mathop{\rm rowsum}\nolimits} \newcommand{\nnode}{n} \newcommand{\suml}[2]{\sum\limits_{#1}^{#2}} $

    When utilizing Galerkin-type solutions for IBVPs, we often have to compute integrals using numerical methods such as Gauss quadrature. In such a solution, we solve for the values of a function at mesh nodes, whereas the integration takes place at the quadrature points. Depending on the case, we may need to compute the values of a function at mesh nodes, given their values at quadrature points, e.g. stress recovery for mechanical problems.

    There are many ways of achieving this, such as superconvergent patch recovery. In this post, I wanted to document a widely used solution which is easy to implement, and which is used in research oriented codebases such as FEAP.

    L2 Projection

    Given a function uL2(Ω)u \in L^2(\Omega), its projection into a finite element space VhL2(Ω)V_h\subset L^2(\Omega) is defined through the following optimization problem:

    Find uhVhu_h\in V_h such that

    Π(uh):=12uhuL2(Ω)2min\begin{equation} \Pi(u_h) := \frac{1}{2}\lVert u_h-u \rVert^2_{L^2(\Omega)} \quad\rightarrow\quad \text{min} \tag{1}\end{equation}

    There is a unique solution to the problem since Π()\Pi(\cdot) is convex. Taking its variation, we have $DΠ(uh)vh=uhu,vh=0\begin{equation} D \Pi(u_h) \cdot v_h = \langle u_h-u, v_h \rangle = 0 \tag{2}\end{equation}$

    for all vhVhv_h\in V_h. Thus we have the following variational formulation

    Find uhVhu_h\in V_h such that

    uh,vh=u,vh\begin{equation} \langle u_h,v_h\rangle = \langle u, v_h\rangle \tag{3}\end{equation}

    for all vhVhv_h\in V_h.

    Here,

    m(uh,vh)=uh,vh=Ωuhvhdxandb(vh)=u,vh=Ωuvhdx\begin{equation} \begin{alignedat}{3} m(u_h,v_h) &= \langle u_h,v_h\rangle && = \int_\Omega u_hv_h \,dx \quad\text{and} \\ b(v_h) &= \langle u, v_h\rangle && = \int_\Omega u v_h \,dx \end{alignedat} \tag{4}\end{equation}

    are our bilinear and linear forms respectively. Substituting FE discretizations uh=J=1nuJNJu_h = \sum_{J=1}^{\nnode} u^JN^J and vh=I=1nvINIv_h = \sum_{I=1}^{\nnode} v^IN^I, we have

    J=1nMI ⁣JuJ=bI\begin{equation} \suml{J=1}{\nnode} M^{I\!J} u^J = b^I \htmlId{eq:projectionsystem1}{} \tag{5}\end{equation}

    for I=1,,nI=1,\dots,\nnode, where the FE matrix and vector are defined as

    MI ⁣J=m(NJ,NI)=ΩNJNIdxandbI=b(NI)=ΩuNIdx\begin{equation} \begin{alignedat}{3} M^{I\!J} &= m(N^J,N^I) &&= \int_\Omega N^JN^I \,dx \quad\text{and} \\ b^{I} &= b(N^I) &&= \int_\Omega u N^I \,dx \end{alignedat} \tag{6}\end{equation}

    Thus L2 projection requires the solution of a linear system

    Mu=b\boldsymbol{M}\boldsymbol{u}=\boldsymbol{b}

    which depending on the algorithm used, can have a complexity of at least O(n2)O(n^2) and at most O(n3)O(n^3).

    Lumped L2 Projection

    The L2 projection requires the solution of a system which can be computationally expensive. It is possible to convert the matrix—called the mass matrix in literature—to a diagonal form through a procedure called lumping.

    The operator for row summation is defined as

    rowsum()i:=j=1n()ij\begin{equation} \rowsum{(\cdot)}_i := \suml{j=1}{\nnode} (\cdot)_{ij} \tag{7}\end{equation}

    For the mass matrix, we have

    rowsumMI=J=1nΩNJNIdx=ΩNIdx=:mI\begin{equation} \rowsum M^{I} = \suml{J=1}{\nnode} \int_\Omega N^JN^I \,dx = \int_\Omega N^I \,dx =: m^I \tag{8}\end{equation}

    since J=1nNJ=1\sum_{J=1}^{\nnode} N^J = 1. Substituting the lumped mass matrix allows us to decouple the linear system of equations in (5) and instead write

    mIuI=bI\begin{equation} m^I u^I = b^I \tag{9}\end{equation}

    for I=1,,nI=1,\dots,\nnode. The lumped L2 projection is then as simple as

    uI=bImI=ΩuNIdxΩNIdx\begin{equation} u^I = \frac{b^I}{m^I} = \frac{\displaystyle\int_\Omega u N^I\,dx}{\displaystyle\int_\Omega N^I \,dx} \tag{10}\end{equation}

    This results in a very efficient algorithm with O(n)O(n) complexity.

    Conclusion

    Lumped L2 projection is a faster working approximation to L2 projection that is easy to implement for quick results. You can use it when developing a solution for an IBVP, and don’t want to wait too long when debugging, while not forgetting that it introduces some error.

  4. Portrait of Onur Solmaz
    Onur Solmaz · Post · /2018/04/13

    Disadvantages of Engineering Notation in Finite Elements

    Suppose we have the following stiffness matrix of linear elasticity:

    AijI ⁣J=ΩBkICikjlBlJdv\begin{equation} A^{I\!J}_{ij} = \int_\Omega B^I_k \, C_{ikjl} \, B^J_l \,dv \htmlId{eq:engnot1}{} \tag{1}\end{equation}

    where BI=NI\boldsymbol{B}^I = \nabla N^I are the gradients of the shape functions NIN^I and C\mathbb{C} is the linear elasticity tensor (you see the contraction of their components in the equation).

    Despite being of the most explicit form, these types of indicial expressions are avoided in most texts on finite elements. There are two reasons for this:

    • Engineers are not taught the Einstein summation convention.
    • The presence of indices result in a seemingly cluttered expression.

    They avoid the indicial expression by reshaping it into matrix multiplications. In engineering notation, the left- and right-hand sides are reshaped as

    Aαβ=ΩBγαCγδBδβdv\begin{equation} A_{\alpha\beta} = \int_\Omega B_{\gamma\alpha}C_{\gamma\delta}B_{\delta\beta} \,dv \htmlId{eq:engnot2}{} \tag{2}\end{equation}

    which allows us to write

    A=ΩB~TC~B~dv\begin{equation} \boldsymbol{A} = \int_\Omega \tilde{\boldsymbol{B}}^T\tilde{\boldsymbol{C}}\tilde{\boldsymbol{B}} \,dv \htmlId{eq:engnot3}{} \tag{3}\end{equation}

    The matrices B~\tilde{\boldsymbol{B}} and C~\tilde{\boldsymbol{C}} are set on with tildes in order to differentiate them from the boldface symbols used in the previous sections. Here,

    • C~\tilde{\boldsymbol{C}} is a matrix containing the unique components of the elasticity tensor C\mathbb{C}, according to the Voigt notation. In this reshaping, only the minor symmetries are taken into account. If the dimension of the vectorial problem is dd, then C~\tilde{\boldsymbol{C}} is of the size d(d+1)/2×d(d+1)/2d(d+1)/2 \times d(d+1)/2. For example, if the problem is 3 dimensional, C~\tilde{\boldsymbol{C}} is of the size 6×66\times 6:
    [C~]=[C1111C1122C1133C1112C1123C1113C2211C2222C2233C2212C2223C2213C3311C3322C3333C3312C3323C3313C1211C1222C1233C1212C1223C1213C2311C2322C2333C2312C2323C2313C1311C1322C1333C1312C1323C1313]\begin{equation} [\tilde{\boldsymbol{C}}] = \begin{bmatrix} C_{1111} & C_{1122} & C_{1133} & C_{1112} & C_{1123} & C_{1113} \\ C_{2211} & C_{2222} & C_{2233} & C_{2212} & C_{2223} & C_{2213} \\ C_{3311} & C_{3322} & C_{3333} & C_{3312} & C_{3323} & C_{3313} \\ C_{1211} & C_{1222} & C_{1233} & C_{1212} & C_{1223} & C_{1213} \\ C_{2311} & C_{2322} & C_{2333} & C_{2312} & C_{2323} & C_{2313} \\ C_{1311} & C_{1322} & C_{1333} & C_{1312} & C_{1323} & C_{1313} \\ \end{bmatrix} \htmlId{eq:engnotC}{} \tag{4}\end{equation}
    • B~\tilde{\boldsymbol{B}} is a nd×d(d+1)/2nd\times d(d+1)/2 matrix whose components are adjusted so that (2) is equivalent to (1). It has the components of BI\boldsymbol{B}^I for I=1,,nI=1,\dots,n where nn is the number of basis functions. Since B~\tilde{\boldsymbol{B}} is adjusted to account for the reshaping of C\mathbb{C}, it has many zero components. A 3d example:
    [B~]=[B1100B1200B1n000B2100B2200B2n000B3100B3200B3nB21B110B22B120B2nB1n00B31B210B32B220B3nB2nB310B11B320B12B3n0B1n]\begin{equation} [\tilde{\boldsymbol{B}}] = \begin{bmatrix} B^1_1 & 0 & 0 & B^2_1 & 0 & 0 & \cdots & B^n_1 & 0 & 0 \\ 0 & B^1_2 & 0 & 0 & B^2_2 & 0 & \cdots & 0 & B^n_2 & 0 \\ 0 & 0 & B^1_3 & 0 & 0 & B^2_3 & \cdots & 0 & 0 & B^n_3 \\ B^1_2 & B^1_1 & 0 & B^2_2 & B^2_1 & 0 & \cdots & B^n_2 & B^n_1 & 0 \\ 0 & B^1_3 & B^1_2 & 0 & B^2_3 & B^2_2 & \cdots & 0 & B^n_3 & B^n_2 \\ B^1_3 & 0 & B^1_1 & B^2_3 & 0 & B^2_1 & \cdots & B^n_3 & 0 & B^n_1 \\ \end{bmatrix} \htmlId{eq:engnotB}{} \tag{5}\end{equation}

    Although (3) looks nice on paper, it is much less optimal for implementation. Implementing it requires the implementation of (5), which adds another layer of complexity to the algorithm. The same cannot be said for (4), because using Voigt notation might be more efficient in inelastic problems. In the most complex problems, the most efficient method is to implement (1) in conjunction with Voigt notation.

    To prove the inefficiency of (3) we can readily compare it with (1) in terms of required number of iterations. Indices in (1) have the following ranges:

    I,J=1,,nandi,j,k,l=1,,d\begin{equation} I,J = 1,\dots,n \quad\text{and}\quad i,j,k,l = 1,\dots,d \tag{6}\end{equation}

    so n2d4n^2d^4 iterations are required. Indices in (2) have the following ranges:

    α,β=1,,ndandγ,δ=1,,d(d+1)/2\begin{equation} \alpha,\beta=1,\dots,nd \quad\text{and}\quad \gamma,\delta=1,\dots,d(d+1)/2 \tag{7}\end{equation}

    so

    (nd)2(d(d+1)2)2=n2d4(d+1)24\begin{equation} (nd)^2\left(\frac{d(d+1)}{2}\right)^2 = n^2d^4\frac{(d+1)^2}{4} \tag{8}\end{equation}

    iterations are required. So engineering notation requires (d+1)2/4(d+1)^2/4 times more equations than index notation. For d=2d=2, engineering notation is 2.252.25 times slower and for d=3d=3 it is 44 times slower. For example, calculation of a stiffness matrix for n=8n=8 and d=3d=3 requires 2073620736 iterations for engineering notation, whereas it only requires 51845184 iterations for index notation.

    Although (3) seems less cluttered, what actually happens is that one trades off complexity in one expression for a much increased complexity in another one, in this case (5). And to make it worse, it results in a slower algorithm.

    The only obstacle to the widespread adoption of index notation seems to be its lack in undergraduate engineering curricula. If engineers were taught the index notation and summation convention as well as the formal notation, such expressions would not be as confusing at first sight. A good place would be in elementary calculus and physics courses, where one heavily uses vector calculus.

  5. Portrait of Onur Solmaz
    Onur Solmaz · Post · /2018/04/01

    Variational Formulation of Elasticity

    $ \newcommand{\argmin}{\mathop{\rm argmin}\nolimits} \newcommand{\cof}{\mathop{\rm cof}\nolimits} \newcommand{\sym}{\mathop{\rm sym}\nolimits} \newcommand{\invtra}{^{-T}} \newcommand{\eps}{\epsilon} \newcommand{\var}{\Delta} \newcommand{\Vvphi}{\Delta\Bvarphi} \newcommand{\vvphi}{\delta\Bvarphi} \newcommand{\BFC}{\boldsymbol{\mathsf{C}}} \newcommand{\BFc}{\boldsymbol{\mathsf{c}}} \newcommand{\push}{\Bvarphi_\ast} \newcommand{\pull}{\Bvarphi^\ast} $

    There are many books that give an outline of hyperelasticity, but there are few that try to help the reader implement solutions, and even fewer that manage to do it in a concise manner. Peter Wriggers’ Nonlinear Finite Element Methods is a great reference for those who like to roll up their sleeves and get lost in theory. It helped me understand a lot about how solutions to hyperelastic and inelastic problems are implemented.

    One thing did not quite fit my taste though—it was very formal in the way that it didn’t give out indicial expressions. And if it wasn’t clear up until this point, I love indicial expressions, because they pack enough information to implement a solution in a single line. Almost all books skip these because they seem cluttered and the professors who wrote them think they’re trivial to derive. In fact, they are not. So below, I’ll try to derive indicial expressions for the update equations of hyperelasticity.

    In the case of a hyperelastic material, there exists a strain energy function

    Ψ:FΨ(F)\begin{equation} \Psi: \BF \mapsto \Psi(\BF) \tag{1}\end{equation}

    which describes the elastic energy stored in the solid, i.e. energy density per unit mass of the reference configuration. The total energy stored in B\CB is described by the the stored energy functional

    E(φ):=BΨ(F)dm=Bρ0Ψ(F)dV\begin{equation} E(\Bvarphi) := \int_{\CB} \Psi(\BF)\, \dif m = \int_{\CB} \rho_0 \Psi(\BF) \dV \tag{2}\end{equation}

    The loads acting on the body also form a potential:

    L(φ):=Bρ0ΓˉφdV+BtTˉφdA\begin{equation} L(\Bvarphi) := \int_{\CB} \rho_0\bar{\BGamma}\dtp\Bvarphi \dV + \int_{\del\CB_t} \bar{\BT}\dtp\Bvarphi \dA \tag{3}\end{equation}

    where Γˉ\bar{\BGamma} and Tˉ\bar{\BT} are the prescribed body forces per unit mass and surface tractions respectively, where T=PN\BT=\BP\BN with Cauchy’s stress theorem.

    The potential energy of B\CB for deformation φ\Bvarphi is defined as

    Π(φ):=E(φ)L(φ)\begin{equation} \Pi(\Bvarphi) := E(\Bvarphi) - L(\Bvarphi) \tag{4}\end{equation}

    Thus the variational formulation reads

    Find φV\Bvarphi\in V such that the functional

    Π(φ)=Bρ0Ψ(F)dVBρ0ΓˉφdVBtTˉφdA\begin{equation} \Pi(\Bvarphi) = \int_{\CB} \rho_0\Psi(\BF) \dV - \int_{\CB} \rho_0\bar{\BGamma}\dtp\Bvarphi \dV - \int_{\del\CB_t} \bar{\BT}\dtp\Bvarphi \dA \tag{5}\end{equation}

    is minimized for φ=φˉ\Bvarphi=\bar{\Bvarphi} on Bu\del\CB_u.

    The solution is one that minimizes the potential energy:

    φ=argminφVΠ(φ)\begin{equation} \Bvarphi^\ast = \argmin_{\Bvarphi\in V} \Pi(\Bvarphi) \tag{6}\end{equation}

    A stationary point for Π\Pi means that its first variation vanishes: ΔΠ=0\var\Pi=0.

    ΔΠ=DφΠδφ=:G(φ,δφ)=Bρ0ΨF:(δφ)dVBρ0ΓˉδφdVBTˉδφdA\begin{equation} \begin{aligned} \var\Pi &= \varn{\Pi}{\Bvarphi}{\vvphi} =: G(\Bvarphi,\vvphi) \\ &= \int_{\CB} \rho_0\partd{\Psi}{\BF}: \nabla(\vvphi) \dV - \int_{\CB} \rho_0\bar{\BGamma}\dtp\vvphi \dV - \int_{\del\CB} \bar{\BT}\dtp\vvphi \dA \end{aligned} \tag{7}\end{equation}

    Using P=FS\BP=\BF\BS and P=ρ0Ψ/F\BP = \rho_0\del\Psi/\del\BF,

    ρ0ΨF:(δφ)=FS:(δφ)=S:FT(δφ)\begin{equation} \rho_0\partd{\Psi}{\BF}: \nabla(\vvphi) = \BF\BS:\nabla(\vvphi) = \BS:\BF\tra\nabla(\vvphi) \tag{8}\end{equation}

    The symmetric part of the term on the right hand side of the contraction is equal to the variation of the Green-Lagrange strain tensor:

    ΔE=DφEδφ=ddϵ12[(φ+ϵδφ)T(φ+ϵδφ)I]ϵ=0=12[(δφ)TF+FT(δφ)]\begin{equation} \begin{aligned} \var\BE = \varn{\BE}{\Bvarphi}{\vvphi} &= \deriv{}{\eps} \frac{1}{2} [\nabla(\Bvarphi+\eps\vvphi)\tra\nabla(\Bvarphi+\eps\vvphi) - \BI]\evat_{\eps=0} \\ &= \frac{1}{2} [\nabla(\vvphi)\tra\BF + \BF\tra\nabla(\vvphi)] \end{aligned} \tag{9}\end{equation}

    Substituting, we obtain the semilinear form GG in terms of the second Piola-Kirchhoff stress tensor:

    G(φ,δφ)=BS:ΔEdVBρ0ΓˉδφdVBTˉδφdA=0\begin{equation} \boxed{ G(\Bvarphi,\vvphi) = \int_{\CB} \BS: \var\BE \dV - \int_{\CB} \rho_0\bar{\BGamma}\dtp\vvphi \dV - \int_{\del\CB} \bar{\BT}\dtp\vvphi \dA = 0 } \htmlId{eq:lagrangianform1}{} \tag{10}\end{equation}

    We can write a Eulerian version of this form by pushing-forward the stresses and strains. The Almansi strain e\Be is the pull-back of the Green-Lagrange strain E\BE and vice versa:

    e=φ(E)=FTEF1andE=φ(e)=FTEF\begin{equation} \Be = \push(\BE) = \BF\invtra \BE \BF\inv \eqand \BE = \pull(\Be) = \BF\tra \BE \BF \tag{11}\end{equation}
    Commutative diagram for the pull-back and push-forward relationships of the Green-Lagrange and Almansi strain tensors.

    Thus we can deduce the variation of the Almansi strain

    Δe=FTΔEF1=12[(δφ)F1+FT(δφ)T]=12[x(δφ)+x(δφ)T]\begin{equation} \begin{aligned} \var \Be = \BF\invtra \var\BE\BF\inv &= \frac{1}{2} [\nabla(\vvphi)\BF\inv+\BF\invtra \nabla(\vvphi)\tra] \\ &= \frac{1}{2} [\nabla_x(\vvphi)+ \nabla_x(\vvphi)\tra] \end{aligned} \tag{12}\end{equation}

    where we have used the identity

    X()F1=x().\begin{equation} \nabla_X(\cdot)\BF\inv = \nabla_x(\cdot). \htmlId{eq:defgradidentity1}{} \tag{13}\end{equation}

    The second Piola-Kirchhoff stress is the pull-back of the Kirchhoff stress τ\Btau:

    S=φ(τ)=F1τFT\begin{equation} \BS = \pull(\Btau) = \BF\inv\Btau\BF\invtra \tag{14}\end{equation}

    Then it is evident that

    S:ΔE=(F1τFT):(FTΔeF)=τ:Δe\begin{equation} \BS:\var\BE = (\BF\inv\Btau\BF\invtra):(\BF\tra\var\Be\BF) = \Btau:\var\Be \tag{15}\end{equation}

    We can thus write the Eulerian version of (10):

    G(φ,δφ)=Bτ:ΔedVBρ0ΓˉδφdVBTˉδφdA=0\begin{equation} \boxed{ G(\Bvarphi,\vvphi) = \int_{\CB} \Btau: \var\Be \dV - \int_{\CB} \rho_0\bar{\BGamma}\dtp\vvphi \dV - \int_{\del\CB} \bar{\BT}\dtp\vvphi \dA = 0 } \tag{16}\end{equation}

    Introducing the Cauchy stresses σ=τ/J\Bsigma=\Btau/J, we can also transport the integrals to the current configuration

    G(φ,δφ)=Sσ:ΔedvSργˉδφdvSttˉδφda=0\begin{equation} \boxed{ G(\Bvarphi,\vvphi) = \int_{\CS} \Bsigma:\var\Be \dv - \int_{\CS} \rho\bar{\Bgamma}\dtp\vvphi \dv - \int_{\del\CS_t} \bar{\Bt}\dtp\vvphi \da = 0 } \tag{17}\end{equation}

    Here, we substituted the following differential identities:

    ρ0ΓdV=ργdv\begin{equation} \rho_0\BGamma\dV = \rho\Bgamma\dv \tag{18}\end{equation}

    for the body forces, and

    TdA=PNdA=σJFTNdA=σnda=tda\begin{equation} \BT\dA = \BP\BN \dA = \Bsigma J\BF\invtra\BN \dA = \Bsigma\Bn \da = \Bt \da \tag{19}\end{equation}

    for the surface tractions, where we used the Piola identity.

    Linearization of the Variational Formulation

    We linearize GG:

    LinGφˉ=G(φˉ,δφ)+ΔGφˉ=0\begin{equation} \Lin G \evat_{\bar{\Bvarphi}} = G(\bar{\Bvarphi}, \vvphi) + \Var G \evat_{\bar{\Bvarphi}} = 0 \tag{20}\end{equation}

    Then we have the variational setting

    a(Δφ,δφ)=b(δφ)\begin{equation} a(\Vvphi,\vvphi)=b(\vvphi) \tag{21}\end{equation}

    where

    a(Δφ,δφ)=ΔGφˉandb(δφ)=G(φˉ,δφ)\begin{equation} a(\Vvphi,\vvphi) = \Var G \evat_{\bar{\Bvarphi}} \eqand b(\vvphi) = -G(\bar{\Bvarphi}, \vvphi) \tag{22}\end{equation}
    Commutative diagram of the linearized solution procedure. Each iteration brings the current iterate φˉ\bar{\Bvarphi} closer to the optimum value φ\Bvarphi^\ast.
    Mappings between line elements belonging to the tangent spaces of the linearization.

    The variation ΔG\Var G is calculated as

    ΔG=DφGΔφ=B[ΔS:ΔE+S:Δ(ΔE)]dV\begin{equation} \Var G = \varn{G}{\Bvarphi}{\Vvphi} = \int_{\CB} [\Var\BS:\var\BE + \BS:\Var(\var\BE)] \dV \tag{23}\end{equation}

    Consecutive variations of the Green-Lagrange strain tensor is calculated as

    Δ(ΔE)=DφΔEΔφ=12[(δφ)T(Δφ)+(Δφ)T(δφ)]\begin{equation} \Var(\var\BE) = \varn{\var\BE}{\Bvarphi}{\Vvphi} = \frac{1}{2}[\nabla(\vvphi)\tra\nabla(\Vvphi) + \nabla(\Vvphi)\tra\nabla(\vvphi)] \tag{24}\end{equation}

    The term on the left is calculated as

    ΔS=DφSΔφ=SC:ΔC=2SC:ΔE\begin{equation} \Var\BS = \varn{\BS}{\Bvarphi}{\Vvphi} = \partd{\BS}{\BC}:\Var\BC = 2 \partd{\BS}{\BC}:\Var\BE \tag{25}\end{equation}

    where we substitute the Lagrangian elasticity tensor

    C:=2SC=4ρ02ΨCC\begin{equation} \BFC := 2 \partd{\BS}{\BC} = 4\rho_0 \frac{\del^2\Psi}{\del\BC\del\BC} \tag{26}\end{equation}

    and ΔE\Var\BE is calculated in the same manner as ΔE\var\BE:

    ΔE=12[(Δφ)TF+FT(Δφ)]\begin{equation} \Var\BE = \frac{1}{2} [\nabla(\Vvphi)\tra\BF + \BF\tra\nabla(\Vvphi)] \tag{27}\end{equation}

    Then the variational forms of the linearized setting are

    a(Δφ,δφ)=BΔEˉ:Cˉ:ΔEˉ+Sˉ:[(δφ)T(Δφ)]dVb(δφ)=BSˉ:ΔEˉdV+Bρ0ΓˉδφdV+BTˉδφdA\begin{equation} \boxed{ \begin{aligned} a(\Vvphi,\vvphi) &= \int_{\CB} \var\bar{\BE}:\bar{\BFC}:\Var\bar{\BE} + \bar{\BS} : [\nabla(\vvphi)\tra\nabla(\Vvphi)] \dV \\ b(\vvphi) &= - \int_{\CB} \bar{\BS}: \var\bar{\BE} \dV + \int_{\CB} \rho_0\bar{\BGamma}\dtp\vvphi \dV + \int_{\del\CB} \bar{\BT}\dtp\vvphi \dA \end{aligned} } \tag{28}\end{equation}

    where the bars denote evaluation φ=φˉ\Bvarphi=\bar{\Bvarphi} of dependent variables.

    Eulerian Version of the Linearization

    We also have the following relationship between the Lagrangian and Eulerian elasticity tensors

    cabcd=FaAFbBFcCFdDCABCD\begin{equation} c_{abcd} = F_{aA}F_{bB}F_{cC}F_{dD} C_{ABCD} \tag{29}\end{equation}

    Substituting Eulerian expansions, we obtain the following identity:

    ΔE:C:ΔE=(FTΔeF):C:(FTΔeF)=FaAΔeabFbBCABCDFcCΔecdFdD=ΔeabcabcdΔecd=Δe:c:Δe\begin{equation} \begin{aligned} \var\BE:\BFC:\Var\BE &= (\BF\tra\var\Be\BF):\BFC:(\BF\tra\Var\Be\BF) \\ &=F_{aA}\var e_{ab} F_{bB} C_{ABCD} F_{cC}\Var e_{cd}F_{dD} \\ &=\var e_{ab} c_{abcd} \Var e_{cd} \\ &= \var\Be:\BFc:\Var\Be \end{aligned} \tag{30}\end{equation}

    Thus we have

    S:[(δφ)T(Δφ)]=[F1τFT]:[(δφ)T(Δφ)]=τ:[((δφ)F1)T(Δφ)F1]=τ:[x(δφ)Tx(Δφ)]\begin{equation} \begin{aligned} \BS:[\nabla(\vvphi)\tra\nabla(\Vvphi)] &= [\BF\inv\Btau\BF\invtra] :[\nabla(\vvphi)\tra\nabla(\Vvphi)] \\ &= \Btau : [(\nabla(\vvphi)\BF\inv)\tra\nabla(\Vvphi)\BF\inv] \\ &= \Btau : [\nabla_x(\vvphi)\tra\nabla_x(\Vvphi)] \\ \end{aligned} \tag{31}\end{equation}

    With these results at hand, we can write the Eulerian version of our variational formulation:

    a(Δφ,δφ)=BΔeˉ:cˉ:Δeˉ+τˉ:[xˉ(δφ)Txˉ(Δφ)]dVb(δφ)=Bτˉ:ΔeˉdV+Bρ0ΓˉδφdV+BTˉδφdA\begin{equation} \boxed{ \begin{aligned} a(\Vvphi,\vvphi) &= \int_{\CB} \var\bar{\Be}:\bar{\BFc}:\Var\bar{\Be} + \bar{\Btau} : [\nabla_{\bar{x}}(\vvphi)\tra\nabla_{\bar{x}}(\Vvphi)] \dV \\ b(\vvphi) &= - \int_{\CB} \bar{\Btau}:\var\bar{\Be} \dV + \int_{\CB} \rho_0\bar{\BGamma}\dtp\vvphi \dV + \int_{\del\CB} \bar{\BT}\dtp\vvphi \dA \end{aligned} } \tag{32}\end{equation}

    If we introduce the Cauchy stress tensor σ\Bsigma and corresponding elasticity tensor cσ=c/J\BFc^\sigma = \BFc/J, our variational formulation can be expressed completely in terms of Eulerian quantities:

    a(Δφ,δφ)=SˉΔeˉ:cˉσ:Δeˉ+σˉ:[xˉ(δφ)Txˉ(Δφ)]dvˉb(δφ)=Sˉσˉ:Δeˉdvˉ+Sˉργˉδφdvˉ+Sˉttˉδφdaˉ\begin{equation} \boxed{ \begin{aligned} a(\Vvphi,\vvphi) &= \int_{\bar{\CS}} \var\bar{\Be}:\bar{\BFc}^\sigma:\Var\bar{\Be} + \bar{\Bsigma} : [\nabla_{\bar{x}}(\vvphi)\tra\nabla_{\bar{x}}(\Vvphi)] \,\dif\bar{v} \\ b(\vvphi) &= - \int_{\bar{\CS}} \bar{\Bsigma}:\var\bar{\Be} \,\dif\bar{v} + \int_{\bar{\CS}} \rho\bar{\Bgamma}\dtp\vvphi \,\dif\bar{v} + \int_{\del\bar{\CS}_t} \bar{\Bt}\dtp\vvphi \,\dif\bar{a} \end{aligned} } \tag{33}\end{equation}

    We have the following relationships of the differential forms:

    dvˉ=Jˉdvandnˉdaˉ=cofFˉNdA\begin{equation} \dif \bar{v} = \bar{J}\dv \eqand \bar{\Bn} \,\dif \bar{a} = \cof \bar{\BF}\BN \dA \tag{34}\end{equation}

    where Fˉ=Xφˉ\bar{\BF} = \nabla_X\bar{\Bvarphi} and Jˉ=detFˉ\bar{J} = \det\bar{\BF}.

    Discretization of the Lagrangian Form

    We use the following FE discretization:

    φh=γ=1nnφγNγ=γ=1nna=1ndφaγeaNγ\begin{equation} \Bvarphi_h = \suml{\gamma=1}{\nnode} \Bvarphi^\gamma N^\gamma = \suml{\gamma=1}{\nnode}\suml{a=1}{\ndim} \varphi_a^\gamma \Be_a N^\gamma \tag{35}\end{equation}

    where nn\nnode is the number of element nodes and nd\ndim is the number of spatial dimensions.

    We use the same discretization for δφ\vvphi and Δφ\Vvphi. Then the linear system at hand becomes

    δ=1nnb=1ndAabγδΔφbδ=baγ\begin{equation} \suml{\delta=1}{\nnode}\suml{b=1}{\ndim}A_{ab}^{\gamma\delta} \Var\varphi_b^\delta = b_a^\gamma \tag{36}\end{equation}

    for a=1,,nda=1,\dots,\ndim and γ=1,,nn\gamma=1,\dots,\nnode where the A\BA and b\Bb are calculated from the variational forms as

    Aabγδ=a(ebNδ,eaNγ)baγ=b(eaNγ)\begin{equation} \begin{aligned} A_{ab}^{\gamma\delta} &= a(\Be_bN^\delta, \Be_aN^\gamma) \\ b_a^\gamma &= b(\Be_aN^\gamma) \end{aligned} \tag{37}\end{equation}

    For detailed derivation, see the post Vectorial Finite Elements.

    For discretized gradients, we have the following relationship

    X(eaNγ)=(eaBγ)\begin{equation} \nabla_X(\Be_aN^\gamma) = (\Be_a\dyd\BB^\gamma) \tag{38}\end{equation}

    where Bγ:=XNγ\BB^\gamma:= \nabla_X N^\gamma. For the first term in aa, we can get rid of the symmetries:

    sym(FˉT(eaNγ)):Cˉ:sym(FˉT(ebNδ))=(FˉT(eaBγ)):Cˉ:(FˉT(ebBδ))=FˉaABBγCˉABCDFˉbCBDδ\begin{equation} \begin{aligned} \sym&(\bar{\BF}\tra\nabla(\Be_aN^\gamma)):\bar{\BFC}: \sym(\bar{\BF}\tra\nabla(\Be_bN^\delta)) \\ &= (\bar{\BF}\tra(\Be_a\dyd\BB^\gamma)):\bar{\BFC}: (\bar{\BF}\tra(\Be_b\dyd\BB^\delta)) \\ &= \bar{F}_{aA}B^\gamma_B\bar{C}_{ABCD}\bar{F}_{bC}B^\delta_D \end{aligned} \tag{39}\end{equation}

    and for the second term, we have

    Sˉ:[(eaNγ)T(ebNδ)]=Sˉ:[(eaBγ)T(ebBδ)]=Sˉ:[BγBδ]gab=BAγSˉABBBδgab\begin{equation} \begin{aligned} \bar{\BS}:[\nabla(\Be_aN^\gamma)\tra \nabla(\Be_bN^\delta)] &= \bar{\BS}:[(\Be_a\dyd \BB^\gamma)\tra (\Be_b \dyd \BB^\delta)] \\ &= \bar{\BS}:[\BB^\gamma\dyd\BB^\delta] g_{ab} \\ &= B^\gamma_A \bar{S}_{AB}B^\delta_B g_{ab} \end{aligned} \tag{40}\end{equation}

    where gabg_{ab} are the components of the Eulerian metric tensor.

    For the first term in bb, we have

    Sˉ:sym(FˉT(eaNγ))=Sˉ:(FˉT(eaBγ))=SˉABFˉaABBγ\begin{equation} \bar{\BS} : \sym(\bar{\BF}\tra\nabla(\Be_aN^\gamma)) = \bar{\BS} : (\bar{\BF}\tra(\Be_a \dyd \BB^\gamma)) = \bar{S}_{AB} \bar{F}_{aA} B^\gamma_B \tag{41}\end{equation}

    Remaining terms can be calculated in a straightforward manner. We then have for A\BA and b\Bb:

    Aabγδ=BFˉaABBγCˉABCDFˉbCBDδ+BAγSˉABBBδgabdVbaγ=BSˉABFˉaABBγdV+Bρ0ΓˉaNγdV+BtTˉaNγdA\begin{equation} \boxed{ \begin{aligned} A_{ab}^{\gamma\delta} &= \int_{\CB} \bar{F}_{aA}B^\gamma_B\bar{C}_{ABCD}\bar{F}_{bC}B^\delta_D + B^\gamma_A \bar{S}_{AB}B^\delta_B g_{ab} \dV \\ b_a^\gamma &= -\int_{\CB} \bar{S}_{AB} \bar{F}_{aA} B^\gamma_B \dV + \int_{\CB}\rho_0\bar{\Gamma}_aN^\gamma \dV + \int_{\del\CB_t}\bar{T}_aN^\gamma \dA \end{aligned} } \tag{42}\end{equation}

    The lowercase indices in γˉ\bar{\Bgamma} and Tˉ\bar{\BT} might be confusing, but in fact

    Γa(X,t)=γa(x,t)φ(X,t)Ta(X,t)=ta(x,t)φ(X,t)\begin{equation} \begin{aligned} \Gamma_a(\BX,t) &= \gamma_a(\Bx, t) \circ \Bvarphi(\BX,t) \\ T_a(\BX,t) &= t_a(\Bx, t) \circ \Bvarphi(\BX,t) \\ \end{aligned} \tag{43}\end{equation}

    The system is solved for Δφ\Vvphi at each Newton iteration with the following update equation:

    φφˉ+Δφ\begin{equation} \Bvarphi \leftarrow \bar{\Bvarphi} + \Vvphi \htmlId{eq:lagrangianupdate1}{} \tag{44}\end{equation}

    Discretization of the Eulerian Form

    Discretization of the Eulerian formulation parallels that of Lagrangian.

    Aabγδ=BBˉcγcˉacbdBˉdδ+BˉeγτˉefBˉfδgabdVbaγ=BτˉabBˉbγdV+Bρ0ΓˉaNγdV+BtTˉaNγdAorAabγδ=SˉBˉcγcˉacbdσBˉdδ+BˉeγσˉefBˉfδgabdvˉbaγ=SˉσˉabBˉbγdvˉ+SˉργˉaNγdvˉ+SˉttˉaNγdaˉ\begin{gather} \boxed{ \begin{aligned} A_{ab}^{\gamma\delta} &= \int_{\CB} \bar{B}^\gamma_c \bar{c}_{acbd}\bar{B}^\delta_d + \bar{B}^\gamma_e \bar{\tau}_{ef}\bar{B}^\delta_f g_{ab} \dV \\ b_a^\gamma &= -\int_{\CB} \bar{\tau}_{ab} \bar{B}^\gamma_b \dV + \int_{\CB}\rho_0 \bar{\Gamma}_aN^\gamma \dV + \int_{\del\CB_t}\bar{T}_aN^\gamma \dA \end{aligned} } \tag{45}\\ \text{or} \nonumber \\ \boxed{ \begin{aligned} A_{ab}^{\gamma\delta} &= \int_{\bar{\CS}} \bar{B}^\gamma_c \bar{c}^\sigma_{acbd}\bar{B}^\delta_d + \bar{B}^\gamma_e \bar{\sigma}_{ef}\bar{B}^\delta_f g_{ab} \,\dif\bar{v} \\ b_a^\gamma &= -\int_{\bar{\CS}} \bar{\sigma}_{ab} \bar{B}^\gamma_b \,\dif\bar{v} + \int_{\bar{\CS}}\rho \bar{\gamma}_aN^\gamma \,\dif\bar{v} + \int_{\del\bar{\CS}_t}\bar{t}_aN^\gamma \,\dif\bar{a} \end{aligned} } \tag{46}\end{gather}

    Here, Bˉγ=xˉNγ\bar{\BB}^\gamma = \nabla_{\bar{x}} N^\gamma denote the spatial gradients of the shape functions. One way of calculating is Bˉγ=FˉTBγ\bar{\BB}^\gamma = \bar{\BF}\invtra\BB^\gamma, similar to (13).

    The update equation (44) holds for the Eulerian version.

    Conclusion

    The equations above in boxes contain all the information needed to implement the nonlinear solution scheme of hyperelasticity.

  6. Portrait of Onur Solmaz
    Onur Solmaz · Post · /2018/01/24

    Discontinuous Divergence Theorem

    $ \newcommand{\div}{\mathop{\rm div}\nolimits} \newcommand{\llbracket}{[[} \newcommand{\rrbracket}{]]} $

    In lecture notes related to the Discontinuous Galerkin method, there is mention of a magic formula which AFAIK first appeared on a paper1 by Douglas Arnold (at least in this context).

    It has been proven and all, but it’s still called magic because its reasoning is not apparent at first glance. The magic formula is actually a superset of the divergence theorem, generalized to discontinuous fields. But to make that generalization, we need to abandon the standard formulation which starts by creating a triangular mesh, and consider arbitrary partitionings of a domain.

    A domain Ω\Omega is partitioned into parts PiP^i, i=1,,ni=1,\dots,n as follows:

    Ω=i=1nPi\Omega=\bigcup_{i=1}^{n} P^i P={P1,,Pn}\mathcal{P} = \{P^1,\dots,P^{n}\}

    We call the set of parts P\mathcal{P} a partition of Ω\Omega.

    Broken Hilbert Spaces

    We allow the vector field u\boldsymbol{u} to be discontinuous at boundaries Pi\partial P^i and continuous in PiP^i, i=1,,ni=1,\dots,n. To this end, we define the broken Hilbert space over partition P\mathcal{P}

    Hm(P):={vL2(Ω)ndPP,vPHm(P)}\begin{equation} H^m(\mathcal{P}) := \{\boldsymbol{v}\in L^2(\Omega)^{n_d} \mid \forall P\in\mathcal{P}, \boldsymbol{v}|_P \in H^m(P)\} \tag{1}\end{equation}

    It can be seen that Hm(P)Hm(Ω)H^m(\mathcal{P})\subseteq H^m(\Omega).

    Part Boundaries

    Topologically, a part may share boundary with Ω\Omega, like P4P^4. In that case, the boundary of the part is divided into an interior boundary and exterior boundary:

    Pexti=PiΩandPinti=PiPexti\begin{equation} \partial P_{\text{ext}}^i = \partial P^i \cap \partial\Omega \quad\text{and}\quad \partial P_{\text{int}}^i = \partial P^i \setminus \partial P_{\text{ext}}^i \tag{2}\end{equation}

    If a part has an exterior boundary, it is said to be an external part (P3P^3, P4P^4, P5P^5, P6P^6). If it does not have any exterior boundary, it is said to be an internal part.(P1P^1, P2P^2).

    Divergence theorem over parts

    For a vector field vH1(P)\boldsymbol{v}\in H^1(\mathcal{P}), i=1,,ni=1,\dots,n, we can write the following integral as a sum and apply the divergence theorem afterward

    ΩdivvdV=i=1nPidivvdV=i=1nPivndA=i=1nPextivndA+i=1nPintivndA\begin{equation} \begin{aligned} \int_\Omega \div{\boldsymbol{v}} \,dV &= \sum\limits_{i=1}^{n}\int_{P^i}\div\boldsymbol{v} \,dV = \sum\limits_{i=1}^{n}\int_{\partial P^i} \boldsymbol{v}\cdot\boldsymbol{n} \,dA \\ &= \sum\limits_{i=1}^{n}\int_{\partial P_{\text{ext}}^i} \boldsymbol{v}\cdot\boldsymbol{n} \,dA +\sum\limits_{i=1}^{n}\int_{\partial P_{\text{int}}^i} \boldsymbol{v}\cdot\boldsymbol{n} \,dA \end{aligned} \tag{3}\end{equation}

    We define the portion Γij\Gamma^{ij} of the boundary that part PiP^i shares with PjP^j as the interface between PiP^i and PjP^j.

    Γij=PiPj\begin{equation} \Gamma^{ij} = \partial P^i \cap \partial P^j \tag{4}\end{equation}

    If PiP^i and PjP^j are not neighbors, we simply have Γij=\Gamma^{ij}=\emptyset.

    Integrals over interior boundaries

    For opposing parts PiP^i and PjP^j,

    we have different values of the function vij=vΓij\boldsymbol{v}^{ij} = \boldsymbol{v}|_{\Gamma^{ij}} and conjugate normal vectors at the interface Γij\Gamma^{ij}:

    vijvjiandnij=nji\begin{equation} \boldsymbol{v}^{ij}\neq\boldsymbol{v}^{ji} \quad\text{and}\quad \boldsymbol{n}^{ij} = -\boldsymbol{n}^{ji} \tag{5}\end{equation}

    Since

    Pinti=j=1nΓijfori=1,,n\begin{equation} \partial P_{\text{int}}^i = \bigcup_{j=1}^{n} \Gamma^{ij} \quad \text{for}\quad i=1,\dots,n \tag{6}\end{equation}

    we can rearrange the integral over interior boundaries as

    i=1nPintivndA=i=1nj=1nΓijvijnijdA\begin{equation} \sum\limits_{i=1}^{n}\int_{\partial P_{\text{int}}^i} \boldsymbol{v}\cdot\boldsymbol{n} \,dA = \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\int_{\Gamma^{ij}} \boldsymbol{v}^{ij}\cdot\boldsymbol{n}^{ij} \,dA \tag{7}\end{equation}

    The jump operator

    Integrals over the same interface can be grouped together:

    i=1nj=1nΓijvijnijdA=i=1nj=inΓijΓji(vijnij+vjinji)dA\begin{equation} \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\int_{\Gamma^{ij}} \boldsymbol{v}^{ij}\cdot\boldsymbol{n}^{ij} \,dA = \sum\limits_{i=1}^{n}\sum\limits_{j=i}^{n}\int_{\Gamma^{ij}\equiv\Gamma^{ji}} (\boldsymbol{v}^{ij}\cdot\boldsymbol{n}^{ij} + \boldsymbol{v}^{ji}\cdot\boldsymbol{n}^{ji}) \,dA \tag{8}\end{equation}

    Defining the jump of v\boldsymbol{v} across Γij\Gamma^{ij}

    [[v]]Γij=vijvji\begin{equation} \llbracket\boldsymbol{v}\rrbracket_{\Gamma^{ij}} = \boldsymbol{v}^{ij} - \boldsymbol{v}^{ji} \tag{9}\end{equation}

    The jump of a function measures its discontinuity across interfaces. We can write

    Γij[[v]]ΓijnijdA=Γij(vijnij+vjinji)dA\begin{equation} \int_{\Gamma^{ij}} \llbracket\boldsymbol{v}\rrbracket_{\Gamma^{ij}}\cdot\boldsymbol{n}^{ij} \,dA = \int_{\Gamma^{ij}} (\boldsymbol{v}^{ij}\cdot\boldsymbol{n}^{ij} + \boldsymbol{v}^{ji}\cdot\boldsymbol{n}^{ji}) \,dA \tag{10}\end{equation}

    We may drop the superscripts where there is no confusion.

    Interfaces and external boundaries

    It is convenient to group the interfaces:

    I:={Γiji=1,,n;j=i,,n}\begin{equation} \boxed{ \mathcal{I} := \{\Gamma^{ij}\mid i=1,\dots,n; j=i,\dots,n\} } \tag{11}\end{equation}

    which allows us to write

    i=1nj=inΓij[[v]]ndA=ΓIΓ[[v]]ndA\begin{equation} \sum\limits_{i=1}^{n}\sum\limits_{j=i}^{n} \int_{\Gamma^{ij}} \llbracket\boldsymbol{v}\rrbracket\cdot\boldsymbol{n} \,dA = \sum\limits_{\Gamma\in\mathcal{I}} \int_{\Gamma} \llbracket\boldsymbol{v}\rrbracket\cdot\boldsymbol{n}\,dA \tag{12}\end{equation}

    It’s obvious that the union of part external boundaries equals the domain boundary:

    i=1nPexti=Ω\begin{equation} \bigcup_{i=1}^{n} \partial P_{\text{ext}}^i = \partial \Omega \tag{13}\end{equation}

    which allows us to write

    i=1nPextivndA=ΩvndA\begin{equation} \sum\limits_{i=1}^{n}\int_{\partial P_{\text{ext}}^i} \boldsymbol{v}\cdot\boldsymbol{n} \,dA = \int_{\partial\Omega} \boldsymbol{v}\cdot\boldsymbol{n} \,dA \tag{14}\end{equation}

    With the results obtained, we put forward a generalized version of the divergence theorem: Let vH1(P)\boldsymbol{v}\in H^1(\mathcal{P}) be a vector field. Then we have

    ΩdivvdV=ΩvndA+ΓIΓ[[v]]ndA\begin{equation} \boxed{ \int_\Omega \div\boldsymbol{v} \,dV = \int_{\partial\Omega} \boldsymbol{v}\cdot\boldsymbol{n} \,dA + \sum\limits_{\Gamma\in\mathcal{I}} \int_{\Gamma} \llbracket\boldsymbol{v}\rrbracket\cdot\boldsymbol{n} \,dA } \tag{15}\end{equation}

    Verbally, the integral of the divergence of a vector field over a domain Ω\Omega equals its integral over the domain boundary Ω\partial\Omega, plus the integral of its jump over part interfaces I\mathcal{I}.

    In the case of a continuous field, the jumps vanish and this reduces to the regular divergence theorem.

    The Magic Formula

    There are different versions of the magic formula for scalar, vector and tensor fields, and for different IBVPs. I won’t try to derive them all, but give an example: If we were substitute a linear mapping Av\boldsymbol{A}\boldsymbol{v} instead of v\boldsymbol{v}, we would have the jump [[Av]]\llbracket \boldsymbol{A}\boldsymbol{v} \rrbracket on the right-hand side.

    We introduce the vector and tensor average operator {}\{\cdot\}

    {v}Γij=12(vij+vji)and{A}Γij=12(Aij+Aji)\begin{equation} \{\boldsymbol{v}\}_{\Gamma^{ij}} = \frac{1}{2} (\boldsymbol{v}^{ij} + \boldsymbol{v}^{ji}) \quad\text{and}\quad \{\boldsymbol{A}\}_{\Gamma^{ij}} = \frac{1}{2} (\boldsymbol{A}^{ij} + \boldsymbol{A}^{ji}) \tag{16}\end{equation}

    and tensor jump operator [[]]\llbracket\cdot\rrbracket

    [[A]]Γij=AijAji\begin{equation} \llbracket\boldsymbol{A}\rrbracket_{\Gamma^{ij}} = \boldsymbol{A}^{ij} - \boldsymbol{A}^{ji} \tag{17}\end{equation}

    We also note the boundary jump/average property which is used on the integral over Ω\partial\Omega

    {v}=[[v]]=vonΩ\begin{equation} \boxed{ \{\boldsymbol{v}\} = \llbracket\boldsymbol{v}\rrbracket = \boldsymbol{v} \quad \text{on}\quad\partial\Omega } \htmlId{eq:property1}{} \tag{18}\end{equation}

    (This property is used implicitly in many places, and often causes confusion).

    These definitions allow us to write the identity

    [[Av]]=[[A]]{v}+{A}[[v]]\begin{equation} \boxed{ \llbracket\boldsymbol{A}\boldsymbol{v}\rrbracket = \llbracket\boldsymbol{A}\rrbracket\{\boldsymbol{v}\} + \{\boldsymbol{A}\}\llbracket\boldsymbol{v}\rrbracket } \tag{19}\end{equation}

    which is easily proven when expanded.

    The different versions of the magic formula are obtained by substituting the identities above—or their analogs—in the discontinuous divergence theorem.

    1. Douglas N. Arnold. An interior penalty finite element method with discontinuous elements. SIAM J. Numer. Anal., 19(4):742–760, 1982.