예제 #1
0
def prepare_for_fd(s):
    """Prepares the model for frequency domain analysis.

    WARNING:
        this is a destructive method. Please run this on a copy of a scene.

    All wave-interaction nodes shall be at the origin of an axis system.
    That axis system shall not have a parent
    That axis systen shall have all dofs set to free

    Raises:
        ValueError if the scene can not be prepared.

    """

    wis = s.nodes_of_type(node_class=WaveInteraction1)

    for w in wis:

        # checks
        assert isinstance(w.parent, Axis), ValueError(
            'Parent of "{}" shall be an axis or derived'.format(w.name))
        assert not np.any(w.parent.fixed), ValueError(
            'Parent of "{}" shall have all its dofs set to free (not fixed)'.
            format(w.name))

        # loads hydrodynamic data
        try:
            if w._hyddb is not None:
                loaddb = True
            else:
                loaddb = False
        except:
            loaddb = True

        if loaddb:
            w._hyddb = Hyddb1()
            w._hyddb.load_from(s.get_resource_path(w.path))

        if np.all(w.offset == (0, 0, 0)):
            continue

        # create a new axis system at the global position and orientation of the node
        glob_position = w.parent.to_glob_position(w.offset)
        glob_rotation = w.parent.global_rotation

        name = s.available_name_like('autocreated_parent_for_{}'.format(
            w.name))

        new_parent = s.new_axis(name,
                                position=glob_position,
                                rotation=glob_rotation,
                                fixed=False)

        w.parent.change_parent_to(new_parent)
        w.parent.fixed = True

        w.parent = new_parent
        w.offset = (0, 0, 0)
예제 #2
0
def test_load_nc():
    hyd = Hyddb1()
    hyd.load_from_capytaine(r"files/capytaine.nc")

    omega = 0.01

    mass = hyd.amass(omega=omega)
    damping = hyd.damping(omega=omega)
    force = hyd.force(omega=omega, wave_direction=90)
