粘弾性解析の基礎①:弾性と粘性

はじめに

 同じ量だけ変形させたのに、ゆっくり引っ張った場合と、素早く引っ張った場合とで、必要な力が違う。変形させたまま待っていると、必要な力が変わる。このような応答を理解するには、変形の大きさに加えて、変形の速さや経過時間を考える必要があります。

この連載では、粘弾性を材料点の計算から学びます。材料点とは、材料の中の小さな一部分を取り出し、その部分に与えた応力やひずみと、その応答だけを考える見方です。物体全体の変形分布を求める前に、材料そのものの応答を理解することが目的です。

第\(1\)回では、弾性を表す「ばね」と、粘性を表す「ダッシュポット」を別々に調べます。力学モデルから式を立て、数値計算を行い、グラフと数値で意味を確かめます。

今回できるようになること

  • 応力・ひずみ・ひずみ速度の違いと単位を説明する。
  • ばねとダッシュポットの構成式を使う。
  • 同じ最終ひずみでも、負荷時間によって応力が変わる理由を説明する。
  • 応力緩和とクリープで、何を一定にして何を測るのかを区別する。
図\(1\):弾性と粘性を表す二つの理想要素。ここでは別々の材料点として比較し、まだ接続しません。図の矢印は引張り方向を表します。

応力・ひずみ・ひずみ速度を整理する

 初期長さが \(L_0\)、断面積が \(A\) の一様な棒を考えます。軸方向の引張り力を \(F\)、伸びを \(u\) とすると、微小変形の範囲では、応力とひずみを次のように定義します。

\begin{align} \sigma&=\frac{F}{A},\qquad \varepsilon=\frac{u}{L_0} \tag{1} \end{align}
\(\sigma\):応力[\(\mathrm{Pa},\,\mathrm{MPa}\)]
\(F\):軸方向の力[\(\mathrm{N}\)]
\(A\):断面積[\(\mathrm{m}^{2},\,\mathrm{mm}^{2}\)]
\(\varepsilon\):ひずみ[無次元]
\(u\):伸び[\(\mathrm{m},\,\mathrm{mm}\)]
\(L_0\):初期長さ[\(\mathrm{m},\,\mathrm{mm}\)]

応力は単位面積あたりの力で、単位は\(\mathrm{Pa}\)や\(\mathrm{MPa}\)です。ひずみは長さの比なので無次元です。ひずみ\(0.01\)は\(1\,\%\)を意味します。本記事では引張りの応力・ひずみを正とします。

ひずみが時間とともに変わるとき、その変化率を「ひずみ速度」と呼びます。上の点は時間微分を表します。

\begin{align} \dot{\varepsilon}&=\frac{d\varepsilon}{dt} \tag{2} \end{align}
\(\dot{\varepsilon}\):ひずみ速度[\(\mathrm{s}^{-1}\)]
\(t\):時間[\(\mathrm{s}\)]

例えば、ひずみを\(0\)から\(0.01\)まで一定の速さで増やすと、\(1\,\mathrm{s}\)かけた場合のひずみ速度は\(0.01\,\mathrm{s}^{-1}\)、\(10\,\mathrm{s}\)かけた場合は\(0.001\,\mathrm{s}^{-1}\)です。同じ最終ひずみでも、ひずみ速度は異なります。

今回の仮定

  • 一次元・微小ひずみで、材料内部の状態は一様とします。
  • 温度は一定とし、材料定数も変化させません。
  • 慣性、塑性、損傷は扱いません。
  • ばねとダッシュポットは、それぞれ線形な理想要素とします。

 ここで求めたいのは、与えたひずみ履歴に対する応力、または与えた応力履歴に対するひずみです。両方を独立に指定するのではなく、一方を入力すると、もう一方が材料の式から決まります。

ばね:応力は現在のひずみで決まる

 弾性を表すばねでは、応力はひずみに比例します。比例係数 \(E\) はヤング率です。

\begin{align} \sigma&=E\varepsilon \tag{3} \end{align}
\(E\):ヤング率[\(\mathrm{MPa}\)]

\(E\) が大きいほど、同じひずみを与えるために大きな応力が必要です。例えば \(E=1000\,\mathrm{MPa}\)のばねを\(1\,\%\)変形させると、応力は \(1000\times0.01=10\,\mathrm{MPa}\)です。

式\((3)\)には時間やひずみ速度が含まれていません。したがって、この理想ばねでは、同じひずみなら、そこまでに要した時間によらず同じ応力になります。ひずみをそのまま保てば応力も一定です。応力をゼロにすれば、ひずみもゼロに戻ります。

ダッシュポット:応力は変形の速さで決まる

 ダッシュポットは、ピストンを動かすと速度に比例した抵抗が生じる要素です。力と変位で表すと、必要な外力は \(F=c\dot{u}\) となります。\(c\) は力を速度に結び付ける係数です。

