Coverage for src/beamme/mesh_creation_functions/nurbs_geometries.py: 99%

205 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-28 15:20 +0000

1# The MIT License (MIT) 

2# 

3# Copyright (c) 2018-2026 BeamMe Authors 

4# 

5# Permission is hereby granted, free of charge, to any person obtaining a copy 

6# of this software and associated documentation files (the "Software"), to deal 

7# in the Software without restriction, including without limitation the rights 

8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 

9# copies of the Software, and to permit persons to whom the Software is 

10# furnished to do so, subject to the following conditions: 

11# 

12# The above copyright notice and this permission notice shall be included in 

13# all copies or substantial portions of the Software. 

14# 

15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 

16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 

18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 

19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 

20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 

21# THE SOFTWARE. 

22"""This file has functions to create NURBS geometries using Geomdl.""" 

23 

24import numpy as _np 

25from geomdl import NURBS as _NURBS 

26from geomdl import compatibility as _compat 

27from geomdl import operations as _operations 

28 

29 

30def create_nurbs_hollow_cylinder_segment_2d( 

31 radius_in, radius_out, angle, *, n_ele_u=1, n_ele_v=1 

32): 

33 """Creates a patch of a 2 dimensional segment of a hollow cylinder. 

34 

35 Args 

36 ---- 

37 radius_in: double 

38 inner cylinder radius 

39 radius_out: double 

40 outer cylinder radius 

41 angle: double 

42 angle of the hollow cylinder section (radians) 

43 n_ele_u: int 

44 number of elements in the parametric u-direction 

45 n_ele_v: int 

46 number of elements in the parametric v-direction 

47 

48 

49 Return 

50 ---- 

51 surf: geomdl object 

52 geomdl object that contains the surface information 

53 """ 

54 # Check the validity of the input values: 

55 if radius_in >= radius_out: 

56 raise ValueError( 

57 "The external radius should be larger than the internal radius of a hollow cylinder." 

58 ) 

59 if (angle > _np.pi) or (angle < 0): 

60 raise ValueError( 

61 "The following algorithm for creating a hollow cylinder section is only valid for 0 < angle <= pi." 

62 ) 

63 

64 # Create a NURBS surface instance 

65 surf = _NURBS.Surface() 

66 

67 # Set degrees 

68 surf.degree_u = 2 

69 surf.degree_v = 2 

70 

71 # Control points and set them to the surface 

72 p_size_u = 3 

73 p_size_v = 3 

74 

75 # To calculate the internal control point cp_2 we have to calculate 

76 # the intersection of the tangents of the points cp_1 and cp_3. 

77 # As point cp_1 is always defined over the x axis, its tangent is simply x = radius 

78 # The equation of the tangent to the circle at the cp_3 point is: 

79 # y - cp_3y = -(cp_3x/cp_3y)*(x - cp_3x). 

80 

81 # Obtaining the control points that define the external arc of the hollow cylinder 

82 cp_ext1 = [radius_out, 0.0, 0.0] 

83 cp_ext3 = [radius_out * _np.cos(angle), radius_out * _np.sin(angle), 0.0] 

84 cp_ext2 = [ 

85 radius_out, 

86 -(cp_ext3[0] / cp_ext3[1]) * (radius_out - cp_ext3[0]) + cp_ext3[1], 

87 0.0, 

88 ] 

89 

90 # Obtaining the control points that define the internal arc of the hollow cylinder 

91 cp_int1 = [radius_in, 0.0, 0.0] 

92 cp_int3 = [radius_in * _np.cos(angle), radius_in * _np.sin(angle), 0.0] 

93 cp_int2 = [ 

94 radius_in, 

95 -(cp_int3[0] / cp_int3[1]) * (radius_in - cp_int3[0]) + cp_int3[1], 

96 0.0, 

97 ] 

98 

99 # Obtaining the control points positioned in the middle of the internal and external arches 

100 cp_middle1 = [(cp_ext1[0] + cp_int1[0]) / 2, (cp_ext1[1] + cp_int1[1]) / 2, 0.0] 

101 cp_middle2 = [(cp_ext2[0] + cp_int2[0]) / 2, (cp_ext2[1] + cp_int2[1]) / 2, 0.0] 

102 cp_middle3 = [(cp_ext3[0] + cp_int3[0]) / 2, (cp_ext3[1] + cp_int3[1]) / 2, 0.0] 

