/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOUSE. See the GNU
 * General Public License for more details.
 *
 * You should have recieved a copy of the GNU General Public License
 * along with this program; if not write to the Free Software
 * Foundation, inc., 59 Temple Place, Suite 330, Boston MA 02111-1307
 * USA
 */

package run.elements;

import run.*;

import java.util.Vector;

import jp.lang.DistributedRuntime;


/**
 * The BeamSpring element is a spring with beam properties. The element accept
 * both axial and torsional spring properties and is useful to model
 * spotwelds. It can also be used to model joints by releasing some degrees of
 * freedom. The elment requires a local coordinate system to be defined by
 * using other nodes as directional guidance.
 */
public class Beam_Spring_2 extends Element {
    private static int DISABLED = 0;
    private static int BASIC = 1;
    private double cross_section_area;
    private Node control_node;
    private Jama.Matrix local_coordinate_system;
    private double length;
    private Jama.Matrix global_force;
    private Jama.Matrix global_moment;
    private Jama.Matrix displacement;
    private Jama.Matrix velocity;
    private Jama.Matrix force;
    private Jama.Matrix m;
    private Jama.Matrix global_m;
    private Jama.Matrix temp;
    private double factor;
    private double diameter;
    private boolean Nodes_are_set;
    private boolean Factor_is_set;
    private boolean Material_is_set;
    private boolean D_is_set;
    private Contact_Line internal_contact_element;
    private int Contact;
    private Material material;

    /**
     * The constructor of this element.
     */
    public Beam_Spring_2() {
        super();
        type = new String("BEAM_STRING_2");
        force = new Jama.Matrix(6, 1);
        displacement = new Jama.Matrix(6, 1);
        velocity = new Jama.Matrix(6, 1);
        global_force = new Jama.Matrix(3, 1);
        global_moment = new Jama.Matrix(3, 1);
        m = new Jama.Matrix(3, 1);
        global_m = new Jama.Matrix(3, 1);
        temp = new Jama.Matrix(6, 1);
        local_coordinate_system = new Jama.Matrix(3, 3);
        processed = false;
        node = new Node[2];
        
        // Disable contact by default
        Contact = DISABLED;
    }

    /**
     * This metod calculates the elements contribution regarding mass and
     * rotational interta to the nodes. Since this spring is concidered
     * massless, the contribution here is neglected.
     */
    public void assembleMassMatrix()
        throws Error
    {
        /* Now, set up the local coordinate system for the element.
           The local x-axis will run from node 1 to node 2. The local y-axis will be in the plane given by
           the local x-axis and the vector from node 1 to node 3.
           The local z-axis will be orthogonal to the same plane.
         */
        this.updateLocalCoordinateSystem();

        /*
           Update the contact element if it is enabled
         */
        if (Contact == BASIC) {
            internal_contact_element.assembleMassMatrix();
        }
    }

    /**
     * If contact is enabled, the contact forces are calculated here.
     */
    public void calculateContactForces() {
        // Use the internal contact element to calculate the contact forces.
        if (Contact == BASIC) {
            internal_contact_element.calculateContactForces();
        }
    }

    /**
     * Here, any external force contribution such as pressure is calculated,
     * but on this element there is no such feature. The only one to concider
     * is the internal contact element, should it be enabled.
     *
     * @param currtime The current time.
     */
    public void calculateExternalForces(double currtime) {
        if (Contact == BASIC) {
            internal_contact_element.calculateExternalForces(currtime);
        }
    }

