sympy / sympy

A computer algebra system written in pure Python

Home Page:https://sympy.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Symbolic specifying of locations that are "just" before or "just" after another location.

mvg2002 opened this issue · comments

In this Issue the code of PR #26594 is used.

In continuum mechancics it can be usefull to be able to specify if a location is "just" before or "just" after another location. This can be done numerically by adding $0.01$ to a location. For example if a user wants to place a moment load "just" after a hinge at location $x = 5$, they can do this by adding a moment load at location $x = 5.01$.

This works fine for numeric examples, but not for symbolic ones. If a hinge is located at $x = \frac{L}{2}$, with $L$ the lenght of the beam, a user cannot specify a moment load at location $x = \frac{L}{2} + 0.01$. This results in incorrect outputs (see example 2). Even if this would work this would not be ideal, because the $0.01$ would end up in all symbolic answers.

It would be much nicer if there was an option to specify symbolically that an item is placed at a location "just" before or "just" after another location. For example by using "-" or "+" in the location parameter. On paper this can already be done by writing the location as $\frac{L}{2}^-$ or $\frac{L}{2}^+$.

Example 1
An example where this would be usefull is if I wanted to add a hinge "just" before fixed support. This is physically possible and should be solvable by the Beam module.
image

The code would look like:

from sympy.physics.continuum_mechanics.beam import Beam
from sympy import Symbol

L = Symbol('L', positive='true')
F = Symbol('F')
E = Symbol('E')
I = Symbol('I')
b = Beam(L, E, I)
r0 = b.apply_support(0, type="pin")
rL, mL = b.apply_support(L, type="fixed")
b.apply_load(F, L/2, -1)
b.apply_rotation_hinge(L)
b.solve_for_reaction_loads(r0, rL, mL)
b.reaction_loads
{R_0: -3*E*I*P_L/L**2 - 5*F/16,
 R_L: 3*E*I*P_L/L**2 - 11*F/16,
 M_L: 3*E*I*P_L/L - 3*F*L/16}

This (probably) happens because in the code the support is placed before the hinge. something like this:
image
Which can't be solved for P_L (the rotation in the hinge). In the ideal situation it would be possible to use the line `b.apply_rotation_hinge(L, "-"). Which would place the hinge just before the fixed support.



Example 2
Another example of this is visible when trying to place a moment load just after a hinge. Like this:
image
When placing it on top of each other (which should not be possible!) the code places the load in front of the hinge.

L = Symbol('L', positive='true')
M = Symbol('M')
E = Symbol('E')
I = Symbol('I')
b = Beam(L, E, I)
r0 = b.apply_support(0, type="pin")
rL, mL = b.apply_support(L, type="fixed")
b.apply_load(M, (L/2), -2)
b.apply_rotation_hinge(L/2)
b.solve_for_reaction_loads(r0, rL, mL)
b.reaction_loads
{R_0: -2*M/L, R_L: 2*M/L, M_L: M}

Using the $+0.01$ in the line for the load: b.apply_load(M, (L/2)+0.01, -2) outputs {R_0: 0, R_L: 0, M_L: 0}. Which is also incorrect.

Conclusion
This problem can be fixed by adding the possibility to specify "+" or "-" when inputting a location. This will determine whether an item is placed "just" before or "just" after another item.

As this is for a school project I will tag my supervisors: @Tom-van-Woudenberg and @moorepants

Can the epsilon symbol be used to track this symbolically?

@moorepants, It is possible to use the epsilon symbol here. This will result in answers that still contain singularity functions. Example 1 with the use of epsilon will look like:

from sympy.physics.continuum_mechanics.beam import Beam
from sympy import Symbol
L = Symbol('L', positive='true')
eps = Symbol('eps', positive='true')
F = Symbol('F')
E = Symbol('E')
I = Symbol('I')
b = Beam(L, E, I)
r0 = b.apply_support(0, type="pin")
rL, mL = b.apply_support(L, type="fixed")
b.apply_load(F, L/2, -1)
b.apply_rotation_hinge(L-eps)
b.solve_for_reaction_loads(r0, rL, mL)
b.reaction_loads
{R_0: -F*SingularityFunction(L - eps, L/2, 1)/SingularityFunction(L - eps, 0, 1),
 R_L: -F + F*SingularityFunction(L - eps, L/2, 1)/SingularityFunction(L - eps, 0, 1),
 M_L: -F*L/2 + F*L*SingularityFunction(L - eps, L/2, 1)/SingularityFunction(L - eps, 0, 1)}

