profile_stress_ratio.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. """Расчёт напряжений в осях разрезов и отношения sigma_x / sigma_y.
  2. Природное поле напряжений задано в главных осях: максимальное горизонтальное
  3. напряжение S_Hmax действует по азимуту A_H, минимальное S_hmin -- в
  4. перпендикулярном горизонтальном направлении, вертикальное S_v занимает
  5. промежуточное положение. Расчётная модель строится в плоскости вертикального
  6. разреза, поэтому горизонтальные напряжения поворачиваются к осям разреза по
  7. формулам преобразования компонент тензора при повороте вокруг вертикали:
  8. Delta = A_sec - A_H,
  9. sigma_s = sigma_H cos^2(Delta) + sigma_h sin^2(Delta),
  10. sigma_o = sigma_H sin^2(Delta) + sigma_h cos^2(Delta),
  11. где A_sec -- азимут разреза; sigma_s действует вдоль плоскости разреза,
  12. sigma_o -- в перпендикулярном горизонтальном направлении. Входным параметром
  13. модели служит отношение sigma_x / sigma_y = sigma_s / S_v, задаваемое в окне
  14. Initial field stress ПК Prorock.
  15. Скрипт формирует три результата.
  16. 1. Сводная таблица по линиям (profile_stresses.csv) на опорной глубине
  17. H = 400 м: азимуты разрезов читаются из самой этой таблицы (столбец
  18. «Азимут разреза»), по ним пересчитываются Δ, σ_s и σ_x/σ_y; столбец
  19. азимутов при записи сохраняется. Напряжения S_Hmax = 16,07 МПа,
  20. S_hmin = 6,97 МПа и S_v = 10,791 МПа -- из раздела «Результаты расчёта
  21. абсолютных значений природного поля напряжений»
  22. 2. Расчётная таблица по разрезам (section_stresses.csv): для каждой
  23. профильной линии на каждый год контура (2026, 2031, 2033, 2035 гг.)
  24. природное поле оценивается на глубине карьера H, а горизонтальные
  25. напряжения определяются из системы уравнений того же раздела:
  26. S_Hmax = (S_v - (1 - R) S_hmin) / R, -- коэффициент формы R,
  27. S_Hmax = A S_hmin - (A - 1) P_p, -- фрикционное ограничение,
  28. где S_hmin = (S_v + R (A - 1) P_p) / (1 - R + R A); S_v = rho g H,
  29. поровое давление принято пропорциональным глубине: P_p = 3,25 МПа · H / 400
  30. (на опорной глубине 400 м -- принятое в отчёте значение 3,25 МПа).
  31. Азимут линии берётся из profile_stresses.csv, глубина карьера -- из
  32. profile_azimuths.csv.
  33. 3. Таблица анализа чувствительности (stress_sensitivity.csv) для профильной
  34. линии № 1. В ней сравнивается отношение sigma_x/sigma_y на глубинах от
  35. 200 до 600 м с шагом 50 м. Поровое давление задано равным 1,750, 3,250
  36. и 4,875 МПа на опорных глубинах 200, 400 и 600 м, на промежуточных
  37. глубинах интерполируется квадратично по трём опорным значениям.
  38. Исходные данные: assets/tables/profile_stresses.csv -- азимуты разрезов
  39. по профильным линиям; assets/tables/profile_azimuths.csv -- глубина карьера
  40. по разрезам (год контура, номер линии, глубина). Вычисления ведутся в точной
  41. рациональной арифметике SymPy.
  42. """
  43. import csv
  44. from fractions import Fraction
  45. from pathlib import Path
  46. import sympy as sp
  47. # Азимут действия максимального горизонтального напряжения, °.
  48. A_H = sp.Rational(703, 10)
  49. # Коэффициент формы тензора и фрикционное ограничение A = (sqrt(1+mu^2)+mu)^2.
  50. R = sp.Rational(42, 100)
  51. A = sp.Rational(345, 100)
  52. # Средняя плотность толщи, кг/м3, и ускорение свободного падения, м/с2.
  53. RHO, G = 2750, sp.Rational(981, 100)
  54. # Опорное поле на глубине H = 400 м, МПа (раздел «Результаты расчёта
  55. # абсолютных значений природного поля напряжений»).
  56. P_REF = sp.Rational(325, 100)
  57. S_V_REF = sp.Rational(10791, 1000)
  58. S_HMIN_REF = (S_V_REF + R*(A - 1)*P_REF) / (1 - R + R*A)
  59. S_HMAX_REF = A*S_HMIN_REF - (A - 1)*P_REF
  60. S_v, P_p, Delta = sp.symbols("S_v P_p Delta", real=True)
  61. # Система двух уравнений решена относительно S_hmin в символьном виде.
  62. S_hmin = (S_v + R*(A - 1)*P_p) / (1 - R + R*A)
  63. S_Hmax = A*S_hmin - (A - 1)*P_p
  64. # Напряжения в осях разреза как функции угла поворота.
  65. sigma_s = S_Hmax*sp.cos(Delta)**2 + S_hmin*sp.sin(Delta)**2
  66. sigma_o = S_Hmax*sp.sin(Delta)**2 + S_hmin*sp.cos(Delta)**2
  67. def rotated(sigma_H, sigma_h, azimuth):
  68. """Напряжения в осях разреза при заданных главных напряжениях."""
  69. d = sp.rad(sp.Rational(azimuth) - A_H)
  70. s = sigma_H*sp.cos(d)**2 + sigma_h*sp.sin(d)**2
  71. o = sigma_H*sp.sin(d)**2 + sigma_h*sp.cos(d)**2
  72. return sp.Rational(azimuth) - A_H, s, o
  73. def section(azimuth, depth, pp):
  74. """Поле на глубине карьера для данного разреза.
  75. Возвращает (S_v, S_Hmax, S_hmin, sigma_s, sigma_o, sigma_x/sigma_y).
  76. """
  77. subs = {S_v: RHO*G*depth/10**6, P_p: pp}
  78. d = sp.rad(sp.Rational(azimuth) - A_H)
  79. sv = S_v.subs(subs) # вертикальное напряжение на глубине карьера
  80. sh = S_hmin.subs(subs) # минимальное горизонтальное
  81. sH = S_Hmax.subs(subs) # максимальное горизонтальное
  82. s = sigma_s.subs({**subs, Delta: d})
  83. o = sigma_o.subs({**subs, Delta: d})
  84. return sv, sH, sh, s, o, s/sv
  85. def fmt(value, digits):
  86. """Число с десятичной запятой и типографским знаком минус."""
  87. text = f"{float(sp.N(value, 30)):.{digits}f}"
  88. return text.replace(".", ",").replace("-", "\u2212")
  89. def num(cell):
  90. """Число из ячейки CSV с десятичной запятой."""
  91. return Fraction(cell.replace(",", "."))
  92. def read_azimuths(root):
  93. """Азимуты линий: {номер линии: азимут} из profile_stresses.csv."""
  94. path = root / "assets/tables/profile_stresses.csv"
  95. azimuths = {}
  96. with open(path, encoding="utf-8", newline="") as f:
  97. for row in csv.reader(f, delimiter=";"):
  98. if len(row) == 5 and row[0].isdigit():
  99. azimuths[int(row[0])] = int(row[1])
  100. return azimuths
  101. def read_sections(root):
  102. """Строки таблицы глубин: [(год, линия, H), ...]."""
  103. path = root / "assets/tables/profile_azimuths.csv"
  104. sections = []
  105. with open(path, encoding="utf-8", newline="") as f:
  106. for row in csv.reader(f, delimiter=";"):
  107. if len(row) == 3 and row[0].isdigit():
  108. sections.append((row[0], int(row[1]), num(row[2])))
  109. return sections
  110. def write_csv(path, rows):
  111. with open(path, "w", encoding="utf-8", newline="") as f:
  112. csv.writer(f, delimiter=";").writerows(rows)
  113. def reference_table(root, azimuths):
  114. """Сводная таблица по линиям на опорной глубине 400 м."""
  115. print("опорная глубина 400 м (поле из отчёта)")
  116. rows = [("№ профильной линии", "Азимут разреза, °", "Δ, °",
  117. "σ_s, МПа", "σ_x/σ_y")]
  118. for line in sorted(azimuths):
  119. delta, s, o = rotated(S_HMAX_REF, S_HMIN_REF, azimuths[line])
  120. print(f"линия {line} азимут {azimuths[line]} Δ={fmt(delta,1)}"
  121. f" σ_s={fmt(s,2)} σ_o={fmt(o,2)}")
  122. rows.append((str(line), str(azimuths[line]), fmt(delta, 1),
  123. fmt(s, 2), fmt(s/S_V_REF, 3)))
  124. write_csv(root / "assets/tables/profile_stresses.csv", rows)
  125. def section_table(root, sections, azimuths):
  126. """Расчётная таблица по разрезам: контур, линия, H, P_p."""
  127. print("\nрасчёт по разрезам (глубина карьера из исходной таблицы, P_p = 3,25·H/400 МПа)")
  128. print(f"{'год':>5} {'лин':>4} {'азимут':>7} {'H,м':>5} {'Pp,МПа':>7} "
  129. f"{'S_Hmax':>8} {'S_hmin':>8} {'σ_s,МПа':>8} {'σ_x/σ_y':>8}")
  130. rows = [("Год контура", "№ профильной линии", "Азимут разреза, °",
  131. "Глубина карьера, м", "P_p, МПа", "Δ, °", "σ_s, МПа", "σ_x/σ_y")]
  132. for year, line, depth in sections:
  133. if line not in azimuths:
  134. raise SystemExit(f"линия {line} отсутствует в profile_stresses.csv")
  135. azimuth = azimuths[line]
  136. # Давление привязано к опорному значению 3,25 МПа на глубине 400 м
  137. # и принято пропорциональным глубине карьера.
  138. pp = P_REF*depth/400
  139. sv, sH, sh, s, o, k = section(azimuth, depth, pp)
  140. print(f"{year:>5} {line:>4} {azimuth:>7} {fmt(depth,0):>5} "
  141. f"{fmt(pp,2):>7} {fmt(sH,2):>8} {fmt(sh,2):>8} "
  142. f"{fmt(s,2):>8} {fmt(k,3):>8}")
  143. rows.append((year, str(line), str(azimuth), fmt(depth, 0), fmt(pp, 2),
  144. fmt(sp.Rational(azimuth) - A_H, 1), fmt(s, 2), fmt(k, 3)))
  145. write_csv(root / "assets/tables/section_stresses.csv", rows)
  146. def stability_table(root):
  147. """Анализ чувствительности sigma_x/sigma_y относительно H"""
  148. # Линия № 1 имеет азимут разреза 0°. Опорное давление при H = 200 м
  149. # задано как 1750 кПа (в расчёте используются МПа),
  150. #Точки выводятся через 50 м; на промежуточных глубинах поровое давление интерполируется
  151. # квадратично по трём опорным значениям.
  152. anchors = ((200, sp.Rational(1750, 1000)),
  153. (400, sp.Rational(3250, 1000)),
  154. (600, sp.Rational(4875, 1000)))
  155. def pp_at(h):
  156. (h1, p1), (h2, p2), (h3, p3) = anchors
  157. return (p1*(h - h2)*(h - h3)/((h1 - h2)*(h1 - h3))
  158. + p2*(h - h1)*(h - h3)/((h2 - h1)*(h2 - h3))
  159. + p3*(h - h1)*(h - h2)/((h3 - h1)*(h3 - h2)))
  160. print("\nанализ чувствительности: профильная линия 1 (азимут 0°)")
  161. print(f"{'H,м':>5} {'Pp,МПа':>7} {'S_v':>8} {'S_Hmax':>8} "
  162. f"{'S_hmin':>8} {'σ_s,МПа':>8} {'σ_x/σ_y':>8}")
  163. rows = [("H, м", "P_p, МПа", "σ_x/σ_y")]
  164. for depth in range(200, 601, 50):
  165. pp = pp_at(sp.Rational(depth))
  166. sv, sH, sh, s, _, k = section(0, depth, pp)
  167. print(f"{depth:>5} {fmt(pp,3):>7} {fmt(sv,3):>8} {fmt(sH,2):>8} "
  168. f"{fmt(sh,2):>8} {fmt(s,2):>8} {fmt(k,3):>8}")
  169. rows.append((str(depth), fmt(pp, 3), fmt(k, 3)))
  170. write_csv(root / "assets/tables/stress_sensitivity.csv", rows)
  171. def main():
  172. root = Path(__file__).resolve().parents[2]
  173. azimuths = read_azimuths(root)
  174. sections = read_sections(root)
  175. reference_table(root, azimuths)
  176. section_table(root, sections, azimuths)
  177. stability_table(root)
  178. if __name__ == "__main__":
  179. main()