    /**
     * This calculates and adds the element internal forces to the nodes. Note,
     * by definition, the internal forces are subtracted from the external
     * loads, thus the "addition" is negative. The incoming loads are already
     * in the 'stress' martrix as axial forces and moments as [fx fy fz mx my
     * mz] where the directions are local.
     *
     * @param integration_point The current integration point
     *
     * @exception java.lang.Error The exception description.
     */
    public void calculateNodalForces(int integration_point)
        throws java.lang.Error
    {
        // calculate the moment contribution from the shear equations
        m.set(1, 0, -length * force.get(2, 0));
        m.set(2, 0, length * force.get(1, 0));

        // Transform forces to global coordinates
        global_force.set(
            0, 0,
            (local_coordinate_system.get(0, 0) * force.get(0, 0)) +
            (local_coordinate_system.get(0, 1) * force.get(1, 0)) +
            (local_coordinate_system.get(0, 2) * force.get(2, 0))
        );
        global_force.set(
            1, 0,
            (local_coordinate_system.get(1, 0) * force.get(0, 0)) +
            (local_coordinate_system.get(1, 1) * force.get(1, 0)) +
            (local_coordinate_system.get(1, 2) * force.get(2, 0))
        );
        global_force.set(
            2, 0,
            (local_coordinate_system.get(2, 0) * force.get(0, 0)) +
            (local_coordinate_system.get(2, 1) * force.get(1, 0)) +
            (local_coordinate_system.get(2, 2) * force.get(2, 0))
        );

        // Transform to global coordinates
        global_moment.set(
            0, 0,
            (local_coordinate_system.get(0, 0) * force.get(3, 0)) +
            (local_coordinate_system.get(0, 1) * force.get(4, 0)) +
            (local_coordinate_system.get(0, 2) * force.get(5, 0))
        );
        global_moment.set(
            1, 0,
            (local_coordinate_system.get(1, 0) * force.get(3, 0)) +
            (local_coordinate_system.get(1, 1) * force.get(4, 0)) +
            (local_coordinate_system.get(1, 2) * force.get(5, 0))
        );
        global_moment.set(
            2, 0,
            (local_coordinate_system.get(2, 0) * force.get(3, 0)) +
            (local_coordinate_system.get(2, 1) * force.get(4, 0)) +
            (local_coordinate_system.get(2, 2) * force.get(5, 0))
        );

        // Transform the moment contributions to global coordinates
        global_m.set(
            0, 0,
            (local_coordinate_system.get(0, 0) * m.get(0, 0)) +
            (local_coordinate_system.get(0, 1) * m.get(1, 0)) +
            (local_coordinate_system.get(0, 2) * m.get(2, 0))
        );
        global_m.set(
            1, 0,
            (local_coordinate_system.get(1, 0) * m.get(0, 0)) +
            (local_coordinate_system.get(1, 1) * m.get(1, 0)) +
            (local_coordinate_system.get(1, 2) * m.get(2, 0))
        );
        global_m.set(
            2, 0,
            (local_coordinate_system.get(2, 0) * m.get(0, 0)) +
            (local_coordinate_system.get(2, 1) * m.get(1, 0)) +
            (local_coordinate_system.get(2, 2) * m.get(2, 0))
        );

        // Add this force contribution to the node
        node[0].addInternalForce(global_force.times(1));
        node[1].addInternalForce(global_force.times(-1));

        // Add the moment to the node
        node[0].addInternalMoment(global_moment.times(1));
        node[1].addInternalMoment(global_moment.times(-1));

        // Add the moment contributions to the nodes (note no change of sign!!)
        node[0].addInternalMoment(global_m);
        node[1].addInternalMoment(global_m);
    }