式\((1)\)を使うと、\(F/A=(cL_0/A)\dot{\varepsilon}\) です。ここで材料点の粘性係数を \(\eta=cL_0/A\) と定義すると、次の構成式が得られます。

\begin{align} \sigma&=\eta\dot{\varepsilon} \tag{4} \end{align}
\(\eta\):材料点の粘性係数[\(\mathrm{MPa}\cdot\mathrm{s}\)]

\(\eta\) の単位は\(\mathrm{MPa}\cdot\mathrm{s}\)です。\(\dot{\varepsilon}\) の単位\(\mathrm{s}^{-1}\)と掛けると、応力の単位\(\mathrm{MPa}\)になります。\(c\) は形状に依存する要素の係数、\(\eta\) はここで用いる材料の係数であり、区別して扱います。ばねでも同様に、力と変位の係数 \(k\) とヤング率 \(E\) は \(k=EA/L_0\) で関係します。

式\((4)\)から、同じひずみまで変形させる場合でも、速く変形させるほど大きな応力が必要だと分かります。また、変形を止めると \(\dot{\varepsilon}=0\) となり、単独のダッシュポットの応力はゼロになります。ひずみ自体がゼロに戻るわけではありません。

応力を入力したときには、過去の時間が効いてくる

 式\((4)\)をひずみ速度について解き、初期時刻から積分します。

\begin{align} \varepsilon(t)&=\varepsilon(0)+\frac{1}{\eta}\int_0^t\sigma(s)\,ds \tag{5} \end{align}
\(s\):時間を積分するための変数[\(\mathrm{s}\)]
\(\varepsilon(0)\):初期ひずみ[無次元]

\(s\) は積分用の時間変数です。同じ応力でも、長く与えれば変形が大きくなります。さらに、現在の応力がゼロでも、それまでに蓄積したひずみは残ります。ダッシュポットには、ばねのように元の長さへ戻そうとする力がありません。

同じ伸びの瞬間を、絵で比べる

図\(2\):\(E=1000\,\mathrm{MPa}\)、\(\eta=1000\,\mathrm{MPa}\cdot\mathrm{s}\)として、ひずみ\(0.5\,\%\)の瞬間を比較。伸びを拡大表示しています。矢印は運動・外力の向きを示し、長さは量の比例表示ではありません。

 左右の図は、同じ時刻の比較ではありません。左は負荷開始から\(0.5\,\mathrm{s}\)、右は\(5\,\mathrm{s}\)の瞬間で、どちらも同じ\(0.5\,\%\)だけ伸びています。ばねは現在の伸びが等しいため、応力も両方\(5\,\mathrm{MPa}\)です。ダッシュポットでは、左のひずみ速度が右の\(10\)倍なので、応力も\(10\)倍になります。

図の破線は変形前の右端の位置です。実線の端が破線からどれだけ移動したかが「伸び」、その端をどの速さで動かしているかが「変形速度」に対応します。この二つを分けて見ることが、以後のモデルを理解する出発点です。

動画\(1\):左右で同じモデル時間を進め、負荷時間\(1\,\mathrm{s}\)と\(10\,\mathrm{s}\)を比較します。上の要素の動きと下のひずみ・応力のグラフが対応します。変形は拡大表示です。

止めることと、力を抜くことを分ける

図\(3\):①変形させる、②長さを固定する、③外力をゼロにする、の順に操作します。単独のダッシュポットでは、②で応力がゼロになっても、伸びはゼロになりません。

 ばねを伸ばしたまま止めると、その長さを保つための力が必要です。力を抜けば、ばねは元の長さへ戻ります。一方、ダッシュポットは、動かしている間には力が必要ですが、止めた後には力が不要です。さらに端を自由にしても、自分で元の長さへ戻る仕組みがないため、変形が残ります。静止後の二つを区別して見てください。

動画\(2\):モデル時間\(0\)〜\(1\,\mathrm{s}\)で伸ばし、\(1\)〜\(4\,\mathrm{s}\)で長さを保ち、\(4\,\mathrm{s}\)で外力をゼロにします。ばねの除荷時の回復は、慣性を無視した理想モデルでは瞬間的です。動画もその瞬間に形状を切り替え、遅い回復としては描いていません。

エネルギーから見た違い

 単位体積あたりの仕事の増分は \(dw=\sigma\,d\varepsilon\) です。ゼロひずみからばねを変形させると、蓄えられる弾性エネルギーは次式になります。