103 

104 ctrlpts = [ 

105 cp_ext1, 

106 cp_ext2, 

107 cp_ext3, 

108 cp_middle1, 

109 cp_middle2, 

110 cp_middle3, 

111 cp_int1, 

112 cp_int2, 

113 cp_int3, 

114 ] 

115 

116 weights = [ 

117 1.0, 

118 _np.cos(angle / 2), 

119 1.0, 

120 1.0, 

121 _np.cos(angle / 2), 

122 1.0, 

123 1.0, 

124 _np.cos(angle / 2), 

125 1.0, 

126 ] 

127 

128 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights) 

129 n_ctrlptsw = _compat.flip_ctrlpts_u(t_ctrlptsw, p_size_u, p_size_v) 

130 

131 surf.ctrlpts_size_u = p_size_u 

132 surf.ctrlpts_size_v = p_size_v 

133 surf.ctrlptsw = n_ctrlptsw 

134 

135 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

136 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

137 

138 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v) 

139 

140 return surf 

141 

142 

143def create_nurbs_cylindrical_shell_sector( 

144 radius: float, angle: float, length: float, *, n_ele_u: int = 1, n_ele_v: int = 1 

145) -> _NURBS.Surface: 

146 """Creates a NURBS surface representing a 3D sector of a cylindrical shell. 

147 

148 The center of the cylindrical shell sector is located at [0, 0, 0]. 

149 

150 Args: 

151 radius: Radius of the cylindrical shell. 

152 angle: Angle of the cylindrical shell sector in radians. 

153 The angle is only valid for 0 < angle <= pi according 

154 to "Isogeometric Analysis: Toward Integration of CAD and FEA" by 

155 J.Austin Cottrell, 2009 

156 length: Length of the cylindrical shell. 

157 n_ele_u: Number of elements in the parametric u-direction. Defaults to 1. 

158 n_ele_v: Number of elements in the parametric v-direction. Defaults to 1. 

159 

160 Returns: 

161 geomdl.NURBS.Surface: A geomdl object that contains the surface information. 

162 """ 

163 # Check the validity of the input values: 

164 if (angle >= _np.pi) or (angle < 0): 

165 raise ValueError( 

166 "The following algorithm for creating a cylindrical shell sector is only valid for 0 < angle <= pi." 

167 ) 

168 

169 # Create a NURBS surface instance 

170 surf = _NURBS.Surface() 

171 

172 # Set degrees 

173 surf.degree_u = 2 

174 surf.degree_v = 2 

175 

176 # Control points and set them to the surface 

177 p_size_u = 3 

178 p_size_v = 3 

179 

180 # Obtaining the control points 

181 cp_1 = [-radius * _np.sin(angle / 2), -length / 2, -radius * _np.cos(angle / 2)] 

182 cp_3 = [radius * _np.sin(angle / 2), -length / 2, -radius * _np.cos(angle / 2)] 

183 

184 # Calculating position of middle points. This is done by 

185 # obtaining the tangents on the points cp_1 and cp_3 and 

186 # calculating the intersection point between the tangents. 

187 m_1 = -_np.tan(-angle / 2) 

188 m_3 = -_np.tan(angle / 2) 

189 b_1 = cp_1[2] + m_1 * cp_1[0] 

190 b_3 = cp_3[2] + m_3 * cp_3[0] 

191 

192 inter_point_x = (b_3 - b_1) / (m_3 - m_1) 

193 inter_point_z = m_1 * inter_point_x + b_1 

194 

195 # The intersection point is assigned to the middle points cp_2, cp_5 and cp_8 

196 cp_2 = [inter_point_x, -length / 2, inter_point_z] 

197 

198 cp_4 = [-radius * _np.sin(angle / 2), 0.0, -radius * _np.cos(angle / 2)] 

199 cp_5 = [inter_point_x, 0.0, inter_point_z] 

200 cp_6 = [radius * _np.sin(angle / 2), 0.0, -radius * _np.cos(angle / 2)] 

201 

202 cp_7 = [ 

203 -radius * _np.sin(angle / 2), 

204 length / 2, 

205 -radius * _np.cos(angle / 2), 

206 ] 

207 cp_8 = [inter_point_x, length / 2, inter_point_z] 

208 cp_9 = [radius * _np.sin(angle / 2), length / 2, -radius * _np.cos(angle / 2)] 

209 