    /**
     * This method normally calculates the true strain, but in this case, it is
     * the displacement that is calculated as input to the force /
     * displacement relation used in the associated material law. The
     * displacement is calculated as [dx dy dz rx ry rz] in local directions
     * Velocity is calculated as [vx vy vz vrx vry vrz] in local directions
     *
     * @param timestep The timestep of the simulation
     * @param integration_point Which integration point of which the strain
     *        should be calculated
     */
    public void calculateStrain(double timestep, int integration_point)
        throws java.lang.Error
    {
        length = Math.sqrt(
                ((node[1].getX_pos() - node[0].getX_pos()) * (node[1].getX_pos() -
                node[0].getX_pos())) +
                ((node[1].getY_pos() - node[0].getY_pos()) * (node[1].getY_pos() -
                node[0].getY_pos())) +
                ((node[1].getZ_pos() - node[0].getZ_pos()) * (node[1].getZ_pos() -
                node[0].getZ_pos()))
            );

        if (length == 0) {
            throw new java.lang.Error(
                "Zero element length for Spring_Beam element nr" + number
            );
        }

        // Create and Transform the global displacements into local (use a temporary matrix)
        temp.set(
            0, 0,
            (node[1].getX_pos() - node[1].getX_pos_orig()) -
            (node[0].getX_pos() - node[0].getX_pos_orig())
        );
        temp.set(
            1, 0,
            (node[1].getY_pos() - node[1].getY_pos_orig()) -
            (node[0].getY_pos() - node[0].getY_pos_orig()) -
            (length * 0.5 * ((node[1].getZ_rot() - node[1].getZ_rot_orig()) +
            (node[0].getZ_rot() - node[0].getZ_rot_orig())))
        );
        temp.set(
            2, 0,
            (node[1].getZ_pos() - node[1].getZ_pos_orig()) -
            (node[0].getZ_pos() - node[0].getZ_pos_orig()) +
            (length * 0.5 * ((node[1].getY_rot() - node[1].getY_rot_orig()) +
            (node[0].getY_rot() - node[0].getY_rot_orig())))
        );
        temp.set(
            3, 0,
            (node[1].getX_rot() - node[1].getX_rot_orig()) -
            (node[0].getX_rot() - node[0].getX_rot_orig())
        );
        temp.set(
            4, 0,
            (node[1].getY_rot() - node[1].getY_rot_orig()) -
            (node[0].getY_rot() - node[0].getY_rot_orig())
        );
        temp.set(
            5, 0,
            (node[1].getZ_rot() - node[1].getZ_rot_orig()) -
            (node[0].getZ_rot() - node[0].getZ_rot_orig())
        );

        displacement.set(
            0, 0,
            (local_coordinate_system.get(0, 0) * temp.get(0, 0)) +
            (local_coordinate_system.get(1, 0) * temp.get(1, 0)) +
            (local_coordinate_system.get(2, 0) * temp.get(2, 0))
        );
        displacement.set(
            1, 0,
            (local_coordinate_system.get(0, 1) * temp.get(0, 0)) +
            (local_coordinate_system.get(1, 1) * temp.get(1, 0)) +
            (local_coordinate_system.get(2, 1) * temp.get(2, 0))
        );
        displacement.set(
            2, 0,
            (local_coordinate_system.get(0, 2) * temp.get(0, 0)) +
            (local_coordinate_system.get(1, 2) * temp.get(1, 0)) +
            (local_coordinate_system.get(2, 2) * temp.get(2, 0))
        );
        displacement.set(
            3, 0,
            (local_coordinate_system.get(0, 0) * temp.get(3, 0)) +
            (local_coordinate_system.get(1, 0) * temp.get(4, 0)) +
            (local_coordinate_system.get(2, 0) * temp.get(5, 0))
        );
        displacement.set(
            4, 0,
            (local_coordinate_system.get(0, 1) * temp.get(3, 0)) +
            (local_coordinate_system.get(1, 1) * temp.get(4, 0)) +
            (local_coordinate_system.get(2, 1) * temp.get(5, 0))
        );
        displacement.set(
            5, 0,
            (local_coordinate_system.get(0, 2) * temp.get(3, 0)) +
            (local_coordinate_system.get(1, 2) * temp.get(4, 0)) +
            (local_coordinate_system.get(2, 2) * temp.get(5, 0))
        );

        // Create and Transform the global velocities into local (use a temporary matrix)
        temp.set(0, 0, node[1].getX_vel() - node[0].getX_vel());
        temp.set(
            1, 0,
            node[1].getY_vel() - node[0].getY_vel() -
            (length * 0.5 * (node[1].getZ_rot_vel() + node[0].getZ_rot_vel()))
        );
        temp.set(
            2, 0,
            node[1].getZ_vel() - node[0].getZ_vel() +
            (length * 0.5 * (node[1].getY_rot_vel() + node[0].getY_rot_vel()))
        );
        temp.set(3, 0, node[1].getX_rot_vel() - node[0].getX_rot_vel());
        temp.set(4, 0, node[1].getY_rot_vel() - node[0].getY_rot_vel());
        temp.set(5, 0, node[1].getZ_rot_vel() - node[0].getZ_rot_vel());

        velocity.set(
            0, 0,
            (local_coordinate_system.get(0, 0) * temp.get(0, 0)) +
            (local_coordinate_system.get(1, 0) * temp.get(1, 0)) +
            (local_coordinate_system.get(2, 0) * temp.get(2, 0))
        );
        velocity.set(
            1, 0,
            (local_coordinate_system.get(0, 1) * temp.get(0, 0)) +
            (local_coordinate_system.get(1, 1) * temp.get(1, 0)) +
            (local_coordinate_system.get(2, 1) * temp.get(2, 0))
        );
        velocity.set(
            2, 0,
            (local_coordinate_system.get(0, 2) * temp.get(0, 0)) +
            (local_coordinate_system.get(1, 2) * temp.get(1, 0)) +
            (local_coordinate_system.get(2, 2) * temp.get(2, 0))
        );
        velocity.set(
            3, 0,
            (local_coordinate_system.get(0, 0) * temp.get(3, 0)) +
            (local_coordinate_system.get(1, 0) * temp.get(4, 0)) +
            (local_coordinate_system.get(2, 0) * temp.get(5, 0))
        );
        velocity.set(
            4, 0,
            (local_coordinate_system.get(0, 1) * temp.get(3, 0)) +
            (local_coordinate_system.get(1, 1) * temp.get(4, 0)) +
            (local_coordinate_system.get(2, 1) * temp.get(5, 0))
        );
        velocity.set(
            5, 0,
            (local_coordinate_system.get(0, 2) * temp.get(3, 0)) +
            (local_coordinate_system.get(1, 2) * temp.get(4, 0)) +
            (local_coordinate_system.get(2, 2) * temp.get(5, 0))
        );
    }