\begin{align} w_e&=\int_0^\varepsilon E e\,de=\frac{1}{2}E\varepsilon^2 \tag{6} \end{align}
\(w_e\):単位体積あたりの弾性エネルギー[\(\mathrm{MJ}/\mathrm{m}^{3}\)]
\(e\):ひずみを積分するための変数[無次元]

一方、ダッシュポットでの単位体積あたりの仕事率は、

\begin{align} \dot{w}_v&=\sigma\dot{\varepsilon}=\eta\dot{\varepsilon}^{\,2}\geq0 \tag{7} \end{align}
\(\dot{w}_v\):単位体積あたりの散逸仕事率[\(\mathrm{MJ}/(\mathrm{m}^{3}\cdot\mathrm{s})\)]

となります。\(\eta>0\) なら、引張りでも圧縮でも負になりません。この理想要素では、機械的な仕事は散逸し、ばねのようには取り戻せません。今回は温度一定の仮定なので、散逸による温度上昇は計算しません。

数値実験:\(1\,\%\)まで引っ張る速さを変える

 同じひずみ\(0.01\)まで、\(1\,\mathrm{s}\)・\(10\,\mathrm{s}\)・\(100\,\mathrm{s}\)で変形させ、その後はひずみを一定に保ちます。変形させる時間を \(t_r\) とすると、入力は次の区分的な直線です。

\begin{align} \varepsilon(t)&=\begin{cases}\varepsilon_0 t/t_r & (0\leq t\leq t_r),\\ \varepsilon_0 & (t>t_r).\end{cases} \tag{8} \end{align}
\(\varepsilon_0\):最終ひずみ[無次元]
\(t_r\):ひずみを増やす負荷時間[\(\mathrm{s}\)]

このように直線的に増やす負荷をランプ負荷と呼びます。負荷中のひずみ速度は \(\varepsilon_0/t_r\)、保持中はゼロです。したがって、両要素の応力は手計算でも求められます。

\begin{align} \sigma_e(t)&=E\varepsilon(t),\qquad \sigma_v(t)=\begin{cases}\eta\varepsilon_0/t_r & (0<t<t_r),\\0 & (t>t_r).\end{cases} \tag{9} \end{align}
\(\sigma_e\):ばねの応力[\(\mathrm{MPa}\)]
\(\sigma_v\):ダッシュポットの応力[\(\mathrm{MPa}\)]

負荷開始点と保持への切替点では、式\((8)\)の傾きが不連続です。その点そのものの速度は一意には定義せず、直前・直後の値を考えます。ダッシュポットの応力が段差になるのは、この入力の理想化によるものです。

教材用の材料定数と予測値

 \(E=1000\,\mathrm{MPa}\)、\(\eta=1000\,\mathrm{MPa}\cdot\mathrm{s}\)、\(\varepsilon_0=0.01\) とします。実在材料に合わせた値ではなく、比較しやすい教材用の値です。

負荷時間 \(t_r\) [\(\mathrm{s}\)]負荷中のひずみ速度 [\(\mathrm{s}^{-1}\)]ばねの到達応力 [\(\mathrm{MPa}\)]負荷中の粘性応力 [\(\mathrm{MPa}\)]
\(1\)\(0.01\)\(10\)\(10\)
\(10\)\(0.001\)\(10\)\(1\)
\(100\)\(0.0001\)\(10\)\(0.1\)

ばねの「到達応力」とダッシュポットの「負荷中の応力」を比べている点に注意してください。保持に入った後、ばねは\(10\,\mathrm{MPa}\)を保ちますが、ダッシュポットはゼロになります。

また、この二つの要素は別々に計算しています。ここで応力を足し合わせると、別の接続モデルを考えたことになります。接続した場合の力と変形の関係は第\(2\)回で扱います。

計算の流れと最小実装

 入力ひずみは既知なので、今回は運動方程式を解く必要がありません。時刻ごとにひずみとひずみ速度を求め、構成式へ代入します。

図\(4\):第\(1\)回の計算フロー。ひずみの差分から区間平均のひずみ速度を求めます。各要素は独立に評価します。

区間 \([t_{n-1},t_n]\) の平均ひずみ速度を、差分で計算します。

\begin{align} \overline{\dot{\varepsilon}}_n&=\frac{\varepsilon_n-\varepsilon_{n-1}}{\Delta t},\quad \sigma_{e,n}=E\varepsilon_n,\quad \overline{\sigma}_{v,n}=\eta\frac{\varepsilon_n-\varepsilon_{n-1}}{\Delta t} \tag{10} \end{align}
\(n\):時刻のステップ番号[無次元]
\(\Delta t\):時間刻み[\(\mathrm{s}\)]
\(\overline{\dot{\varepsilon}}_n\):区間平均のひずみ速度[\(\mathrm{s}^{-1}\)]
\(\overline{\sigma}_{v,n}\):区間平均の粘性応力[\(\mathrm{MPa}\)]

