thegrumpys / odop

Open Design Optimization Platform (ODOP) - Coil spring design app; mechanical springs; compression spring, extension spring, torsion spring
https://www.springdesignsoftware.org
MIT License
4 stars 5 forks source link

Implement Alert facility #569

Closed grumpyinca closed 2 years ago

grumpyinca commented 3 years ago

As currently envisioned, a new "Alerts facility" provides design-type specific messages. It is potentially manifested by a button in the ResultTable adjacent to the Search button of issue #560.

Labeled "Alerts", this button would have three possible states:

For the Spring design types, the Alert message string is currently visualized as a stack or table of currently active individual alert messages, each with a color-coded severity. The individual alert messages would include existing warning messages produced in Spring Report1 (including the compression spring buckling and extension spring end fatigue failure messages) plus new alert/warning messages. The full set of alerts should continue to appear in Report1. This may require a re-implementation based on the new Alerts facility.

Alternative renderings in the UI:

Possible new alert/warning messages include:

Implementation The Alert conditions may be computed in a single place, but the alert text likely needs to be rendered in multiple places, including:

Ideally, the criteria for active alerts would be computed only as it is needed. For example, when the ResultTable.js is invoked and before the ResultTable is rendered. As this is design-type specific information, there may be challenges to maintaining appropriate modularity and isolation. In order to keep design-type specific messages isolated and not re-computed every time that eqnset is called during a search, it might be necessary to put the alert conditions into a separate design-type specific function that is called somewhat more frequently than ResultTable is rendered. For example, whenever the Objective Value is updated outside of Search. A quick review suggests that might be one place in updateViolationsAndObjectiveValue.js or multiple (13?) places in dispatcher.js.

If the alert conditions are computed in one place but rendered in multiple places, consider defining the message table in a .json file and structuring each message with "condition", "details", "severity", "viewed" and "control" properties.

The first generation Alerts facility does not need to include the generic messages implemented in #557 (Add warning messages prior to Action : Search).

grumpyinca commented 3 years ago

Consider blocking the operation of Search, Seek & Trade if there are any un-read (un-viewed) alerts.

grumpyinca commented 3 years ago

Pseudo-code implementing first generation design-type specific alerts

Per 6/12/2021 discussion, there is an analogy with the "Validator" in the UT CLE system. This code might be implemented as {designtype}/validate.js files. Separately, the design-type independent code currently in code currently in

Note that there is a bit of terminology clash on the term "validator". In spring design, the term "design validation" is used to describe the process of checking an existing design for correctness.

See the initial description of this issue for a more detailed description of "condition", "details", "severity", "viewed" and "control" properties.

As noted above in the original issue description:

Compression Spring

