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

Dealing with over/properly/under-specified design situations #801

Open grumpyinca opened 1 year ago

grumpyinca commented 1 year ago

This issue is intended to document various discussions on how best to accommodate novice users that are having difficulty with over-specified design situations. While originally intended as a design discussion rather than providing a branch for code development, branches 801.1 and 792.1 have become a home for a simplified Hooke's Law Plus Lengths model.

The UML diagrams are transferred from Slack so as to provide a longer-term repository.

grumpyinca commented 1 year ago

UML diagrams generated from the eqnset code can identify dependency relationships and thus provide an organized way to recognize an over-specified design situation.

December 8, 2022

CompressionSpring

January 1, 2023

HookesLawDOFAnalysis

HookesLawDOFAnalysis.txt

grumpyinca commented 1 year ago

This diagram is intended as a possible way to represent over-specified situations. It is an incomplete rough draft.

Hooke'sLaw_DOF_Analysis.xlsx

Hooke'sLaw_DOF_Analysis.pdf

grumpyinca commented 1 year ago

Similar to the previous Hooke'sLaw_DOF_Analysis. More complete but in a more limited subset. FDgrid1.pdf FDgrid1.xlsx

Updated per Jan 14, 2023 conversation: FDgrid2.pdf FDgrid2.xlsx

grumpyinca commented 1 year ago

Proposal for the next generation of Calculator View

updated January 16, 2023
(Includes features previously described as "Data Entry View")

Objective:

Provide a data entry experience that decreases the likelihood of an over-specified design situation by selectively populating related values as the user makes entries. The notion is that alerting at the moment of first conflict will help user comprehension of the over-specification issue because yet-to-be-entered values and their dependencies are not a distraction. Similarly, perhaps the "progressive reveal" aspect of populating the value fields will also improve user comprehension of dependency relationships within the full set of variables.

Description:

Add a feature to Calculator View that can optionally hide quantities / variables that are not central to specifying the design. For compression springs, this might include Weight, Cycle_Life, Safe Load and Pitch.
When Calculator view starts, all value fields are empty (blank). The optional values described above default to hidden.
The current convention of white background fill indicating input and gray background fill indicating output continues.
See the "All fields empty" entry in Notes below.

Each time the user enters a value, the code scans related* values for their initialized / uninitialized status.
It is possible that some related values cannot be calculated because other inputs are still missing (uninitialized). Those fields remain empty.
If all necessary values are available (not empty), related empty values get over-written by calculation** from the new input value.

*Related
Related values are those that affect or are affected by the user entered value. The UML diagram above is one way of determining these relationships. These are the variables pointed to in the "sets" and "refs" properties.

**Calculation
Uninitialized values may be calculated by either:

In summary, there are three ways that a value can be established:

If the relationship check determines that a newly entered value allows the calculation of a variable that has already been initialized there is a possibility of over specification. If the existing value and the new calculation are identical within "engineering precision", no further action is required. If the values differ by a greater amount, the user should be alerted. See the "Alert user to possible over-specification" entry in Notes below.

When the relationship check calculates a new value, the algorithm should check the relationships of that new value, i.e. cascaded results or recursive descent.

Some form of "input completed" indication (more visible than just all fields populated) may be desirable. One possibility is a thumbs up icon with a tooltip providing a more detailed description. Another possibility is the presence or absence of the Multi-color Feasibility Indicator. Also, the option to reveal Weight, Cycle_Life, Safe Load and Pitch can appear at this time.

Compression Spring Example Sequence:

Start with all fields empty.
User selects Material_Type & End_type from table (keep defaults ?)

Key:

Ref User enters Fields get updated Relationship(s), Notes
1 Deflect_2 {none}
2 OD_Free {none}
3 Rate
3a Force_2 = iF(Rate, Deflect_2)
4 Wire_Dia
4a {Mean_Dia} = F(OD_Free, Wire_Dia)
4b     Spring_Index = F({Mean_Dia}, Wire_Dia)
4c Coils_A = iF(OD_Free, Rate, Wire_Dia, Hot_Factor_Kh)
    Rate = F({Mean_Dia}, Coils_A, Spring_Index, Torsion_Modulus)