    /**
     * This method calculates the stress of the element, but since the material
     * law for the spring is used for the displacement / force relation, the
     * parameters are "faked" here. The force matrix is updated by the
     * "material" laws and used in the calculateNodalForces.
     *
     * @param integration_point The integration point of which the stress is to
     *        be calculated
     */
    public void calculateStress(int integration_point, double timestep)
        throws java.lang.Error
    {
        material.calculateStressOneDimensional(
            displacement, velocity, force, timestep
        );
    }

    /**
     * This method calculates the timestep for the element and returns the
     * smallest of the current timestep and the element timestep since it is
     * the smallest element that determines the stability of the solution.
     *
     * @param current_timestep The current timestep
     *
     * @return double
     */
    public double checkTimestep(double current_timestep) {
        double I1;
        double I2;

        I1 = (node[0].getInertia().get(0, 0) + node[0].getInertia().get(1, 1) +
            node[0].getInertia().get(2, 2)) / 3.0;
        I2 = (node[1].getInertia().get(0, 0) + node[1].getInertia().get(1, 1) +
            node[1].getInertia().get(2, 2)) / 3.0;

        return Math.min(
            current_timestep,
            material.wavespeedOneDimensional(
                (4 * node[0].getMass() * node[1].getMass()) / (node[0].getMass() +
                node[1].getMass()), (4 * I1 * I2) / (I1 + I2)
            )
        );
    }

    /**
     * This element only has one integration point (gauss point)
     *
     * @return Number of integration points
     */
    public int getNumberOfIntegrationPoints() {
        return 1;
    }

    /**
     * Returns the local force matrix of the BeamSpring.
     *
     * @return Jama.Matrix Local force matrix
     */
    public Jama.Matrix getForce() {
        return force;
    }

