Skip to content

Commit df52c15

Browse files
committed
MNT: fix cyclic import
1 parent dc6e878 commit df52c15

4 files changed

Lines changed: 217 additions & 268 deletions

File tree

rocketpy/mathutils/function.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import matplotlib.pyplot as plt
1919
import numpy as np
20+
from numpy import trapezoid
2021
from scipy import integrate, linalg, optimize
2122
from scipy.interpolate import (
2223
LinearNDInterpolator,
@@ -28,14 +29,6 @@
2829
from rocketpy.plots.plot_helpers import show_or_save_plot
2930
from rocketpy.tools import deprecated, from_hex_decode, to_hex_encode
3031

31-
# Numpy 1.x compatibility,
32-
# TODO: remove these lines when all dependencies support numpy>=2.0.0
33-
if np.lib.NumpyVersion(np.__version__) >= "2.0.0b1":
34-
# pylint: disable=no-name-in-module
35-
from numpy import trapezoid # pragma: no cover
36-
else:
37-
from numpy import trapz as trapezoid # pragma: no cover
38-
3932
NUMERICAL_TYPES = (float, int, complex, np.integer, np.floating)
4033
INTERPOLATION_TYPES = {
4134
"linear": 0,

rocketpy/rocket/aero_surface/generic_surface.py

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import copy
2+
import csv
23
import math
34

45
import numpy as np
56

67
from rocketpy.mathutils import Function
78
from rocketpy.mathutils.vector_matrix import Matrix, Vector
8-
from rocketpy.tools import load_generic_surface_csv
99

1010

1111
class GenericSurface:
@@ -328,7 +328,7 @@ def _process_input(self, input_data, coeff_name):
328328
"""
329329
if isinstance(input_data, str):
330330
# Input is assumed to be a file path to a CSV
331-
return load_generic_surface_csv(input_data, coeff_name)
331+
return self.__load_generic_surface_csv(input_data, coeff_name)
332332
elif isinstance(input_data, Function):
333333
if input_data.__dom_dim__ != 7:
334334
raise ValueError(
@@ -379,3 +379,89 @@ def _process_input(self, input_data, coeff_name):
379379
f"Invalid input for {coeff_name}: must be a CSV file path"
380380
" or a callable."
381381
)
382+
383+
def __load_generic_surface_csv(self, file_path, coeff_name): # pylint: disable=too-many-statements,import-outside-toplevel
384+
"""Load GenericSurface coefficient CSV into a 7D Function.
385+
386+
This loader expects header-based CSV data with one or more independent
387+
variables among: alpha, beta, mach, reynolds, pitch_rate, yaw_rate,
388+
roll_rate.
389+
"""
390+
independent_vars = [
391+
"alpha",
392+
"beta",
393+
"mach",
394+
"reynolds",
395+
"pitch_rate",
396+
"yaw_rate",
397+
"roll_rate",
398+
]
399+
400+
try:
401+
with open(file_path, mode="r") as file:
402+
reader = csv.reader(file)
403+
header = next(reader)
404+
except (FileNotFoundError, IOError) as e:
405+
raise ValueError(f"Error reading {coeff_name} CSV file: {e}") from e
406+
except StopIteration as e:
407+
raise ValueError(f"Invalid or empty CSV file for {coeff_name}.") from e
408+
409+
if not header:
410+
raise ValueError(f"Invalid or empty CSV file for {coeff_name}.")
411+
412+
header = [column.strip() for column in header]
413+
present_columns = [col for col in independent_vars if col in header]
414+
415+
invalid_columns = [col for col in header[:-1] if col not in independent_vars]
416+
if invalid_columns:
417+
raise ValueError(
418+
f"Invalid independent variable(s) in {coeff_name} CSV: "
419+
f"{invalid_columns}. Valid options are: {independent_vars}."
420+
)
421+
422+
if header[-1] in independent_vars:
423+
raise ValueError(
424+
f"Last column in {coeff_name} CSV must be the coefficient"
425+
" value, not an independent variable."
426+
)
427+
428+
if not present_columns:
429+
raise ValueError(f"No independent variables found in {coeff_name} CSV.")
430+
431+
ordered_present_columns = [
432+
col for col in header[:-1] if col in independent_vars
433+
]
434+
435+
csv_func = Function.from_regular_grid_csv(
436+
file_path,
437+
ordered_present_columns,
438+
coeff_name,
439+
extrapolation="natural",
440+
)
441+
if csv_func is None:
442+
csv_func = Function(
443+
file_path,
444+
interpolation="linear",
445+
extrapolation="natural",
446+
)
447+
448+
def wrapper(alpha, beta, mach, reynolds, pitch_rate, yaw_rate, roll_rate):
449+
args_by_name = {
450+
"alpha": alpha,
451+
"beta": beta,
452+
"mach": mach,
453+
"reynolds": reynolds,
454+
"pitch_rate": pitch_rate,
455+
"yaw_rate": yaw_rate,
456+
"roll_rate": roll_rate,
457+
}
458+
selected_args = [args_by_name[col] for col in ordered_present_columns]
459+
return csv_func(*selected_args)
460+
461+
return Function(
462+
wrapper,
463+
independent_vars,
464+
[coeff_name],
465+
interpolation="linear",
466+
extrapolation="natural",
467+
)

rocketpy/rocket/rocket.py

Lines changed: 128 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import csv
12
import inspect
23
import math
34
import warnings
@@ -27,7 +28,6 @@
2728
from rocketpy.tools import (
2829
deprecated,
2930
find_obj_from_hash,
30-
load_rocket_drag_csv,
3131
parallel_axis_theorem_from_com,
3232
)
3333

@@ -2243,7 +2243,7 @@ def _count_positional_args(callable_obj):
22432243
# Case 1: string input can be a CSV path or any Function-supported source.
22442244
if isinstance(input_data, str):
22452245
if input_data.lower().endswith(".csv"):
2246-
return load_rocket_drag_csv(input_data, coeff_name)
2246+
return self.__load_rocket_drag_csv(input_data, coeff_name)
22472247

22482248
function_data = Function(input_data)
22492249
_validate_function_domain_dimension(function_data)
@@ -2319,3 +2319,129 @@ def _count_positional_args(callable_obj):
23192319
f"Invalid input for {coeff_name}: must be int, float, CSV file path, "
23202320
"Function, or callable."
23212321
)
2322+
2323+
def __load_rocket_drag_csv(self, file_path, coeff_name): # pylint: disable=too-many-statements,import-outside-toplevel
2324+
"""Load Rocket drag CSV into a 7D Function.
2325+
2326+
Supports either headerless two-column (mach, coefficient) tables or
2327+
header-based multi-variable CSV tables.
2328+
"""
2329+
independent_vars = [
2330+
"alpha",
2331+
"beta",
2332+
"mach",
2333+
"reynolds",
2334+
"pitch_rate",
2335+
"yaw_rate",
2336+
"roll_rate",
2337+
]
2338+
2339+
def _is_numeric(value):
2340+
try:
2341+
float(value)
2342+
return True
2343+
except (TypeError, ValueError):
2344+
try:
2345+
int(value)
2346+
return True
2347+
except (TypeError, ValueError):
2348+
return False
2349+
2350+
try:
2351+
with open(file_path, mode="r") as file:
2352+
reader = csv.reader(file)
2353+
first_row = next(reader)
2354+
except (FileNotFoundError, IOError) as e:
2355+
raise ValueError(f"Error reading {coeff_name} CSV file: {e}") from e
2356+
except StopIteration as e:
2357+
raise ValueError(f"Invalid or empty CSV file for {coeff_name}.") from e
2358+
2359+
if not first_row:
2360+
raise ValueError(f"Invalid or empty CSV file for {coeff_name}.")
2361+
2362+
is_headerless_two_column = len(first_row) == 2 and all(
2363+
_is_numeric(cell) for cell in first_row
2364+
)
2365+
2366+
if is_headerless_two_column:
2367+
csv_func = Function(
2368+
file_path,
2369+
interpolation="linear",
2370+
extrapolation="constant",
2371+
)
2372+
2373+
def mach_wrapper(
2374+
_alpha,
2375+
_beta,
2376+
mach,
2377+
_reynolds,
2378+
_pitch_rate,
2379+
_yaw_rate,
2380+
_roll_rate,
2381+
):
2382+
return csv_func(mach)
2383+
2384+
return Function(
2385+
mach_wrapper,
2386+
independent_vars,
2387+
[coeff_name],
2388+
interpolation="linear",
2389+
extrapolation="constant",
2390+
)
2391+
2392+
header = [column.strip() for column in first_row]
2393+
present_columns = [col for col in independent_vars if col in header]
2394+
2395+
invalid_columns = [col for col in header[:-1] if col not in independent_vars]
2396+
if invalid_columns:
2397+
raise ValueError(
2398+
f"Invalid independent variable(s) in {coeff_name} CSV: "
2399+
f"{invalid_columns}. Valid options are: {independent_vars}."
2400+
)
2401+
2402+
if header[-1] in independent_vars:
2403+
raise ValueError(
2404+
f"Last column in {coeff_name} CSV must be the coefficient "
2405+
"value, not an independent variable."
2406+
)
2407+
2408+
if not present_columns:
2409+
raise ValueError(f"No independent variables found in {coeff_name} CSV.")
2410+
2411+
ordered_present_columns = [
2412+
col for col in header[:-1] if col in independent_vars
2413+
]
2414+
2415+
csv_func = Function.from_regular_grid_csv(
2416+
file_path,
2417+
ordered_present_columns,
2418+
coeff_name,
2419+
extrapolation="constant",
2420+
)
2421+
if csv_func is None:
2422+
csv_func = Function(
2423+
file_path,
2424+
interpolation="linear",
2425+
extrapolation="constant",
2426+
)
2427+
2428+
def wrapper(alpha, beta, mach, reynolds, pitch_rate, yaw_rate, roll_rate):
2429+
args_by_name = {
2430+
"alpha": alpha,
2431+
"beta": beta,
2432+
"mach": mach,
2433+
"reynolds": reynolds,
2434+
"pitch_rate": pitch_rate,
2435+
"yaw_rate": yaw_rate,
2436+
"roll_rate": roll_rate,
2437+
}
2438+
selected_args = [args_by_name[col] for col in ordered_present_columns]
2439+
return csv_func(*selected_args)
2440+
2441+
return Function(
2442+
wrapper,
2443+
independent_vars,
2444+
[coeff_name],
2445+
interpolation="linear",
2446+
extrapolation="constant",
2447+
)

0 commit comments

Comments
 (0)