ばねの応力は時刻 \(t_n\) の値、粘性応力は一つ前からの区間平均値です。今回の区分直線入力では、切替時刻を時間格子に合わせれば、区間内のひずみ速度を正確に表せます。

以下は\(1\)条件を計算する最小部分です。配布スクリプトでは、この計算を\(3\)種類の負荷時間で行い、図とCSVを出力します。

E = 1000;                 % MPa
eta = 1000;               % MPa·s
eps0 = 0.01;              % 1% (dimensionless)
tr = 10;                  % ramp time [$\mathrm{s}$]
dt = 0.02;
t = (0:dt:120)';

strain = eps0 * min(t/tr, 1);
rate = [0; diff(strain)/dt];
sigmaE = E * strain;
sigmaV = eta * rate;

tiledlayout(3,1);
nexttile; plot(t,100*strain); ylabel('strain [%]');
nexttile; plot(t,sigmaE); ylabel('elastic stress [MPa]');
nexttile; stairs(t,sigmaV); ylabel('viscous stress [MPa]');
xlabel('time [$\mathrm{s}$]');

diff(strain) は隣り合うひずみの差です。先頭に置いたゼロは、負荷開始直前の静止状態を表すための値であり、最初の区間のひずみ速度ではありません。最初の区間の値は\(2\)番目に入ります。

plot と stairs はどちらも横軸に区間終端の時刻を使っています。粘性応力の段差を描く位置には時間刻み\(1\)個分の表示上のずれが生じ得ます。切替点の一点ではなく、負荷中と保持中の区間値を比較してください。

なぜ、いきなりひずみを与えないのか

 理想的なステップ入力では、ひずみをゼロ時間で変化させます。ダッシュポットに対してこれを要求すると、有限な通常の応力値では表せません。数値上も、ひずみ増分を一つの時間刻みに押し込むと \(\eta\Delta\varepsilon/\Delta t\) となり、時間刻みを小さくするほど応力が大きくなります。

これは、そのまま計算精度の改善とは解釈できません。入力そのものを鋭くしているためです。今回、有限のランプ時間を決めてから時間刻みを設定するのは、物理的な負荷速度と数値的な分割を分けるためです。

計算結果を読む

図\(5\):上から入力ひずみ、ばねの応力、ダッシュポットの応力。同じ最終ひずみでも、粘性応力は負荷速度によって変わります。

 ばねでは、\(1\,\mathrm{s}\)・\(10\,\mathrm{s}\)・\(100\,\mathrm{s}\)のどの条件でも、最終的に\(10\,\mathrm{MPa}\)へ到達します。到達する時刻は異なりますが、同じひずみの時点を比べれば応力も同じです。

ダッシュポットでは、負荷中の応力がそれぞれ\(10\)・\(1\)・\(0.1\,\mathrm{MPa}\)となります。保持に入ると変形速度がゼロになるため、応力もゼロです。これは「時間をかけて応力が減衰する応答」を表したものではありません。速度をゼロに切り替えた結果です。

図\(6\):開始後\(2\,\mathrm{s}\)間の拡大図。全体図で短く見える\(1\,\mathrm{s}\)負荷についても、負荷中は\(10\,\mathrm{MPa}\)、保持中はゼロであることを確認できます。

仕事にも負荷速度の違いが現れる

 ばねを最終ひずみまで変形させる仕事は、どの条件でも式\((6)\)より\(0.05\,\mathrm{MJ}/\mathrm{m}^{3}\)です。ダッシュポットに対しては、式\((7)\)を負荷時間だけ積分すると、

\begin{align} w_v&=\eta\left(\frac{\varepsilon_0}{t_r}\right)^2t_r =\frac{\eta\varepsilon_0^2}{t_r} \tag{11} \end{align}
\(w_v\):単位体積あたりの散逸仕事[\(\mathrm{MJ}/\mathrm{m}^{3}\)]

となります。負荷時間\(1\)・\(10\)・\(100\,\mathrm{s}\)で、それぞれ\(0.1\)・\(0.01\)・\(0.001\,\mathrm{MJ}/\mathrm{m}^{3}\)です。\(\mathrm{MPa}\)をひずみについて積分した値は、エネルギー密度として\(\mathrm{MJ}/\mathrm{m}^{3}\)になります。

同じ変形量でも、ダッシュポットでは速く変形させるほど散逸する仕事が大きくなります。一方、ばねに蓄えられるエネルギーは、現在のひずみで決まります。

応力を一定にするとどうなるか

 次に、入力をひずみから応力へ切り替えます。\(1\,\mathrm{MPa}\)を\(10\,\mathrm{s}\)間与え、その後にゼロへ戻します。初期ひずみはゼロです。