210 ctrlpts = [cp_1, cp_4, cp_7, cp_2, cp_5, cp_8, cp_3, cp_6, cp_9] 

211 

212 weights = [ 

213 1.0, 

214 1.0, 

215 1.0, 

216 _np.cos(angle / 2), 

217 _np.cos(angle / 2), 

218 _np.cos(angle / 2), 

219 1.0, 

220 1.0, 

221 1.0, 

222 ] 

223 

224 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights) 

225 

226 surf.ctrlpts_size_u = p_size_u 

227 surf.ctrlpts_size_v = p_size_v 

228 surf.ctrlptsw = t_ctrlptsw 

229 

230 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

231 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

232 

233 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v) 

234 

235 return surf 

236 

237 

238def create_nurbs_flat_plate_2d(width, length, *, n_ele_u=1, n_ele_v=1): 

239 """Creates a patch of a 2 dimensional flat plate. 

240 

241 Args 

242 ---- 

243 width: double 

244 dimension of the plate in the x-direction 

245 length: double 

246 dimension of the plate in the y-direction 

247 n_ele_u: int 

248 number of elements in the parametric u-direction 

249 n_ele_v: int 

250 number of elements in the parametric v-direction 

251 

252 

253 Return 

254 ---- 

255 surf: geomdl object 

256 geomdl object that contains the surface information 

257 """ 

258 # Create a NURBS surface instance 

259 surf = _NURBS.Surface() 

260 

261 # Set degrees 

262 surf.degree_u = 2 

263 surf.degree_v = 2 

264 

265 # Control points and set them to the surface 

266 p_size_u = 3 

267 p_size_v = 3 

268 

269 ctrlpts = [ 

270 [-width / 2, -length / 2, 0.0], 

271 [0.0, -length / 2, 0.0], 

272 [width / 2, -length / 2, 0.0], 

273 [-width / 2, 0.0, 0.0], 

274 [0.0, 0.0, 0.0], 

275 [width / 2, 0.0, 0.0], 

276 [-width / 2, length / 2, 0.0], 

277 [0.0, length / 2, 0.0], 

278 [width / 2, length / 2, 0.0], 

279 ] 

280 

281 weights = [1.0] * 9 

282 

283 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights) 

284 n_ctrlptsw = _compat.flip_ctrlpts_u(t_ctrlptsw, p_size_u, p_size_v) 

285 

286 surf.ctrlpts_size_u = p_size_u 

287 surf.ctrlpts_size_v = p_size_v 

288 surf.ctrlptsw = n_ctrlptsw 

289 

290 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

291 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

292 

293 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v) 

294 

295 return surf 

296 

297 

298def create_nurbs_sphere_surface(radius, n_ele_u=1, n_ele_v=1): 

299 """Generates a patch of a sphere as a NURBS surface. 

300 

301 This function constructs a segment of a spherical surface using Non-Uniform Rational 

302 B-Splines (NURBS) based on the specified radius and the number of elements 

303 in the parametric u and v directions. 

304 

305 Args 

306 --- 

307 radius: double 

308 radius of the sphere 

309 n_ele_u: int 

310 number of elements in the parametric u-direction 

311 n_ele_v: int 

312 number of elements in the parametric v-direction 

313 

314 Return 

315 ---- 

316 surf: geomdl object 

317 geomdl object that contains the surface information 

318 """ 

319 # Create a NURBS surface instance 

320 surf = _NURBS.Surface() 

321 

322 # Set degrees 

323 surf.degree_u = 2 

324 surf.degree_v = 2 

325 

326 # Control points and set them to the surface 

327 p_size_u = 3 

328 p_size_v = 3 

329 

330 dummy = 6.0 * ( 

331 5.0 / 12.0 

332 + 0.5 * _np.sqrt(2.0 / 3.0) 

333 - 0.25 / _np.sqrt(3.0) 

334 - 0.5 * _np.sqrt(2.0 / 3.0) * _np.sqrt(3.0) / 2.0 

335 ) 

336 