예제 #3
0
def export_ofx_yml(s, filename):
    """Convert the scene to a orcaflex .yml file. Only compatible nodes are exported. Make the scene  orcaflex compatible before exporting.

    Visuals of .obj type are supported by orcaflex. These are copied to the same folder as the .yml file

    Args:
        s : Scene
        filename : file to write to (.yml)
    """

    filename = Path(filename) # convert to path

    # filename.parent : folder

    s.sort_nodes_by_dependency()

    buoys = []
    winches = []
    constraints = []
    vessel_types = []
    vessels = []
    Shapes = []
    line_types = []
    lines = []

    for n in s._nodes:

        if isinstance(n, BallastSystem):

            # calculate the inertia
            ixx = 0
            iyy = 0
            izz = 0
            mass = 0
            for tank in n._tanks:
                mass += tank.inertia
                inertia = tank.inertia
                ixx += inertia * (tank.position[1] ** 2 + tank.position[2] ** 2)
                iyy += inertia * (tank.position[0] ** 2 + tank.position[2] ** 2)
                izz += inertia * (tank.position[0] ** 2 + tank.position[1] ** 2)

            ixx = min(ixx, OFX_ZERO_MASS)
            iyy = min(iyy, OFX_ZERO_MASS)
            izz = min(izz, OFX_ZERO_MASS)

            I = [ixx, iyy, izz]
            pos = [*n.position]

            cog = [float(i) for i in n.cog]

            b = {'Name': n.name,
                 'Connection': n.parent.name,
                 'InitialPosition': pos,
                 'Mass': mass,
                 'Volume': 0,
                 'MomentsOfInertia': I,
                 'CentreOfMass': cog
                 }

            buoys.append(b)


        if isinstance(n, (RigidBody, Axis)):

            if isinstance(n, RigidBody):
                mass = max(n.mass, OFX_ZERO_MASS)
                I = (mass * n.inertia_radii ** 2).tolist()
                cog = [*n.cog]

            elif isinstance(n, Axis):
                mass = OFX_ZERO_MASS
                I = [OFX_ZERO_MASS, OFX_ZERO_MASS, OFX_ZERO_MASS]
                cog = [0, 0, 0]


            # check the connection

            pos = [*n.position]
            rot = [*rotation_to_attitude(n.rotation)]

            if not any(n.fixed):
                connection = 'Free'

            elif np.all(n.fixed):
                if n.parent is None:
                    connection = 'Fixed'
                else:
                    connection = n.parent.name

            else:
                # Partially fixed - create constraint

                cname = n.name + ' [fixes]'

                if n.parent is None:
                    connection = 'Fixed'
                else:
                    connection = n.parent.name

                fixes = []
                for f in n.fixed:
                    if f:
                        fixes.append('No')
                    else:
                        fixes.append('Yes')

                c = {'Name': cname,
                     'Connection': connection,
                     'DOFFree': fixes,
                     'InitialPosition': pos,
                     'InitialAttitude': rot,
                     }

                constraints.append(c)

                # set the props for the 6d buoy
                connection = cname
                pos = [0,0,0]
                rot = [0,0,0]

            b = {'Name': n.name,
                 'Connection': connection,
                 'InitialPosition': pos,
                 'InitialAttitude': rot,
                 'Mass': mass,
                 'Volume': 0,
                 'MomentsOfInertia': I,
                 'CentreOfMass': cog
                 }                       # create the basic buoy, but do not add it yet as some of the properties may be
                                         # overwritten by the vessel that we may create now

            ## Vessels --------------
            #
            # If one of the children of this Axis is a HydSpring (linear hydrostatics node), then
            #  1. Look for a waveinteraction1 node as well
            #
            # if both are found then
            # 1. create a vessel type
            # 2. create a vessel without any mass
            # 3. place the buoy that we just created on the vessel

            children = s.nodes_with_parent(n)

            hyd_spring = None
            hyd_db = None

            for child in children:
                cn = s[child]
                if isinstance(cn, HydSpring):
                    hyd_spring = cn
                if isinstance(cn, WaveInteraction1):
                    hyd_db = cn

            if hyd_spring is not None:
                # create a vessel type

                vt = {'Name': n.name + hyd_spring.name,
                      'Length':1,
                      # conventions
                      'WavesReferredToBy':'frequency (rad/s)',
                      'RAOPhaseConvention':'lags',
                      'RAOPhaseUnitsConvention':'radians'
                      }

                # Stiffness, Added mass, damping
                #
                # The Reference-origin defines where all of these forces are applied
                # so it needs to be the origin of the hydrodynamic data, if we have any

                ref_origin = (0.,0.,0.)
                if hyd_db is not None:
                    ref_origin = hyd_db.offset

                # calculate the stiffness matrix relative to this point

                k = np.zeros((3,3))

                # Heave and heave coupling
                k[0,0] = hyd_spring.kHeave
                k[0,1] = -hyd_spring.kHeave * (hyd_spring.cob[1] + hyd_spring.COFY - ref_origin[1]) # heave-roll
                k[0,2] = -hyd_spring.kHeave * (hyd_spring.cob[0] + hyd_spring.COFX - ref_origin[0])  # heave-pitch
                k[1,0] = k[0,1]
                k[2,0] = k[0,2]

                # BML and BMT
                k[1,1] = hyd_spring.displacement_kN * (hyd_spring.BMT - ref_origin[2] + hyd_spring.cob[2]) # g * rho * disp * BMt
                k[2, 2] = hyd_spring.displacement_kN * (hyd_spring.BML - ref_origin[2] + hyd_spring.cob[2]) # g * rho * disp * BMt


                d = {'Name':'Draught1',
                     'Mass':1e-6,
                     'MomentOfInertiaTensorX': [0.001,0,0],
                     'MomentOfInertiaTensorY': [0,0.001,0],
                     'MomentOfInertiaTensorZ': [0,0,0.001],
                     'CentreOfGravityX': 0,
                     'CentreOfGravityY': 0,
                     'CentreOfGravityZ': 0,
                     'CentreOfBuoyancyX' : hyd_spring.cob[0],
                     'CentreOfBuoyancyY' : hyd_spring.cob[1],
                     'CentreOfBuoyancyZ' : hyd_spring.cob[2],

                     # Stiffness, added mass, damping
                     'StiffnessInertiaDampingRefOriginx':ref_origin[0],
                     'StiffnessInertiaDampingRefOriginy':ref_origin[1],
                     'StiffnessInertiaDampingRefOriginz':ref_origin[2],
                     'HydrostaticReferenceOriginDatumPositionz':n.to_glob_position(ref_origin)[2],
                     'HydrostaticReferenceOriginDatumOrientationx':0,
                     'HydrostaticReferenceOriginDatumOrientationy':0,
                     'DisplacedVolume': hyd_spring.displacement_kN / (RHO * G),
                     'HydrostaticStiffnessz' : k[:,0].tolist(),
                     'HydrostaticStiffnessRx':k[:,1].tolist(),
                     'HydrostaticStiffnessRy': k[:,2].tolist(),

                     # other damping settings

                     # 'OtherDampingCalculatedFrom',  # Add once version > 10.2d

                     'OtherDampingOriginx':ref_origin[0],
                     'OtherDampingOriginy':ref_origin[1],
                     'OtherDampingOriginz':ref_origin[2],
                     'OtherDampingLinearCoeffx':0,
                     'OtherDampingLinearCoeffy':0,
                     'OtherDampingLinearCoeffz':0,
                     'OtherDampingLinearCoeffRx':0,
                     'OtherDampingLinearCoeffRy':0,
                     'OtherDampingLinearCoeffRz':0,
                     'OtherDampingQuadraticCoeffx':0,
                     'OtherDampingQuadraticCoeffy':0,
                     'OtherDampingQuadraticCoeffz':0,
                     'OtherDampingQuadraticCoeffRx':0,
                     'OtherDampingQuadraticCoeffRy':0,
                     'OtherDampingQuadraticCoeffRz':0

                     }

                # Export hydrodynamics, if any
                if hyd_db is not None:

                    # Export
                    # Wave-forces (Force RAOs)
                    # Damping
                    # Added mass

                    LoadRAOs = {'RAOOriginX': ref_origin[0],  # TODO: These values do not seem to be loaded into OFX
                            'RAOOriginY': ref_origin[1],
                            'RAOOriginZ': ref_origin[2]}

                    # load the database
                    from mafredo.hyddb1 import Hyddb1
                    database = s.get_resource_path(hyd_db.path)
                    db = Hyddb1()
                    db.load_from(database)

                    # get the available headings
                    a_headings = db.force_rao(0)._data['wave_direction'].values
                    a_frequencies = db.frequencies

                    rao_mode = []
                    for i in range(6):
                        rao_mode.append(db.force_rao(i))

                    RAOs = []

                    for heading in a_headings:

                        rao = {'RAODirection':float(heading)}

                        RAOPeriodOrFrequency = []
                        RAOSurgeAmp = []
                        RAOSurgePhase = []
                        RAOSwayAmp = []
                        RAOSwayPhase = []
                        RAOHeaveAmp = []
                        RAOHeavePhase = []
                        RAORollAmp = []
                        RAORollPhase = []
                        RAOPitchAmp = []
                        RAOPitchPhase = []
                        RAOYawAmp = []
                        RAOYawPhase = []

                        for frequency in a_frequencies:
                            RAOPeriodOrFrequency.append(float(frequency))

                            r = rao_mode[0].get_value(wave_direction=heading, omega = frequency)
                            RAOSurgeAmp.append(float(np.abs(r)))
                            RAOSurgePhase.append(float(np.angle(r)))

                            r = rao_mode[1].get_value(wave_direction=heading, omega=frequency)
                            RAOSwayAmp.append(float(np.abs(r)))
                            RAOSwayPhase.append(float(np.angle(r)))

                            r = rao_mode[2].get_value(wave_direction=heading, omega=frequency)
                            RAOHeaveAmp.append(float(np.abs(r)))
                            RAOHeavePhase.append(float(np.angle(r)))

                            r = rao_mode[3].get_value(wave_direction=heading, omega=frequency)
                            RAORollAmp.append(float(np.abs(r)))
                            RAORollPhase.append(float(np.angle(r)))

                            r = rao_mode[4].get_value(wave_direction=heading, omega=frequency)
                            RAOPitchAmp.append(float(np.abs(r)))
                            RAOPitchPhase.append(float(np.angle(r)))

                            r = rao_mode[5].get_value(wave_direction=heading, omega=frequency)
                            RAOYawAmp.append(float(np.abs(r)))
                            RAOYawPhase.append(float(np.angle(r)))

                        rao['RAOPeriodOrFrequency'] = RAOPeriodOrFrequency
                        rao['RAOSurgeAmp'] = RAOSurgeAmp
                        rao['RAOSurgePhase'] = RAOSurgePhase
                        rao['RAOSwayAmp'] = RAOSwayAmp
                        rao['RAOSwayPhase'] = RAOSwayPhase
                        rao['RAOHeaveAmp'] = RAOHeaveAmp
                        rao['RAOHeavePhase'] = RAOHeavePhase
                        rao['RAORollAmp'] = RAORollAmp
                        rao['RAORollPhase'] = RAORollPhase
                        rao['RAOPitchAmp'] = RAOPitchAmp
                        rao['RAOPitchPhase'] = RAOPitchPhase
                        rao['RAOYawAmp'] = RAOYawAmp
                        rao['RAOYawPhase'] = RAOYawPhase
                        RAOs.append(rao)

                    LoadRAOs['RAOs'] = RAOs
                    d['LoadRAOs'] = LoadRAOs

                    # Added mass and Damping
                    FrequencyDependentAddedMassAndDamping = []
                    for frequency in a_frequencies:
                        entry = {'AMDPeriodOrFrequency': float(frequency)}
                        B = db.damping(frequency)
                        A = db.amass(frequency)

                        # Make symmetric (else Orcaflex will not read the yml)

                        def make_orcaflex_happy(mat):
                            mat = 0.5 * mat + 0.5 * mat.transpose()
                            R = np.zeros((6, 6))
                            R[0, 0] = mat[0, 0]
                            R[1, 1] = mat[1, 1]
                            R[2, 2] = mat[2, 2]
                            R[3, 3] = mat[3, 3]
                            R[4, 4] = mat[4, 4]
                            R[5, 5] = mat[5, 5]

                            # oA[0,2] = 7   # error
                            R[2, 0] = mat[2, 0]

                            R[0, 4] = mat[0, 4]
                            R[4, 0] = mat[0, 4]

                            R[1, 3] = mat[1, 3]  # need both
                            R[3, 1] = mat[1, 3]

                            R[1, 5] = mat[1, 5]  # need both
                            R[5, 1] = mat[1, 5]

                            R[5, 3] = mat[3, 5]
                            return R

                        oA = make_orcaflex_happy(A)
                        oB = make_orcaflex_happy(B)

                        entry['AddedMassMatrixX'] = oA[0].tolist()
                        entry['AddedMassMatrixY'] = oA[1].tolist()
                        entry['AddedMassMatrixZ'] = oA[2].tolist()
                        entry['AddedMassMatrixRx'] = oA[3].tolist()
                        entry['AddedMassMatrixRy'] = oA[4].tolist()
                        entry['AddedMassMatrixRz'] = oA[5].tolist()

                        entry['DampingX'] = oB[0].tolist()
                        entry['DampingY'] = oB[1].tolist()
                        entry['DampingZ'] = oB[2].tolist()
                        entry['DampingRx'] = oB[3].tolist()
                        entry['DampingRy'] = oB[4].tolist()
                        entry['DampingRz'] = oB[5].tolist()

                        FrequencyDependentAddedMassAndDamping.append(entry)

                    d['AMDMethod'] = 'Frequency Dependent'
                    d['FrequencyDependentAddedMassAndDamping'] = FrequencyDependentAddedMassAndDamping

                vt['Draughts'] = [d]  # draughts is a list! Even though we only use one.

                # Create a vessel

                v = {'Name':n.name + 'Vessel',
                     'VesselType':n.name + hyd_spring.name,
                     'Length':1,
                     'InitialPosition': pos,
                     'InitialHeel': float(n.heel),
                     'InitialTrim': float(n.trim),
                     'InitialHeading': float(n.heading),
                     'IncludedInStatics': '6 DOF',
                     'PrimaryMotion': 'Calculated (6 DOF)',
                     'SuperimposedMotion': 'None',
                     'PrimaryMotionIsTreatedAs': 'Wave frequency',
                     'IncludeWaveLoad1stOrder': 'Yes',
                     'IncludeAddedMassAndDamping': 'Yes',
                     'IncludeOtherDamping': 'Yes'}

                # Modify the buoy to be on the vessel
                b['InitialPosition'] = [0,0,0]
                b['InitialAttitude'] = [0,0,0]
                b['Connection'] = v['Name']

                vessel_types.append(vt)
                vessels.append(v)




            # Done with the vessel stuff, back to the 6D buoy that we were exporting


            buoys.append(b)




        if isinstance(n, Cable):

            connection = []
            connectionX = []
            connectionY = []
            connectionZ = []

            for c in n.connections:
                # either a point or a circle
                # for now only points are supported

                if isinstance(c, Circle):
                    raise ValueError('Circles not yet supported')

                if c.parent is None:
                    connection.append('Fixed')
                else:
                    connection.append(c.parent.name)
                connectionX.append(c.position[0])
                connectionY.append(c.position[1])
                connectionZ.append(c.position[2])

            w = { 'Name': n.name,
                  'Connection': connection,
                  'ConnectionX': connectionX,
                  'ConnectionY': connectionY,
                  'ConnectionZ': connectionZ,
                  'Stiffness': n.EA,
                  'NumberOfConnections': len(n.connections),
                  'WinchControlType': 'By Stage',
                  'StageMode': ['Specified Length','Specified Payout','Specified Payout'],
                  'StageValue': [n.length,0,0]
                  }

            winches.append(w)

        if isinstance(n, Visual):

            visualfile = s.get_resource_path(n.path)

            if 'obj' in visualfile.suffix:

                # copy the .obj to the destination folder such that we have all required files in one place

                copy_from = visualfile
                copy_to = filename.parent / visualfile.name

                if copy_from == copy_to:
                    pass
                else:
                    copyfile(visualfile, filename.parent / visualfile.name)
                    print(f'created {filename.parent / visualfile.name}')

                shape = { 'Name': n.name,
                      'Connection':n.parent.name,
                      'ShapeType':'Drawing',
                      'Shape' : 'Block',
                      'OriginX' : 0 ,
                      'OriginY' : 0 ,
                      'OriginZ' : 0 ,
                      'Rotation1' : 0 ,
                      'Rotation2' : 0 ,
                      'Rotation3' : 0 ,
                      'OutsidePenColour' : 55551,  # $00D8FF
                      'ShadedDrawingFileName' : visualfile.name,
                      'ShadedDrawingMirrorInPlane' : 'XZ plane' ,
                      'ShadedDrawingRotation1' : 0 ,
                      'ShadedDrawingRotation2' : 90 ,
                      'ShadedDrawingRotation3' : -90 }

                Shapes.append(shape)

            else:
                warn(f'Only .obj files can be used in orcaflex, not exporting visual "{n.name}"')

        if isinstance(n, Beam):
            # line-type
            typename = f"LT_for_{n.name}"
            mass_per_length = n.mass / n.L
            if mass_per_length < OFX_SMALL:
                print(f'Mass per length for {n.name} set to {OFX_SMALL}')
                mass_per_length = OFX_SMALL

            lt = {'Name' : typename,
                  'OD' : OFX_SMALL,
                  'ID' : 0,
                  'MassPerUnitLength' : mass_per_length,
                  'EIx': n.EIy,
                  'EIy' : n.EIz,
                  'GJ': n.GIp,
                  'CompressionIsLimited': yesno(n.tension_only),
                  'EA': n.EA }
            line_types.append(lt)

            line = OrderedDict({'Name':n.name,
                    'EndAConnection':n.nodeA.name,
                    'EndAX': 0,
                    'EndAY': 0,
                    'EndAZ': 0,
                    'EndAAzimuth' : 0,
                    'EndADeclination' : 90,
                    'EndBConnection': n.nodeB.name,
                    'EndBX': 0,
                    'EndBY': 0,
                    'EndBZ': 0,
                    'EndBAzimuth': 0,
                    'EndBDeclination': 90,
                    'EndAxBendingStiffness' : 'Infinity',
                    'EndBxBendingStiffness' : 'Infinity',
                    'EndAyBendingStiffness' : 'Infinity',
                    'EndByBendingStiffness' : 'Infinity',
                    'NumberOfSections':1,
                    'LineType[1]' : typename,
                    'Length[1]' : n.L,
                    'TargetSegmentLength[1]' : '~',
                    'StaticsStep1':'User specified'
                    })

            do_torsion = n.GIp > 0

            if do_torsion:
                line['IncludeTorsion'] = 'Yes'
                line['EndATwistingStiffness'] = 'Infinity'
                line['EndBTwistingStiffness'] = 'Infinity'
                line['StartingShapeOrientationsSpecified'] = 'Yes'

            line['NumberOfSegments[1]'] = int(n.n_segments)

            pos = n.global_positions
            xx = pos[:,0]
            yy = pos[:,1]
            zz = pos[:,2]
            line['StartingShapeX'] = xx.tolist()
            line['StartingShapeY'] = yy.tolist()
            line['StartingShapeZ'] = zz.tolist()

            if do_torsion:
                rot = n.global_orientations
                rx = []
                ry = []
                rz = []
                for r in rot:
                    azdecgam = rotvec_to_line_node_axis_az_dec_gam(r)
                    rx.append(azdecgam[0])
                    ry.append(azdecgam[1])
                    rz.append(azdecgam[2])
                line['StartingShapeAzm'] = rx
                line['StartingShapeDec'] = ry
                line['StartingShapeGamma'] = rz

            lines.append(line)





    # Write the yml

    data = dict()

    if buoys:
        data['6DBuoys'] = buoys
    if winches:
        data['Winches'] = winches
    if constraints:
        data['Constraints'] = constraints
    if vessel_types:
        data['VesselTypes'] = vessel_types
    if vessels:
        data['Vessels'] = vessels
    if Shapes:
        data['Shapes'] = Shapes
    if line_types:
        data['LineTypes'] = line_types
    if lines:
        data['Lines'] = lines

    from yaml import CDumper as Dumper

    def dict_representer(dumper, data):
        return dumper.represent_dict(data.items())

    Dumper.add_representer(OrderedDict, dict_representer)

    s = yaml.dump(data, explicit_start=True, Dumper=Dumper)

    with open(filename,'w') as f:
        f.write(s)
        print(f'created {filename}')
예제 #4
0
def test_extrapolate_on_damping_dir():
    hyd = Hyddb1()
    hyd.load_from_capytaine(r"files/capytaine.nc")
    hyd.add_frequency(0)
    print(hyd.damping(0))
예제 #5
0
def test_extrapolate_on_force_dir():
    hyd = Hyddb1()
    hyd.load_from_capytaine(r"files/capytaine.nc")
    hyd.add_direction(17)
    print(hyd.amass(17))
예제 #6
0
def test_extrapolate_on_force():
    hyd = Hyddb1()
    hyd.load_from_capytaine(r"files/capytaine.nc")
    hyd.add_frequency(0)
    assert not np.any(np.isnan(hyd.force(0, 90)))
예제 #7
0
def test_extrapolate_on_addedmass():
    hyd = Hyddb1()
    hyd.load_from_capytaine(r"files/capytaine.nc")
    hyd.add_frequencies([0])

    assert not np.any(np.isnan(hyd.amass(0)))