Simplifying this with the knowledge that eps is very close to 0 gives:
{R_0: -F/2, R_L: -F/2, M_L: 0}

This is correct. The problem is that sympy does not know that eps is very close to zero and is only there to show which item should be evaluated first. That is why it can't solve the SingularityFunction(L - eps, L/2, 1) to L/2.

This problem can be solved by hand for these type of equations, but for larger equations this will become a lot of work.

There used to be an infinitesimal assumption that symbols could use. Use of a specific symbol seems fragile. What about using an expression eps = 1/Dummy('OO', infinite=True)? Used with L=var('L', positive=True) gives SingularityFunction(L - eps, L/2, 1)->L/2-1/OO For clean-up, replace with oo and you should have what you want.

@smichr Thanks for thinking with me. This is something I would not have come up with. The strange thing is that the solution changes when doing this.

from sympy.physics.continuum_mechanics.beam import Beam
from sympy import Symbol, Dummy, var

L = var('L', positive=True)
eps = 1/Dummy('OO', infinite=True)
F = Symbol('F')
E = Symbol('E')
I = Symbol('I')
b = Beam(L, E, I)
r0 = b.apply_support(0, type="pin")
rL, mL = b.apply_support(L, type="fixed")
b.apply_load(F, L/2, -1)
b.apply_rotation_hinge(L-eps)
b.solve_for_reaction_loads(r0, rL, mL)
b.reaction_loads

Outputs: {R_0: -F, R_L: 0, M_L: F*L/2}

It should not be possible to get a bending moment in the fixed support as there is a hinge right before it. I am not sure why this still happens.

A way that does work:

from sympy.physics.continuum_mechanics.beam import Beam
from sympy import Symbol, Dummy, var

L = var('L', positive=True)
eps = Dummy('eps', positive=True)
F = Symbol('F')
E = Symbol('E')
I = Symbol('I')
b = Beam(L, E, I)
r0 = b.apply_support(0, type="pin")
rL, mL = b.apply_support(L, type="fixed")
b.apply_load(F, L/2, -1)
b.apply_rotation_hinge(L - eps)
b.solve_for_reaction_loads(r0, rL, mL)
b.subbed_reaction_loads = {reaction: eq.subs(eps, 0) for reaction, eq in b.reaction_loads.items()}
b.subbed_reaction_loads

Ouputs: {R_0: -F/2, R_L: -F/2, M_L: 0}

This does not seem like the most robust way to do this, but does give the correct answer for this example.

Another strange thing. If you try the same code for example 2, the correct output depends on if you use b.apply_load(M, L/2+eps, -2) or b.apply_rotation_hinge(L/2-eps).

from sympy.physics.continuum_mechanics.beam import Beam
from sympy import Symbol, Dummy, var
L = var('L', positive=True)
eps = Dummy('eps', positive=True)
M = Symbol('M')
E = Symbol('E')
I = Symbol('I')
b = Beam(L, E, I)
r0 = b.apply_support(0, type="pin")
rL, mL = b.apply_support(L, type="fixed")
b.apply_load(M, L/2+eps, -2)
b.apply_rotation_hinge(L/2)
b.solve_for_reaction_loads(r0, rL, mL)
b.reaction_loads

Outputs: {R_0: 0, R_L: 0, M_L: 0}

While

from sympy.physics.continuum_mechanics.beam import Beam
from sympy import Symbol, Dummy, var
L = var('L', positive=True)
eps = Dummy('eps', positive=True)
M = Symbol('M')
E = Symbol('E')
I = Symbol('I')
b = Beam(L, E, I)
r0 = b.apply_support(0, type="pin")
rL, mL = b.apply_support(L, type="fixed")
b.apply_load(M, L/2, -2)
b.apply_rotation_hinge(L/2-eps)
b.solve_for_reaction_loads(r0, rL, mL)
b.reaction_loads

Outputs: {R_0: 0, R_L: 0, M_L: -M}, which is correct.

Rethinking about OO: if it is only infinite, then the sign is unknown so x+1/OO might be greater, equal or less than x. If you make it positive, there is no guarantee about ordering, e.g. x+eps might be bigger than y. But maybe (in this case) that is not a concern?

If you need a number that is guaranteed less than 1 you could use 1/Dummy(prime=True).