ばねのひずみは負荷直後に \(\sigma_0/E=0.001\)、すなわち\(0.1\,\%\)となり、保持中は一定です。除荷すると直ちにゼロへ戻ります。慣性を無視した理想ばねなので、変形の瞬間的な変化を許しています。

ダッシュポットでは、負荷中に \(\dot{\varepsilon}=\sigma_0/\eta=0.001\,\mathrm{s}^{-1}\)で変形が進みます。\(10\,\mathrm{s}\)後にはひずみ\(0.01\)、すなわち\(1\,\%\)になります。除荷後はひずみ速度がゼロとなり、このひずみを保ちます。

図\(7\):一定応力保持と除荷の比較。ダッシュポットは負荷中に変形が進み、除荷後もその変形が残ります。これは塑性の降伏条件を導入した結果ではありません。

応力緩和とクリープは「何を一定にするか」が違う

図\(8\):応力緩和では端の位置を保って力を測り、クリープでは力を保って伸びを測ります。断面積一定の微小変形を仮定した模式図です。右側の破線は変形前の端の位置です。
観察方法一定に保つ量観察する量
応力緩和ひずみ応力の時間変化
クリープ応力ひずみの時間変化

 応力緩和試験では、変形を与えて保持した後に応力がどう変わるかを調べます。クリープ試験では、一定応力のもとで変形がどう進むかを調べます。試験を表す言葉と、材料が実際に示す応答は区別しましょう。理想ばねは、ひずみ保持中に緩和せず、応力保持中にも追加の変形をしません。

単独のダッシュポットは、一定応力のもとで変形し続けますが、有限な時間をかけた応力緩和や、除荷後に遅れて回復する変形は表せません。弾性と粘性を組み合わせる必要が見えてきました。

MATLABコード

 以下に、数値計算・グラフ・動画を再生成する全コードを掲載します。外部のデータや補助関数のダウンロードは不要です。各コードを指定したファイル名で保存して実行してください。MATLAB R2020a以降を対象とし、追加Toolboxは使いません。

最初のコードは、負荷速度の比較と一定応力保持・除荷を計算し、グラフとCSVを保存します。構成式・解析解・仕事を assert で確認し、すべて満たすと PASS と表示します。今回の入力では切替時刻を時間格子に合わせているため、区間平均速度と仕事は丸め誤差の範囲で手計算と一致します。

このコードを elasticity_viscosity_demo.m として保存し、実行します。保存先の figures/ に計算図、data/ にCSVを出力します。

%% 第1回:弾性と粘性 ― 材料点の最小数値実験
% MATLAB R2020a以降。追加Toolbox不要。
% このファイルを実行すると、figures/ にPNG・PDF、data/ にCSVを保存します。
% 応力: MPa、時間: s、ひずみ: 無次元、粘性係数: MPa s。
clear; close all; clc;
root = fileparts(mfilename('fullpath'));
figdir = fullfile(root,'figures'); datadir = fullfile(root,'data');
if ~exist(figdir,'dir'), mkdir(figdir); end
if ~exist(datadir,'dir'), mkdir(datadir); end
set(groot,'defaultAxesFontName','Yu Gothic','defaultTextFontName','Yu Gothic');
set(groot,'defaultAxesFontSize',11,'defaultLineLineWidth',1.8);

E = 1000; eta = 1000; eps0 = 0.01;
ramps = [1 10 100]; dt = 0.02; t = (0:dt:120)';
colors = [0.10 0.35 0.65; 0.85 0.38 0.10; 0.15 0.55 0.38];
labels = {'1 sで負荷','10 sで負荷','100 sで負荷'};
strain = zeros(numel(t),3); rate = strain; spring = strain; dashpot = strain;
workS = zeros(1,3); workD = workS;
for j = 1:numel(ramps)
    tr = ramps(j);
    strain(:,j) = eps0*min(t/tr,1);
    % 各区間の平均ひずみ速度。0番目の点は負荷開始直前の状態。
    rate(:,j) = [0; diff(strain(:,j))/dt];
    spring(:,j) = E*strain(:,j);
    dashpot(:,j) = eta*rate(:,j);
    de = diff(strain(:,j));
    % ばねは区間両端応力の平均、ダッシュポットは区間一定応力で仕事を計算。
    workS(j) = sum(0.5*(spring(1:end-1,j)+spring(2:end,j)).*de);
    workD(j) = sum(dashpot(2:end,j).*de);
    assert(max(abs(spring(:,j)-E*strain(:,j))) < 1e-10);
    assert(abs(workS(j)-0.5*E*eps0^2) < 1e-10);
    assert(abs(workD(j)-eta*eps0^2/tr) < 1e-10);
    inside = t > 0 & t < tr;
    assert(max(abs(dashpot(inside,j)-eta*eps0/tr)) < 1e-9);
    assert(max(abs(dashpot(t > tr+dt,j))) < 1e-9);