337 ctrlpts = [ 

338 [ 

339 2.0 * radius / _np.sqrt(6.0) * -_np.sin(1.0 / 4.0 * _np.pi), 

340 -radius / _np.sqrt(3.0), 

341 2.0 * radius / _np.sqrt(6.0) * _np.cos(1.0 / 4.0 * _np.pi), 

342 ], 

343 [ 

344 radius * _np.sqrt(3.0) / (_np.sqrt(2.0) * 2.0) * _np.cos(1.0 / 4.0 * _np.pi) 

345 + radius 

346 * _np.sqrt(3.0) 

347 / (_np.sqrt(2.0) * 2.0) 

348 * -_np.sin(1.0 / 4.0 * _np.pi), 

349 -_np.sqrt(3.0) / 2 * radius, 

350 radius * _np.sqrt(3.0) / (_np.sqrt(2.0) * 2.0) * _np.cos(1.0 / 4.0 * _np.pi) 

351 + radius 

352 * _np.sqrt(3.0) 

353 / (_np.sqrt(2.0) * 2.0) 

354 * _np.sin(1.0 / 4.0 * _np.pi), 

355 ], 

356 [ 

357 2.0 * radius / _np.sqrt(6.0) * _np.cos(1.0 / 4.0 * _np.pi), 

358 -radius / _np.sqrt(3.0), 

359 2.0 * radius / _np.sqrt(6.0) * _np.sin(1.0 / 4.0 * _np.pi), 

360 ], 

361 [ 

362 radius * _np.sqrt(6.0) / 2.0 * -_np.sin(1.0 / 4.0 * _np.pi), 

363 0.0, 

364 radius * _np.sqrt(6.0) / 2.0 * _np.cos(1.0 / 4.0 * _np.pi), 

365 ], 

366 [ 

367 radius * dummy * _np.sqrt(2.0) / 2.0 * _np.cos(1.0 / 4.0 * _np.pi) 

368 + radius * dummy * _np.sqrt(2.0) / 2.0 * -_np.sin(1.0 / 4.0 * _np.pi), 

369 0.0, 

370 radius * dummy * _np.sqrt(2.0) / 2.0 * _np.cos(1.0 / 4.0 * _np.pi) 

371 + radius * dummy * _np.sqrt(2.0) / 2.0 * _np.sin(1.0 / 4.0 * _np.pi), 

372 ], 

373 [ 

374 radius * _np.sqrt(6.0) / 2.0 * _np.cos(1.0 / 4.0 * _np.pi), 

375 0.0, 

376 radius * _np.sqrt(6.0) / 2.0 * _np.sin(1.0 / 4.0 * _np.pi), 

377 ], 

378 [ 

379 2.0 * radius / _np.sqrt(6.0) * -_np.sin(1.0 / 4.0 * _np.pi), 

380 2.0 * radius / _np.sqrt(6.0) * _np.cos(1.0 / 4.0 * _np.pi), 

381 radius / _np.sqrt(3.0), 

382 ], 

383 [ 

384 radius * _np.sqrt(3.0) / (_np.sqrt(2.0) * 2.0) * _np.cos(1.0 / 4.0 * _np.pi) 

385 + radius 

386 * _np.sqrt(3.0) 

387 / (_np.sqrt(2.0) * 2.0) 

388 * -_np.sin(1.0 / 4.0 * _np.pi), 

389 _np.sqrt(3.0) / 2 * radius, 

390 radius * _np.sqrt(3.0) / (_np.sqrt(2.0) * 2.0) * _np.cos(1.0 / 4.0 * _np.pi) 

391 + radius 

392 * _np.sqrt(3.0) 

393 / (_np.sqrt(2.0) * 2.0) 

394 * _np.sin(1.0 / 4.0 * _np.pi), 

395 ], 

396 [ 

397 2.0 * radius / _np.sqrt(6.0) * _np.cos(1.0 / 4.0 * _np.pi), 

398 radius / _np.sqrt(3.0), 

399 2.0 * radius / _np.sqrt(6.0) * _np.sin(1.0 / 4.0 * _np.pi), 

400 ], 

401 ] 

402 

403 weights = [ 

404 1.0, 

405 2.0 / _np.sqrt(6.0), 

406 1.0, 

407 2.0 / _np.sqrt(6.0), 

408 2.0 / 3.0, 

409 2.0 / _np.sqrt(6.0), 

410 1.0, 

411 2.0 / _np.sqrt(6.0), 

412 1.0, 

413 ] 

414 

415 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights) 

416 n_ctrlptsw = _compat.flip_ctrlpts_u(t_ctrlptsw, p_size_u, p_size_v) 

417 

418 surf.ctrlpts_size_u = p_size_u 

419 surf.ctrlpts_size_v = p_size_v 

