shipshape 0.1.0__py3-none-any.whl
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.
- shipshape/__init__.py +3 -0
- shipshape/parameter/__init__.py +5 -0
- shipshape/parameter/__main__.py +177 -0
- shipshape/validate_structure/__init__.py +5 -0
- shipshape/validate_structure/__main__.py +653 -0
- shipshape/validate_structure/aka_analysis.py +261 -0
- shipshape/validate_structure/aka_point_load.py +176 -0
- shipshape/validate_structure/beam_mechanics.py +191 -0
- shipshape/validate_structure/brace_analysis.py +290 -0
- shipshape/validate_structure/capsize_analysis.py +222 -0
- shipshape/validate_structure/diagrams.py +972 -0
- shipshape/validate_structure/gunwale_analysis.py +432 -0
- shipshape/validate_structure/lifting_sling.py +483 -0
- shipshape/validate_structure/mast_analysis.py +391 -0
- shipshape/validate_structure/spine_analysis.py +357 -0
- shipshape/validate_structure/wave_slam.py +847 -0
- shipshape-0.1.0.dist-info/METADATA +43 -0
- shipshape-0.1.0.dist-info/RECORD +20 -0
- shipshape-0.1.0.dist-info/WHEEL +4 -0
- shipshape-0.1.0.dist-info/licenses/LICENSE +201 -0
shipshape/__init__.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Compute derived parameters from base parameters.
|
|
4
|
+
This module loads base parameters from JSON and computes all derived values.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import argparse
|
|
10
|
+
from typing import Dict, Any
|
|
11
|
+
|
|
12
|
+
def compute_derived(base: Dict[str, Any]) -> Dict[str, Any]:
|
|
13
|
+
"""
|
|
14
|
+
Compute all derived parameters from base parameters.
|
|
15
|
+
Returns a complete parameter dictionary with both base and derived values.
|
|
16
|
+
"""
|
|
17
|
+
params = base.copy()
|
|
18
|
+
|
|
19
|
+
# Constants
|
|
20
|
+
mm_in_one_inch = 25.4
|
|
21
|
+
|
|
22
|
+
# Derived dimensions
|
|
23
|
+
params['mm_in_one_inch'] = mm_in_one_inch
|
|
24
|
+
params['stringer_width'] = base['stringer_width_inches'] * mm_in_one_inch
|
|
25
|
+
params['clamp_width'] = base['clamp_width_inches'] * mm_in_one_inch
|
|
26
|
+
params['clamp_height'] = base['clamp_height_inches'] * mm_in_one_inch
|
|
27
|
+
params['vaka_stringer_width'] = base['vaka_stringer_width_inches'] * mm_in_one_inch
|
|
28
|
+
params['vaka_stringer_height'] = base['vaka_stringer_height_inches'] * mm_in_one_inch
|
|
29
|
+
params['frame_width'] = base['frame_width_inches'] * mm_in_one_inch
|
|
30
|
+
params['frame_depth'] = base['frame_depth_inches'] * mm_in_one_inch
|
|
31
|
+
params['bottom_height'] = base['bottom_height_inches'] * mm_in_one_inch
|
|
32
|
+
|
|
33
|
+
# Aka length depends on panels and deck
|
|
34
|
+
params['aka_length'] = (base['panel_length'] * base['panels_transversal'] +
|
|
35
|
+
base['deck_width'])
|
|
36
|
+
|
|
37
|
+
# Bottom thickness same as vaka
|
|
38
|
+
params['bottom_thickness'] = base['vaka_thickness']
|
|
39
|
+
|
|
40
|
+
# Crossdeck dimensions
|
|
41
|
+
params['crossdeck_width'] = base['panel_width'] / base['akas_per_panel']
|
|
42
|
+
params['crossdeck_thickness'] = base['deck_thickness']
|
|
43
|
+
params['crossdeck_length'] = (base['panels_transversal'] * base['panel_length'] +
|
|
44
|
+
(base['deck_width'] - base['vaka_width']) / 2 +
|
|
45
|
+
params['stringer_width'])
|
|
46
|
+
|
|
47
|
+
# Cockpit length: distance from center to first aka's inner edge, doubled
|
|
48
|
+
# First aka Y position depends on akas_per_panel:
|
|
49
|
+
# - Single aka: centered in panel at crossdeck_width/2 + panel_width/2
|
|
50
|
+
# - Multiple akas: at rim distance from panel edge at crossdeck_width/2 + aka_rim
|
|
51
|
+
if base.get('akas_per_panel', 1) == 1:
|
|
52
|
+
first_aka_y = params['crossdeck_width'] / 2 + base['panel_width'] / 2
|
|
53
|
+
else:
|
|
54
|
+
first_aka_y = params['crossdeck_width'] / 2 + base['aka_rim']
|
|
55
|
+
params['cockpit_length'] = 2 * first_aka_y - base['aka_width']
|
|
56
|
+
|
|
57
|
+
# Panel stringer calculations
|
|
58
|
+
params['panel_stringer_offset'] = (base['panel_length'] / 4 -
|
|
59
|
+
params['stringer_width'] / 2)
|
|
60
|
+
params['panel_stringer_length'] = (params['crossdeck_width'] +
|
|
61
|
+
base['panels_longitudinal'] * base['panel_width'])
|
|
62
|
+
|
|
63
|
+
# Vertical levels (build up from bottom)
|
|
64
|
+
params['clamp_base_level'] = (params['bottom_height'] + base['freeboard'] -
|
|
65
|
+
params['clamp_height'])
|
|
66
|
+
params['vaka_stringer_base_level'] = params['clamp_base_level'] - params['freeboard'] / 2
|
|
67
|
+
params['overhead_base_level'] = (params['clamp_base_level'] +
|
|
68
|
+
params['clamp_height'])
|
|
69
|
+
params['aka_base_level'] = (params['overhead_base_level'] +
|
|
70
|
+
base['overhead_thickness'])
|
|
71
|
+
params['stringer_base_level'] = params['aka_base_level'] + base['aka_height']
|
|
72
|
+
params['panel_base_level'] = params['stringer_base_level'] + params['stringer_width']
|
|
73
|
+
params['deck_base_level'] = params['panel_base_level']
|
|
74
|
+
params['deck_level'] = params['deck_base_level'] + base['deck_thickness']
|
|
75
|
+
|
|
76
|
+
# Spine (uses same sizes as aka)
|
|
77
|
+
params['spine_thickness'] = base['aka_thickness']
|
|
78
|
+
params['spine_width'] = base['aka_width']
|
|
79
|
+
params['spine_base_level'] = params['aka_base_level'] - params['spine_width']
|
|
80
|
+
params['spine_length'] = (base['panel_width'] * base['panels_longitudinal'] +
|
|
81
|
+
params['crossdeck_width'] + base['spine_length_extension'])
|
|
82
|
+
|
|
83
|
+
# Beam calculation
|
|
84
|
+
params['beam'] = (params['aka_length'] + base['aka_cap_thickness'] -
|
|
85
|
+
params['spine_width'] + base['ama_diameter'] / 2)
|
|
86
|
+
|
|
87
|
+
# Pillar (uses same sizes as aka)
|
|
88
|
+
params['pillar_thickness'] = base['aka_thickness']
|
|
89
|
+
params['pillar_width'] = base['aka_width']
|
|
90
|
+
params['pillar_height'] = params['spine_base_level'] - base['ama_thickness']
|
|
91
|
+
|
|
92
|
+
# Ama cone length: cone starts at outer edge of outermost pillar
|
|
93
|
+
# Calculate Y position of the outermost (last) aka
|
|
94
|
+
last_panel_index = base['panels_longitudinal'] // 2 - 1
|
|
95
|
+
last_aka_index = base.get('akas_per_panel', 1) - 1
|
|
96
|
+
if base.get('akas_per_panel', 1) == 1:
|
|
97
|
+
last_aka_y = (params['crossdeck_width'] / 2
|
|
98
|
+
+ last_panel_index * base['panel_width']
|
|
99
|
+
+ base['panel_width'] / 2)
|
|
100
|
+
else:
|
|
101
|
+
aka_spacing = ((base['panel_width'] - 2 * base['aka_rim'])
|
|
102
|
+
/ (base['akas_per_panel'] - 1))
|
|
103
|
+
last_aka_y = (params['crossdeck_width'] / 2
|
|
104
|
+
+ last_panel_index * base['panel_width']
|
|
105
|
+
+ base['aka_rim'] + last_aka_index * aka_spacing)
|
|
106
|
+
|
|
107
|
+
# Vaka x offset (distance from ama centerline to vaka centerline)
|
|
108
|
+
params['vaka_x_offset'] = (- params['pillar_width'] / 2
|
|
109
|
+
+ base['panel_length'] * base['panels_transversal']
|
|
110
|
+
+ base['deck_width'] / 2)
|
|
111
|
+
|
|
112
|
+
# Mast calculations
|
|
113
|
+
params['mast_distance_from_center'] = (base['vaka_length'] / 4 +
|
|
114
|
+
base['mast_distance_from_center_offset'])
|
|
115
|
+
params['mast_base_level'] = params['bottom_height'] + base['sole_thickness']
|
|
116
|
+
params['mast_partner_length'] = (base['vaka_width'] -
|
|
117
|
+
base['mast_partner_vaka_clearance'])
|
|
118
|
+
params['mast_partner_width'] = (base['mast_diameter'] +
|
|
119
|
+
base['mast_partner_width_offset'])
|
|
120
|
+
params['mast_step_outer_diameter'] = (base['mast_diameter'] +
|
|
121
|
+
base['mast_step_diameter_offset'])
|
|
122
|
+
params['mast_step_inner_diameter'] = base['mast_diameter']
|
|
123
|
+
|
|
124
|
+
# Yard spar height
|
|
125
|
+
params['yard_spar_height'] = (base['mast_height'] -
|
|
126
|
+
base['yard_spar_distance_from_top'])
|
|
127
|
+
|
|
128
|
+
# Boom length matches yard length (rectangular sails)
|
|
129
|
+
params['boom_length'] = base['yard_length']
|
|
130
|
+
params['sail_area_m2'] = (2 * base['sail_height'] / 1000
|
|
131
|
+
* base['sail_width'] / 1000)
|
|
132
|
+
|
|
133
|
+
# Rudder calculations
|
|
134
|
+
params['rudder_bearing_block_height'] = params['stringer_width']
|
|
135
|
+
params['rudder_vaka_mount_base_level'] = ((params['bottom_height'] +
|
|
136
|
+
base['freeboard']) / 2)
|
|
137
|
+
params['rudder_rib_length'] = (base['rudder_blade_length'] -
|
|
138
|
+
base['rudder_rib_clearance'])
|
|
139
|
+
|
|
140
|
+
# Tiller dimensions (uses stringer sizes)
|
|
141
|
+
params['tiller_width'] = params['stringer_width']
|
|
142
|
+
params['tiller_thickness'] = base['stringer_thickness']
|
|
143
|
+
params['tiller_length'] = 490 # This was hardcoded in original
|
|
144
|
+
|
|
145
|
+
return params
|
|
146
|
+
|
|
147
|
+
def main():
|
|
148
|
+
parser = argparse.ArgumentParser(description='Compute parameters')
|
|
149
|
+
parser.add_argument('--boat', required=True, help='Path to boat constants')
|
|
150
|
+
parser.add_argument('--configuration', required=True, help='Path to configuration constants')
|
|
151
|
+
parser.add_argument('--output', required=True, help='Path to output JSON artifact')
|
|
152
|
+
|
|
153
|
+
args = parser.parse_args()
|
|
154
|
+
|
|
155
|
+
# Load boat parameters
|
|
156
|
+
with open(args.boat, 'r') as b:
|
|
157
|
+
boat_data = json.load(b)
|
|
158
|
+
|
|
159
|
+
with open(args.configuration, 'r') as c:
|
|
160
|
+
configuration_data = json.load(c)
|
|
161
|
+
|
|
162
|
+
data = boat_data | configuration_data
|
|
163
|
+
|
|
164
|
+
data = compute_derived(data)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# Write JSON output
|
|
169
|
+
os.makedirs(os.path.dirname(args.output) or '.', exist_ok=True)
|
|
170
|
+
with open(args.output, 'w') as f:
|
|
171
|
+
json.dump(data, f, indent=2)
|
|
172
|
+
|
|
173
|
+
print(f"✓ Parameters complete")
|
|
174
|
+
print(f" Output: {args.output}")
|
|
175
|
+
|
|
176
|
+
if __name__ == "__main__":
|
|
177
|
+
main()
|