end

f=figure('Color','w','Position',[100 100 1000 920]);
tl=tiledlayout(3,1,'TileSpacing','compact','Padding','compact');
nexttile; hold on;
for j=1:3, plot(t,100*strain(:,j),'Color',colors(j,:)); end
ylabel('ひずみ [%]'); title('(a) 同じ最終ひずみを、異なる速さで与える');
legend(labels,'Location','southeast'); grid on; xlim([0 120]); ylim([0 1.15]);
nexttile; hold on;
for j=1:3, plot(t,spring(:,j),'Color',colors(j,:)); end
ylabel('ばねの応力 [MPa]'); title('(b) ばね:同じひずみなら同じ応力');
grid on; xlim([0 120]); ylim([0 11.5]);
nexttile; hold on;
for j=1:3, stairs(t,dashpot(:,j),'Color',colors(j,:)); end
xlabel('時間 [s]'); ylabel('ダッシュポットの応力 [MPa]');
title('(c) ダッシュポット:速く変形させるほど大きな応力');
grid on; xlim([0 120]); ylim([0 11.5]);
export_pair(f,figdir,'ramp_response');

f=figure('Color','w','Position',[100 100 1000 440]);
tiledlayout(1,2,'TileSpacing','compact','Padding','compact');
nexttile; hold on;
for j=1:3, plot(t,100*strain(:,j),'Color',colors(j,:)); end
title('(a) 負荷開始直後のひずみ'); xlabel('時間 [s]'); ylabel('ひずみ [%]');
xlim([0 2]); ylim([0 1.15]); grid on;
nexttile; hold on;
for j=1:3, stairs(t,dashpot(:,j),'Color',colors(j,:)); end
title('(b) 負荷開始直後の粘性応力'); xlabel('時間 [s]'); ylabel('応力 [MPa]');
xlim([0 2]); ylim([0 11.5]); grid on; legend(labels,'Location','northeast');
export_pair(f,figdir,'ramp_zoom');

% 一定応力を時刻0の直後から10秒間与え、その後に除荷する。
% ばねの瞬間変形は繰返し時刻で描く。慣性を無視した理想化。
sig0=1; thold=10;
tc=[0;0;(dt:dt:thold)';thold;(thold+dt:dt:20)'];
sc=[0;sig0;sig0*ones(round(thold/dt),1);0;zeros(round(thold/dt),1)];
ecS=sc/E; ecD=sig0/eta*min(tc,thold);
assert(abs(ecD(end)-0.01)<1e-12);
assert(ecS(end)==0);
f=figure('Color','w','Position',[100 100 1000 680]);
tiledlayout(2,1,'TileSpacing','compact','Padding','compact');
nexttile; plot(tc,sc,'Color',colors(1,:)); ylabel('応力 [MPa]');
title('(a) 1 MPaを10秒間与え、その後に除荷'); grid on; ylim([-0.1 1.2]);
nexttile; plot(tc,100*ecS,'Color',colors(1,:)); hold on;
plot(tc,100*ecD,'Color',colors(2,:)); xlabel('時間 [s]'); ylabel('ひずみ [%]');
title('(b) ばねは直ちに回復し、単独のダッシュポットは変形が残る');
legend('ばね','ダッシュポット','Location','southeast'); grid on; ylim([-0.05 1.15]);
export_pair(f,figdir,'creep_recovery');

writetable(table(ramps',eta*eps0./ramps',workS',workD', ...
    'VariableNames',{'ramp_time_s','viscous_stress_MPa','spring_work_MJ_m3','dashpot_work_MJ_m3'}), ...
    fullfile(datadir,'summary.csv'));
writetable(table(t,strain(:,1),strain(:,2),strain(:,3),spring(:,1),spring(:,2),spring(:,3), ...
    dashpot(:,1),dashpot(:,2),dashpot(:,3),'VariableNames', ...
    {'time_s','strain_1s','strain_10s','strain_100s','spring_1s_MPa','spring_10s_MPa', ...
    'spring_100s_MPa','dashpot_1s_MPa','dashpot_10s_MPa','dashpot_100s_MPa'}),fullfile(datadir,'ramp_history.csv'));
writetable(table(tc,sc,ecS,ecD,'VariableNames', ...
    {'time_s','stress_MPa','spring_strain','dashpot_strain'}),fullfile(datadir,'creep_history.csv'));
disp('PASS: 構成式・解析解・仕事・除荷後ひずみを確認しました。');
disp(readtable(fullfile(datadir,'summary.csv')));

