Hysteresis and Avalanches: Windows

Physics 681, Materials Simulations

Jim Sethna, Spring 1999

  1. Start up Visual C++, and start a new MFC App Wizard exe; call it HysteresisWin
  2. File-Projects-MFC App Wizard (exe)

  3. Make it a single document, and otherwise accept the defaults.
  4. Copy the four files you developed last lab (excepting the main loop), and rand.h, into the directory. Add them to the project.
  5. (Technical Microsoft jibberish): Add #include "stdafx.h" before any other includes to your two .cpp files. (In the other assignments, I’ve instead deleted /Yu "stdafx.h" from Project-Settings-C/C++ General-Project Headings. Microsoft adds a huge header file to all its MFC C++ files, and pre-compiles it so that you don’t complain about slow performance: it assumes everything up to stdafx.h is pre-compiled, and complains if it’s not there.) A more elegant solution would be nice. Compile and test.
  6. We’ll add our simulation to the CHysteresisWinView class. (It’s more convenient but less standard than adding it to the Doc class.) Add a member variable HysteresisSimulation *sim, add BruteForceSimulation.h to the header file, and create sim as a new BruteForceSimulation(3.0, 3, 1) in the constructor. Delete sim in the destructor. Compile and debug.
  7. Add a "Run" button to the simulation. ResourceView-Toolbar-IDR_MAINFRAME, edit the last grey button, View Properties, change ID to RUN_BUTTON and add a caption if you wish (Long-winded caption\nShort). View Class Wizard, add a Message Map to ID_RUN_BUTTON, set Class Name to CHysteresisWinView, add a COMMAND function, and Edit Code. Add a loop to flip all the avalanches until the system is fully magnetized upwards, just as you did in the main loop last lab. Debug to make sure the simulation works. Change the size to L=100, and make sure it runs in a reasonable time.
  8. Now, let’s add the graphics. We need a way for HysteresisSimulation to send information to CHysteresisWinView about what spins flip when. The proper way is to make CHysteresisWinView an observer, and have two kinds of notification (one passing the spin, and one ending the avalanche). We’ll do it a less elegant, but simpler way. Add a pointer to a CClientDC, called pDC, as a member variable to HysteresisSimulation. (DC is a device context, which you can think of as the canvas to which the view draws.) Add a SetDC member function to allow one to set the pointer.
  9. Add a COLORREF member variable currentColor to HysteresisSimulation.
  10. In BruteForceSimulation, remove the print statements from FlipSpin and AvalancheDone (in a Windows simulation, the print statements go into limbo and are never seen). In AvalancheDone, use RGB to set currentColor to a random value. (RGB(255,0,0), for example, is red.) You can use rand.discrete(0,255) to pick a number in the range. In FlipSpin, set one pixel to the current color for each spin flipped:
  11. pDC->SetPixel(i,j,currentColor).

    Set up the pointer to the device context and initialize the color map to black in OnRunButton:

    CClientDC dc(this);

    sim->SetDC(&dc);

    dc.PatBlt(0,0,sim->GetL(),sim->GetL(),BLACKNESS);

    while () { … }

  12. Compile, and run.
  13. Try varying the disorder R. For smaller disorder, should the avalanches get larger or smaller? We believe the distribution of avalanches becomes self-similar as the disorder is lowered, but the evidence in two dimensions isn’t as convincing as it is in three dimensions.
  14. Try a bigger simulation: when does it get painfully slow? Next lab we’ll implement a much more efficient algorithm, based on sorted lists.
  15. For those with time and ambition, you might try some of the following:

  16. Add a loop, so you can run repeated simulations with different seeds. The easiest way to do it is to delete the existing simulation and recreate it. Remember to save the old R, L, and seed. A more flexible way is to set up a parameter dialog box: each time you run another simulation, you can change the size, randomness, and perhaps the algorithm... Go to Resource View-Dialog, and Insert Dialog with the right mouse button. View Properties, and change the name to IDD_PARAM. Add text boxes and edit boxes for R, L, and the seed. Change the ID for each of the edit boxes to an appropriate name using Properties. Using the Class Wizard, generate a class ParameterDialog for the dialog box, and add variables m_R, m_L, and m_Seed to the three control ID’s for the three edit boxes. Add a member function void OnParamChange() to the View class:
  17. ParameterDialog dialog_box;

    dialog_box.m_L = sim->GetL();

    ...

    if (dialog_box.DoModal() == IDOK)

    { delete sim;

    sim = new BruteForceSimulation(dialog_box.m_R, ...) }

    Then change OnRunButton to call OnParamChange() first.

  18. Make a subject and observer class for avalanche simulations. These should have two notify commands: one passing the (i,j) coordinates for the spin that flipped, and one that announces the end of an avalanche. Make CHysteresisWinView into an observer. Make an observer that keeps a histogram of the avalanche sizes. You may want to use logarithmic binning.
  19. Change your BruteForceSimulation so that it works in all dimensions. Add D as a member variable to HysteresisSimulation. If you continue just plotting the (i,j) coordinates in the view, you’ll see a top view of the avalanches in three dimensions.
  20. Set up a separate thread for the computation. This is a bit fussy: we’re really supposed to pass a handle to the view between threads, not the view. This version seems to work for now...

  21. Add a new thread function to the top of CHysteresisWinView.cpp, called simThread. It passes a parameter which contains a pointer to the view: get the simulation and the device context from the view, and set the DC in sim:
  22. UINT simThread(LPVOID param)

    { CHysteresisWinView *view = (CHysteresisWinView *) param;

    CClientDC dc(view);

    HysteresisSimulation *sim = view->sim;

    sim->SetDC(&dc);

    // stuff from OnRunButton() here; }

  23. Now, change OnRunButton to start the thread: AfxBeginThread(simThread, this). The "this" passes the view to simThread as the parameter.
  24. This version of threading will cause error messages if you shut down the application or change the parameters during a simulation. This is because the simulation is being destroyed and the memory returned to the system while the thread is still flipping spins. To fix these problems, you’ll need to set up signaling between the two threads. Add a volatile bool threadHaltedFlag to the View class, and also a CEvent threadHaltedEvent. For the main thread, the flag will say whether the simulation thread is running: for the simulation thread, the flag will tell it when to stop. The CEvent is there to confirm that the thread actually has finished halting. In the constructor for the View class, initialize threadHaltedFlag to TRUE. In OnRunButton, just before the thread is launched, set it to FALSE. In the destructor and in OnParamChange (if you have a dialog box), just before deleting sim you should check threadHaltedFlag to see if the simulation is running. If so, set it to TRUE, and call threadHaltedEvent.Lock() (to wait for the thread to acknowledge its demise) and threadHaltedEvent.Unlock() (to make Microsoft happy: it’s some convention.) In the thread, you’ll want to check after each avalanche whether threadHaltedFlag has been set; if so, halt. Before returning from the loop, call

view->threadHaltedEvent.SetEvent() to acknowledge, and set threadHaltedFlag to TRUE (in case this is a normal termination).

Statistical Mechanics: Entropy, Order Parameters, and Complexity, now available at Oxford University Press (USA, Europe).