madrs 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
madrs-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: madrs
3
+ Version: 0.1.0
4
+ Summary: Modified Acceleration-Displacement Response Spectrum (MADRS) seismic analysis method
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/YOUR_USERNAME/MADRS
7
+ Project-URL: Issues, https://github.com/YOUR_USERNAME/MADRS/issues
8
+ Keywords: seismic,structural-engineering,pushover,ADRS,MADRS
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Topic :: Scientific/Engineering
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: numpy>=1.24
21
+ Requires-Dist: scipy>=1.10
22
+ Requires-Dist: matplotlib>=3.7
23
+
24
+ # MADRS — Modified Acceleration-Displacement Response Spectrum
25
+
26
+ A Python package implementing the **MADRS** method of FEMA 440 for seismic performance-point calculation using pushover curves and response spectra.
27
+
28
+ ---
29
+
30
+ ## Installation
31
+
32
+ ### From PyPI (once published)
33
+ ```bash
34
+ pip install madrs
35
+ ```
36
+
37
+ ### Directly from GitHub
38
+ ```bash
39
+ pip install git+https://github.com/YOUR_USERNAME/MADRS.git
40
+ ```
41
+
42
+ ### Local development install (editable)
43
+ ```bash
44
+ git clone https://github.com/YOUR_USERNAME/MADRS.git
45
+ cd MADRS
46
+ pip install -e .
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Quick Start
52
+
53
+ ```python
54
+ import numpy as np
55
+ from madrs import MADRS_Method
56
+
57
+ # --- Pushover curve: columns = [displacement (m), base shear (kN)] ---
58
+ PO = np.loadtxt("pushover.csv", delimiter=",")
59
+
60
+ # --- Demand curve: columns = [period (s), Sa (g)] ---
61
+ DC = np.loadtxt("spectrum.csv", delimiter=",")
62
+
63
+ # --- Modal / structural parameters ---
64
+ pf1 = 1.30 # Modal participation factor (1st mode)
65
+ alpha1 = 0.85 # Modal mass coefficient (1st mode)
66
+ wt = 5000.0 # Seismic weight (kN)
67
+ phi_roof1 = 1.0 # 1st-mode shape at roof
68
+
69
+ tol = 1e-4 # Area-balance tolerance for bilinear fitting
70
+ CP1 = 0.5 # Lower ay search bound (fraction of api)
71
+ CP2 = 0.95 # Upper ay search bound (fraction of api)
72
+
73
+ results = MADRS_Method(PO, DC, pf1, alpha1, wt, phi_roof1, tol, CP1, CP2)
74
+
75
+ dpi, api, dy, ay, roof_disp, flag, *_ = results
76
+
77
+ if flag:
78
+ print(f"Performance point — Sd: {dpi:.4f} m | Sa: {api:.4f} g")
79
+ print(f"Roof displacement : {roof_disp:.4f} m")
80
+ else:
81
+ print("No performance point found — adjust CP1, CP2, or tol.")
82
+ ```
83
+
84
+ ---
85
+
86
+ ## API Reference
87
+
88
+ ### `MADRS_Method`
89
+
90
+ ```python
91
+ MADRS_Method(PO, DC, pf1, alpha1, wt, phi_roof1, tol, CP1, CP2,
92
+ show_intermediate_plots=True)
93
+ ```
94
+
95
+ | Parameter | Type | Description |
96
+ |-----------|------|-------------|
97
+ | `PO` | `ndarray (N, 2)` | Pushover curve — `[displacement (m), base shear (kN)]` |
98
+ | `DC` | `ndarray (M, 2)` | Demand/response spectrum — `[period (s), Sa (g)]` |
99
+ | `pf1` | `float` | Modal participation factor (1st mode) |
100
+ | `alpha1` | `float` | Modal mass coefficient (1st mode) |
101
+ | `wt` | `float` | Seismic weight (kN) |
102
+ | `phi_roof1` | `float` | 1st-mode shape value at roof |
103
+ | `tol` | `float` | Tolerance for bilinear area balance |
104
+ | `CP1` | `float` | Lower multiplier for `ay` search |
105
+ | `CP2` | `float` | Upper multiplier for `ay` search |
106
+ | `show_intermediate_plots` | `bool` | Show 6-panel diagnostic figure (default `True`) |
107
+
108
+ **Returns** `(dpi, api, dy, ay, roof_disp, flag, Sd_spectra, Sa_Spectranew, Sd, Sa, x_bilinear, y_bilinear)`
109
+
110
+ ### Helper functions
111
+
112
+ ```python
113
+ from madrs import curve_intersections, closest_point_on_curve, area_between_curves
114
+ ```
115
+
116
+ - **`curve_intersections(x1, y1, x2, y2)`** — find intersection points between two polylines
117
+ - **`closest_point_on_curve(P, x_curve, y_curve)`** — nearest point on a polyline to point P
118
+ - **`area_between_curves(x, y1, y2)`** — signed area between two curves via Simpson's rule
119
+
120
+ ---
121
+
122
+ ## Requirements
123
+
124
+ - Python ≥ 3.9
125
+ - numpy ≥ 1.24
126
+ - scipy ≥ 1.10
127
+ - matplotlib ≥ 3.7
128
+
129
+ ---
130
+
131
+ ## License
132
+
133
+ MIT
madrs-0.1.0/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # MADRS — Modified Acceleration-Displacement Response Spectrum
2
+
3
+ A Python package implementing the **MADRS** method of FEMA 440 for seismic performance-point calculation using pushover curves and response spectra.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ ### From PyPI (once published)
10
+ ```bash
11
+ pip install madrs
12
+ ```
13
+
14
+ ### Directly from GitHub
15
+ ```bash
16
+ pip install git+https://github.com/YOUR_USERNAME/MADRS.git
17
+ ```
18
+
19
+ ### Local development install (editable)
20
+ ```bash
21
+ git clone https://github.com/YOUR_USERNAME/MADRS.git
22
+ cd MADRS
23
+ pip install -e .
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Quick Start
29
+
30
+ ```python
31
+ import numpy as np
32
+ from madrs import MADRS_Method
33
+
34
+ # --- Pushover curve: columns = [displacement (m), base shear (kN)] ---
35
+ PO = np.loadtxt("pushover.csv", delimiter=",")
36
+
37
+ # --- Demand curve: columns = [period (s), Sa (g)] ---
38
+ DC = np.loadtxt("spectrum.csv", delimiter=",")
39
+
40
+ # --- Modal / structural parameters ---
41
+ pf1 = 1.30 # Modal participation factor (1st mode)
42
+ alpha1 = 0.85 # Modal mass coefficient (1st mode)
43
+ wt = 5000.0 # Seismic weight (kN)
44
+ phi_roof1 = 1.0 # 1st-mode shape at roof
45
+
46
+ tol = 1e-4 # Area-balance tolerance for bilinear fitting
47
+ CP1 = 0.5 # Lower ay search bound (fraction of api)
48
+ CP2 = 0.95 # Upper ay search bound (fraction of api)
49
+
50
+ results = MADRS_Method(PO, DC, pf1, alpha1, wt, phi_roof1, tol, CP1, CP2)
51
+
52
+ dpi, api, dy, ay, roof_disp, flag, *_ = results
53
+
54
+ if flag:
55
+ print(f"Performance point — Sd: {dpi:.4f} m | Sa: {api:.4f} g")
56
+ print(f"Roof displacement : {roof_disp:.4f} m")
57
+ else:
58
+ print("No performance point found — adjust CP1, CP2, or tol.")
59
+ ```
60
+
61
+ ---
62
+
63
+ ## API Reference
64
+
65
+ ### `MADRS_Method`
66
+
67
+ ```python
68
+ MADRS_Method(PO, DC, pf1, alpha1, wt, phi_roof1, tol, CP1, CP2,
69
+ show_intermediate_plots=True)
70
+ ```
71
+
72
+ | Parameter | Type | Description |
73
+ |-----------|------|-------------|
74
+ | `PO` | `ndarray (N, 2)` | Pushover curve — `[displacement (m), base shear (kN)]` |
75
+ | `DC` | `ndarray (M, 2)` | Demand/response spectrum — `[period (s), Sa (g)]` |
76
+ | `pf1` | `float` | Modal participation factor (1st mode) |
77
+ | `alpha1` | `float` | Modal mass coefficient (1st mode) |
78
+ | `wt` | `float` | Seismic weight (kN) |
79
+ | `phi_roof1` | `float` | 1st-mode shape value at roof |
80
+ | `tol` | `float` | Tolerance for bilinear area balance |
81
+ | `CP1` | `float` | Lower multiplier for `ay` search |
82
+ | `CP2` | `float` | Upper multiplier for `ay` search |
83
+ | `show_intermediate_plots` | `bool` | Show 6-panel diagnostic figure (default `True`) |
84
+
85
+ **Returns** `(dpi, api, dy, ay, roof_disp, flag, Sd_spectra, Sa_Spectranew, Sd, Sa, x_bilinear, y_bilinear)`
86
+
87
+ ### Helper functions
88
+
89
+ ```python
90
+ from madrs import curve_intersections, closest_point_on_curve, area_between_curves
91
+ ```
92
+
93
+ - **`curve_intersections(x1, y1, x2, y2)`** — find intersection points between two polylines
94
+ - **`closest_point_on_curve(P, x_curve, y_curve)`** — nearest point on a polyline to point P
95
+ - **`area_between_curves(x, y1, y2)`** — signed area between two curves via Simpson's rule
96
+
97
+ ---
98
+
99
+ ## Requirements
100
+
101
+ - Python ≥ 3.9
102
+ - numpy ≥ 1.24
103
+ - scipy ≥ 1.10
104
+ - matplotlib ≥ 3.7
105
+
106
+ ---
107
+
108
+ ## License
109
+
110
+ MIT
@@ -0,0 +1,23 @@
1
+ """
2
+ madrs — Modified Acceleration-Displacement Response Spectrum method.
3
+
4
+ Quickly run a seismic performance-point analysis:
5
+
6
+ from madrs import MADRS_Method, curve_intersections, closest_point_on_curve
7
+ """
8
+
9
+ from .core import (
10
+ MADRS_Method,
11
+ area_between_curves,
12
+ curve_intersections,
13
+ closest_point_on_curve,
14
+ )
15
+
16
+ __all__ = [
17
+ "MADRS_Method",
18
+ "area_between_curves",
19
+ "curve_intersections",
20
+ "closest_point_on_curve",
21
+ ]
22
+
23
+ __version__ = "0.1.0"
@@ -0,0 +1,321 @@
1
+ import numpy as np
2
+ import matplotlib.pyplot as plt
3
+ from scipy.integrate import simpson
4
+
5
+ TOL_INTER = 5 # 5% error tolerance at intersection point
6
+
7
+
8
+ def area_between_curves(x_common, y1_common, y2_common):
9
+ """Compute the signed area between two curves using Simpson's rule."""
10
+ return simpson(y1_common - y2_common, x=x_common)
11
+
12
+
13
+ def curve_intersections(x1, y1, x2, y2):
14
+ """
15
+ Find intersection points between two curves defined by (x1, y1) and (x2, y2).
16
+
17
+ Returns
18
+ -------
19
+ list of (x, y) tuples at intersection points
20
+ """
21
+ x1, y1, x2, y2 = map(np.asarray, (x1, y1, x2, y2))
22
+ y2_interp = np.interp(x1, x2, y2)
23
+ diff = y1 - y2_interp
24
+ sign_changes = np.where(np.diff(np.sign(diff)) != 0)[0]
25
+
26
+ intersections = []
27
+ for idx in sign_changes:
28
+ x_left, x_right = x1[idx], x1[idx + 1]
29
+ y_left, y_right = diff[idx], diff[idx + 1]
30
+ x_inter = x_left - y_left * (x_right - x_left) / (y_right - y_left)
31
+ y_inter = np.interp(x_inter, x1, y1)
32
+ intersections.append((x_inter, y_inter))
33
+
34
+ return intersections
35
+
36
+
37
+ def closest_point_on_curve(P, x_curve, y_curve):
38
+ """
39
+ Find the closest point on a polyline curve to a given point P.
40
+
41
+ Parameters
42
+ ----------
43
+ P : array-like, shape (2,)
44
+ x_curve, y_curve : array-like
45
+
46
+ Returns
47
+ -------
48
+ best_point : ndarray, shape (2,)
49
+ min_dist : float
50
+ percent_error : ndarray, shape (2,)
51
+ """
52
+ P = np.array(P, dtype=float)
53
+ x_curve = np.array(x_curve, dtype=float)
54
+ y_curve = np.array(y_curve, dtype=float)
55
+
56
+ min_dist = np.inf
57
+ best_point = None
58
+
59
+ for i in range(len(x_curve) - 1):
60
+ A = np.array([x_curve[i], y_curve[i]])
61
+ B = np.array([x_curve[i + 1], y_curve[i + 1]])
62
+ AB = B - A
63
+ AP = P - A
64
+ t = np.clip(np.dot(AP, AB) / np.dot(AB, AB), 0, 1)
65
+ Q = A + t * AB
66
+ dist = np.linalg.norm(P - Q)
67
+
68
+ if dist < min_dist:
69
+ min_dist = dist
70
+ best_point = Q
71
+
72
+ if best_point is None:
73
+ raise ValueError("No valid closest point found on curve")
74
+
75
+ percent_error = np.abs((P - best_point) / (best_point + 1e-12)) * 100
76
+ return best_point, min_dist, percent_error
77
+
78
+
79
+ def MADRS_Method(PO, DC, pf1, alpha1, wt, phi_roof1, tol, CP1, CP2, show_intermediate_plots=True):
80
+ """
81
+ Modified Acceleration-Displacement Response Spectrum (MADRS) method.
82
+
83
+ Parameters
84
+ ----------
85
+ PO : ndarray, shape (N, 2)
86
+ Pushover / capacity curve. Columns: [displacement (m), base shear (kN)].
87
+ DC : ndarray, shape (M, 2)
88
+ Demand curve (elastic response spectrum). Columns: [period (s), Sa (g)].
89
+ pf1 : float
90
+ Modal participation factor for the first mode.
91
+ alpha1 : float
92
+ Modal mass coefficient for the first mode.
93
+ wt : float
94
+ Seismic weight of the structure (kN).
95
+ phi_roof1 : float
96
+ First-mode shape value at roof level.
97
+ tol : float
98
+ Tolerance for area difference in bilinear curve fitting.
99
+ CP1 : float
100
+ Lower bound multiplier for ay search (fraction of api).
101
+ CP2 : float
102
+ Upper bound multiplier for ay search (fraction of api).
103
+ show_intermediate_plots : bool, optional
104
+ If True (default), display the 6-panel diagnostic figure.
105
+
106
+ Returns
107
+ -------
108
+ dpi : float or None
109
+ Spectral displacement at performance point (m).
110
+ api : float or None
111
+ Spectral acceleration at performance point (g).
112
+ dy : float or None
113
+ Yield spectral displacement of bilinear curve (m).
114
+ ay : float or None
115
+ Yield spectral acceleration of bilinear curve (g).
116
+ roof_disp : float or None
117
+ Roof displacement at performance point (m).
118
+ flag : int
119
+ 1 if a performance point was found, 0 otherwise.
120
+ Sd_spectra : ndarray or None
121
+ Spectral displacements of the demand spectrum.
122
+ Sa_Spectranew : ndarray or None
123
+ Scaled demand spectrum spectral accelerations.
124
+ Sd : ndarray or None
125
+ Capacity spectrum spectral displacements.
126
+ Sa : ndarray or None
127
+ Capacity spectrum spectral accelerations.
128
+ x_bilinear : ndarray or None
129
+ x-coordinates of fitted bilinear curve.
130
+ y_bilinear : ndarray or None
131
+ y-coordinates of fitted bilinear curve.
132
+ """
133
+ fig, axes = plt.subplots(2, 3, figsize=(15, 9))
134
+ axes = axes.ravel()
135
+
136
+ # --- Capacity curve ---
137
+ pushover_curve = PO
138
+ axes[0].plot(pushover_curve[:, 0], pushover_curve[:, 1],
139
+ label="Capacity Curve", color="blue")
140
+ axes[0].set_xlabel("Displacement (m)", fontsize=12)
141
+ axes[0].set_ylabel("Base Shear (kN)", fontsize=12)
142
+ axes[0].legend(loc="lower right", framealpha=0.0, fontsize=10)
143
+
144
+ # --- Truncate at 0.8 * RF_max ---
145
+ rf_max = np.max(pushover_curve[:, 1])
146
+ index = np.argmax(pushover_curve[:, 1])
147
+ narray = pushover_curve[index:, 1]
148
+ narray2 = narray - 0.8 * rf_max
149
+ index_min = np.argmin(np.abs(narray2))
150
+ po_y = pushover_curve[: index + index_min, 1]
151
+ po_x = pushover_curve[: index + index_min, 0]
152
+
153
+ axes[1].plot(po_x, po_y, label="Capacity Curve up to 0.8*Max", color="green")
154
+ axes[1].set_xlabel("Displacement (m)", fontsize=12)
155
+ axes[1].set_ylabel("Base Shear (kN)", fontsize=12)
156
+ axes[1].legend(loc="lower right", framealpha=0.0, fontsize=10)
157
+
158
+ # --- Capacity spectrum (Sa, Sd) ---
159
+ Sa = po_y / (wt * alpha1)
160
+ Sd = po_x / (pf1 * phi_roof1)
161
+ axes[2].plot(Sd, Sa, label="Capacity Spectrum", color="blue")
162
+ axes[2].set_xlabel("Spectral Displacement (m)", fontsize=12)
163
+ axes[2].set_ylabel("Spectral Acceleration (g)", fontsize=12)
164
+ axes[2].legend(loc="lower right", framealpha=0.0, fontsize=10)
165
+
166
+ # --- Demand curve ---
167
+ EC_Spectrum = DC
168
+ axes[3].plot(EC_Spectrum[:, 0], EC_Spectrum[:, 1],
169
+ label="Demand Curve", color="red")
170
+ axes[3].set_xlabel("Time Period (Sec)", fontsize=12)
171
+ axes[3].set_ylabel("Spectral Acceleration (g)", fontsize=12)
172
+ axes[3].legend(loc="lower right", framealpha=0.0, fontsize=10)
173
+
174
+ # --- Demand spectrum in Sa-Sd space ---
175
+ Sd_spectra = EC_Spectrum[:, 1] * (EC_Spectrum[:, 0] ** 2) / (4 * np.pi ** 2)
176
+ axes[4].plot(Sd_spectra, EC_Spectrum[:, 1],
177
+ label="Demand Spectrum", color="orange")
178
+ axes[4].set_xlabel("Spectral Displacement (m)", fontsize=12)
179
+ axes[4].set_ylabel("Spectral Acceleration (g)", fontsize=12)
180
+ axes[4].legend(loc="lower right", framealpha=0.0, fontsize=10)
181
+
182
+ # --- Initial slope & initial guess ---
183
+ Sa_per_ind = np.argmin(np.abs(Sa - 0.6 * Sa.max()))
184
+ k_init = (Sa[Sa_per_ind] - Sa[0]) / (Sd[Sa_per_ind] - Sd[0])
185
+ x2 = np.array([0, 1 / k_init])
186
+ y2 = np.array([0, 1])
187
+
188
+ points = curve_intersections(Sd_spectra, EC_Spectrum[:, 1], x2, y2)
189
+ print("Intersection points:", points)
190
+
191
+ x3 = np.array([points[0][0], points[0][0]])
192
+ y3 = np.array([0, points[0][1]])
193
+ points2 = curve_intersections(Sd, Sa, x3, y3)
194
+ x4 = points2[-1][0]
195
+ y4 = points2[-1][1]
196
+
197
+ axes[5].plot(Sd, Sa, label="Capacity Spectrum", color="blue")
198
+ axes[5].plot(Sd_spectra, EC_Spectrum[:, 1],
199
+ label="Demand Spectrum", color="orange")
200
+ axes[5].plot(x2, y2, label="Initial Slope")
201
+ axes[5].plot(points[0][0], points[0][1], "ro", label="Intersection")
202
+ axes[5].plot(x4, y4, "go", label="Initial Guess")
203
+ axes[5].set_xlabel("Spectral Displacement (m)", fontsize=12)
204
+ axes[5].set_ylabel("Spectral Acceleration (g)", fontsize=12)
205
+ axes[5].legend(loc="upper right", framealpha=0.0, fontsize=10)
206
+
207
+ for ax in axes:
208
+ ax.set_xlim(left=0)
209
+ ax.set_ylim(bottom=0)
210
+
211
+ plt.tight_layout()
212
+
213
+ if show_intermediate_plots:
214
+ plt.show()
215
+ else:
216
+ plt.close(fig)
217
+
218
+ # --- Main MADRS iteration ---
219
+ loc = np.argmin(abs(Sa - points2[-1][1]))
220
+
221
+ for newloc in np.arange(int(0.1 * loc), len(Sa), 2):
222
+ x4 = Sd[newloc]
223
+ y4 = Sa[newloc]
224
+
225
+ for i in np.arange(CP1 * y4, CP2 * y4, (CP2 - CP1) * y4 / 100):
226
+ x = np.array([0])
227
+ y = np.array([0])
228
+ y = np.append(y, i)
229
+ x = np.append(x, y[-1] / k_init)
230
+ x = np.append(x, x4)
231
+ y = np.append(y, y4)
232
+
233
+ x_common = np.unique(np.concatenate([x, Sd[:newloc]]))
234
+ x_common = x_common[x_common <= x[-1]]
235
+ Sa_common = np.interp(x_common, Sd, Sa)
236
+ y_common = np.interp(x_common, x, y)
237
+ A_diff = area_between_curves(x_common, Sa_common, y_common)
238
+
239
+ if abs(A_diff) < tol:
240
+ break
241
+
242
+ api = y[-1]
243
+ dpi = x[-1]
244
+ ay = y[1]
245
+ dy = x[1]
246
+ mue = dpi / dy
247
+
248
+ beta_zero = 5
249
+ T_zero = 2 * np.pi * (dy / ay) ** 0.5
250
+
251
+ if mue < 4:
252
+ T_eff = (0.2 * (mue - 1) ** 2 - 0.038 * (mue - 1) ** 3 + 1) * T_zero
253
+ elif 4 <= mue <= 6.5:
254
+ T_eff = (0.28 + 0.13 * (mue - 1) + 1) * T_zero
255
+ else:
256
+ T_eff = (0.89 * (((mue - 1) / (1 + 0.05 * (mue - 2))) ** 0.5 - 1) + 1) * T_zero
257
+
258
+ if mue < 4:
259
+ beta_eff = 4.9 * (mue - 1) ** 2 - 1.1 * (mue - 1) ** 3 + beta_zero
260
+ elif 4 <= mue <= 6.5:
261
+ beta_eff = 14 + 0.32 * (mue - 1) + beta_zero
262
+ else:
263
+ beta_eff = (
264
+ 19
265
+ * ((0.64 * (mue - 1) - 1) / ((0.64 * (mue - 1)) ** 2))
266
+ * ((T_eff / T_zero) ** 2)
267
+ + beta_zero
268
+ )
269
+
270
+ alpha = ((api - ay) / (dpi - dy)) / (ay / dy)
271
+ B_beta_eff = 4 / (5.6 - np.log(beta_eff))
272
+ T_sec = T_zero / ((1 + alpha * (mue - 1)) / mue) ** 0.5
273
+ M = (T_eff / T_sec) ** 2
274
+
275
+ Sa_Spectranew = (EC_Spectrum[:, 1] / B_beta_eff) * M
276
+
277
+ P = [x[-1], y[-1]]
278
+ Q, error, percent_error = closest_point_on_curve(P, Sd_spectra, Sa_Spectranew)
279
+
280
+ if percent_error[0] <= TOL_INTER and percent_error[1] <= TOL_INTER:
281
+ flag = 1
282
+
283
+ fig2, ax2 = plt.subplots(figsize=(8, 6))
284
+ ax2.plot(Sd_spectra, EC_Spectrum[:, 1],
285
+ label="Demand Spectrum (EC2)", color="orange")
286
+ ax2.plot(Sd_spectra, Sa_Spectranew,
287
+ label="Demand Spectrum (Scaled)", color="green")
288
+ ax2.plot(Sd, Sa, label="Capacity Spectrum", color="blue")
289
+ ax2.plot(x, y, label="Bilinear Curve", color="cyan")
290
+ ax2.plot(x[-1], y[-1], "ro", label="api, dpi")
291
+ ax2.plot(x[1], y[1], "go", label="ay, dy")
292
+ ax2.legend()
293
+ ax2.set_title("MADRS Approach")
294
+ ax2.set_xlim(left=0)
295
+ ax2.set_ylim(bottom=0)
296
+ ax2.set_xlabel("Spectral displacement (m)")
297
+ ax2.set_ylabel("Spectral acceleration (g)")
298
+ plt.tight_layout()
299
+ plt.show()
300
+
301
+ print("Passed")
302
+ print("Area difference (curve1 - curve2) =", A_diff)
303
+
304
+ BOLD_RED = "\033[1;31m"
305
+ RESET = "\033[0m"
306
+ if abs(A_diff) > tol:
307
+ print(f"{BOLD_RED}Area difference is more than tolerance, "
308
+ f"change bi-linear curve style{RESET}")
309
+
310
+ print("Mue =", mue)
311
+ print("Effective Damping =", beta_eff)
312
+ roof_disp = x[-1] * (pf1 * phi_roof1)
313
+ print("Roof displacement at performance point (m) =", roof_disp)
314
+
315
+ return dpi, api, dy, ay, roof_disp, flag, Sd_spectra, Sa_Spectranew, Sd, Sa, x, y
316
+
317
+ else:
318
+ flag = 0
319
+
320
+ print("No performance point found. Try adjusting CP1, CP2, or tol.")
321
+ return None, None, None, None, None, 0, None, None, None, None, None, None
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: madrs
3
+ Version: 0.1.0
4
+ Summary: Modified Acceleration-Displacement Response Spectrum (MADRS) seismic analysis method
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/YOUR_USERNAME/MADRS
7
+ Project-URL: Issues, https://github.com/YOUR_USERNAME/MADRS/issues
8
+ Keywords: seismic,structural-engineering,pushover,ADRS,MADRS
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Topic :: Scientific/Engineering
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: numpy>=1.24
21
+ Requires-Dist: scipy>=1.10
22
+ Requires-Dist: matplotlib>=3.7
23
+
24
+ # MADRS — Modified Acceleration-Displacement Response Spectrum
25
+
26
+ A Python package implementing the **MADRS** method of FEMA 440 for seismic performance-point calculation using pushover curves and response spectra.
27
+
28
+ ---
29
+
30
+ ## Installation
31
+
32
+ ### From PyPI (once published)
33
+ ```bash
34
+ pip install madrs
35
+ ```
36
+
37
+ ### Directly from GitHub
38
+ ```bash
39
+ pip install git+https://github.com/YOUR_USERNAME/MADRS.git
40
+ ```
41
+
42
+ ### Local development install (editable)
43
+ ```bash
44
+ git clone https://github.com/YOUR_USERNAME/MADRS.git
45
+ cd MADRS
46
+ pip install -e .
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Quick Start
52
+
53
+ ```python
54
+ import numpy as np
55
+ from madrs import MADRS_Method
56
+
57
+ # --- Pushover curve: columns = [displacement (m), base shear (kN)] ---
58
+ PO = np.loadtxt("pushover.csv", delimiter=",")
59
+
60
+ # --- Demand curve: columns = [period (s), Sa (g)] ---
61
+ DC = np.loadtxt("spectrum.csv", delimiter=",")
62
+
63
+ # --- Modal / structural parameters ---
64
+ pf1 = 1.30 # Modal participation factor (1st mode)
65
+ alpha1 = 0.85 # Modal mass coefficient (1st mode)
66
+ wt = 5000.0 # Seismic weight (kN)
67
+ phi_roof1 = 1.0 # 1st-mode shape at roof
68
+
69
+ tol = 1e-4 # Area-balance tolerance for bilinear fitting
70
+ CP1 = 0.5 # Lower ay search bound (fraction of api)
71
+ CP2 = 0.95 # Upper ay search bound (fraction of api)
72
+
73
+ results = MADRS_Method(PO, DC, pf1, alpha1, wt, phi_roof1, tol, CP1, CP2)
74
+
75
+ dpi, api, dy, ay, roof_disp, flag, *_ = results
76
+
77
+ if flag:
78
+ print(f"Performance point — Sd: {dpi:.4f} m | Sa: {api:.4f} g")
79
+ print(f"Roof displacement : {roof_disp:.4f} m")
80
+ else:
81
+ print("No performance point found — adjust CP1, CP2, or tol.")
82
+ ```
83
+
84
+ ---
85
+
86
+ ## API Reference
87
+
88
+ ### `MADRS_Method`
89
+
90
+ ```python
91
+ MADRS_Method(PO, DC, pf1, alpha1, wt, phi_roof1, tol, CP1, CP2,
92
+ show_intermediate_plots=True)
93
+ ```
94
+
95
+ | Parameter | Type | Description |
96
+ |-----------|------|-------------|
97
+ | `PO` | `ndarray (N, 2)` | Pushover curve — `[displacement (m), base shear (kN)]` |
98
+ | `DC` | `ndarray (M, 2)` | Demand/response spectrum — `[period (s), Sa (g)]` |
99
+ | `pf1` | `float` | Modal participation factor (1st mode) |
100
+ | `alpha1` | `float` | Modal mass coefficient (1st mode) |
101
+ | `wt` | `float` | Seismic weight (kN) |
102
+ | `phi_roof1` | `float` | 1st-mode shape value at roof |
103
+ | `tol` | `float` | Tolerance for bilinear area balance |
104
+ | `CP1` | `float` | Lower multiplier for `ay` search |
105
+ | `CP2` | `float` | Upper multiplier for `ay` search |
106
+ | `show_intermediate_plots` | `bool` | Show 6-panel diagnostic figure (default `True`) |
107
+
108
+ **Returns** `(dpi, api, dy, ay, roof_disp, flag, Sd_spectra, Sa_Spectranew, Sd, Sa, x_bilinear, y_bilinear)`
109
+
110
+ ### Helper functions
111
+
112
+ ```python
113
+ from madrs import curve_intersections, closest_point_on_curve, area_between_curves
114
+ ```
115
+
116
+ - **`curve_intersections(x1, y1, x2, y2)`** — find intersection points between two polylines
117
+ - **`closest_point_on_curve(P, x_curve, y_curve)`** — nearest point on a polyline to point P
118
+ - **`area_between_curves(x, y1, y2)`** — signed area between two curves via Simpson's rule
119
+
120
+ ---
121
+
122
+ ## Requirements
123
+
124
+ - Python ≥ 3.9
125
+ - numpy ≥ 1.24
126
+ - scipy ≥ 1.10
127
+ - matplotlib ≥ 3.7
128
+
129
+ ---
130
+
131
+ ## License
132
+
133
+ MIT
@@ -0,0 +1,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ madrs/__init__.py
4
+ madrs/core.py
5
+ madrs.egg-info/PKG-INFO
6
+ madrs.egg-info/SOURCES.txt
7
+ madrs.egg-info/dependency_links.txt
8
+ madrs.egg-info/requires.txt
9
+ madrs.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ numpy>=1.24
2
+ scipy>=1.10
3
+ matplotlib>=3.7
@@ -0,0 +1 @@
1
+ madrs
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "madrs"
7
+ version = "0.1.0"
8
+ description = "Modified Acceleration-Displacement Response Spectrum (MADRS) seismic analysis method"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.9"
12
+ dependencies = [
13
+ "numpy>=1.24",
14
+ "scipy>=1.10",
15
+ "matplotlib>=3.7",
16
+ ]
17
+ keywords = ["seismic", "structural-engineering", "pushover", "ADRS", "MADRS"]
18
+ classifiers = [
19
+ "Development Status :: 3 - Alpha",
20
+ "Intended Audience :: Science/Research",
21
+ "Topic :: Scientific/Engineering",
22
+ "License :: OSI Approved :: MIT License",
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.9",
25
+ "Programming Language :: Python :: 3.10",
26
+ "Programming Language :: Python :: 3.11",
27
+ "Programming Language :: Python :: 3.12",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/YOUR_USERNAME/MADRS"
32
+ Issues = "https://github.com/YOUR_USERNAME/MADRS/issues"
33
+
34
+ [tool.setuptools.packages.find]
35
+ where = ["."]
36
+ include = ["madrs*"]
madrs-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+