Spring-Mass_Hysteresis_Simulation/ source code Spring-Mass_Hysteresis_Simulation.zip |
Exploring Hysteresis with a V-Shaped Spring-Mass System Simulation
Understanding complex physical phenomena can be made easier and more engaging through interactive simulations. Today, we delve into the hysteresis effect using a V-shaped spring-mass system simulation, inspired by the work of Christopher Ong. This simulation allows you to visualize how hysteresis occurs in such a system, illustrating the energy loss typically observed due to this phenomenon.
What is Hysteresis?
Hysteresis is a phenomenon where the state of a system depends not only on its current conditions but also on its history. In other words, the path of deformation and recovery of the system differs, leading to energy dissipation, usually in the form of heat. This is commonly seen in magnetic materials, elastic hysteresis in rubbers, and mechanical systems like the one we are exploring here.
The Simulation
This simulation models a V-shaped spring-mass system, where the mass is connected to two springs anchored at fixed points. By adjusting various parameters, you can see how the system behaves under different conditions and observe the hysteresis effect in action.
Features of the Simulation
- External Force Adjustment: Apply an upward force to the mass to see how it affects the system.
- Spring Natural Length: Change the natural length of the springs to see how the system's equilibrium changes.
- Mass: Adjust the mass of the object to observe how inertia influences the system's behavior.
- Damping Factor: Modify the damping factor to see how energy dissipation affects the system.
- Gravity: Change the gravity to simulate different gravitational environments.
How to Use the Simulation
- Play/Pause: Start or pause the simulation.
- Step: Advance the simulation step by step to observe gradual changes.
- Reset: Reset all parameters to their default values.
The controls are intuitive and allow for a hands-on approach to learning about hysteresis.
Description of the Simulation
This simulation demonstrates the hysteresis effect in a V-shaped spring-mass system based on the work of Christopher Ong. For more details, you can refer to the original paper.
Hysteresis occurs when the path of deformation and recovery of the spring-mass system differ, leading to energy loss typically as heat. The simulation models the forces acting on the mass, including spring forces, damping, external force, and gravity.
Created by weelookang@gmail.com using Claude (initial code) and GPT-4 (finishing the code).
Visual Representation
Code Implementation
Here's a snippet of the code that powers the simulation. Feel free to explore and modify it to suit your learning needs.
html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Spring-Mass Hysteresis Simulation</title>
<style>
/* Styling for the body, container, and canvas */
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
.container {
background-color: rgb(110, 100, 100);
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
width: 100%;
box-sizing: border-box;
position: relative;
}
canvas {
border: 1px solid #ddd;
width: 100%;
height: 60vh;
}
.controls {
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: center;
margin-bottom: 10px;
}
.controls h1 {
margin: 0;
margin-right: 20px;
font-size: 1.5em;
color: white;
flex: 1 100%;
text-align: center;
}
button {
margin-right: 5px;
margin-top: 5px;
display: inline-block;
}
.slider-container {
margin-bottom: 10px;
flex: 1 100%;
text-align: center;
}
.slider-container label {
display: inline-block;
width: 150px;
}
.slider-container input {
width: 50%;
}
.value-display {
display: inline-block;
width: 50px;
text-align: right;
}
#info {
position: relative;
text-align: center;
color: white;
margin-bottom: 10px;
}
#footer {
margin-top: 10px;
color: rgb(110, 100, 100);
text-align: center;
}
#description {
color: rgb(110, 100, 100);
text-align: center;
margin-top: 10px;
margin-bottom: 10px;
}
@media (max-width: 768px) {
.slider-container label {
width: auto;
display: block;
margin-bottom: 5px;
}
.slider-container input {
width: 100%;
}
.controls {
flex-direction: column;
}
}
</style>
</head>
<body>
<div class="container">
<!-- Control buttons for play/pause, step, and reset -->
<div class="controls">
<h1>Spring-Mass Hysteresis Simulation</h1>
<div>
<button id="playPauseBtn">Play</button>
<button id="stepBtn">Step</button>
<button id="resetBtn">Reset</button>
</div>
</div>
<!-- Slider for adjusting external force -->
<div class="slider-container">
<label for="forceSlider">External Force (Upward):</label>
<input type="range" id="forceSlider" min="-40" max="40" step="1" value="0">
<span>Force: <span id="forceValue" class="value-display">0</span></span>
</div>
<!-- Slider for adjusting spring natural length -->
<div class="slider-container">
<label for="lengthSlider">Spring Natural Length:</label>
<input type="range" id="lengthSlider" min="50" max="150" step="1" value="100">
<span>Length: <span id="lengthValue" class="value-display">100</span></span>
</div>
<!-- Additional sliders -->
<div class="slider-container">
<label for="massSlider">Mass:</label>
<input type="range" id="massSlider" min="0.5" max="5" step="0.1" value="1">
<span>Mass: <span id="massValue" class="value-display">1</span></span>
</div>
<div class="slider-container">
<label for="dampingSlider">Damping Factor:</label>
<input type="range" id="dampingSlider" min="0" max="1" step="0.01" value="0.1">
<span>Damping: <span id="dampingValue" class="value-display">0.1</span></span>
</div>
<div class="slider-container">
<label for="gravitySlider">Gravity:</label>
<input type="range" id="gravitySlider" min="-9.81" max="9.81" step="0.1" value="-9.81">
<span>Gravity: <span id="gravityValue" class="value-display">-9.81</span></span>
</div>
<!-- Canvas for drawing the simulation -->
<canvas id="simCanvas"></canvas>
<!-- Info section to display time and mass position -->
<div id="info">Time: 0.00s | Mass Position: (0.00, 0.00)</div>
</div>
<!-- Description of the simulation -->
<div id="description">
<p>This simulation demonstrates the hysteresis effect in a V-shaped spring-mass system based on the work of Christopher Ong. For more details, you can refer to the <a href="https://pubs.aip.org/aapt/ajp/article-abstract/89/7/663/1056905/Hysteresis-in-a-simple-V-shaped-spring-mass-system?redirectedFrom=fulltext" target="_blank">original paper</a>.</p>
<p>Hysteresis occurs when the path of deformation and recovery of the spring-mass system differ, leading to energy loss typically as heat.</p>
<p>The simulation models the forces acting on the mass, including spring forces, damping, external force, and gravity.</p>
</div>
<!-- Footer for credits -->
<div id="footer">
Created by weelookang@gmail.com using Claude (initial code) and GPT-4 (finishing the code)
</div>
<!-- Google Analytics and Ads scripts -->
<script async="true" src="https://www.googletagmanager.com/gtag/js?id=G-S9EWRY1CPJ"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-S9EWRY1CPJ');
</script>
<script data-ad-client="ca-pub-0121577198857509" async="true" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<script>
// Get references to HTML elements
const canvas = document.getElementById('simCanvas');
const ctx = canvas.getContext('2d');
const playPauseBtn = document.getElementById('playPauseBtn');
const stepBtn = document.getElementById('stepBtn');
const resetBtn = document.getElementById('resetBtn');
const forceSlider = document.getElementById('forceSlider');
const forceValue = document.getElementById('forceValue');
const lengthSlider = document.getElementById('lengthSlider');
const lengthValue = document.getElementById('lengthValue');
const massSlider = document.getElementById('massSlider');
const massValue = document.getElementById('massValue');
const dampingSlider = document.getElementById('dampingSlider');
const dampingValue = document.getElementById('dampingValue');
const gravitySlider = document.getElementById('gravitySlider');
const gravityValue = document.getElementById('gravityValue');
const info = document.getElementById('info');
let isPlaying = false; // Variable to track if the simulation is playing
let externalForce = 0; // External force acting on the mass
let massPosition = { x: 0, y: 0 }; // Position of the mass
let massVelocity = { x: 0, y: 0 }; // Velocity of the mass
let time = 0; // Simulation time
// Constants for the spring and mass system
const k = 0.5; // Spring constant
let m = 1; // Mass
let L0 = 100; // Natural length of springs
let dampingFactor = 0.1; // Damping factor for the system
let gravity = -9.81; // Gravitational acceleration
const dt = 0.1; // Time step for the simulation
// Function to resize canvas dimensions
function resizeCanvas() {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
drawSystem();
}
// Function to draw an arrow on the canvas
function drawArrow(fromx, fromy, tox, toy, color) {
const headlen = 10; // Length of arrowhead in pixels
const dx = tox - fromx; // Change in x
const dy = toy - fromy; // Change in y
const angle = Math.atan2(dy, dx); // Angle of the arrow
ctx.strokeStyle = color; // Set arrow color
ctx.beginPath();
ctx.moveTo(fromx, fromy);
ctx.lineTo(tox, toy);
ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 6), toy - headlen * Math.sin(angle - Math.PI / 6));
ctx.moveTo(tox, toy);
ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 6), toy - headlen * Math.sin(angle + Math.PI / 6));
ctx.stroke();
}
// Function to draw a spring between two points
function drawSpring(x1, y1, x2, y2, naturalLength, segments = 20) {
const dx = x2 - x1; // Change in x
const dy = y2 - y1; // Change in y
const length = Math.sqrt(dx * dx + dy * dy); // Length of the spring
const angle = Math.atan2(dy, dx); // Angle of the spring
ctx.save(); // Save the current context state
ctx.translate(x1, y1); // Translate the context to the starting point of the spring
ctx.rotate(angle); // Rotate the context to align with the spring
const segmentLength = length / segments; // Length of each segment
const amplitude = segmentLength / 2; // Amplitude of the zigzag
ctx.beginPath();
ctx.moveTo(0, 0);
for (let i = 1; i <= segments; i++) {
const x = i * segmentLength;
const y = (i % 2 === 0 ? 1 : -1) * amplitude;
ctx.lineTo(x, y);
}
// Set the color based on spring length
if (length < naturalLength) {
ctx.strokeStyle = 'orange'; // Compression
} else {
ctx.strokeStyle = 'yellow'; // Extension
}
ctx.stroke();
ctx.restore(); // Restore the previous context state
}
// Function to draw the entire system
function drawSystem() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
const centerX = canvas.width / 2; // Center x-coordinate
const centerY = canvas.height / 2; // Center y-coordinate
// Draw left spring
drawSpring(centerX - 100, centerY - 100, centerX + massPosition.x, centerY + massPosition.y, L0);
// Draw right spring
drawSpring(centerX + 100, centerY - 100, centerX + massPosition.x, centerY + massPosition.y, L0);
// Draw the mass
ctx.beginPath();
ctx.arc(centerX + massPosition.x, centerY + massPosition.y, 10, 0, 2 * Math.PI);
ctx.fillStyle = '#f00';
ctx.fill();
// Draw anchor points
ctx.beginPath();
ctx.arc(centerX - 100, centerY - 100, 5, 0, 2 * Math.PI);
ctx.arc(centerX + 100, centerY - 100, 5, 0, 2 * Math.PI);
ctx.fillStyle = '#00f';
ctx.fill();
// Draw the external force vector if any
if (externalForce !== 0) {
const forceScale = 5; // Scale factor for force visualization
drawArrow(
centerX + massPosition.x,
centerY + massPosition.y,
centerX + massPosition.x,
centerY + massPosition.y - externalForce * forceScale,
'purple'
);
}
}
// Function to update the position of the mass
function updatePosition() {
const dx1 = massPosition.x + 100; // Distance from left spring anchor
const dy1 = massPosition.y + 100; // Distance from left spring anchor
const dx2 = massPosition.x - 100; // Distance from right spring anchor
const dy2 = massPosition.y + 100; // Distance from right spring anchor
const L1 = Math.sqrt(dx1 * dx1 + dy1 * dy1); // Length of left spring
const L2 = Math.sqrt(dx2 * dx2 + dy2 * dy2); // Length of right spring
const Fx1 = -k * (L1 - L0) * dx1 / L1; // Force by left spring in x
const Fy1 = -k * (L1 - L0) * dy1 / L1; // Force by left spring in y
const Fx2 = -k * (L2 - L0) * dx2 / L2; // Force by right spring in x
const Fy2 = -k * (L2 - L0) * dy2 / L2; // Force by right spring in y
const Fx = Fx1 + Fx2; // Total force in x
const Fy = Fy1 + Fy2 - externalForce - m * gravity; // Total force in y, include external force and gravity
const ax = Fx / m - dampingFactor * massVelocity.x; // Acceleration in x
const ay = Fy / m - dampingFactor * massVelocity.y; // Acceleration in y
massVelocity.x += ax * dt; // Update velocity in x
massVelocity.y += ay * dt; // Update velocity in y
massPosition.x += massVelocity.x * dt; // Update position in x
massPosition.y += massVelocity.y * dt; // Update position in y
time += dt; // Increment time
drawSystem(); // Redraw the system
updateInfo(); // Update the info display
}
// Function to update the information display
function updateInfo() {
info.textContent = `Time: ${time.toFixed(2)}s | Mass Position: (${massPosition.x.toFixed(2)}, ${massPosition.y.toFixed(2)})`;
}
// Function to toggle the simulation play/pause state
function toggleSimulation() {
isPlaying = !isPlaying;
playPauseBtn.textContent = isPlaying ? 'Pause' : 'Play';
if (isPlaying) {
simulationLoop();
}
}
// Simulation loop to update the position continuously
function simulationLoop() {
if (isPlaying) {
updatePosition();
requestAnimationFrame(simulationLoop);
}
}
// Function to perform a single step of the simulation
function step() {
isPlaying = false;
playPauseBtn.textContent = 'Play';
updatePosition();
}
// Function to reset the simulation to its initial state
function reset() {
isPlaying = false;
playPauseBtn.textContent = 'Play';
massPosition = { x: 0, y: 0 };
massVelocity = { x: 0, y: 0 };
time = 0;
externalForce = 0;
forceSlider.value = 0;
forceValue.textContent = '0';
L0 = 100;
lengthSlider.value = 100;
lengthValue.textContent = '100';
m = 1;
massSlider.value = 1;
massValue.textContent = '1';
dampingFactor = 0.1;
dampingSlider.value = 0.1;
dampingValue.textContent = '0.1';
gravity = -9.81;
gravitySlider.value = -9.81;
gravityValue.textContent = '-9.81';
drawSystem();
updateInfo();
}
// Event listeners for the buttons and sliders
playPauseBtn.addEventListener('click', toggleSimulation);
stepBtn.addEventListener('click', step);
resetBtn.addEventListener('click', reset);
forceSlider.addEventListener('input', () => {
externalForce = parseFloat(forceSlider.value);
forceValue.textContent = externalForce.toFixed(1);
drawSystem();
});
lengthSlider.addEventListener('input', () => {
L0 = parseFloat(lengthSlider.value);
lengthValue.textContent = L0.toFixed(0);
drawSystem();
});
massSlider.addEventListener('input', () => {
m = parseFloat(massSlider.value);
massValue.textContent = m.toFixed(1);
drawSystem();
});
dampingSlider.addEventListener('input', () => {
dampingFactor = parseFloat(dampingSlider.value);
dampingValue.textContent = dampingFactor.toFixed(2);
drawSystem();
});
gravitySlider.addEventListener('input', () => {
gravity = parseFloat(gravitySlider.value);
gravityValue.textContent = gravity.toFixed(2);
drawSystem();
});
// Initial draw and info update
drawSystem();
updateInfo();
// Resize canvas when window is resized
window.addEventListener('resize', resizeCanvas);
resizeCanvas(); // Initial resize to set canvas dimensions
</script>
</body>
</html>
Activities to Try
- Observe Hysteresis: Apply an external force and vary it to see how the system’s path differs during loading and unloading.
- Damping Effects: Adjust the damping factor and observe how quickly the system returns to equilibrium. Note how energy dissipation changes with different damping values.
- Mass Variation: Change the mass and see how inertia affects the system’s response to external forces and damping.
- Gravity Simulation: Experiment with different gravity values to simulate the system on different planets or in a microgravity environment.
Conclusion
Interactive simulations like this provide a powerful tool for visualizing and understanding complex physical phenomena. By adjusting parameters and observing outcomes, you gain a deeper insight into concepts like hysteresis and the dynamics of spring-mass systems. Explore the simulation, modify the code, and share your findings!
Feel free to reach out with any questions or feedback on the simulation. Happy learning!
No comments:
Post a Comment