From existing code in ReportBase.js: if (this.props.symbol_table[o.L_Free].value < this.props.symbol_table[o.L_Solid].value) { this.hits++; msg.condition = "L_Free < L_Solid" msg.severity = "warn" msg.viewed = FALSE msg.control = "always" msg.details = this.props.symbol_table[o.L_Free].name + " < " + this.props.symbol_table[o.L_Solid].name; } if (this.props.symbol_table[o.L_2].value < this.props.symbol_table[o.L_Solid].value) { this.hits++; msg.condition = "L_2 < L_Solid" msg.severity = "warn" msg.viewed = FALSE msg.control = "always" msg.details = this.props.symbol_table[o.L_2].name + " < " + this.props.symbol_table[o.L_Solid].name; } if (this.props.symbol_table[o.ID_Free].value < 0.0) { this.hits++; msg.condition = "ID_Free < Wire_Dia" msg.severity = "error" msg.viewed = FALSE msg.control = "always" msg.details = this.props.symbol_table[o.ID_Free].name + " < Wire_Dia"; } if (this.props.symbol_table[o.Coils_A].value < 1.0) { this.hits++; msg.condition = "Coils_A < 1.0" msg.severity = "warn" msg.viewed = FALSE msg.control = "always" msg.details = this.props.symbol_table[o.Coils_A].name + " < 1.0"; } if (this.props.symbol_table[o.Wire_Dia].value < 0.1 this.props.symbol_table[o.tbase010].value) { this.hits++; msg.condition = "Wire_Dia < reasonable" msg.severity = "warn" msg.viewed = FALSE msg.control = "always" msg.details = this.props.symbol_table[o.Wire_Dia].name + " < reasonable"; } if (this.props.symbol_table[o.Wire_Dia].value > 5.0 this.props.symbol_table[o.tbase400].value) { this.hits++; msg.condition = "Wire_Dia > reasonable" msg.severity = "warn" msg.viewed = FALSE msg.control = "always" msg.details = this.props.symbol_table[o.Wire_Dia].name + " > reasonable"; } if (this.props.symbol_table[o.Tensile].value <= this.props.system_controls.smallnum) { this.hits++; msg.condition = "Tensile < reasonable" msg.severity = "warn" msg.viewed = FALSE msg.control = "always" msg.details = this.props.symbol_table[o.Tensile].name + " < reasonable"; } if (this.props.symbol_table[o.PC_Avail_Deflect].value > 80.0) { msg.condition = "%_Avail_Deflect > 80" msg.severity = "info" msg.viewed = FALSE msg.control = "always" msg.details = "Deflection at load point 2 is" + {this.props.symbol_table[o.PC_Avail_Deflect].value.toFixed(0)} + "% of total available deflection." + "Coil to coil contact may cause inaccuracy in point 2."; }

    temp = this.props.symbol_table[o.Deflect_2].value / this.props.symbol_table[o.L_Free].value;
    sq1 = 1.4 * this.props.symbol_table[o.Slenderness].value - 4.0;
    this.errmsg1 = undefined;
    this.errmsg0 = undefined;
    if (sq1 > this.props.system_controls.smallnum) {
        /* structured to avoid div by 0 */
        if (temp > 0.76 / sq1) {
            this.errmsg1 = "Given a deflection ratio of " + temp.toFixed(3) +
                           "  and a Slenderness ratio of " + this.props.symbol_table[o.Slenderness].value.toFixed(1) + ", " +
                           "the spring will tend to buckle with fixed/free  ends.";
            sq1 = 2.0 * this.props.symbol_table[o.Slenderness].value - 8.0;
            if (sq1 <= 0.0 || temp < 1.6 / sq1)
                this.errmsg0 = " not";
            else
                this.errmsg0 = "";
            this.errmsg0 = "The spring will" + this.errmsg0 + " tend to buckle with fixed/fixed ends.";
        }
    }
     if (this.errmsg1 !== undefined) {
        msg.condition = "Buckling concern"
        msg.severity = "info"
        msg.viewed = FALSE
        msg.control = "always"
        msg.details = errmsg1 + errmsg0;
    }