420 surf.ctrlptsw = n_ctrlptsw 

421 

422 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

423 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

424 

425 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v) 

426 

427 return surf 

428 

429 

430def create_nurbs_hemisphere_surface(radius, n_ele_uv=1): 

431 """Generates a hemisphere as a NURBS surface. 

432 

433 This function constructs five segments that represent the surface of a 

434 hemisphere using Non-Uniform Rational B-Splines (NURBS) based on the specified 

435 radius and the number of elements in the parametric u and v directions. 

436 To secure the connectivity between surfaces, all surfaces must have the same 

437 parametric representation in any parametric direction. Therefore, the number 

438 of elements in u- and v-directions must be the same. 

439 

440 This function generates a list of five NURBS geomdl objects. 

441 

442 Args 

443 --- 

444 radius: double 

445 Radius of the hemisphere 

446 n_ele_uv: int 

447 Number of elements in the parametric u- and v- directions 

448 

449 Return 

450 ---- 

451 list: list(geomdl object) 

452 A list of geomdl objects that contains the surface information 

453 """ 

454 # Create the first section of the hemisphere 

455 hemisphere_1 = create_nurbs_sphere_surface(radius, n_ele_u=1, n_ele_v=1) 

456 

457 # Create a temporary section by rotating and translating the 

458 # first section of the hemisphere 

459 temp_hemisphere = _operations.rotate(hemisphere_1, 90, axis=1) 

460 temp_hemisphere = _operations.translate( 

461 temp_hemisphere, 

462 (0, 0, -2.0 * radius / _np.sqrt(6.0) * _np.sin(1.0 / 4.0 * _np.pi) * 2), 

463 ) 

464 

465 # To create the hemisphere it is necessary to split the temporary 

466 # sphere section in two pieces. This split is done in u = 0.5. After 

467 # the split, the second surface is taken as it will be 

468 # adjacent to the first section of the sphere 

469 cut_section_sphere = _operations.split_surface_u(temp_hemisphere, param=0.5) 

470 hemisphere_2 = cut_section_sphere[1] 

471 

472 translation_component = radius * _np.sin(1.0 / 4.0 * _np.pi) * 2 

473 # Create the third section. Rotate and translate it accordingly 

474 hemisphere_3 = _operations.rotate(hemisphere_2, 90, axis=2) 

475 hemisphere_3 = _operations.translate(hemisphere_3, (translation_component, 0, 0)) 

476 

477 # Create the forth section. Rotate and translate it accordingly 

478 hemisphere_4 = _operations.rotate(hemisphere_3, 90, axis=2) 

479 hemisphere_4 = _operations.translate(hemisphere_4, (0, translation_component, 0)) 

480 

481 # Create the fifth section. Rotate and translate it accordingly 

482 hemisphere_5 = _operations.rotate(hemisphere_4, 90, axis=2) 

483 hemisphere_5 = _operations.translate(hemisphere_5, (-translation_component, 0, 0)) 

484 

485 patches = [hemisphere_1, hemisphere_2, hemisphere_3, hemisphere_4, hemisphere_5] 

486 for patch in patches: 

487 do_uniform_knot_refinement_surface(patch, n_ele_uv, n_ele_uv) 

488 return patches 

489 

490 

491def create_nurbs_torus_surface(radius_torus, radius_circle, *, n_ele_u=1, n_ele_v=1): 

492 """Creates a NURBS patch for the construction of a ring torus (outer 

493 surface). A ring torus is a surface of revolution generated by revolving a 

494 circle in a 3-dimensional space around an axis of revolution (axis z). The 

495 center of the torus is located at the coordinates [0, 0, 0]. 

496 

497 This function constructs the surface of a ring torus in the following manner: 

498 - The quarter of the torus is made by 4 patches 

499 - These patches are rotated three times around the z-axis by 90°, e.g. theta = 90°, 180°, 270°, 

500 leading to a total of 16 NURBS patches. 

501 

502 The number of elements in this function are given for a single patch, where: 

503 - n_ele_u is the number of elements in the "radius_torus" direction 

504 - n_ele_v is the number of elements in the "radius_circle" direction 

505 

506 This function generates a list of 16 NURBS geomdl objects. 

507 

508 Args 

509 ---- 

510 radius_torus: double 

511 distance from the axis of revolution (axis z) to the center of the revolving circle 

512 radius_circle: double 

513 radius of the circle that revolves around the axis of revolution 

514 n_ele_u: int 

515 number of elements in the parametric u-direction 

516 n_ele_v: int 

517 number of elements in the parametric v-direction 

518 """ 