function export_pair(f,folder,name)
    exportgraphics(f,fullfile(folder,[name '.png']),'Resolution',180,'BackgroundColor','white');
    exportgraphics(f,fullfile(folder,[name '.pdf']),'ContentType','vector','BackgroundColor','white');
end

このコードを viscoelasticity_videos.m として保存し、viscoelasticity_videos を実行します。保存先の videos/ にMP4を出力します。描画の伸びは拡大し、動画の再生時間とモデル時間を区別しています。

function viscoelasticity_videos
% 記事第1回の2本のMP4を再生成。追加Toolbox不要。
% 描画の伸びは誇張。動画の再生時間とモデル時間を区別して表示。
root=fileparts(mfilename('fullpath')); out=fullfile(root,'videos');
if ~exist(out,'dir'),mkdir(out);end
set(groot,'defaultAxesFontName','Yu Gothic','defaultTextFontName','Yu Gothic');
set(groot,'defaultAxesFontSize',12);
E=1000;eta=1000;fps=20;
for mode=1:2
    f=figure('Color','w','Position',[60 60 1280 720],'MenuBar','none','ToolBar','none','Visible','off');
    annotation(f,'textbox',[.02 .92 .96 .07],'String', ...
        pick(mode==1,'動画1:同じ最終ひずみでも、速さによって粘性応力が変わる', ...
        '動画2:伸ばす → 長さを保つ → 力をゼロにする'), ...
        'FontName','Yu Gothic','FontSize',22,'FontWeight','bold','EdgeColor','none','HorizontalAlignment','center');
    clock=annotation(f,'textbox',[.02 .86 .96 .05],'String','','FontSize',15,'EdgeColor','none','HorizontalAlignment','center');
    annotation(f,'textbox',[.02 .005 .96 .05],'String', ...
        '青:ばね   橙:ダッシュポット   破線:変形前の端   /   伸びは拡大表示。慣性を無視した理想要素。', ...
        'FontSize',12,'EdgeColor','none','HorizontalAlignment','center');
    colors={[.08 .34 .62],[.73 .29 .07]};
    models=cell(2,2); traces=cell(2,2); axesTrace=cell(2,2);
    for col=1:2
        a=axes(f,'Position',[.045+(col-1)*.48 .43 .43 .40]);
        axis(a,[0 340 0 200]);axis(a,'off');hold(a,'on');
        if mode==1
            title(a,pick(col==1,'速い負荷:1秒で1%','遅い負荷:10秒で1%'),'FontSize',17);
        else
            title(a,pick(col==1,'ばね:力を抜くと戻る','ダッシュポット:伸びたまま'),'FontSize',17);
        end
        for row=1:2
            if mode==1, kind=row; y=145-(row-1)*105;
            else, kind=col;y=110;if row==2,continue;end,end
            models{row,col}=make_element(a,kind,y,colors{kind});
        end
        for row=1:2
            at=axes(f,'Position',[.06+(col-1)*.48 .29-(row-1)*.16 .40 .12]);hold(at,'on');grid(at,'on');
            horizon=pick(mode==1,12,7);
            xlim(at,[0 horizon]);ylim(at,pick(row==1,[-.05 1.15],[-.5 11.5]));
            ylabel(at,pick(row==1,'ひずみ [%]','応力 [MPa]'));
            if row==2,xlabel(at,'モデル時間 [s]');end
            traces{row,col}=plot(at,nan,nan,'LineWidth',2,'Color',pick(mode==1,colors{1},colors{col}));
            axesTrace{row,col}=at;
        end
        if mode==1
            traces{3,col}=plot(axesTrace{2,col},nan,nan,'LineWidth',2,'Color',colors{2});
        end
    end
    if mode==1
        name='video01_loading_speed';ts=linspace(0,12,321);ramps=[1 10];
    else
        name='video02_hold_release';ts=linspace(0,7,281);ramps=[];
    end
    v=VideoWriter(fullfile(out,[name '.mp4']),'MPEG-4');v.FrameRate=fps;v.Quality=92;open(v);
    saved=false(1,3);
    for k=1:numel(ts)
        t=ts(k);
        if mode==1
            clock.String=sprintf('モデル時間 %.2f s   /   両条件とも最終ひずみ1%%。時間は同じ速さで進みます。',t);
            for col=1:2
                e=.01*min(t/ramps(col),1);rate=.01/ramps(col)*(t<ramps(col));
                update_element(models{1,col},e,E*e,rate,'ばね');
                update_element(models{2,col},e,eta*rate,rate,'ダッシュポット');
                et=.01*min(ts(1:k)/ramps(col),1);
                set(traces{1,col},'XData',ts(1:k),'YData',et*100);
                set(traces{2,col},'XData',ts(1:k),'YData',E*et);
                set(traces{3,col},'XData',ts(1:k),'YData',eta*.01/ramps(col)*(ts(1:k)<ramps(col)));
            end
        else
            stage=1+(t>=1)+(t>=4);
            names={'① 1秒で1%まで伸ばす','② 長さを固定:動かさずに保つ','③ 外力ゼロ:ばねの戻りは理想的に瞬間'};
            clock.String=sprintf('モデル時間 %.2f s   /   %s',t,names{stage});
            eh=.01*min(ts(1:k),1);springe=eh.*(ts(1:k)<4);
            current=[springe(end),eh(end)];stresses=[E*current(1),eta*.01*(t<1)];
            for col=1:2
                update_element(models{1,col},current(col),stresses(col),.01*(t<1),pick(col==1,'ばね','ダッシュポット'));
                set(traces{1,col},'XData',ts(1:k),'YData',pick(col==1,springe,eh)*100);
                set(traces{2,col},'XData',ts(1:k),'YData',pick(col==1,E*springe,eta*.01*(ts(1:k)<1)));
            end
        end
        drawnow;
        frame=getframe(f);writeVideo(v,frame);
        for j=1:3
            mark=pick(mode==1,[.5 4 11],[.8 2.5 5]);
            if ~saved(j) && t>=mark(j)
                imwrite(frame.cdata,fullfile(out,sprintf('%s_check%02d.png',name,j)));saved(j)=true;
            end
        end
        if mode==1 && k==round(numel(ts)*.35) || mode==2 && k==round(numel(ts)*.37)
            imwrite(frame.cdata,fullfile(out,[name '_poster.png']));
        end
    end
    close(v);close(f);
    r=VideoReader(fullfile(out,[name '.mp4']));assert(r.Width==1280 && r.Height==720);
    assert(r.Duration>10);fprintf('PASS: %s, %.2f seconds, %dx%d\n',name,r.Duration,r.Width,r.Height);