4d     Coils_T = iF(Inactive_Coils, Coils_A)
4e     L_Solid = F(Coils_T, Wire_Dia, Add_Coils_Solid)
4f     Stress_2 = F({Mean_Dia}, Wire_Dia, Force_2)
4g     FS_2 = F(Stress_2, Stress_Lim_Stat)
4h     ID_Free = F({Mean_Dia}, Wire_Dia)
{Calc Input Stress_Lim_Stat is a function of Wire_Dia}
{ L_Free constrained by validity. See Notes below}
5 Force_1
5a Deflect_1 = F(Rate, Force_1)
5b     L_Stroke = iF(Deflect_2, Deflect_1)
5c Stress_1 = F({Mean_Dia}, Wire_Dia, Force_1)
5d     FS_1 = F(Stress_1, Stress_Lim_Stat)
6 L_Free
6a Force_Solid = F(L_Free, Rate, L_Solid)
6b     Stress_Solid = F(Force_Solid, {Mean_Dia}, Wire_Dia)
6c     FS_Solid = F(Stress_Solid, Stress_Lim_Stat)
6d L_1 = F(L_Free, Deflect_1)
6e L_2 = F(L_Free, Deflect_2)

Input completed. See "All fields populated" entry in Notes below.
Make associated UI changes (icon with tooltip, Multi-color Feasibility Indicator, enable optional quantities).
Now that input is complete, further operation of value changes, Search, Seek, etc. should resemble the legacy operation.
See "Allow the user to un-initialize a previously initialized variable" in Notes below.

Notes:

  1. All fields empty:
    AllEmpty

  2. Related to Auto-Search:
    An implementation where calculation results are determined by extracting the desired value(s) from the model after Fix & Search seems to be closely related to the previously discussed Auto-Search.

  3. Response to "Not Feasible" Search result:
    In an implementation where calculation results are determined by extracting the desired value(s) from the model after Fix & Search, a UI design challenge is encountered if Search returns "Not Feasible". Some sort of descriptive status message / alert / pop-up / tooltip is likely a first step. It seems reasonable to allow the user to enter a different value. Perhaps the system can suggest the (known to be inaccurate) value produced by Search. After that, are there other options that don't force the user to guess an appropriate value ?

  4. Alert user to possible over-specification:
    When alerting the user of a possible over specified situation, it is desirable to highlight all of the values involved. One possibility is to use the color of the background fill for the values involved. Perhaps the most recently entered value (the one that triggered the conflict alert) should get a light pink background while immediately related values get a light-yellow background.
    See February 4, 2023 comment below.

  5. Values become constrained by validity:
    In this compression spring example, the value of L_Free becomes constrained by prior entries that determine solid height. Any value specified for L_Free less than the solid height is not valid. Actually, this is just a special case of the previous note entry.
    Consider alternative ways to represent / communicate this situation. For example, once the still uninitialized value of L_Free becomes limited by the calculated value of L_Solid, is it reasonable to put (light gray?) text in the L_Free field that says: "> x.xx" where x.xx is the value of L_solid ? Large values of L_Free will cause a constraint on FS_Solid and perhaps FS_2 to be violated. Perhaps it would be best to place an info icon ("i" in blue circle) with tooltip into that field. The tooltip can provide the necessary detail including the upper and lower bounds for L_Free. Also, the same information can appear in the Calculator View Variable Value Input dialog box.

  6. All fields populated:

    AllPopulated
  7. Allow the user to un-initialize a previously initialized variable:
    Even after input is completed, the user should be able to blank a value in order to return to the situation where related values cascade and alerts to possible over-specification are generated. Currently, empty or blank values are not accepted inputs. Discussion suggests that a button labeled "clear", "reset", "empty" or something similar can be provided on the Calculator View Variable Value Input dialog box.
    Are there unintended consequences ?

  8. Do we need precedence ?
    There are several cases in this example where some sort of ordering is important. For example:

    • Mean_Dia (4a) must be calculated before Spring_Index (4b) and Stress_2 (4f)
    • Coils_A (4c) must be calculated before Coils_T (4d) and L_Solid (4e)
    • other examples are available
       
      Is there another kind of precedence to consider ?