New messages: PC_Avail_Deflect1 = 100.0 * this.props.symbol_table[o.Deflect_1] / (this.props.symbol_table[o.L_Free] - this.props.symbol_table[o.L_Solid]) if PC_Avail_Deflect1 < 20.0) { msg.condition = "Deflect_1 < 20 %_Avail_Deflect" msg.severity = "info" msg.viewed = FALSE msg.control = "always" msg.details = "Deflection at load point 1 is" + PC_Avail_Deflect1.toFixed(0) + "% of total available deflection. " + "End effects may cause inaccuracy in point 1."; } if this.props.symbol_table[o.FS_Solid] < 1.0) { msg.condition = "FS_Solid < 1.0" msg.severity = "warn" msg.viewed = FALSE msg.control = "always" msg.details = "This spring may be over-stressed if deflected to solid. It may 'set' as in not return to its original free length."; } if (this.props.symbol_table[o.FS_2].lmax = 'CONSTRAINED' && this.props.symbol_table[o.FS_2].value > this.props.symbol_table[o.FS_2].cmax) { msg.condition = "Over design concern" msg.severity = "info" msg.viewed = FALSE msg.control = "always" msg.details = "Factor of Safety at load point 2 exceeds its maximum constraint. This design may be excessively conservative. Suggest investigating a smaller wire diameter. Alternatively, increase the MAX constraint level or disable the MAX constraint"; } if (this.props.symbol_table[o.Life_Category].value > 1 && this.props.symbol_table[o.FS_Cycle_Life].lmin != 'CONSTRAINED') { msg.condition = "No FS_Cycle_Life constraint" msg.severity = "info" msg.viewed = FALSE msg.control = "always" msg.details = "A more restrictive Life_Category has been selected but the corresponding constraint on FS_Cycle_Life is not enabled. Suggest enabling FS_Cycle_Life MIN constraint. See Cycle Life section of Help Spring Overview and tutorial session tutor4"; } if (this.props.symbol_table[o.Force_2].lmin != 'FIXED' && this.props.symbol_table[o.Force_2].lmin != 'CONSTRAINED' && this.props.symbol_table[o.Rate].lmin != 'FIXED' && this.props.symbol_table[o.Rate].lmin != 'CONSTRAINED' && this.props.symbol_table[o.Rate].lmax != 'CONSTRAINED' && this.props.symbol_table[o.Deflect_2].lmin != 'FIXED' && this.props.symbol_table[o.Deflect_2].lmin != 'CONSTRAINED' && this.props.symbol_table[o.Deflect_2].lmax != 'CONSTRAINED' && this.props.symbol_table[o.L_2].lmin != 'FIXED' && this.props.symbol_table[o.L_2].lmin != 'CONSTRAINED' && this.props.symbol_table[o.L_2].lmax != 'CONSTRAINED'
) { msg.condition = "Under-specified?" msg.severity = "warn" msg.viewed = FALSE msg.control = "onSearchSeek" msg.details = "It appears that no constraints on Force_2, Rate, Deflect_2 or L_2 are enabled. The design may be under-specified. Search & Seek may generate trivial solutions. Refer to Help on Design Situations."; } if (this.props.symbol_table[o.Deflect_2].value <= 0.0) { this.hits++; msg.condition = "Deflect_2 <= 0" msg.severity = "warn" msg.viewed = FALSE msg.control = "always" msg.details = this.props.symbol_table[o.Deflect_2].name + " <= 0. Calculations for L_Stroke and Cycle_Life may not be valid. Confirm that input values are not negative and Force_2 is greater than Force_1."; } if (this.props.symbol_table[o.L_Stroke].value <= 0.0) { this.hits++; msg.condition = "L_Stroke <= 0" msg.severity = "warn" msg.viewed = FALSE msg.control = "always" msg.details = this.props.symbol_table[o.Deflect_2].name + " <= 0. Calculations for Cycle_Life may not be valid. Confirm that input values are not negative and Force_2 is greater than Force_1."; } if (this.props.symbol_table[o.Spring_Index].value < 4.0) { this.hits++; msg.condition = "Spring_Index < 4" msg.severity = "warn" msg.viewed = FALSE msg.control = "always" msg.details = this.props.symbol_table[o.Spring_Index].name + " less than 4 is difficult to manufacture. Suggest enabling the " + this.props.symbol_table[o.Spring_Index].name + " MIN constraint and possibly increasing the value of " + this.props.symbol_table[o.OD_Free].name + " to provide a better starting point for the next Search."; } if (this.props.symbol_table[o.Spring_Index].value > 25.0) { this.hits++; msg.condition = "Spring_Index > 25" msg.severity = "warn" msg.viewed = FALSE msg.control = "always" msg.details = this.props.symbol_table[o.Spring_Index].name + " greater than 25 is difficult to manufacture. Suggest enabling the " + this.props.symbol_table[o.Spring_Index].name + " MAX constraint and possibly decreasing the value of " + this.props.symbol_table[o.OD_Free].name + " to provide a better starting point for the next Search."; } if (this.props.symbol_table[o.Force_2].value <= this.props.symbol_table[o.Force_1].value) { this.hits++; msg.condition = "Force_2 <= Force_1" msg.severity = "warn" msg.viewed = FALSE msg.control = "always" msg.details = this.props.symbol_table[o.Force_2].name + " must be greater than " + this.props.symbol_table[o.Force_1].name + "."; }

At end of table of rendered messages: if (this.hits) { msg.condition = "Potential numeric difficulties" msg.severity = "warn" msg.viewed = FALSE msg.control = "always" msg.details = "Physically unrealistic values may cause numeric difficulties that the Search algorithm is not able to resolve. Consider adjusting values to create a more reasonable start point before continuing with Search, Seek or Trade. Any "NaN" values are "Not a Number". See Help Alerts for additional information."; }

Extension Spring

Continue with the pattern developed for compression springs.

    if (this.props.symbol_table[o.Force_1].value <  this.props.symbol_table[o.Initial_tension].value) {
        msg.condition = "Force_1 < Initial_Tension"
        msg.severity = "info"
        msg.viewed = FALSE
        msg.control = "always"
        msg.details = "The current value of Force_1 is less than the current value of Initial_Tension.
                       Calculations for L_Stroke and Cycle_Life may not be valid.
                       Suggest increasing the value of Force_1 so that it is greater than Initial_Tension. 
                       See Help Terminology FUNCTION CONSTRAINTS (FDCL) and
                       Help Extension Spring (Constraints unique to extension springs)";
    }

Torsion Spring

Continue with the pattern developed for compression springs.

grumpyinca commented 3 years ago

Consider having eqnset return a parameter that says "Out of bounds" or "Invalid combination of inputs" or "Not physically realistic".

This parameter could be checked before starting a search (resulting in pop-up warning) and also at each iteration in the search (resulting in search termination and return with an error condition). The error message may provide a suggestion as to how the user may correct the situation.

grumpyinca commented 3 years ago

Consider adding a "user having difficulty" measurement. If the measurement exceeds some threshold. trigger an alert that offers free technical support.

