縄跳び紐の張力計算
ヤコビの楕円関数の計算に縄跳び紐の計算が有ります。
縄跳び紐の回転時の形状は、ヤコビの楕円関数snの計算を使用します。
此処では、縄跳び紐の張力の計算をします。
張力の計算には、第一種楕円積分を利用する方法と、二次方程式を利用する方法が有ったので、両方の計算をしてみました。
二次方程式を利用する方法でも、cの値の計算に第一種楕円積分を使用しているので、両方とも第一種楕円積分を利用していることは変わりません。
紐の張力は水平方向の張力を計算して、紐端点の紐の角度により、紐の端点の張力を求めます。
実際には、重力加速度の影響があるのですが、計算に入っていません。
紐を廻す為には、端点も小さな回転をしているし、空気の抵抗があるので、厳密な計算は難しく、ここでは全て無視をして向心力だけの計算をしています。
紐の長さ計算には、第一種楕円積分と第二種楕円積分を利用します。
左図は、X方向を分割計算して、垂直張力の分布を表示しています。
分割計算の場合、垂直方向の値を求めるのが容易だからです。
中央部分が少し凹んでいるのは、X方向に対して、紐が水平に近くなるため、傾斜のある部分より、紐の長さが短くなる為です。
張力のグラフは、分割計算により表示しています。
表示方法を変更することにより、中央からの合計値の表示も可能です。
紐の水平張力1は、第一種楕円積分による張力計算、紐の水平張力2は、第一種楕円積分と二次方程式利用による張力計算です。
プログラム
//----------------------------------------------------- // ヤコビの楕円関数と縄跳びの紐計算 // http://fuchino.ddo.jp/yatsugatake/ellipticx.pdf //----------------------------------------------------- unit Main; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, VclTee.TeeGDIPlus, VCLTee.TeEngine, VCLTee.Series, VCLTee.TeeProcs, VCLTee.Chart; type TForm1 = class(TForm) LabeledEdit10: TLabeledEdit; LabeledEdit11: TLabeledEdit; Memo1: TMemo; nawaBtn: TButton; Chart1: TChart; Series1: TLineSeries; Series2: TPointSeries; massEdit: TLabeledEdit; rotational_speed_Edit: TLabeledEdit; Series3: TLineSeries; RadioButton1: TRadioButton; RadioButton2: TRadioButton; procedure nawaBtnClick(Sender: TObject); private { Private 宣言 } function RJ(x1, y1, z1, p1: double): double; function RF(x1, y1, z1: double): double; function calc_first_elliptic_integral(k, dpi: double; var ALFRG : byte): double; function calc_second_elliptic_integral(k, dpi: double): double; function jacobi_sn(u, k: double): double; function Jacobi_am(u, k: double): double; procedure tension(a, k, b, c: double); public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} uses Math; // Carlson's Elliptic Integral RJ function TForm1.RJ(x1, y1, z1, p1: double): double; var s, fa, rj, rk : double; la, mu : double; al, be : double; r1, a1, b1 : double; a2, b2, mv, s1 : double; rc, r0 : double; s12, s13, s14 : double; s15, s16 : double; x2, y2, z2 : double; x3, y3, z3 : double; s2, s3, p2, p3 : double; s4, s5 : double; x31, y31, z31, p31 : double; s22, s23, s2s3 : double; procedure rcab; begin rc := 1; a1 := al; b1 := be; repeat r1 := rc; la := 2 * sqrt(a1 * b1) + b1; a2 := (a1 + la) / 4; b2 := (b1 + la) / 4; mv := (a1 + 2 * b1) / 3; s1 := (b1 - a1) / (3 * mv); s12 := s1 * s1; s13 := s12 * s1; s14 := s13 * s1; s15 := s14 * s1; s16 := s15 * s1; rc := (1 + 3 * s12 / 10 + s13 / 7 + 3 * s14 / 8 + 9 * s15 / 22 + 159 * s16 / 208) / sqrt(mv); a1 := a2; b1 := b2; until r1 = rc; end; function ss: double; begin x31 := x31 * x3; y31 := y31 * y3; z31 := z31 * z3; p31 := p31 * p3; result := (x31 + y31 + z31 + p31); end; begin s := 0; fa := 3; rj := 0; repeat r0 := rj; la := sqrt(x1 * y1) + sqrt(x1 * z1) + sqrt(y1 * z1); mu := (x1 + y1 + z1 + 2 * p1) / 5; x2 := (x1 + la) / 4; y2 := (y1 + la) / 4; z2 := (z1 + la) / 4; p2 := (p1 + la) / 4; x3 := 1 - x1 / mu; y3 := 1 - y1 / mu; z3 := 1 - z1 / mu; p3 := 1 - p1 / mu; x31 := x3; y31 := y3; z31 := z3; p31 := 2 * p3; s2 := ss / 4; s3 := ss / 6; s4 := ss / 8; s5 := ss / 10; al := (sqrt(x1) + sqrt(y1) + sqrt(z1)) * p1 + sqrt(x1 * y1 * z1); al := al * al; be := p1 * (p1 + la) * (p1 + la); rcab; s22 := 3 * s2 * s2 / 22; s23 := s2 * s2 * s2 / 10; s2s3 := s2 * s3 * 3 / 13; s := s + fa * rc; rk := 1 + 3 * s2 / 7 + s3 / 3 + s22 + s2s3 + 3 * s4 / 11 + 3 * s5 / 13 - s23; fa := fa / 4; mu := sqrt(1 / (mu * mu * mu)); rj := s + fa / 4 * (rk + 3 * s3 * s3 / 10 + 3 * s2 * s4 / 5) * mu; x1 := x2; y1 := y2; z1 := z2; p1 := p2; until rj = r0; result := rj; end; // Carlson's Elliptic Integral RF function TForm1.RF(x1, y1, z1: double): double; var la, mu : double; x2, y2, z2 : double; x3, y3, z3 : double; s2, s3 : double; r0, rf : double; s22, s23, s2s3, s32 : double; begin rf := 0; repeat r0 := rf; la := sqrt(x1 * Y1) + sqrt(x1 * z1) + sqrt(y1 * z1); mu := (x1 + y1 + z1) / 3; x2 := (x1 + la) / 4; y2 := (y1 + la) / 4; z2 := (z1 + la) / 4; x3 := 1 - x2 / mu; y3 := 1 - y2 / mu; z3 := 1 - z2 / mu; s2 := (x3 * x3 + y3 * y3 + z3 * z3) / 4; s3 := (x3 * x3 * x3 + y3 * y3 * y3 + z3 * z3 * z3) / 6; s22 := s2 * s2 / 6; s23 := 5 * s2 * s2 * s2 / 26; s32 := 3 * s3 * s3 / 26; s2s3 := s2 * s3 * 3 / 11; rf := (1 + s2 / 5 + s3 / 7 + s22 + s2s3 + s23 + s32) / sqrt(mu); x1 := x2; y1 := y2; z1 := z2; until rf = r0; result := rf; end; // 第一種楕円積分 ルーチン // π/2迄 function TForm1.calc_first_elliptic_integral(k, dpi: double; var ALFRG : byte): double; var s, s2 : double; c2 : double; rf0 : double; k2 : double; eq : double; fqk : double; dpib : double; begin ALFRG := 0; fqk := 0; dpib := dpi; dpi := abs(dpi); s := sin(dpi); // sin(φ) s2 := s * s; // sin~2(φ) eq := dpi; k2 := k * k; // k^2 if ((1 - k2 * s2) <= 0) then begin // 1-K^2*sin^2(φ) = 0 K=1 φ=π/2 なら ALFRG := 1; end else begin // 1-K^2*sin^2(φ) > 0 なら c2 := cos(eq) * cos(eq); // cos^2(φ') s := sin(eq); // sin(φ') s2 := s * s; // sin^2(φ') rf0 := RF(c2, 1 - k2 * s2, 1); fqk := s * rf0; // F(φ',K) = rf0 * sin(φ') if dpib < 0 then fqk := - fqk; // 積分範囲の符号で積分値の符号設定 end; result := fqk; end; // 第二種楕円積分 ルーチン // プラス側のみ pi / 2 迄 function TForm1.calc_second_elliptic_integral(k, dpi: double): double; const KS = 1E-15; var s, s2, s3 : double; c2 : double; rf0 : double; k2 : double; eq : double; rd0 : double; eek : double; begin eq := dpi; k2 := k * k; // k^2 if (eq < pi / 2) or ((1 - abs(K)) >= KS) then begin // if (eqd < 90) or (abs(K) < 1) then begin if (1 - abs(K)) >= KS Then begin // if abs(K) < 1 Then begin c2 := cos(eq) * cos(eq); s := sin(eq); s2 := s * s; s3 := s2 * s; rf0 := RF(c2, 1 - k2 * s2, 1); rd0 := RJ(c2, 1 - k2 * s2, 1, 1); eek := s * rf0 - k2 / 3 * s3 * rd0; end else eek := sin(eq); end else eek := 1; result := eek; end; // 振幅関数 am(u, k) function TForm1.Jacobi_am(u, k: double): double; const N = 30; EPSILON = 1E-15; var a: array[0..N] of Extended; g: array[0..N] of Extended; c: array[0..N] of Extended; two_n : Extended; phi : Extended; half : Extended; i, j : integer; begin half := 1 / 2; if k = 0 then begin result := u; exit; end; if k = 1 then begin result := 2 * arctan(exp(u)) - pi / 2; exit; end; a[0] := 1; g[0] := sqrt(1 - k * k); c[0] := k; two_n := 1; for i := 0 to N do begin if abs(a[i] - g[i]) < a[i] * EPSILON then break; two_n := two_n + two_n; a[i + 1] := half * (a[i] + g[i]); g[i + 1] := sqrt(a[i] * g[i]); c[i + 1] := half * (a[i] - g[i]); end; phi := two_n * a[i] * u; for j := i downto 1 do phi := half * (phi + arcsin(c[j] * sin(phi) / a[j])); result := phi; end; // ヤコビの楕円関数 sn(u, k) function TForm1.jacobi_sn(u, k: double):double; begin result := sin(Jacobi_am(u, k)); end; // 紐の張力 procedure TForm1.tension(a, k, b, c: double); const KN = 10000; var ch : integer; p, w : double; R, T : double; Tv, T0, g : double; u : double; a0, b0, c0 : double; i : integer; x, y : double; xb, yb : double; rx, ty : double; dx : double; dx2 : double; snuk : double; lp, fp : double; Sl, Th : double; Ke : double; FR : byte; a2 : double; begin val(massEdit.Text, p, ch); if ch <> 0 then begin application.MessageBox('紐の質量に間違いがあります。','注意', 0); exit; end; val(rotational_speed_Edit.Text, R, ch); if ch <> 0 then begin application.MessageBox('回転数に間違いがあります。','注意', 0); exit; end; w := R / 60 * pi * 2; // 角速度 ω g := p * w * w; // γ=ρ*ω^2 // 水平張力計算1 FR := 0; a2 := a + a; Ke := calc_first_elliptic_integral(k, pi / 2, FR); // 第一種完全積分 T0 := g * a2 * a2 / 4 / (1 - k * k) / Ke / Ke; // 水平張力 memo1.Lines.Add('紐の水平張力1(N) To1= ' + floatTostrF(T0, ffFixed, 10, 5)); // T := T0 * sqrt(b * b / c / c + 1); // 水平張力計算2 if b <> 0 then begin a0 := -1 / c / c; b0 := g; c0 := g * g * b * b / 4; T0 := (-b0 - sqrt(b0 * b0 - 4 * a0 * c0)) / 2 / a0; // 二次方程式の解法 end else T0 := 0; memo1.Lines.Add('紐の水平張力2(N) To2= ' + floatTostrF(T0, ffFixed, 10, 5)); T := T0 * sqrt(b * b / c / c + 1); // T0 / cosθ memo1.Lines.Add('紐の張力(N) T = ' + floatTostrF(T, ffFixed, 10, 5)); memo1.Lines.Add('分割積分計算'); a0 := sqrt(c * c / b / b + 1); // b0 := sqrt(b) / c / c; dx := a / KN; dx2 := dx / 2; Tv := 0; Th := 0; xb := a; u := xb / c; // F(α,k)=u // jacobi_sn ヤコビの楕円関数 snuk := jacobi_sn(u, k); yb := snuk * b; // x位置のyの値 Sl := 0; for i := KN downto 0 do begin x := i * dx - dx2; // x位置 u := x / c; // F(α,k)=u // jacobi_sn ヤコビの楕円関数 snuk := jacobi_sn(u, k); y := snuk * b; // x位置のyの値 rx := (x - xb); // Δx ty := (y - yb); // Δy if i = 0 then begin rx := 0 - xb; ty := 0 - yb; end; lp := sqrt(rx * rx + ty * ty); // Δl Sl := Sl + lp; // L fp := lp * (y + yb) * 0.5 * g; // F 向心力= mrω^2 if Radiobutton1.Checked then if i < KN then Series3.AddXY(-x + a, fp * KN); // グラフに追加 Tv := Tv + fp; // 垂直張力 if y <> 0 then begin fp := fp * a0; Th := Th + fp; end; xb := x; yb := y; if Radiobutton2.Checked then Series3.AddXY(-x + a, Tv); // グラフに追加 end; Sl := Sl * 2; memo1.Lines.Add(' 紐の 長さ L'' = ' + floatTostrF(Sl, ffFixed, 10, 5)); memo1.Lines.Add(' 紐の 張力(N) T = ' + floatTostrF(Th, ffFixed, 10, 5)); memo1.Lines.Add(' 紐の水直張力(N) Tv= ' + floatTostrF(Tv, ffFixed, 10, 5)); if b = 0 then T := 0 else T := Tv * sqrt(c * c / b / b + 1); // Tv / cos(pi/2 - θ) // a0 := arctan(b / c); // T := tv / cos(pi/ 2 - a0); memo1.Lines.Add(' 紐の張力(N) T'' = ' + floatTostrF(T, ffFixed, 10, 5)); end; // 縄跳びの縄弛み計算 procedure TForm1.nawaBtnClick(Sender: TObject); const kN = 200; var ALFRG : byte; k : double; ch : integer; a, a2 : double; fkd, b, u : double; c, L : double; Ek : double; i : integer; x, y, dx : double; snuk : double; ty, by : double; lx, rx : double; def, ddf : double; begin val(Labelededit10.Text, a2, ch); if ch <> 0 then begin application.MessageBox('縄跳び紐支持間に間違いがあります。','注意',0); exit; end; val(LabeledEdit11.Text, k, ch); if ch <> 0 then begin application.MessageBox('縄跳び紐離心率Kに間違いがあります。','注意',0); exit; end; if k = 0 then begin application.MessageBox('k(離心率)の値がゼロでは張力計算出来ません。','注意',0); exit; end; if abs(k) > 1 then begin application.MessageBox('k(離心率)の最大値は±1です。','注意',0); exit; end; memo1.Clear; ALFRG := 0; fkd := calc_first_elliptic_integral(k, pi / 2, ALFRG); // 2 * 第一種楕円積分(90°) if ALFRG = 1 then begin memo1.Lines.Add('支持間の距離係数 第一種楕円積分の'); memo1.Lines.Add('積分用の値がゼロになります。'); memo1.Lines.Add('Kの値を1以下にして下さい。'); exit; end; if radiobutton1.Checked then Chart1.RightAxis.Title.Caption := '垂直応力分布(単位無し)' else Chart1.RightAxis.Title.Caption := '垂直応力積分値'; // 各種計算 a := a2 / 2; c := a / fkd; // 積分値に対する補正係数 b := 2 * k / (1 - k * k) * c; // 最大値 中央の位置 Ek := calc_second_elliptic_integral(k, pi / 2); // 第二種楕円積分(90°) L := 4 * c * Ek / (1 - k * k) - a2; // 弧の長さ計算 memo1.Lines.Add('補正係数 c = ' + floatTostrF(c, ffFixed, 10, 5)); memo1.Lines.Add('紐の中心高さ b = ' + floatTostrF(b, ffFixed, 10, 5)); memo1.Lines.Add('紐の 長さ L = ' + floatTostrF(L, ffFixed, 10, 5)); Series1.Clear; Series2.Clear; Series3.Clear; // グラフスケール設定 if abs(a2) > b then begin ddf := a2 / 10; def := (a2 - b) / 2; ty := b + def + ddf; by := 0 - def - ddf; rx := a + ddf; lx := -a - ddf; end else begin ddf := b / 10; def := (b - a2) / 2; ty := b + ddf; by := 0 - ddf; rx := a + def + ddf; lx := -a - def - ddf; end; // グラフのスケール設定値セット Series2.AddXY(lx, ty); Series2.AddXY(lx, by); Series2.AddXY(rx, by); application.ProcessMessages; // グラフ作図と分割積分 dx := a / KN; // ΔX // 0~KN 計算作図 for i := 0 to KN do begin x := i * dx; // x位置 u := x / c; // F(α,k)=u // jacobi_sn ヤコビの楕円関数 snuk := jacobi_sn(u, k); y := snuk * b; // x位置のyの値 Series1.AddXY(x - a, y); // グラフに追加 end; for i := KN + 1 downto 0 do begin x := i * dx; // x位置 u := x / c; // F(α,k)=u // jacobi_sn ヤコビの楕円関数 snuk := jacobi_sn(u, k); x := a2 - x; y := snuk * b; // x位置のyの値 Series1.AddXY(x - a, y); // グラフに追加 end; application.ProcessMessages; // 張力計算 tension(a, k, b, c); end; end.