1fifoto commented 1 year ago

See branch 801.1 for a simplified Hooke's Law Plus Lengths model. Sample screen shots below of Advanced and Calculator View

Screenshot 2023-01-15 at 4 30 06 PM

Screenshot 2023-01-15 at 4 30 16 PM

Proposed structure for adding eqns objects contains refs, eqn, and sets for "Hooke's Law plus Lengths".

    {
        "name": "Force_1",
        "eqns": [{
            "eqn": null, // User input
            "sets": [o.Rate, o.Deflect_1]
        },
        {
            "refs": [o.Rate, o.Deflect_1],
            "eqn": (rate, deflect_1) => {return deflect_1 * rate;}
            "sets": [o.L_1]
        }]
    },
    {
        "name": "Force_2",
        "eqns": [{
            "eqn": null, // User input
            "sets": [o.Rate, o.Deflect_2]
        },
        {
            "refs": [o.Rate, o.Deflect_2],
            "eqn": (rate, deflect_2) => {return deflect_2 * rate;}
            "sets": [o.L_2]
        }]
    },
    {
        "name": "Rate",
        "eqns": [{
            "eqn": null, // User input
            "sets": [o.Force_1, o.Deflect_1, o.Force_2, o.Deflect_2]
        },
        {
            "refs": [o.Force_1, o.Deflect_1],
            "eqn": (force_1, deflect_1) => {return force_1 / deflect_1;}
            "sets": [o.L_1]
        },
        {
            "refs": [o.Force_2, o.Deflect_2],
            "eqn": (force_2, deflect_2) => {return force_2 / deflect_2;}
            "sets": [o.L_2]
        }]
    },
    {
        "name": "Deflect_1",
        "eqns": [{
            "eqn": null, // User input
            "sets": [o.Force_1, o.Rate, o.L_Free, o.L_1]
        },
        {
            "refs": [o.Force_1, o.Rate],
            "eqn": (force_1, rate) => {return force_1 / rate;}
            "sets": [o.L_1]
        },
        {
            "refs": [o.L_Free, o.L_1],
            "eqn": (l_free, l_1) => {return l_free - l_1;}
            "sets": [o.Rate]
        }]
    },
    {
        "name": "Deflect_2",
        "eqns": [{
            "eqn": null, // User input
            "sets": [o.Force_2, o.Rate, o.L_Free, o.L_2]
        },
        {
            "refs": [o.Force_2, o.Rate],
            "eqn": (force_2, rate) => {return force_2 / rate;}
            "sets": [o.L_2]
        },
        {
            "refs": [o.L_Free, o.L_2],
            "eqn": (l_free, l_2) => {return l_free - l_2;}
            "sets": [o.Rate]
        }]
    },
    {
        "name": "L_Free",
        "eqns": [{
            "eqn": null, // User input
            "sets": [o.L_1, o.Deflect_1, o.L_2, o.Deflect_2]
        },
        {
            "refs": [o.L_1, o.Deflect_1],
            "eqn": (l_1, deflect_1) => {return deflect_1 + l_1;}
            "sets": [o.Rate]
        },
        {
            "refs": [o.L_2, o.Deflect_2],
            "eqn": (l_2, deflect_2) => {return deflect_2 + l_2;}
            "sets": [o.Rate]
        }]
    },
    {
        "name": "L_1",
        "eqns": [{
            "eqn": null, // User input
            "sets": [o.L_Free, o.Deflect_1]
        },
        {
            "refs": [o.L_Free, o.Deflect_1],
            "eqn": (l_free, deflect_1) => {return l_free - deflect_1;}
            "sets": [o.Rate]
        }]
    },
    {
        "name": "L_2",
        "eqns": [{
            "eqn": null, // User input
            "sets": [o.L_Free, o.Deflect_2]
        },
        {
            "refs": [o.L_Free, o.Deflect_2],
            "eqn": (l_free, deflect_2) => {return l_free - deflect_2;}
            "sets": [o.Rate]
        }]
    },