519 

520 # Create four NURBS surface instances. These are the base patches of the torus. 

521 surf_1 = _NURBS.Surface() 

522 surf_2 = _NURBS.Surface() 

523 surf_3 = _NURBS.Surface() 

524 surf_4 = _NURBS.Surface() 

525 base_surfs = [surf_1, surf_2, surf_3, surf_4] 

526 

527 # Define control points and set them to the surfaces 

528 p_size_u = 3 

529 p_size_v = 3 

530 

531 dummy_surf1 = radius_torus + radius_circle 

532 dummy_surf2 = radius_torus - radius_circle 

533 

534 ctrlpts_surf1 = [ 

535 [dummy_surf1, 0.0, 0.0], 

536 [dummy_surf1, 0.0, radius_circle], 

537 [radius_torus, 0.0, radius_circle], 

538 [dummy_surf1, dummy_surf1, 0.0], 

539 [dummy_surf1, dummy_surf1, radius_circle], 

540 [radius_torus, radius_torus, radius_circle], 

541 [0.0, dummy_surf1, 0.0], 

542 [0.0, dummy_surf1, radius_circle], 

543 [0.0, radius_torus, radius_circle], 

544 ] 

545 

546 ctrlpts_surf2 = [ 

547 [dummy_surf1, 0.0, 0.0], 

548 [dummy_surf1, 0.0, -radius_circle], 

549 [radius_torus, 0.0, -radius_circle], 

550 [dummy_surf1, dummy_surf1, 0.0], 

551 [dummy_surf1, dummy_surf1, -radius_circle], 

552 [radius_torus, radius_torus, -radius_circle], 

553 [0.0, dummy_surf1, 0.0], 

554 [0.0, dummy_surf1, -radius_circle], 

555 [0.0, radius_torus, -radius_circle], 

556 ] 

557 

558 ctrlpts_surf3 = [ 

559 [radius_torus, 0.0, -radius_circle], 

560 [dummy_surf2, 0.0, -radius_circle], 

561 [dummy_surf2, 0.0, 0.0], 

562 [radius_torus, radius_torus, -radius_circle], 

563 [dummy_surf2, dummy_surf2, -radius_circle], 

564 [dummy_surf2, dummy_surf2, 0.0], 

565 [0.0, radius_torus, -radius_circle], 

566 [0.0, dummy_surf2, -radius_circle], 

567 [0.0, dummy_surf2, 0.0], 

568 ] 

569 

570 ctrlpts_surf4 = [ 

571 [0.0, dummy_surf2, 0.0], 

572 [0.0, dummy_surf2, radius_circle], 

573 [0.0, radius_torus, radius_circle], 

574 [dummy_surf2, dummy_surf2, 0.0], 

575 [dummy_surf2, dummy_surf2, radius_circle], 

576 [radius_torus, radius_torus, radius_circle], 

577 [dummy_surf2, 0.0, 0.0], 

578 [dummy_surf2, 0.0, radius_circle], 

579 [radius_torus, 0.0, radius_circle], 

580 ] 

581 

582 weights = [ 

583 1.0, 

584 _np.sqrt(2) / 2, 

585 1.0, 

586 _np.sqrt(2) / 2, 

587 0.5, 

588 _np.sqrt(2) / 2, 

589 1.0, 

590 _np.sqrt(2) / 2, 

591 1.0, 

592 ] 

593 

594 t_ctrlptsw1 = _compat.combine_ctrlpts_weights(ctrlpts_surf1, weights) 

595 t_ctrlptsw2 = _compat.combine_ctrlpts_weights(ctrlpts_surf2, weights) 

596 t_ctrlptsw3 = _compat.combine_ctrlpts_weights(ctrlpts_surf3, weights) 

597 t_ctrlptsw4 = _compat.combine_ctrlpts_weights(ctrlpts_surf4, weights) 

598 

599 t_ctrlpts_surfs = [t_ctrlptsw1, t_ctrlptsw2, t_ctrlptsw3, t_ctrlptsw4] 

600 

601 for surf, t_ctrlpts in zip(base_surfs, t_ctrlpts_surfs): 

602 # Set degrees 

603 surf.degree_u = 2 

