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
This works fine for numeric examples, but not for symbolic ones. If a hinge is located at
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
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.
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:
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:
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 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)
.