    /**
     * This method parses the input data to the element from the Fembic indata
     * file.
     *
     * @param param The input, split up into an array of Tokens
     * @param lineno The line number.
     * @param nodelist The global nodelist
     * @param materiallist The global material list
     * @param loadlist The global load list
     *
     * @exception java.text.ParseException The exception description.
     */
    public void parse_Fembic(
        Token[] param, int lineno, RplVector nodelist, RplVector materiallist,
        RplVector loadlist
    )
        throws java.text.ParseException
    {
        int nodenumber;
        int i = 0;
        Token[] contact_input;

        while (i < param.length) {
            // The nodes of the element are defined
            if (
                param[i].getw().toUpperCase().equals("NODES") &&
                param[i + 1].getw().toUpperCase().equals("=")
            ) {
                // Assume now that the nodes are delivered in param3, with the format
                // [nodenr,nodenr]
                if (
                    ! param[i + 2].getw().toUpperCase().startsWith("[") ||
                    ! param[i + 2].getw().toUpperCase().endsWith("]")
                ) {
                    throw new java.text.ParseException(
                        "Error, node number definition should be [nodenr,nodenr,nodenr]",
                        lineno
                    );
                }

                // Ok, now find the numbers
                try {
                    nodenumber = super.getNodeNumber(1, param[i + 2].getw());

                    // We now have the node number, but want to have the handle to the node object.
                    node[0] = super.findNode(nodenumber, nodelist);

                    // And repeat for second node
                    nodenumber = super.getNodeNumber(2, param[i + 2].getw());
                    node[1] = super.findNode(nodenumber, nodelist);

                    // And finally for third node
                    nodenumber = super.getNodeNumber(3, param[i + 2].getw());
                    control_node = super.findNode(nodenumber, nodelist);
                    i += 3;
                    Nodes_are_set = true;
                } catch (IllegalArgumentException e) {
                    throw new java.text.ParseException(
                        "Error in BeamSpring element\n" + e.getMessage() +
                        "in line ", lineno
                    );
                }
            } else
            // The material of the element is defined
            if (
                param[i].getw().toUpperCase().equals("MATERIAL") &&
                param[i + 1].getw().toUpperCase().equals("=")
            ) {
                // Assume now that the material name is delivered in param3
                try {
                    // We want the handle to the material object.
                    material = super.findMaterial(
                            param[i + 2].getw().toUpperCase(), materiallist
                        );
                    i += 3;
                    Material_is_set = true;
                } catch (IllegalArgumentException e) {
                    throw new java.text.ParseException(e.getMessage(), lineno);
                }
            } else
            // The cross section area of the element is defined
            if (
                param[i].getw().toUpperCase().equals("D") &&
                param[i + 1].getw().toUpperCase().equals("=")
            ) {
                // The value of the cross section area is in param4. Set this in the element
                diameter = param[i + 2].getn();
                i += 3;
                D_is_set = true;
            } else
            // The factor for the internal contact element is defined
            if (
                param[i].getw().toUpperCase().equals("FACTOR") &&
                param[i + 1].getw().toUpperCase().equals("=")
            ) {
                // The value of the element thickness is in param3. Set this in the element
                factor = param[i + 2].getn();
                i += 3;
                Factor_is_set = true;
            } else
            // The contact element of the shell is specified
            if (
                param[i].getw().toUpperCase().equals("CONTACT") &&
                param[i + 1].getw().toUpperCase().equals("=")
            ) {
                // The value of the element thickness is in param3. Set this in the element
                if (param[i + 2].getw().toUpperCase().equals("BASIC")) {
                    Contact = BASIC;
                } else {
                    throw new java.text.ParseException(
                        "Unrecognized contact parameter", lineno
                    );
                }

                i += 3;
            } else {
                // Neither material or nodes are defined. Then the parameter is wrong.
                throw new java.text.ParseException(
                    "Unknown BeamSpring element parameter ", lineno
                );
            }
        }

        // Make a local instance of a contact element to handle the contacts
        if (Contact == BASIC) {
            internal_contact_element = new Contact_Line();
            internal_contact_element.setNumber(-1);

            // Make the input for the contact element
            if (Factor_is_set) {
                contact_input = new Token[9];
            } else {
                contact_input = new Token[6];
            }

            contact_input[0] = new Token(new String("nodes"));
            contact_input[1] = new Token(new String("="));
            contact_input[2] = new Token(
                    new String(
                        "[" + node[0].getNumber() + "," + node[1].getNumber() +
                        "]"
                    )
                );
            contact_input[3] = new Token(new String("D"));
            contact_input[4] = new Token(new String("="));
            contact_input[5] = new Token(diameter);

            if (Factor_is_set) {
                contact_input[6] = new Token(new String("factor"));
                contact_input[7] = new Token(new String("="));
                contact_input[8] = new Token(factor);
            }

            // Now, set the input for the contact element also
            internal_contact_element.parse_Fembic(
                contact_input, lineno, nodelist, materiallist, loadlist
            );
        }
    }

	/**
 * Insert the method's description here. Creation date: (25/12/01 %T)
 */
    public void parse_Nastran(
        Token[] param, Token[] param2, int lineno, RplVector nodelist, RplVector materiallist,
        RplVector loadlist
    )
        throws java.text.ParseException
    {
    }

