post/primer/head_excursion_zone.js

// module: TRUE
/* This script is run in PRIMER to create a ptf file containing the head excursion zone bands in car model space.
 *
 * It is run from the D3PLOT script d3plot_automotive_assessment.mjs using the System() function to start PRIMER,
 * read in the appropriate keyword file and pass the required variables to the script with -js_arg= command line
 * arguments which can be read from the global <arguments> array in this script.
 *
 * They are defined in the following order:
 *
 * 1. The cut section thickness
 * 2. The cut section node
 * 3. The first shift deform node
 * 4. The second shift deform node
 * 5. The third shift deform value
 * 6. The seat centre y coordinate
 * 7. The intrusion from the seat centre y coordinate
 * 8. The vehicle direction
 * 9. Unit system (Workflow.UNIT_SYSTEM_U1, etc..)
 *
 * If the order changes in the D3PLOT script, this script must be updated to match.
 */

import { JSPath } from "../../modules/shared/path.mjs";
import { WorkflowUnits } from "../../../modules/units.mjs";

// @ts-ignore
create_zones_ptf(arguments);

/**
 * Creates the head excursion zone bands as null shell parts in car model space
 * and writes them to a ptf file.
 * @param {string[]} args Arguments passed to this script from the D3PLOT script
 */
function create_zones_ptf(args) {
    let m_first = Model.First();

    /* Get the arguments passed to the script */

    let cut_section_thickness = Number.parseFloat(args[1]);
    let cut_section_node_1 = Node.GetFromID(m_first, Number.parseInt(args[2]));
    let shift_deformed_node_1 = Node.GetFromID(m_first, Number.parseInt(args[3]));
    let shift_deformed_node_2 = Node.GetFromID(m_first, Number.parseInt(args[4]));
    let shift_deformed_node_3 = Node.GetFromID(m_first, Number.parseInt(args[5]));
    let seat_centre_y = Number.parseFloat(args[6]);
    let intrusion_from_seat_centre_y = Number.parseFloat(args[7]);
    let vehicle_direction = args[8];
    let unit_system = Number.parseInt(args[9]);

    /* Set the workflows directory - whilst this is a PRIMER script, it is saved
     * in the scripts/automotive_assessments/post/primer folder hence passing
     * JSPath.POST to set the workflow directory */

    JSPath.SetWorkflowsDirectory(JSPath.POST);

    /*
---> X  Car pointing in -ve X
=============================
        
        LHD CAR                                 RHD CAR
        near seat (passenger Y) > 0             near seat (passenger Y) < 0
        +                                       -

                (O) barrier
                 |  
                 v
  Y      /--OOOO---------OOOO---]        Y      /--OOOO---------OOOO---]
  ^     /<   [Near]        [ ]  |        ^     /<   [Driver]     [ ]   |
  |     |                       |        |     |                       |
  |     |                       |        |     |                       |
  |     \<   [Driver]      [ ]  |        |     \<   [Near]        [ ]  |
         \--OOOO---------OOOO---]               \--OOOO---------OOOO---]
                                                       ^
                                                       |
                                                      (O) barrier
        
<--- X  Car pointing in +VE X
=============================
      
        LHD CAR                                 RHD CAR
        near seat (passenger Y) > 0             near seat (passenger Y) < 0
        -                                       +

                (O) barrier
                 |  
                 v
         /--OOOO---------OOOO---]                /--OOOO---------OOOO---]
  |     /<   [Near]        [ ]  |         |     /<   [Driver]     [ ]   |
  |     |                       |         |     |                       |
  |     |                       |         |     |                       |
  V     \<   [Driver]      [ ]  |         V     \<   [Near]        [ ]  |
  Y      \--OOOO---------OOOO---]         Y      \--OOOO---------OOOO---]
                                                        ^
                                                        |
                                                       (O) barrier
*/

    let y_excursion = 0.0; /* Start of red zone */

    if (seat_centre_y > 0) {
        /* Note the car is assumed to point in the -ve x direction
         * "right" means from the perspective of the analyst looking at the front of the vehicle (this is "left side for the occupants")
         *
         * if the passenger seat (struck side seat a.k.a near seat) y coordinate is +ve
         * then the car is assumed to be struck from the vehicle right and intrusion is right to left (in the -ve y direction)
         * so to get intrusion_y coordinate add intrusion_from_seat_center_y to seat_y */

        y_excursion = seat_centre_y + intrusion_from_seat_centre_y;
    } else {
        /* Intrusion is from left to right when looking at the front of the vehicle (i.e. in the +ve y direction) */

        y_excursion = seat_centre_y - intrusion_from_seat_centre_y;
    }

    let output_dir = `${JSPath.GetWorkflowsDirectory()}scripts/automotive_assessments/post/primer/output`;

    let ptf_file = `${output_dir}/zones.ptf`;

    if (File.Exists(ptf_file)) {
        Message(`${ptf_file} exists so deleting old ptfs before creating new ptf`);
        File.Delete(ptf_file);

        if (File.Exists(`${ptf_file}01`)) File.Delete(`${ptf_file}01`);
    }

    /* Height (assume -1m to +2m is suitable) */

    var z_bot = -1 * WorkflowUnits.LengthToMetresFactor(unit_system);
    var z_top = 2 * WorkflowUnits.LengthToMetresFactor(unit_system);

    if (cut_section_thickness == 0)
        cut_section_thickness = 10 * WorkflowUnits.LengthToMillimetresFactor(unit_system); /* Nominal 10mm thick */

    let thick_cut = cut_section_thickness;

    let xsign = 1;
    if (/pos/i.test(vehicle_direction)) xsign = -1;

    let x =
        cut_section_node_1.x +
        (xsign * thick_cut * 0.99) / 2; /* issue with cut section if shells lie exactly on cut-section x hence '-1' */
    let y = seat_centre_y;

    /* Multiply by sign for the case when the car is struck from the left hand side */

    let sign = 1;
    if (y_excursion < 0) sign = -1;

    let y_red_orange = y;
    let y_orange_yellow = y - 0.125 * WorkflowUnits.LengthToMetresFactor(unit_system) * sign;
    let y_yellow_green = y - 0.25 * WorkflowUnits.LengthToMetresFactor(unit_system) * sign;
    let width = 10 * WorkflowUnits.LengthToMetresFactor(unit_system) * sign;

    let bool_red = true;
    let bool_orange = true;
    let bool_yellow = true;

    if (Math.abs(y_excursion) <= Math.abs(y_yellow_green)) {
        /* No red, orange and yellow. Green starts at y_excursion */
        y_yellow_green = y_excursion;
        bool_red = false;
        bool_orange = false;
        bool_yellow = false;
    } else if (Math.abs(y_excursion) <= Math.abs(y_orange_yellow)) {
        /* No red and orange. Yellow starts at y_excursion */
        y_orange_yellow = y_excursion;
        bool_red = false;
        bool_orange = false;
    } else if (Math.abs(y_excursion) <= Math.abs(y_red_orange)) {
        /* No red. Orange starts at y_excursion */
        y_red_orange = y_excursion;
        bool_red = false;
    }

    /* Assume struck far side is at +ve y and driver/occupant is sat at -ve y such that they move in +ve y direcion
    /* Model to create null shells in */

    Message("Making new model...");
    let m = new Model();

    /* Make dummy material */

    let mat = new Material(m, 1, "NULL");
    mat.SetPropertyByName("RO", WorkflowUnits.MassToKilogramsFactor(unit_system));

    /* Make dummy section */

    let section = new Section(m, 1, Section.SHELL, "NULL_SHELL");

    /* Set section thickness to 1mm */

    section.t1 = 0.001 * WorkflowUnits.LengthToMetresFactor(unit_system);
    section.t2 = 0.001 * WorkflowUnits.LengthToMetresFactor(unit_system);
    section.t3 = 0.001 * WorkflowUnits.LengthToMetresFactor(unit_system);
    section.t4 = 0.001 * WorkflowUnits.LengthToMetresFactor(unit_system);

    /* Make parts */

    let parts_list = [1, 5, 6, 7, 8]; /* Used in D3PLOT excursion plot to control which parts are visible */

    let shift_deformed_pid = 1;
    let red_zone_pid = 2;
    let orange_zone_pid = 3;
    let yellow_zone_pid = 4;
    let green_zone_pid = 5;
    let blue_centre_line_pid = 6;
    let capping_zone_pid = 7;
    let white_zone_pid = 8;

    new Part(m, shift_deformed_pid, 1, 1, "Shift Deformed");

    if (bool_red) {
        new Part(m, red_zone_pid, 1, 1, "RED ZONE");
        parts_list.push(2);
    }

    if (bool_orange) {
        new Part(m, orange_zone_pid, 1, 1, "ORANGE ZONE");
        parts_list.push(3);
    }

    if (bool_yellow) {
        new Part(m, yellow_zone_pid, 1, 1, "YELLOW ZONE");
        parts_list.push(4);
    }

    new Part(m, green_zone_pid, 1, 1, "GREEN ZONE");
    new Part(m, blue_centre_line_pid, 1, 1, "BLUE CENTER-LINE");
    new Part(m, capping_zone_pid, 1, 1, "GREY CAPPING ZONE");
    new Part(m, white_zone_pid, 1, 1, "WHITE ZONE"); /* Used for consistent image scaling */

    /* Make nodes */

    let sdn1 = copy_node_to_new_model_return_nid(shift_deformed_node_1, m);
    let sdn2 = copy_node_to_new_model_return_nid(shift_deformed_node_2, m);
    let sdn3 = copy_node_to_new_model_return_nid(shift_deformed_node_3, m);
    let csn4 = copy_node_to_new_model_return_nid(cut_section_node_1, m);

    /* shell nodes */

    /* RED/ORANGE ZONE (seat centreline) */
    new Node(m, 1, x, y_red_orange, z_bot);
    new Node(m, 2, x, y_red_orange, z_top);

    /* RED/CAPPING ZONE (excursion taken from previous side impact analysis)
     * note if y_excursion < y then orange starts at y_excursion
     *     if y_excursion < y -125 then no orange and yellow starts at y_excursion
     *     if y_excursion < y -255 then no yellow and green starts at y_excursion */

    new Node(m, 3, x, y_excursion, z_top);
    new Node(m, 4, x, y_excursion, z_bot);

    /* ORANGE/YELLOW ZONE */
    new Node(m, 5, x, y_orange_yellow, z_bot);
    new Node(m, 6, x, y_orange_yellow, z_top);

    /* YELLOW/GREEN ZONE */
    new Node(m, 7, x, y_yellow_green, z_bot);
    new Node(m, 8, x, y_yellow_green, z_top);

    /* GREEN ZONE (arbitrarily chosen to end at far seat centreline (assumed to be  -y)) */
    new Node(m, 9, x, -y, z_bot);
    new Node(m, 10, x, -y, z_top);

    /* BLUE CENTERLINE (10mm wide) */
    let blue_line_width = 0.01 * WorkflowUnits.LengthToMetresFactor(unit_system);
    let x_blue_line =
        x -
        0.002 *
            WorkflowUnits.LengthToMetresFactor(
                unit_system
            ); /* Offset blue line forewards by 2mm so that it is more visible */
    new Node(m, 11, x_blue_line, -blue_line_width / 2, z_bot);
    new Node(m, 12, x_blue_line, -blue_line_width / 2, z_top);
    new Node(m, 13, x_blue_line, blue_line_width / 2, z_top);
    new Node(m, 14, x_blue_line, blue_line_width / 2, z_bot);

    /* Capping Zone (10m wide) grey part */
    new Node(m, 15, x, width / 2, z_bot);
    new Node(m, 16, x, width / 2, z_top);

    /* Right border (white part) //used for consistent zoom/scale */
    new Node(m, 17, x, -width / 2, z_bot);
    new Node(m, 18, x, -width / 2, z_top);

    /* Make shells */
    new Shell(m, 1, shift_deformed_pid, sdn1, sdn2, csn4, csn4);
    new Shell(m, 2, shift_deformed_pid, sdn1, sdn3, csn4, csn4);
    new Shell(m, 3, shift_deformed_pid, sdn2, sdn3, csn4, csn4);
    new Shell(m, 4, shift_deformed_pid, sdn1, sdn2, sdn3, sdn3);

    if (bool_red) new Shell(m, 5, red_zone_pid, 1, 2, 3, 4);
    if (bool_orange) new Shell(m, 6, orange_zone_pid, 1, 5, 6, 2);
    if (bool_yellow) new Shell(m, 7, yellow_zone_pid, 5, 7, 8, 6);
    new Shell(m, 8, green_zone_pid, 7, 9, 10, 8);
    new Shell(m, 9, blue_centre_line_pid, 11, 12, 13, 14);
    new Shell(m, 10, capping_zone_pid, 3, 4, 15, 16);
    new Shell(m, 11, white_zone_pid, 9, 10, 18, 17);

    m_first.Delete();

    //write out pft file using temp macro
    let write_ptf_macro_file = File.Mktemp();

    let macro =
        `Window("Tools/Keywords").Button("Model Tab")
    Window("Model functions").Button("Write")
    In Window("WRITE TO FILE")
        .Radio("Filetype Radio") = "PTF / d3plot"
        .Textbox("File:") = "` +
        ptf_file +
        '"\n' +
        `    .Button("Apply")
    End In
    `;

    Message("Writing macro to " + write_ptf_macro_file);
    let f = new File(write_ptf_macro_file, File.WRITE);
    f.Write(macro);
    f.Close();

    Message("Running macro " + write_ptf_macro_file);
    PlayMacro(write_ptf_macro_file);
    Message("Finish macro " + write_ptf_macro_file);

    File.Delete(write_ptf_macro_file);

    Message("Exiting primer after successfully writing excursion zones to " + ptf_file);
}

/**
 * Copies a node to a new model and returns the new node id
 * @param {Node} n Node to copy
 * @param {Model} m model to copy node to
 * @returns {number}
 */
function copy_node_to_new_model_return_nid(n, m) {
    let new_node = new Node(m, n.nid, n.x, n.y, n.z);
    return new_node.nid;
}