One possible "user having difficulty" measurement is the number of searches executed within the current session. I am thinking of a simple counter that does not persist. So for example, if the user executes more than 10? searches within the current session, an alert that says something along the lines of: "Having difficulty? Contact us"

grumpyinca commented 3 years ago

Should Alerts be automatically cleared at the time that they are viewed?

Should informational alerts that have been viewed multiple times in the current session be suppressed from recurrence ?

grumpyinca commented 3 years ago

Consider enhancing the Execute (script) facility to allow it to write alerts and result panel messages.

grumpyinca commented 2 years ago

To do items:

  1. {done in branch #698} Adjust all initialState files such that there are no alerts above info level pending when the user starts up. Note, the changes to compression initialState will need to be tested against demo and tutorial sessions.
  2. {done} Add design type sub-folders to /docs/Help/DesignTypes. Split the alerts.md file into a general plus 5 design type specific files.
  3. {done} Organize the alerts.md files by general / design type specific, then severity.
  4. {done #707} Put link to Alerts (and errors?) into the Help index.
  5. {Considered and rejected} Consider adding some kind of Alert notice for Save and Save As. Perhaps a pop-up if Severity level "Error" alerts are present ?
  6. {See #705} Review things that block search. Implement a trial search and then a pop-up if result is invalid.
  7. {done} Remove now redundant warnings in Report 1 & Calculator view. Replace "YOU MAY WISH TO CHOOSE A MORE REASONABLE START POINT ... " in Calculator View & Report 1 to something more appropriate. Remove NaNmsg.
grumpyinca commented 2 years ago

This comment may be more appropriate in issue 590.

Search needs to be able to start from an invalid start point. The pre-590 Search could do that. There were relatively common cases where a start from what post-590 considers to be an invalid start point produced a valid result.

Once Search can start from an invalid start point, it will be reasonable to move validity checks and UI elements (pop-ups) from before Search to after Search. The pop-up will be necessary only for the case of an invalid result.

DONE - before 6/26/2022 BDW & MKM

grumpyinca commented 2 years ago

The current state of branch 569 has a minor bug in failing to highlight constraint violations in the green zone. This is true in both Advanced and Calculator Views.

To reproduce the problem:

  1. Open Torsion spring initialState
  2. Change value of M_2 to 5.0
  3. Look at %_Safe_Deflect (Advanced View) / Deflection at point 2 (maximum operating load) is 95.23 % of total safe deflection. (Calculator View)
  4. Observe the constraint violation (0.2411) and black color. This is the bug. A violated constraint should take the color indicated by the multi-color feasibility indicator (green in this case). The fact that there is no alert is as expected. We put in code to suppress alerts for violated constraints when OBJ < OBJMIN. I suspect that code is also killing the green color.
  5. Observe that sufficiently increasing the constraint violation will change the color to orange or red and create an alert, just as expected.

THIS IS GOING TO BE IGNORED AND NOT DONE - WON'T FIX - 6/25/2022 BDW AND MKM.

grumpyinca commented 2 years ago

The current state of branch 569 has a minor bug in that it can carry "stale" alert triangle(s) across a change of design type. To demonstrate this behavior:

  1. Reload the app (compression spring, startup)
  2. Enter a value of 2.5 for Wire_Dia
  3. File : Open - Torsion Startup
  4. Observe that most (if not all) the alert triangles from Compression have carried over to Torsion. <-- This is the bug.
  5. File : Open - Torsion Startup (a second time)
  6. Observe that there are no remaining alert triangles.

FIXED AND DONE - before 6/25/2022.

grumpyinca commented 2 years ago

This comment is more a question than a specification for a change in app behavior.

Would it be better for alert severity to adapt a density range of a single color (purple?, violet?) as opposed to the current overlap with the color scheme of the multi-color feasibility indicator ? The highest severity would be the brightest possible shade of the selected color. The decreasing severity levels could have darker shades of the single color until the lowest severity ("Info") would be black.

The multi-color feasibility indicator would get a narrow band of the selected color at the right end. Objective Value of Infinity would point there.

What about changing the severity designations from (Err, Warn, Notice, Info) to (Invalid, Concern, Notice, Info) ?

NOT USE A COLOR, BUT INSTEAD USE BLACK and FONT STYLE (ITALIC) & WIDTH (BOLD) CHANGES. NO POS_INFINITY SO WE ARE NOT GOING TO MODIFY THE FEASIBILITY INDICATOR. NOR WILL WE CHANGE THE SEVERITY DESIGNATION. - 6/25/2022 BDW AND MKM.

1fifoto commented 2 years ago

Merged into master, closing