	/**
 * Insert the method's description here. Creation date: (25/12/01 %T)
 */
    public void parse_Gmsh(
        Token[] param, int lineno, RplVector nodelist, RplVector materiallist,
        RplVector loadlist
    )
        throws java.text.ParseException
    {
		int i;
		Token[] contact_input;
		
       	number = (int)param[0].getn();



        // Make a local instance of a contact element to handle the contacts
        if (Contact == BASIC) {
            internal_contact_element = new Contact_Line();
            internal_contact_element.setNumber(-1);

            // Make the input for the contact element
            if (Factor_is_set) {
                contact_input = new Token[9];
            } else {
                contact_input = new Token[6];
            }

            contact_input[0] = new Token(new String("nodes"));
            contact_input[1] = new Token(new String("="));
            contact_input[2] = new Token(
                    new String(
                        "[" + node[0].getNumber() + "," + node[1].getNumber() +
                        "]"
                    )
                );
            contact_input[3] = new Token(new String("D"));
            contact_input[4] = new Token(new String("="));
            contact_input[5] = new Token(diameter);

            if (Factor_is_set) {
                contact_input[6] = new Token(new String("factor"));
                contact_input[7] = new Token(new String("="));
                contact_input[8] = new Token(factor);
            }

            // Now, set the input for the contact element also
            internal_contact_element.parse_Fembic(
                contact_input, lineno, nodelist, materiallist, loadlist
            );
        }
    	    	
    	
    }









    /**
     * Checks the indata so that everyting required has been set. Note that the
     * diameter is only needed for contact purpouse.
     */
    public void checkIndata()
        throws Error
    {
        // Check that all the required parameters have been parsed
        if ((Contact == BASIC) && ! D_is_set) {
            throw new Error(
                "No Diameter defined for BeamSpring element nr" + number
            );
        }

        if (! Nodes_are_set) {
            throw new Error(
                "No nodes defined for BeamSpring element nr" + number
            );
        }

        if (! Material_is_set) {
            throw new Error(
                "No Material defined for BeamSpring element nr" + number
            );
        }

        //
        if (Contact == BASIC) {
            internal_contact_element.checkIndata();
        }
    }

    /**
     * This method returns true if an element has failed. Creation date:
     * (25/12/01 %T)
     *
     * @return int
     */
    public boolean hasFailed() {
        return false;
    }

    /**
     * This method is used to create the lines needed in the result file. The
     * method returns a string which is printed directly. However, due to the
     * fact that the line may be different depending on what is requested to
     * be printed and that the number of methods should be kept down, the
     * first parameter here is a control parameter. This parameter describes
     * what should be printed. The second parameter is a required input when
     * gauss point results are to be printed. Creation date: (09/12/01 %T)
     *
     * @param ctrl The control number to say if a header of result file is to
     *        be printed.
     * @param gpn The gauss point number.
     *
     * @return java.lang.String
     */
    public String print_Gid(int ctrl, int gpn) {
        String out;

        switch (ctrl) {
        case MESH_HEADER:

            /* Print the header for the mesh.
             * In this case, the type of element is Linear and it a Rod uses 2 nodes
             */
            out = new String(
                    "MESH \"MeshType" + type +
                    "\" Dimension 3 ElemType Linear Nnode 2\n"
                );

            return out;

        case MESH:

            /* Print the element number and connected nodes */
            out = new String(
                    number + "\t" + node[0].getNumber() + "\t" +
                    node[1].getNumber() + "\n"
                );

            return out;

        case RESULT_HEADER:

            /* Print the header of the result file, for the block of Rod elements */
            out = new String(
                    "GaussPoints \"Type" + type +
                    "\" ElemType Linear \"MeshType" + type + "\"\n"
                );
            out += "Number Of Gauss Points: 1\n";
            out += "Nodes Not Included\n";
            out += "Natural Coordinates: Internal\n";
            out += "End GaussPoints\n";

            return out;

        case RESULT_SUB_HEADER:

            /* Print the subheader for the resultfile to initate each block of data from this element type */
            out = new String(" 1 2 0 \"Type" + type + "\"\n");

            return out;

        case RESULT_STRESS_LOCAL:

            /* Print a dummy value for this element, remembering the previous line index */
            out = new String(number + " ");
            out += "0.0 \n";

            return out;

        case RESULT_STRAIN_LOCAL:

            /* Print a dummy value for this element, remembering the previous line index */
            out = new String(number + " ");
            out += "0.0 \n";

            return out;

        default:
            return new String("");
        }
    }