604 surf.degree_v = 2 

605 

606 surf.ctrlpts_size_u = p_size_u 

607 surf.ctrlpts_size_v = p_size_v 

608 

609 # Set control points 

610 surf.ctrlptsw = t_ctrlpts 

611 

612 # Set knot vectors 

613 surf.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

614 surf.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

615 

616 do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v) 

617 

618 # Define the rotations and translations to rotate the base patches and form a complete torus 

619 tmp_trans = [ 

620 radius_torus + radius_circle, 

621 radius_torus + radius_circle, 

622 radius_torus, 

623 radius_torus - radius_circle, 

624 ] 

625 

626 transform_surf1 = [ 

627 [(-tmp_trans[0], tmp_trans[0], 0), 90, 2], 

628 [(-2 * tmp_trans[0], 0, 0), 180, 2], 

629 [(-tmp_trans[0], -tmp_trans[0], 0), 270, 2], 

630 ] 

631 

632 transform_surf2 = [ 

633 [(-tmp_trans[1], tmp_trans[1], 0), 90, 2], 

634 [(-2 * tmp_trans[1], 0, 0), 180, 2], 

635 [(-tmp_trans[1], -tmp_trans[1], 0), 270, 2], 

636 ] 

637 

638 transform_surf3 = [ 

639 [(-tmp_trans[2], tmp_trans[2], 0), 90, 2], 

640 [(-2 * tmp_trans[2], 0, 0), 180, 2], 

641 [(-tmp_trans[2], -tmp_trans[2], 0), 270, 2], 

642 ] 

643 

644 transform_surf4 = [ 

645 [(-tmp_trans[3], -tmp_trans[3], 0), 90, 2], 

646 [(0, -2 * tmp_trans[3], 0), 180, 2], 

647 [(tmp_trans[3], -tmp_trans[3], 0), 270, 2], 

648 ] 

649 

650 # Rotate base patches and store them 

651 surfaces_torus = [surf_1, surf_2, surf_3, surf_4] 

652 for transform1, transform2, transform3, transform4 in zip( 

653 transform_surf1, transform_surf2, transform_surf3, transform_surf4 

654 ): 

655 new_surf1 = _operations.translate(surf_1, transform1[0]) 

656 new_surf1 = _operations.rotate(new_surf1, transform1[1], axis=transform1[2]) 

657 surfaces_torus.append(new_surf1) 

658 

659 new_surf2 = _operations.translate(surf_2, transform2[0]) 

660 new_surf2 = _operations.rotate(new_surf2, transform2[1], axis=transform2[2]) 

661 surfaces_torus.append(new_surf2) 

662 

663 new_surf3 = _operations.translate(surf_3, transform3[0]) 

664 new_surf3 = _operations.rotate(new_surf3, transform3[1], axis=transform3[2]) 

665 surfaces_torus.append(new_surf3) 

666 

667 new_surf4 = _operations.translate(surf_4, transform4[0]) 

668 new_surf4 = _operations.rotate(new_surf4, transform4[1], axis=transform4[2]) 

669 surfaces_torus.append(new_surf4) 

670 

671 return surfaces_torus 

672 

673 

674def create_nurbs_brick(width, length, height, *, n_ele_u=1, n_ele_v=1, n_ele_w=1): 

675 """Creates a patch of a 3 dimensional brick. 

676 

677 Args 

678 ---- 

679 width: double 

680 dimension of the plate in the x-direction 

681 length: double 

682 dimension of the plate in the y-direction 

683 height: double 

684 dimension of the plate in the z-direction 

685 n_ele_u: int 

686 number of elements in the parametric u-direction 

687 n_ele_v: int 

688 number of elements in the parametric v-direction 

689 n_ele_w: int 

690 number of elements in the parametric w-direction 

691 

692 

693 Return 

694 ---- 

695 vol: geomdl object 

696 geomdl object that contains the volume information 

697 """ 

698 # Create a NURBS volume instance 

699 vol = _NURBS.Volume() 

700 

701 # Set degrees 

702 vol.degree_u = 2 

703 vol.degree_v = 2 

704 vol.degree_w = 2 

705 

706 # Create control points and set them to the volume 

707 cp_size_u = 3 

708 cp_size_v = 3 

709 cp_size_w = 3 

710 

