capytaine 2.2.1__cp313-cp313-macosx_14_0_arm64.whl → 2.3__cp313-cp313-macosx_14_0_arm64.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.
Files changed (48) hide show
  1. capytaine/.dylibs/libgcc_s.1.1.dylib +0 -0
  2. capytaine/.dylibs/libgfortran.5.dylib +0 -0
  3. capytaine/.dylibs/libquadmath.0.dylib +0 -0
  4. capytaine/__about__.py +1 -1
  5. capytaine/__init__.py +2 -1
  6. capytaine/bem/airy_waves.py +7 -2
  7. capytaine/bem/problems_and_results.py +78 -34
  8. capytaine/bem/solver.py +127 -39
  9. capytaine/bodies/bodies.py +30 -10
  10. capytaine/bodies/predefined/rectangles.py +2 -0
  11. capytaine/green_functions/FinGreen3D/.gitignore +1 -0
  12. capytaine/green_functions/FinGreen3D/FinGreen3D.f90 +3589 -0
  13. capytaine/green_functions/FinGreen3D/LICENSE +165 -0
  14. capytaine/green_functions/FinGreen3D/Makefile +16 -0
  15. capytaine/green_functions/FinGreen3D/README.md +24 -0
  16. capytaine/green_functions/FinGreen3D/test_program.f90 +39 -0
  17. capytaine/green_functions/LiangWuNoblesse/.gitignore +1 -0
  18. capytaine/green_functions/LiangWuNoblesse/LICENSE +504 -0
  19. capytaine/green_functions/LiangWuNoblesse/LiangWuNoblesseWaveTerm.f90 +751 -0
  20. capytaine/green_functions/LiangWuNoblesse/Makefile +18 -0
  21. capytaine/green_functions/LiangWuNoblesse/README.md +2 -0
  22. capytaine/green_functions/LiangWuNoblesse/test_program.f90 +28 -0
  23. capytaine/green_functions/abstract_green_function.py +55 -3
  24. capytaine/green_functions/delhommeau.py +186 -115
  25. capytaine/green_functions/hams.py +204 -0
  26. capytaine/green_functions/libs/Delhommeau_float32.cpython-313-darwin.so +0 -0
  27. capytaine/green_functions/libs/Delhommeau_float64.cpython-313-darwin.so +0 -0
  28. capytaine/io/bemio.py +14 -2
  29. capytaine/io/mesh_loaders.py +1 -1
  30. capytaine/io/wamit.py +479 -0
  31. capytaine/io/xarray.py +257 -113
  32. capytaine/matrices/linear_solvers.py +1 -1
  33. capytaine/meshes/clipper.py +1 -0
  34. capytaine/meshes/collections.py +11 -1
  35. capytaine/meshes/mesh_like_protocol.py +37 -0
  36. capytaine/meshes/meshes.py +17 -6
  37. capytaine/meshes/symmetric.py +11 -2
  38. capytaine/post_pro/kochin.py +4 -4
  39. capytaine/tools/lists_of_points.py +3 -3
  40. capytaine/tools/prony_decomposition.py +60 -4
  41. capytaine/tools/symbolic_multiplication.py +12 -0
  42. capytaine/tools/timer.py +64 -0
  43. {capytaine-2.2.1.dist-info → capytaine-2.3.dist-info}/METADATA +9 -2
  44. capytaine-2.3.dist-info/RECORD +92 -0
  45. capytaine-2.2.1.dist-info/RECORD +0 -76
  46. {capytaine-2.2.1.dist-info → capytaine-2.3.dist-info}/LICENSE +0 -0
  47. {capytaine-2.2.1.dist-info → capytaine-2.3.dist-info}/WHEEL +0 -0
  48. {capytaine-2.2.1.dist-info → capytaine-2.3.dist-info}/entry_points.txt +0 -0