end
end

function m=make_element(a,kind,y,color)
plot(a,[25 25],[y-18 y+18],'Color',[.5 .55 .6],'LineWidth',4);
plot(a,[180 180],[y-24 y+24],'--','Color',[.6 .65 .7]);
m.a=a;m.kind=kind;m.y=y;m.color=color;
m.line=plot(a,nan,nan,'Color',color,'LineWidth',2.7);
m.end=plot(a,nan,nan,'Color',color,'LineWidth',5);
m.force=quiver(a,0,0,0,0,0,'Color',color,'LineWidth',2,'MaxHeadSize',.8);
m.label=text(a,170,y-35,'','FontName','Yu Gothic','FontSize',15,'Color',color,'HorizontalAlignment','center');
m.motion=text(a,170,y+31,'','FontName','Yu Gothic','FontSize',12,'Color',[.1 .45 .35],'HorizontalAlignment','center');
end

function update_element(m,e,s,rate,label)
xend=180+e/.01*65;y=m.y;
if m.kind==1
    xx=[25 45 linspace(48,xend-18,17) xend];yy=[y y y+10*(-1).^(0:16) y];
else
    piston=xend-75;
    xx=[25 65 65 185 nan 65 65 185 nan piston piston nan piston xend];
    yy=[y y y+18 y+18 nan y y-18 y-18 nan y-15 y+15 nan y y];
end
set(m.line,'XData',xx,'YData',yy);set(m.end,'XData',[xend xend],'YData',[y-12 y+12]);
set(m.force,'XData',xend+7,'YData',y,'UData',35*(s>1e-9),'VData',0);
m.label.String=sprintf('%s:ひずみ %.2f%% / 応力 %.2f MPa',label,100*e,s);
m.motion.String=pick(rate>0,'右端を動かす →','右端は静止');
end

function y=pick(c,a,b)
if c,y=a;else,y=b;end
end

おわりに

 ばねは変形を蓄え、除荷すると回復します。ダッシュポットは速度に応じて抵抗し、応力を与えた時間に応じて変形が蓄積します。この二つだけでも、現在のひずみ、変形速度、過去の負荷時間という異なる情報が応答を決めることが分かりました。

次回の記事では、この二つを直列・並列につなぎます。接続条件からMaxwellモデルとKelvin–Voigtモデルの式を導き、「保持中に応力が徐々に減る」「一定応力で変形が徐々に進む」という時間依存の応答を計算します。

参考文献

 以下は、ばね・ダッシュポットの構成式と、応力緩和・クリープの観察方法に対応する参考文献です。記事の数値例と図は、本記事用の理想要素・教材定数から作成しています。

  • David Roylance, Engineering Viscoelasticity, MIT OpenCourseWare, \(2001\). ばね・ダッシュポットを用いた線形粘弾性の入門資料。公開PDF

コメント

タイトルとURLをコピーしました