1fifoto commented 1 year ago

See How to store a javascript function in JSON

grumpyinca commented 1 year ago

Possible test cases for Hooke's Law (plus length). These are intended for the compression spring example.

Test Case 1

Set up then enter value for L_Stroke that requires a negative value for Deflect_1. In production, there is a default constraint for Deflect_1 Min of 0.0.

Ref User enters Fields get updated Relationship(s), Notes
1 Force_2 = 20 {none}
2 Deflect_2 = 2
2a Rate =10 = Force_2 / Deflect_2
3 L_Stroke = 2.5 {none} Any value >2.0 will conflict with Min constraint on Deflect_1

 

Test Case 2

Similar to above with different variables, different order.

Ref User enters Fields get updated Relationship(s), Notes
1 L_Free = 3.25 {none}
2 L_Stroke = 2.5 {none}
3 Force_2 = 20 {none}
4 Deflect_2 = 2.0 conflict with Min constraint on Deflect_1
4a Rate = Force_2 / Deflect_2

 

Test Case 3

Still working this case

Ref User enters Fields get updated Relationship(s), Notes
1 Deflect_1 = .5 {none}
2 Force_1 = 10 {none}
2a Rate = Force_1/ Deflect_1
3 L_1 = 4 ? L_Free gets a lower limit
4 L_2 = 2
4a Deflect_2 = ?
4b Force_2 = ?

 

Test Case 4

Originally written for the Hooke's Law example, this case has been expanded to the full Compression Spring model. Specify force & deflection at one point, force & length at the other and then learn about acceptable ranges for other variables. Try to specify L_Free.

Ref User enters Fields get updated Relationship(s), Notes
1 Force_1 = 10 {none}
2 Deflect_1 = .5
2a Rate {=20} = Force_1/ Deflect_1
3 Force_2 = 40
3a Deflect_2 {=2} = Force_2 / Rate
* L_Stroke could be calculated by Deflect_2 - Deflect_1
4 L_2 = 2.5
4a L_Free {=4.5} = Force_2 / Rate + L_2
* Any value of L_Free not = 4.5 is over-specified
4b L_1 {=4.0} = L_Free - Deflect_1
4c L_Stroke {=1.5} = L_1 - L_2
5 OD_Free = 1.0
* OD_Free is constrained to the range 0.54 - 1.07
6 Wire_Dia = 0.107 (Non Std)
* Wire_Dia is constrained to a narrow range
6a Stress_1 {=21826} = s_f * Force_1
6b Stress_2 {=87303} = s_f * Force_2
6c FS_1 {=5.974} = Stress_Lim_Stat / Stress_1
6d FS_2 {=1.493} = Stress_Lim_Stat / Stress_2
7 Coils_T = 15.23
* Coils_T is constrained to a single value
7a Coils_A {=13.23} = Coils_T - Inactive_Coils
7b L_Solid {=1.63} Wire_Dia * Coils_T + Add_Coils_Solid

All values should be populated at this point.

1fifoto commented 1 year ago

Latest dependency diagram for Compression Spring without any local variables

SpringCompression

1fifoto commented 1 year ago

Computed transitive closure and found that this is not what I wanted. Instead I want to find cycles.

grumpyinca commented 1 year ago

Regarding "Alert user to possible over-specification:" in the January 12, 2023 entry above ...

Today's discussion developed the idea that if the user enters a new (and conflicting) value for an already initialized value, a pop-up could have wording along the lines of:

In order to establish a different value for {this variable} 
check boxes to put one or more of these variables in Free status.
{set of variables with checkboxes}  

The set of variables with checkboxes would be those variables in fixed status that are known (via sets, refs & eqns) to determine the value of the variable that the user is entering.

A context-sensitive Help button may be provided in order to clarify the issues of over-specification.