711 ctrlpts = [ 

712 [-width / 2, -length / 2, -height / 2], 

713 [-width / 2, 0.0, -height / 2], 

714 [-width / 2, length / 2, -height / 2], 

715 [0.0, -length / 2, -height / 2], 

716 [0.0, 0.0, -height / 2], 

717 [0.0, length / 2, -height / 2], 

718 [width / 2, -length / 2, -height / 2], 

719 [width / 2, 0.0, -height / 2], 

720 [width / 2, length / 2, -height / 2], 

721 [-width / 2, -length / 2, 0.0], 

722 [-width / 2, 0.0, 0.0], 

723 [-width / 2, length / 2, 0.0], 

724 [0.0, -length / 2, 0.0], 

725 [0.0, 0.0, 0.0], 

726 [0.0, length / 2, 0.0], 

727 [width / 2, -length / 2, 0.0], 

728 [width / 2, 0.0, 0.0], 

729 [width / 2, length / 2, 0.0], 

730 [-width / 2, -length / 2, height / 2], 

731 [-width / 2, 0.0, height / 2], 

732 [-width / 2, length / 2, height / 2], 

733 [0.0, -length / 2, height / 2], 

734 [0.0, 0.0, height / 2], 

735 [0.0, length / 2, height / 2], 

736 [width / 2, -length / 2, height / 2], 

737 [width / 2, 0.0, height / 2], 

738 [width / 2, length / 2, height / 2], 

739 ] 

740 

741 weights = [1.0] * 27 

742 

743 vol.ctrlpts_size_u = cp_size_u 

744 vol.ctrlpts_size_v = cp_size_v 

745 vol.ctrlpts_size_w = cp_size_w 

746 

747 t_ctrlptsw = _compat.combine_ctrlpts_weights(ctrlpts, weights) 

748 

749 vol.set_ctrlpts(t_ctrlptsw, cp_size_u, cp_size_v, cp_size_w) 

750 

751 vol.knotvector_u = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

752 vol.knotvector_v = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

753 vol.knotvector_w = [0.0, 0.0, 0.0, 1.0, 1.0, 1.0] 

754 

755 do_uniform_knot_refinement_volume(vol, n_ele_u, n_ele_v, n_ele_w) 

756 

757 return vol 

758 

759 

760def do_uniform_knot_refinement_surface(surf, n_ele_u, n_ele_v): 

761 """This function does an uniform knot refinement in the u- and v- direction. 

762 

763 Args 

764 ---- 

765 surf: geomdl object 

766 geomdl object that contains the surface information 

767 n_ele_u: int 

768 number of elements in the parametric u-direction 

769 n_ele_v: int 

770 number of elements in the parametric v-direction 

771 

772 Return 

773 ---- 

774 surf: geomdl object 

775 """ 

776 size_of_knotvector_u = 1 / n_ele_u 

777 size_of_knotvector_v = 1 / n_ele_v 

778 

779 for i in range(1, n_ele_u): 

780 _operations.insert_knot(surf, [size_of_knotvector_u * i, None], [1, 0]) 

781 for j in range(1, n_ele_v): 

782 _operations.insert_knot(surf, [None, size_of_knotvector_v * j], [0, 1]) 

783 

784 

785def do_uniform_knot_refinement_volume(vol, n_ele_u, n_ele_v, n_ele_w): 

786 """This function does an uniform knot refinement in the u-, v- and w- direction. 

787 

788 Args 

789 ---- 

790 vol: geomdl object 

791 geomdl object that contains the volume information 

792 n_ele_u: int 

793 number of elements in the parametric u-direction 

794 n_ele_v: int 

795 number of elements in the parametric v-direction 

796 n_ele_w: int 

797 number of elements in the parametric w-direction 

798 

799 Return 

800 ---- 

801 vol: geomdl object 

802 """ 

803 size_of_knotvector_u = 1 / n_ele_u 

804 size_of_knotvector_v = 1 / n_ele_v 

805 size_of_knotvector_w = 1 / n_ele_w 

806 

807 for i in range(1, n_ele_u): 

808 _operations.insert_knot(vol, [size_of_knotvector_u * i, None, None], [1, 0, 0]) 

809 for j in range(1, n_ele_v): 

810 _operations.insert_knot(vol, [None, size_of_knotvector_v * j, None], [0, 1, 0]) 

811 for k in range(1, n_ele_w): 

812 _operations.insert_knot(vol, [None, None, size_of_knotvector_w * k], [0, 0, 1])