Binary file
Binary file
Binary file
capytaine/__about__.py CHANGED
@@ -5,7 +5,7 @@ __all__ = ["__title__", "__description__", "__version__", "__author__", "__uri__
5
5
  __title__ = "capytaine"
6
6
  __description__ = """Python BEM solver for linear potential flow, based on Nemoh"""
7
7
 
8
- __version__ = "2.2.1"
8
+ __version__ = "2.3"
9
9
 
10
10
  __author__ = "Matthieu Ancellin"
11
11
  __uri__ = "https://github.com/capytaine/capytaine"
capytaine/__init__.py CHANGED
@@ -24,11 +24,12 @@ from capytaine.bem.problems_and_results import RadiationProblem, DiffractionProb
24
24
  from capytaine.bem.solver import BEMSolver
25
25
  from capytaine.bem.engines import BasicMatrixEngine, HierarchicalToeplitzMatrixEngine, HierarchicalPrecondMatrixEngine
26
26
  from capytaine.green_functions.delhommeau import Delhommeau, XieDelhommeau
27
+ from capytaine.green_functions.hams import LiangWuNoblesseGF, FinGreen3D, HAMS_GF
27
28
 
28
29
  from capytaine.post_pro.free_surfaces import FreeSurface
29
30
 
30
31
  from capytaine.io.mesh_loaders import load_mesh
31
- from capytaine.io.xarray import assemble_dataset
32
+ from capytaine.io.xarray import assemble_dataframe, assemble_dataset, assemble_matrices, export_dataset
32
33
 
33
34
  from capytaine.ui.rich import set_logging
34
35
 
@@ -22,6 +22,9 @@ def airy_waves_potential(points, pb):
22
22
  """
23
23
  points, output_shape = _normalize_points(points)
24
24
 
25
+ if float(pb.wavenumber) == 0.0 or float(pb.wavenumber) == np.inf:
26
+ return np.nan * np.ones(output_shape)
27
+
25
28
  x, y, z = points.T
26
29
  k = pb.wavenumber
27
30
  h = pb.water_depth
@@ -54,9 +57,11 @@ def airy_waves_velocity(points, pb):
54
57
  array of shape (3) or (N x 3)
55
58
  the velocity vectors
56
59
  """
57
-
58
60
  points, output_shape = _normalize_points(points)
59
61
 
62
+ if float(pb.wavenumber) == 0.0 or float(pb.wavenumber) == np.inf:
63
+ return np.nan * np.ones((*output_shape, 3))
64
+
60
65
  x, y, z = points.T
61
66
  k = pb.wavenumber
62
67
  h = pb.water_depth
@@ -79,7 +84,7 @@ def airy_waves_velocity(points, pb):
79
84
 
80
85
 
81
86
  def airy_waves_pressure(points, pb):
82
- return 1j * pb.omega * pb.rho * airy_waves_potential(points, pb)
87
+ return 1j * float(pb.omega) * pb.rho * airy_waves_potential(points, pb)
83
88
 
84
89
 
85
90
  def froude_krylov_force(pb):
@@ -24,8 +24,8 @@ _default_parameters = {'rho': 1000.0, 'g': 9.81, 'omega': 1.0,
24
24
  class LinearPotentialFlowProblem:
25
25
  """General class of a potential flow problem.
26
26
 
27
- At most one of the following parameter must be provided: omega, period, wavenumber or wavelength.
28
- Internally only omega is stored, hence setting another parameter can lead to small rounding errors.
27
+ At most one of the following parameters must be provided:
28
+ omega, freq, period, wavenumber or wavelength.
29
29
 
30
30
  Parameters
31
31
  ----------
@@ -39,6 +39,8 @@ class LinearPotentialFlowProblem:
39
39
  The position of the sea bottom (deprecated: please prefer setting water_depth)
40
40
  omega: float, optional
41
41
  The angular frequency of the waves in rad/s
42
+ freq: float, optional
43
+ The frequency of the waves in Hz
42
44
  period: float, optional
43
45
  The period of the waves in s
44
46
  wavenumber: float, optional
@@ -59,7 +61,7 @@ class LinearPotentialFlowProblem:
59
61
  body=None,
60
62
  free_surface=_default_parameters['free_surface'],
61
63
  water_depth=None, sea_bottom=None,
62
- omega=None, period=None, wavenumber=None, wavelength=None,
64
+ omega=None, freq=None, period=None, wavenumber=None, wavelength=None,
63
65
  forward_speed=_default_parameters['forward_speed'],
64
66
  rho=_default_parameters['rho'],
65
67
  g=_default_parameters['g'],
@@ -76,14 +78,14 @@ class LinearPotentialFlowProblem:
76
78
  self.boundary_condition = boundary_condition
77
79
 
78
80
  self.water_depth = _get_water_depth(free_surface, water_depth, sea_bottom, default_water_depth=_default_parameters["water_depth"])
79
- self.omega, self.period, self.wavenumber, self.wavelength, self.provided_freq_type = \
80
- self._get_frequencies(omega=omega, period=period, wavenumber=wavenumber, wavelength=wavelength)
81
+ self.omega, self.freq, self.period, self.wavenumber, self.wavelength, self.provided_freq_type = \
82
+ self._get_frequencies(omega=omega, freq=freq, period=period, wavenumber=wavenumber, wavelength=wavelength)
81
83
 
82
84
  self._check_data()
83
85
 
84
86
  if forward_speed != 0.0:
85
87
  dopplered_omega = self.omega - self.wavenumber*self.forward_speed*np.cos(self.wave_direction)
86
- self.encounter_omega, self.encounter_period, self.encounter_wavenumber, self.encounter_wavelength, _ = \
88
+ self.encounter_omega, self.encounter_freq, self.encounter_period, self.encounter_wavenumber, self.encounter_wavelength, _ = \
87
89
  self._get_frequencies(omega=abs(dopplered_omega))
88
90
 
89
91
  if dopplered_omega >= 0.0:
@@ -92,18 +94,19 @@ class LinearPotentialFlowProblem:
92
94
  self.encounter_wave_direction = self.wave_direction + np.pi
93
95
  else:
94
96
  self.encounter_omega = self.omega
97
+ self.encounter_freq = self.freq
95
98
  self.encounter_period = self.period
96
99
  self.encounter_wavenumber = self.wavenumber
97
100
  self.encounter_wavelength = self.wavelength
98
101
  self.encounter_wave_direction = self.wave_direction
99
102
 
100
103
 
101
- def _get_frequencies(self, *, omega=None, period=None, wavenumber=None, wavelength=None):
102
- frequency_data = dict(omega=omega, period=period, wavenumber=wavenumber, wavelength=wavelength)
103
- nb_provided_frequency_data = 4 - list(frequency_data.values()).count(None)
104
+ def _get_frequencies(self, *, omega=None, freq=None, period=None, wavenumber=None, wavelength=None):
105
+ frequency_data = dict(omega=omega, freq=freq, period=period, wavenumber=wavenumber, wavelength=wavelength)
106
+ nb_provided_frequency_data = len(frequency_data) - list(frequency_data.values()).count(None)
104
107
 
105
108
  if nb_provided_frequency_data > 1:
106
- raise ValueError("Settings a problem requires at most one of the following: omega (angular frequency) OR period OR wavenumber OR wavelength.\n"
109
+ raise ValueError("Settings a problem requires at most one of the following: omega (angular frequency) OR freq (in Hz) OR period OR wavenumber OR wavelength.\n"
107
110
  "Received {} of them: {}".format(nb_provided_frequency_data, {k: v for k, v in frequency_data.items() if v is not None}))
108
111
 
109
112
  if nb_provided_frequency_data == 0:
@@ -112,27 +115,35 @@ class LinearPotentialFlowProblem:
112
115
  else:
113
116
  provided_freq_type = [k for (k, v) in frequency_data.items() if v is not None][0]
114
117
 
115
- if ((float(frequency_data[provided_freq_type]) == 0.0 and provided_freq_type in {'omega', 'wavenumber'})
118
+ if ((float(frequency_data[provided_freq_type]) == 0.0 and provided_freq_type in {'omega', 'freq', 'wavenumber'})
116
119
  or (float(frequency_data[provided_freq_type]) == np.inf and provided_freq_type in {'period', 'wavelength'})):
117
120
  omega = SymbolicMultiplication("0")
121
+ freq = SymbolicMultiplication("0")
118
122
  wavenumber = SymbolicMultiplication("0")
119
123
  period = SymbolicMultiplication("∞")
120
124
  wavelength = SymbolicMultiplication("∞")
121
125
  elif ((float(frequency_data[provided_freq_type]) == 0.0 and provided_freq_type in {'period', 'wavelength'})
122
- or (float(frequency_data[provided_freq_type]) == np.inf and provided_freq_type in {'omega', 'wavenumber'})):
126
+ or (float(frequency_data[provided_freq_type]) == np.inf and provided_freq_type in {'omega', 'freq', 'wavenumber'})):
123
127
  omega = SymbolicMultiplication("∞")
128
+ freq = SymbolicMultiplication("∞")
124
129
  wavenumber = SymbolicMultiplication("∞")
125
130
  period = SymbolicMultiplication("0")
126
131
  wavelength = SymbolicMultiplication("0")
127
132
  else:
128
133
 
129
- if provided_freq_type in {'omega', 'period'}:
134
+ if provided_freq_type in {'omega', 'freq', 'period'}:
130
135
  if provided_freq_type == 'omega':
131
136
  omega = frequency_data['omega']
132
137
  period = 2*np.pi/omega
138
+ freq = omega/2/np.pi
139
+ elif provided_freq_type == 'freq':
140
+ freq = frequency_data['freq']
141
+ omega = 2*np.pi*freq
142
+ period = 1/freq
133
143
  else: # provided_freq_type is 'period'
134
144
  period = frequency_data['period']
135
145
  omega = 2*np.pi/period
146
+ freq = 1/period
136
147
 
137
148
  if self.water_depth == np.inf:
138
149
  wavenumber = omega**2/self.g
@@ -150,8 +161,9 @@ class LinearPotentialFlowProblem:
150
161
 
151
162
  omega = np.sqrt(self.g*wavenumber*np.tanh(wavenumber*self.water_depth))
152
163
  period = 2*np.pi/omega
164
+ freq = 1/period
153
165
 
154
- return omega, period, wavenumber, wavelength, provided_freq_type
166
+ return omega, freq, period, wavenumber, wavelength, provided_freq_type
155
167
 
156
168
  def _check_data(self):
157
169
  """Sanity checks on the data."""
@@ -168,7 +180,6 @@ class LinearPotentialFlowProblem:
168
180
  "If you were actually giving an angle in radians, use the modulo operator to give a value between -2π and 2π to disable this warning.")
169
181
 
170
182
  if self.free_surface == np.inf and self.water_depth != np.inf:
171
-
172
183
  raise NotImplementedError(
173
184
  "Problems with a sea bottom but no free surface have not been implemented."
174
185
  )
@@ -177,11 +188,6 @@ class LinearPotentialFlowProblem:
177
188
  raise ValueError("`water_depth` should be strictly positive (provided water depth: {self.water_depth}).")
178
189
 
179
190
  if float(self.omega) in {0, np.inf}:
180
- if self.water_depth != np.inf:
181
- LOG.warning(
182
- f"Default Green function allows for {self.provided_freq_type}={float(self.__getattribute__(self.provided_freq_type))} only for infinite depth (provided water depth: {self.water_depth})."
183
- )
184
-
185
191
  if self.forward_speed != 0.0:
186
192
  raise NotImplementedError(
187
193
  f"omega={float(self.omega)} is only implemented without forward speed (provided forward speed: {self.forward_speed})."
@@ -193,6 +199,8 @@ class LinearPotentialFlowProblem:
193
199
  or len(self.body.mesh.faces) == 0):
194
200
  raise ValueError(f"The mesh of the body {self.body.__short_str__()} is empty.")
195
201
 
202
+ self.body._check_dofs_shape_consistency()
203
+
196
204
  panels_above_fs = self.body.mesh.faces_centers[:, 2] >= self.free_surface + 1e-8
197
205
  panels_below_sb = self.body.mesh.faces_centers[:, 2] <= -self.water_depth
198
206
  if (any(panels_above_fs) or any(panels_below_sb)):
@@ -231,8 +239,11 @@ class LinearPotentialFlowProblem:
231
239
  def _asdict(self):
232
240
  return {"body_name": self.body_name,
233
241
  "water_depth": self.water_depth,
242
+ "free_surface": self.free_surface,
234
243
  "omega": float(self.omega),
244
+ "freq": float(self.freq),
235
245
  "encounter_omega": float(self.encounter_omega),
246
+ "encounter_freq": float(self.encounter_freq),
236
247
  "period": float(self.period),
237
248
  "wavelength": float(self.wavelength),
238
249
  "wavenumber": float(self.wavenumber),
@@ -322,8 +333,11 @@ class LinearPotentialFlowProblem:
322
333
  # TODO: let the user choose the influenced dofs
323
334
  return self.body.dofs if self.body is not None else set()
324
335
 
325
- def make_results_container(self):
326
- return LinearPotentialFlowResult(self)
336
+ def make_results_container(self, *args, **kwargs):
337
+ return LinearPotentialFlowResult(self, *args, **kwargs)
338
+
339
+ def make_failed_results_container(self, *args, **kwargs):
340
+ return FailedLinearPotentialFlowResult(self, *args, **kwargs)
327
341
 
328
342
 
329
343
  class DiffractionProblem(LinearPotentialFlowProblem):
@@ -334,19 +348,16 @@ class DiffractionProblem(LinearPotentialFlowProblem):
334
348
  body=None,
335
349
  free_surface=_default_parameters['free_surface'],
336
350
  water_depth=None, sea_bottom=None,
337
- omega=None, period=None, wavenumber=None, wavelength=None,
351
+ omega=None, freq=None, period=None, wavenumber=None, wavelength=None,
338
352
  forward_speed=_default_parameters['forward_speed'],
339
353
  rho=_default_parameters['rho'],
340
354
  g=_default_parameters['g'],
341
355
  wave_direction=_default_parameters['wave_direction']):
342
356
 
343
357
  super().__init__(body=body, free_surface=free_surface, water_depth=water_depth, sea_bottom=sea_bottom,
344
- omega=omega, period=period, wavenumber=wavenumber, wavelength=wavelength, wave_direction=wave_direction,
358
+ omega=omega, freq=freq, period=period, wavenumber=wavenumber, wavelength=wavelength, wave_direction=wave_direction,
345
359
  forward_speed=forward_speed, rho=rho, g=g)
346
360
 
347
- if float(self.omega) in {0.0, np.inf}:
348
- raise NotImplementedError("DiffractionProblem does not support zero or infinite frequency.")
349
-
350
361
  if self.body is not None:
351
362
 
352
363
  self.boundary_condition = -(
@@ -371,6 +382,9 @@ class DiffractionProblem(LinearPotentialFlowProblem):
371
382
  def make_results_container(self, *args, **kwargs):
372
383
  return DiffractionResult(self, *args, **kwargs)
373
384
 
385
+ def make_failed_results_container(self, *args, **kwargs):
386
+ return FailedDiffractionResult(self, *args, **kwargs)
387
+
374
388
 
375
389
  class RadiationProblem(LinearPotentialFlowProblem):
376
390
  """Particular LinearPotentialFlowProblem whose boundary conditions have
@@ -379,7 +393,7 @@ class RadiationProblem(LinearPotentialFlowProblem):
379
393
  def __init__(self, *, body=None,
380
394
  free_surface=_default_parameters['free_surface'],
381
395
  water_depth=None, sea_bottom=None,
382
- omega=None, period=None, wavenumber=None, wavelength=None,
396
+ omega=None, freq=None, period=None, wavenumber=None, wavelength=None,
383
397
  forward_speed=_default_parameters['forward_speed'],
384
398
  wave_direction=_default_parameters['wave_direction'],
385
399
  rho=_default_parameters['rho'],
@@ -389,7 +403,7 @@ class RadiationProblem(LinearPotentialFlowProblem):
389
403
  self.radiating_dof = radiating_dof
390
404
 
391
405
  super().__init__(body=body, free_surface=free_surface, water_depth=water_depth, sea_bottom=sea_bottom,
392
- omega=omega, period=period, wavenumber=wavenumber, wavelength=wavelength,
406
+ omega=omega, freq=freq, period=period, wavenumber=wavenumber, wavelength=wavelength,
393
407
  wave_direction=wave_direction, forward_speed=forward_speed, rho=rho, g=g)
394
408
 
395
409
  if self.body is not None:
@@ -407,7 +421,11 @@ class RadiationProblem(LinearPotentialFlowProblem):
407
421
 
408
422
  dof = self.body.dofs[self.radiating_dof]
409
423
 
410
- self.boundary_condition = -1j * self.encounter_omega * np.sum(dof * self.body.mesh.faces_normals, axis=1)
424
+ displacement_on_face = np.sum(dof * self.body.mesh.faces_normals, axis=1) # This is a dot product on each face
425
+ if self.body.lid_mesh is not None:
426
+ displacement_on_face = np.concatenate([displacement_on_face, np.zeros(self.body.lid_mesh.nb_faces)])
427
+
428
+ self.boundary_condition = -1j * self.encounter_omega * displacement_on_face
411
429
 
412
430
  if self.forward_speed != 0.0:
413
431
  if self.radiating_dof.lower() == "pitch":
@@ -424,8 +442,6 @@ class RadiationProblem(LinearPotentialFlowProblem):
424
442
  )
425
443
  self.boundary_condition += self.forward_speed * ddofdx_dot_n
426
444
 
427
- if self.body.lid_mesh is not None:
428
- self.boundary_condition = np.concatenate([self.boundary_condition, np.zeros(self.body.lid_mesh.nb_faces)])
429
445
 
430
446
 
431
447
  def _astuple(self):
@@ -448,6 +464,9 @@ class RadiationProblem(LinearPotentialFlowProblem):
448
464
  def make_results_container(self, *args, **kwargs):
449
465
  return RadiationResult(self, *args, **kwargs)
450
466
 
467
+ def make_failed_results_container(self, *args, **kwargs):
468
+ return FailedRadiationResult(self, *args, **kwargs)
469
+
451
470
 
452
471
  class LinearPotentialFlowResult:
453
472
 
@@ -465,12 +484,14 @@ class LinearPotentialFlowResult:
465
484
  self.body = self.problem.body
466
485
  self.free_surface = self.problem.free_surface
467
486
  self.omega = self.problem.omega
487
+ self.freq = self.problem.freq
468
488
  self.period = self.problem.period
469
489
  self.wavenumber = self.problem.wavenumber
470
490
  self.wavelength = self.problem.wavelength
471
491
  self.forward_speed = self.problem.forward_speed
472
492
  self.wave_direction = self.problem.wave_direction
473
493
  self.encounter_omega = self.problem.encounter_omega
494
+ self.encounter_freq = self.problem.encounter_freq
474
495
  self.encounter_period = self.problem.encounter_period
475
496
  self.encounter_wavenumber = self.problem.encounter_wavenumber
476
497
  self.encounter_wavelength = self.problem.encounter_wavelength
@@ -495,6 +516,13 @@ class LinearPotentialFlowResult:
495
516
  __rich_repr__ = LinearPotentialFlowProblem.__rich_repr__
496
517
 
497
518
 
519
+ class FailedLinearPotentialFlowResult(LinearPotentialFlowResult):
520
+ def __init__(self, problem, exception):
521
+ LinearPotentialFlowResult.__init__(self, problem)
522
+ self.forces = {dof: np.nan + 1j*np.nan for dof in self.influenced_dofs}
523
+ self.exception = exception
524
+
525
+
498
526
  class DiffractionResult(LinearPotentialFlowResult):
499
527
 
500
528
  def __init__(self, problem, *args, **kwargs):
@@ -510,10 +538,18 @@ class DiffractionResult(LinearPotentialFlowResult):
510
538
  return [dict(**params,
511
539
  influenced_dof=dof,
512
540
  diffraction_force=self.forces[dof],
513
- Froude_Krylov_force=FK[dof])
541
+ Froude_Krylov_force=FK[dof],
542
+ kind="DiffractionResult")
514
543
  for dof in self.influenced_dofs]
515
544
 
516
545
 
546
+ class FailedDiffractionResult(DiffractionResult):
547
+ def __init__(self, problem, exception):
548
+ DiffractionResult.__init__(self, problem)
549
+ self.forces = {dof: np.nan for dof in self.influenced_dofs}
550
+ self.exception = exception
551
+
552
+
517
553
  class RadiationResult(LinearPotentialFlowResult):
518
554
 
519
555
  def __init__(self, problem, *args, **kwargs):
@@ -544,5 +580,13 @@ class RadiationResult(LinearPotentialFlowResult):
544
580
  return [dict(params,
545
581
  influenced_dof=dof,
546
582
  added_mass=self.added_mass[dof],
547
- radiation_damping=self.radiation_damping[dof])
583
+ radiation_damping=self.radiation_damping[dof],
584
+ kind="RadiationResult")
548
585
  for dof in self.influenced_dofs]
586
+
587
+
588
+ class FailedRadiationResult(RadiationResult):
589
+ def __init__(self, problem, exception):
590
+ RadiationResult.__init__(self, problem)
591
+ self.forces = {dof: np.nan + 1j*np.nan for dof in self.influenced_dofs}
592
+ self.exception = exception