    /**
     * This method is used to create the lines needed in the result file. The
     * method returns a string which is printed directly. However, due to the
     * fact that the line may be different depending on what is requested to
     * be printed and that the number of methods should be kept down, the
     * first parameter here is a control parameter. This parameter describes
     * what should be printed. The second parameter is a required input when
     * gauss point results are to be printed. Creation date: (09/12/01 %T)
     *
     * @param ctrl The control number to say if a header of result file is to
     *        be printed.
     * @param gpn The gauss point number.
     *
     * @return java.lang.String
     */
    public String print_Fembic(int ctrl, int gpn) {
        String out;

        switch (ctrl) {

        case MESH:

            /* Print the element number and connected nodes */
            out = new String(
                    number + "\t  nodes = [" + node[0].getNumber() + "," +
                    node[1].getNumber() + "," + control_node.getNumber() + "]\t"
                    + "material = " + material.getName() + "\t"
                );
                
            if (D_is_set) 
            	out += " D = " + diameter;

            if (Factor_is_set) 
            	out += " factor = " + factor;

            if (Contact == BASIC) 
            	out += " contact = BASIC";

			out += "\n";

            return out;


        default:
            return new String("");
        }
    }


    /**
     * Insert the method's description here. Creation date: (27/09/01 %T)
     */
    public void setInitialConditions()
        throws Error
    {
        /* Make a local copy of the material object and keep it inside the element.
         * The reason for this is twofold.
         * 1. The material law is now tied to the element and will remember the element properties and history
         * 2. Since the material is cloned now, after Initialize, all the parameters are defined in the material object and are
         * now automatically transferred to the local copy.
         */
        try {
            material = (Material) material.copy();
        } catch (CloneNotSupportedException e) {
            System.err.println("Object cannot clone");
        }

        // Now call any necessary initialisations in the law.
        material.setInitialConditions();

        // Initalize the contact element also
        if (Contact == BASIC) {
            internal_contact_element.setInitialConditions();
        }
    }

    /**
     * This method sets up the local coordinate system for the element.     The
     * local x-axis will run from node 1 to node 2. The local y-axis will be
     * in the plane given by the local x-axis     and the point formed by the
     * coordinates of node 1, but with and offset of 1 mm in x and y
     * direction.     The local z-axis will be orthogonal to the same plane.
     * In the case of a rod, the directions of the y-and z-axis are really not
     * that important since it is symmetric.     The local system is unified
     * in length. The forces and inertias can now be expresses relative to
     * this     system and then transformed Creation date: (19/11/01 %T)
     */
    public void updateLocalCoordinateSystem() {
        super.calculateLocalBaseVectors(
            0, 0, 0, node[1].getX_pos() - node[0].getX_pos(),
            node[1].getY_pos() - node[0].getY_pos(),
            node[1].getZ_pos() - node[0].getZ_pos(),
            control_node.getX_pos() - node[0].getX_pos(),
            control_node.getY_pos() - node[0].getY_pos(),
            control_node.getZ_pos() - node[0].getZ_pos(), local_coordinate_system
        );

        // Update local coordiante system on the contact element
        if (Contact == BASIC) {
            internal_contact_element.updateLocalCoordinateSystem();
        }
    }

    /**
     * Returns the local Fx force in the spring
     *
     * @return the local Fx force in the spring
     */
    public double getFx() {
        return force.get(0, 0);
    }

    /**
     * Returns the local Fy force in the spring
     *
     * @return the local Fy force in the spring
     */
    public double getFy() {
        return force.get(1, 0);
    }

    /**
     * Returns the local Fz force in the spring
     *
     * @return the local Fz force in the spring
     */
    public double getFz() {
        return force.get(2, 0);
    }

    /**
     * Returns the local Mx force in the spring
     *
     * @return the local Mx force in the spring
     */
    public double getMx() {
        return force.get(3, 0);
    }

    /**
     * Returns the local My force in the spring
     *
     * @return the local My force in the spring
     */
    public double getMy() {
        return force.get(4, 0);
    }

    /**
     * Returns the local Mz force in the spring
     *
     * @return the local Mz force in the spring
     */
    public double getMz() {
        return force.get(5, 0);
    }
}

