Search This Blog

Saturday, July 29, 2023

Magnetohydrodynamic drive

From Wikipedia, the free encyclopedia
Yamato 1 on display in Kobe, Japan. The first working full-scale MHD ship.

A magnetohydrodynamic drive or MHD accelerator is a method for propelling vehicles using only electric and magnetic fields with no moving parts, accelerating an electrically conductive propellant (liquid or gas) with magnetohydrodynamics. The fluid is directed to the rear and as a reaction, the vehicle accelerates forward.

Studies examining MHD in the field of marine propulsion began in the late 1950s.

Few large-scale marine prototypes have been built, limited by the low electrical conductivity of seawater. Increasing current density is limited by Joule heating and water electrolysis in the vicinity of electrodes, and increasing the magnetic field strength is limited by the cost, size and weight (as well as technological limitations) of electromagnets and the power available to feed them. In 2023 DARPA launched the PUMP program to build a marine engine using superconducting magnets expected to reach a field strength of 20 Tesla.

Stronger technical limitations apply to air-breathing MHD propulsion (where ambient air is ionized) that is still limited to theoretical concepts and early experiments.

Plasma propulsion engines using magnetohydrodynamics for space exploration have also been actively studied as such electromagnetic propulsion offers high thrust and high specific impulse at the same time, and the propellant would last much longer than chemical rockets.

Principle

Illustration of the right-hand rule for the Lorentz force, cross product of an electric current with a magnetic field.

The working principle involves the acceleration of an electrically conductive fluid (which can be a liquid or an ionized gas called a plasma) by the Lorentz force, resulting from the cross product of an electric current (motion of charge carriers accelerated by an electric field applied between two electrodes) with a perpendicular magnetic field. The Lorentz force accelerates all charged particles, positive and negative species (in opposite directions). If either positive or negative species dominate the vehicle is put in motion in the opposite direction from the net charge.

This is the same working principle as an electric motor (more exactly a linear motor) except that in an MHD drive, the solid moving rotor is replaced by the fluid acting directly as the propellant. As with all electromagnetic devices, an MHD accelerator is reversible: if the ambient working fluid is moving relatively to the magnetic field, charge separation induces an electric potential difference that can be harnessed with electrodes: the device then acts as a power source with no moving parts, transforming the kinetic energy of the incoming fluid into electricity, called an MHD generator.

Crossed-field magnetohydrodynamic converters (linear Faraday type with segmented electrodes). A: MHD generator mode. B: MHD accelerator mode.

As the Lorentz force in an MHD converter does not act on a single isolated charged particle nor on electrons in a solid electrical wire, but on a continuous charge distribution in motion, it is a "volumetric" (body) force, a force per unit volume:

where f is the force density (force per unit volume), ρ the charge density (charge per unit volume), E the electric field, J the current density (current per unit area) and B the magnetic field.

Typology

MHD thrusters are classified in two categories according to the way the electromagnetic fields operate:

  • Conduction devices when a direct current flows in the fluid due to an applied voltage between pairs of electrodes, the magnetic field being steady.
  • Induction devices when alternating currents are induced by a rapidly varying magnetic field, as eddy currents. No electrodes are required in this case.

As induction MHD accelerators are electrodeless, they do not exhibit the common issues related to conduction systems (especially Joule heating, bubbles and redox from electrolysis) but need much more intense peak magnetic fields to operate. Since one of the biggest issues with such thrusters is the limited energy available on-board, induction MHD drives have not been developed out of the laboratory.

Both systems can put the working fluid in motion according to two main designs:

  • Internal flow when the fluid is accelerated within and propelled back out of a nozzle of tubular or ring-shaped cross-section, the MHD interaction being concentrated within the pipe (similarly to rocket or jet engines).
  • External flow when the fluid is accelerated around the whole wetted area of the vehicle, the electromagnetic fields extending around the body of the vehicle. The propulsion force results from the pressure distribution on the shell (as lift on a wing, or how ciliate microorganisms such as Paramecium move water around them).

Internal flow systems concentrate the MHD interaction in a limited volume, preserving stealth characteristics. External field systems on the contrary have the ability to act on a very large expanse of surrounding water volume with higher efficiency and the ability to decrease drag, increasing the efficiency even further.

Marine propulsion

A view through a tube in the thruster of Yamato I, at the Ship Science Museum in Tokyo. The electrode plates are visible top and bottom.
A view of the end of the thruster unit from Yamato I, at the Ship Science Museum in Tokyo.

MHD has no moving parts, which means that a good design might be silent, reliable, and efficient. Additionally, the MHD design eliminates many of the wear and friction pieces of the drivetrain with a directly driven propeller by an engine. Problems with current technologies include expense and slow speed compared to a propeller driven by an engine. The extra expense is from the large generator that must be driven by an engine. Such a large generator is not required when an engine directly drives a propeller.

The first prototype, a 3-meter (10-feet) long submarine called EMS-1, was designed and tested in 1966 by Stewart Way, a professor of mechanical engineering at the University of California, Santa Barbara. Way, on leave from his job at Westinghouse Electric, assigned his senior year undergraduate students to build the operational unit. This MHD submarine operated on batteries delivering power to electrodes and electromagnets, which produced a magnetic field of 0.015 tesla. The cruise speed was about 0.4 meter per second (15 inches per second) during the test in the bay of Santa Barbara, California, in accordance with theoretical predictions.

Later, a Japanese prototype, the 3.6-meter long "ST-500", achieved speeds of up to 0.6 m/s in 1979.

In 1991, the world's first full-size prototype Yamato 1 was completed in Japan after 6 years of research and development (R&D) by the Ship & Ocean Foundation (later known as the Ocean Policy Research Foundation). The ship successfully carried a crew of ten plus passengers at speeds of up to 15 km/h (8.1 kn) in Kobe Harbour in June 1992.

Small-scale ship models were later built and studied extensively in the laboratory, leading to successful comparisons between the measurements and the theoretical prediction of ship terminal speeds.

Military research about underwater MHD propulsion included high-speed torpedoes, remotely operated underwater vehicles (ROV), autonomous underwater vehicles (AUV), up to larger ones such as submarines.

Aircraft propulsion

Passive flow control

First studies of the interaction of plasmas with hypersonic flows around vehicles date back to the late 1950s, with the concept of a new kind of thermal protection system for space capsules during high-speed reentry. As low-pressure air is naturally ionized at such very high velocities and altitude, it was thought to use the effect of a magnetic field produced by an electromagnet to replace thermal ablative shields by a "magnetic shield". Hypersonic ionized flow interacts with the magnetic field, inducing eddy currents in the plasma. The current combines with the magnetic field to give Lorentz forces that oppose the flow and detach the bow shock wave further ahead of the vehicle, lowering the heat flux which is due to the brutal recompression of air behind the stagnation point. Such passive flow control studies are still ongoing, but a large-scale demonstrator has yet to be built.

Active flow control

Active flow control by MHD force fields on the contrary involves a direct and imperious action of forces to locally accelerate or slow down the airflow, modifying its velocity, direction, pressure, friction, heat flux parameters, in order to preserve materials and engines from stress, allowing hypersonic flight. It is a field of magnetohydrodynamics also called magnetogasdynamics, magnetoaerodynamics or magnetoplasma aerodynamics, as the working fluid is the air (a gas instead of a liquid) ionized to become electrically conductive (a plasma).

Air ionization is achieved at high altitude (electrical conductivity of air increases as atmospheric pressure reduces according to Paschen's law) using various techniques: high voltage electric arc discharge, RF (microwaves) electromagnetic glow discharge, laser, e-beam or betatron, radioactive source… with or without seeding of low ionization potential alkali substances (like caesium) into the flow.

MHD studies applied to aeronautics try to extend the domain of hypersonic planes to higher Mach regimes:

  • Action on the boundary layer to prevent laminar flow to become turbulent.
  • Shock wave mitigation for thermal control and reduction of the wave drag and form drag. Some theoretical studies suggest the flow velocity could be controlled everywhere on the wetted area of an aircraft, so shock waves could be totally cancelled when using enough power.
  • Inlet flow control.
  • Airflow velocity reduction upstream to feed a scramjet by the use of an MHD generator section combined with an MHD accelerator downstream at the exhaust nozzle, powered by the generator through an MHD bypass system.

The Russian project Ayaks (Ajax) is an example of MHD-controlled hypersonic aircraft concept. A US program also exists to design a hypersonic MHD bypass system, the Hypersonic Vehicle Electric Power System (HVEPS). A working prototype was completed in 2017 under development by General Atomics and the University of Tennessee Space Institute, sponsored by the US Air Force Research Laboratory. These projects aim to develop MHD generators feeding MHD accelerators for a new generation of high-speed vehicles. Such MHD bypass systems are often designed around a scramjet engine, but easier to design turbojets are also considered, as well as subsonic ramjets.

Such studies covers a field of resistive MHD with magnetic Reynolds number ≪ 1 using nonthermal weakly ionized gases, making the development of demonstrators much more difficult to realize than for MHD in liquids. "Cold plasmas" with magnetic fields are subject to the electrothermal instability occurring at a critical Hall parameter, which makes full-scale developments difficult.

Prospects

MHD propulsion has been considered as the main propulsion system for both marine and space ships since there is no need to produce lift to counter the gravity of Earth in water (due to buoyancy) nor in space (due to weightlessness), which is ruled out in the case of flight in the atmosphere.

Nonetheless, considering the current problem of the electric power source solved (for example with the availability of a still missing multi-megawatt compact fusion reactor), one could imagine future aircraft of a new kind silently powered by MHD accelerators, able to ionize and direct enough air downward to lift several tonnes. As external flow systems can control the flow over the whole wetted area, limiting thermal issues at high speeds, ambient air would be ionized and radially accelerated by Lorentz forces around an axisymmetric body (shaped as a cylinder, a cone, a sphere…), the entire airframe being the engine. Lift and thrust would arise as a consequence of a pressure difference between the upper and lower surfaces, induced by the Coandă effect. In order to maximize such pressure difference between the two opposite sides, and since the most efficient MHD converters (with a high Hall effect) are disk-shaped, such MHD aircraft would be preferably flattened to take the shape of a biconvex lens. Having no wings nor airbreathing jet engines, it would share no similarities with conventional aircraft, but it would behave like a helicopter whose rotor blades would have been replaced by a "purely electromagnetic rotor" with no moving part, sucking the air downward. Such concepts of flying MHD disks have been developed in the peer review literature from the mid 1970s mainly by physicists Leik Myrabo with the Lightcraft, and Subrata Roy with the Wingless Electromagnetic Air Vehicle (WEAV).

These futuristic visions have been advertised in the media although they still remain beyond the reach of modern technology.

Spacecraft propulsion

A number of experimental methods of spacecraft propulsion are based on magnetohydrodynamics. As this kind of MHD propulsion involves compressible fluids in the form of plasmas (ionized gases) it is also referred to as magnetogasdynamics or magnetoplasmadynamics.

In such electromagnetic thrusters, the working fluid is most of the time ionized hydrazine, xenon or lithium. Depending on the propellant used, it can be seeded with alkali such as potassium or caesium to improve its electrical conductivity. All charged species within the plasma, from positive and negative ions to free electrons, as well as neutral atoms by the effect of collisions, are accelerated in the same direction by the Lorentz "body" force, which results from the combination of a magnetic field with an orthogonal electric field (hence the name of "cross-field accelerator"), these fields not being in the direction of the acceleration. This is a fundamental difference with ion thrusters which rely on electrostatics to accelerate only positive ions using the Coulomb force along a high voltage electric field.

First experimental studies involving cross-field plasma accelerators (square channels and rocket nozzles) date back to the late 1950s. Such systems provide greater thrust and higher specific impulse than conventional chemical rockets and even modern ion drives, at the cost of a higher required energy density.

Some devices also studied nowadays besides cross-field accelerators include the magnetoplasmadynamic thruster sometimes referred to as the Lorentz force accelerator (LFA), and the electrodeless pulsed inductive thruster (PIT).

Even today, these systems are not ready to be launched in space as they still lack a suitable compact power source offering enough energy density (such as hypothetical fusion reactors) to feed the power-greedy electromagnets, especially pulsed inductive ones. The rapid ablation of electrodes under the intense thermal flow is also a concern. For these reasons, studies remain largely theoretical and experiments are still conducted in the laboratory, although over 60 years have passed since the first research in this kind of thrusters.

Fiction

Oregon, a ship in the Oregon Files series of books by author Clive Cussler, has a magnetohydrodynamic drive. This allows the ship to turn very sharply and brake instantly, instead of gliding for a few miles. In Valhalla Rising, Clive Cussler writes the same drive into the powering of Captain Nemo's Nautilus.

The film adaptation of The Hunt for Red October popularized the magnetohydrodynamic drive as a "caterpillar drive" for submarines, a nearly undetectable "silent drive" intended to achieve stealth in submarine warfare. In reality, the current traveling through the water would create gases and noise, and the magnetic fields would induce a detectable magnetic signature. In the film, it was suggested that this sound could be confused with geological activity. In the novel from which the film was adapted, the caterpillar that Red October used was actually a pump-jet of the so-called "tunnel drive" type (the tunnels provided acoustic camouflage for the cavitation from the propellers).

In the Ben Bova novel The Precipice, the ship where some of the action took place, Starpower 1, built to prove that exploration and mining of the Asteroid Belt was feasible and potentially profitable, had a magnetohydrodynamic drive mated to a fusion power plant.

Function (computer programming)

From Wikipedia, the free encyclopedia
https://en.wikipedia.org/wiki/Function_(computer_programming)

In computer programming, a function or subroutine is a sequence of program instructions that performs a specific task, packaged as a unit. This unit can then be used in programs wherever that particular task should be performed.

Functions may be defined within programs, or separately in libraries that can be used by many programs. In different programming languages, a function may be called a routine, subprogram, subroutine, or procedure; in object-oriented programming (OOP), it may be called a method. Technically, these terms all have different definitions, and the nomenclature varies from language to language. The generic umbrella term callable unit is sometimes used.

A function is often coded so that it can be started several times and from several places during one execution of the program, including from other functions, and then branch back (return) to the next instruction after the call, once the function's task is done.

The idea of a subroutine was initially conceived by John Mauchly and Kathleen Antonelli during their work on ENIAC, and recorded in a January 1947 Harvard symposium on "Preparation of Problems for EDVAC-type Machines". Maurice Wilkes, David Wheeler, and Stanley Gill are generally credited with the formal invention of this concept, which they termed a closed sub-routine, contrasted with an open subroutine or macro. However, Alan Turing had discussed subroutines in a paper of 1945 on design proposals for the NPL ACE, going so far as to invent the concept of a return address stack.

Functions are a powerful programming tool, and the syntax of many programming languages includes support for writing and using subroutines. Judicious use of functions (for example, through the structured programming approach) will often substantially reduce the cost of developing and maintaining a large program, while increasing its quality and reliability. Functions, often collected into libraries, are an important mechanism for sharing and trading software. The discipline of OOP is based on objects and methods (which are functions attached to these objects or object classes).

Main concepts

The content of a function is its body, which is the piece of program code that is executed when the function is called or invoked.

A function may be written so that it expects to obtain one or more data values from the calling program (to replace its parameters or formal parameters). The calling program provides actual values for these parameters, called arguments. Different programming languages may use different conventions for passing arguments:

Convention Description Common use
Call by value Argument is evaluated and copy of the value is passed to function Default in most Algol-like languages after Algol 60, such as Pascal, Delphi, Simula, CPL, PL/M, Modula, Oberon, Ada, and many others. C, C++, Java (References to objects and arrays are also passed by value)
Call by reference Reference to an argument, typically its address is passed Selectable in most Algol-like languages after Algol 60, such as Algol 68, Pascal, Delphi, Simula, CPL, PL/M, Modula, Oberon, Ada, and many others. C++, Fortran, PL/I
Call by result Parameter value is copied back to argument on return from the function Ada OUT parameters
Call by value-result Parameter value is copied back on entry to the function and again on return Algol, Swift in-out parameters
Call by name Like a macro – replace the parameters with the unevaluated argument expressions, then evaluate the argument in the context of the caller every time that the called routine uses the parameter. Algol, Scala
Call by constant value Like call by value except that the parameter is treated as a constant PL/I NONASSIGNABLE parameters, Ada IN parameters

A function call may also have side effects such as modifying data structures in a computer memory, reading from or writing to a peripheral device, creating a file, halting the program or the machine, or even delaying the program's execution for a specified time. A subprogram with side effects may return different results each time it is called, even if it is called with the same arguments. An example is a random number function, available in many languages, that returns a different pseudo-random number each time it is called. The widespread use of functions with side effects is a characteristic of imperative programming languages.

A function can be coded so that it may call itself recursively, at one or more places, to perform its task. This method allows direct implementation of functions defined by mathematical induction and recursive divide and conquer algorithms.

A function whose purpose is to compute one boolean-valued function (that is, to answer a yes/no question) is sometimes called a predicate. In logic programming languages, often all functions are called predicates, since they primarily determine success or failure.

A function that returns no value or returns a null value is sometimes called a procedure. Procedures usually modify their arguments and are a core part of procedural programming.

Terminology

A subroutine is a function that doesn't return a value. The primary purpose of functions is to break up complicated computations into meaningful chunks and name them. The function may return a computed value to its caller (its return value), or provide various result values or output parameters. Indeed, a common use of functions is to implement mathematical functions, in which the purpose of the function is purely to compute one or more results whose values are entirely determined by the arguments passed to the function. (Examples might include computing the logarithm of a number or the determinant of a matrix.) In some languages the syntax for a procedure that returns a value is essentially the same as the syntax for a procedure that does not return a value, except for the absence of, e.g., RETURNS clause. In some languages a procedure may dynamically choose to return with or without a value, depending on its arguments.

Language support

High-level programming languages usually include specific constructs to:

  • Delimit the part of the program (body) that makes up the function
  • Assign an identifier (name) to the function
  • Specify the names and data types of its parameters and return values
  • Provide a private naming scope for its temporary variables
  • Identify variables outside the function that are accessible within it
  • Call the function
  • Provide values to its parameters
  • The main program contains the address of the subprogram
  • The subprogram contains the address of the next instruction of the function call in the main program
  • Specify the return values from within its body
  • Return to the calling program
  • Dispose of the values returned by a call
  • Handle any exceptional conditions encountered during the call
  • Package functions into a module, library, object, or class

Some programming languages, such as Pascal, Fortran, Ada and many dialects of BASIC, distinguish between functions or function subprograms, which provide an explicit return value to the calling program, and subroutines or procedures, which do not. In those languages, function calls are normally embedded in expressions (e.g., a sqrt function may be called as y = z + sqrt(x)). Procedure calls either behave syntactically as statements (e.g., a print procedure may be called as if x > 0 then print(x) or are explicitly invoked by a statement such as CALL or GOSUB (e.g., call print(x)). Other languages, such as C and Lisp, do not distinguish between functions and subroutines.

In strictly functional programming languages such as Haskell, subprograms can have no side effects, which means that various internal states of the program will not change. Functions will always return the same result if repeatedly called with the same arguments. Such languages typically only support functions that return a value, since functions that do not return a value have no use unless they can cause a side effect.

In programming languages such as C, C++, and C#, functions that return a value and functions that return no value are both called "functions" (not to be confused with mathematical functions or functional programming, which are different concepts).

A language's compiler will usually translate procedure calls and returns into machine instructions according to a well-defined calling convention, so that functions can be compiled separately from the programs that call them. The instruction sequences corresponding to call and return statements are called the procedure's prologue and epilogue.

Advantages

The advantages of breaking a program into functions include:

  • Decomposing a complex programming task into simpler steps: this is one of the two main tools of structured programming, along with data structures
  • Reducing duplicate code within a program
  • Enabling reuse of code across multiple programs
  • Dividing a large programming task among various programmers or various stages of a project
  • Hiding implementation details from users of the function
  • Improving readability of code by replacing a block of code with a function call where a descriptive function name serves to describe the block of code. This makes the calling code concise and readable even if the function is not meant to be reused.
  • Improving traceability (i.e. most languages offer ways to obtain the call trace which includes the names of the involved functions and perhaps even more information such as file names and line numbers); by not decomposing the code into functions, debugging would be severely impaired

Disadvantages

Compared to using in-line code, invoking a function imposes some computational overhead in the call mechanism.

A function typically requires standard housekeeping code – both at the entry to, and exit from, the function (function prologue and epilogue – usually saving general purpose registers and return address as a minimum).

History

The idea of a subroutine was worked out after computing machines had already existed for some time. The arithmetic and conditional jump instructions were planned ahead of time and have changed relatively little, but the special instructions used for procedure calls have changed greatly over the years. The earliest computers and microprocessors, such as the Manchester Baby and the RCA 1802, did not have a single subroutine call instruction. Subroutines could be implemented, but they required programmers to use the call sequence—a series of instructions—at each call site.

Subroutines were implemented in Konrad Zuse's Z4 in 1945.

In 1945, Alan M. Turing used the terms "bury" and "unbury" as a means of calling and returning from subroutines.

In January 1947 John Mauchly presented general notes at 'A Symposium of Large Scale Digital Calculating Machinery' under the joint sponsorship of Harvard University and the Bureau of Ordnance, United States Navy. Here he discusses serial and parallel operation suggesting

...the structure of the machine need not be complicated one bit. It is possible, since all the logical characteristics essential to this procedure are available, to evolve a coding instruction for placing the subroutines in the memory at places known to the machine, and in such a way that they may easily be called into use.

In other words, one can designate subroutine A as division and subroutine B as complex multiplication and subroutine C as the evaluation of a standard error of a sequence of numbers, and so on through the list of subroutines needed for a particular problem. ... All these subroutines will then be stored in the machine, and all one needs to do is make a brief reference to them by number, as they are indicated in the coding.

Kay McNulty had worked closely with John Mauchly on the ENIAC team and developed an idea for subroutines for the ENIAC computer she was programming during World War II. She and the other ENIAC programmers used the subroutines to help calculate missile trajectories.

Goldstine and von Neumann wrote a paper dated 16 August 1948 discussing the use of subroutines.

Some very early computers and microprocessors, such as the IBM 1620, the Intel 4004 and Intel 8008, and the PIC microcontrollers, have a single-instruction subroutine call that uses a dedicated hardware stack to store return addresses—such hardware supports only a few levels of subroutine nesting, but can support recursive subroutines. Machines before the mid-1960s—such as the UNIVAC I, the PDP-1, and the IBM 1130—typically use a calling convention which saved the instruction counter in the first memory location of the called subroutine. This allows arbitrarily deep levels of subroutine nesting but does not support recursive subroutines. The IBM System/360 had a subroutine call instruction that placed the saved instruction counter value into a general-purpose register; this can be used to support arbitrarily deep subroutine nesting and recursive subroutines. The PDP-11 (1970) is one of the first computers with a stack-pushing subroutine call instruction; this feature also supports both arbitrarily deep subroutine nesting and recursive subroutines.

Language support

In the very early assemblers, subroutine support was limited. Subroutines were not explicitly separated from each other or from the main program, and indeed the source code of a subroutine could be interspersed with that of other subprograms. Some assemblers would offer predefined macros to generate the call and return sequences. By the 1960s, assemblers usually had much more sophisticated support for both inline and separately assembled subroutines that could be linked together.

One of the first programming languages to support user-written subroutines and functions was FORTRAN II. The IBM FORTRAN II compiler was released in 1958. ALGOL 58 and other early programming languages also supported procedural programming.

Libraries

Even with this cumbersome approach, subroutines proved very useful. They allowed the use of the same code in many different programs. Memory was a very scarce resource on early computers, and subroutines allowed significant savings in the size of programs.

Many early computers loaded the program instructions into memory from a punched paper tape. Each subroutine could then be provided by a separate piece of tape, loaded or spliced before or after the main program (or "mainline"); and the same subroutine tape could then be used by many different programs. A similar approach applied in computers that used punched cards for their main input. The name subroutine library originally meant a library, in the literal sense, which kept indexed collections of tapes or card-decks for collective use.

Return by indirect jump

To remove the need for self-modifying code, computer designers eventually provided an indirect jump instruction, whose operand, instead of being the return address itself, was the location of a variable or processor register containing the return address.

On those computers, instead of modifying the function's return jump, the calling program would store the return address in a variable so that when the function completed, it would execute an indirect jump that would direct execution to the location given by the predefined variable.

Jump to subroutine

Another advance was the jump to subroutine instruction, which combined the saving of the return address with the calling jump, thereby minimizing overhead significantly.

In the IBM System/360, for example, the branch instructions BAL or BALR, designed for procedure calling, would save the return address in a processor register specified in the instruction, by convention register 14. To return, the subroutine had only to execute an indirect branch instruction (BR) through that register. If the subroutine needed that register for some other purpose (such as calling another subroutine), it would save the register's contents to a private memory location or a register stack.

In systems such as the HP 2100, the JSB instruction would perform a similar task, except that the return address was stored in the memory location that was the target of the branch. Execution of the procedure would actually begin at the next memory location. In the HP 2100 assembly language, one would write, for example

...
JSB MYSUB    (Calls subroutine MYSUB.)
BB    ...          (Will return here after MYSUB is done.)

to call a subroutine called MYSUB from the main program. The subroutine would be coded as

MYSUB NOP          (Storage for MYSUB's return address.)
AA    ...          (Start of MYSUB's body.)
...
JMP MYSUB,I  (Returns to the calling program.)

The JSB instruction placed the address of the NEXT instruction (namely, BB) into the location specified as its operand (namely, MYSUB), and then branched to the NEXT location after that (namely, AA = MYSUB + 1). The subroutine could then return to the main program by executing the indirect jump JMP MYSUB, I which branched to the location stored at location MYSUB.

Compilers for Fortran and other languages could easily make use of these instructions when available. This approach supported multiple levels of calls; however, since the return address, parameters, and return values of a subroutine were assigned fixed memory locations, it did not allow for recursive calls.

Incidentally, a similar method was used by Lotus 1-2-3, in the early 1980s, to discover the recalculation dependencies in a spreadsheet. Namely, a location was reserved in each cell to store the return address. Since circular references are not allowed for natural recalculation order, this allows a tree walk without reserving space for a stack in memory, which was very limited on small computers such as the IBM PC.

Call stack

Most modern implementations of a function call use a call stack, a special case of the stack data structure, to implement function calls and returns. Each procedure call creates a new entry, called a stack frame, at the top of the stack; when the procedure returns, its stack frame is deleted from the stack, and its space may be used for other procedure calls. Each stack frame contains the private data of the corresponding call, which typically includes the procedure's parameters and internal variables, and the return address.

The call sequence can be implemented by a sequence of ordinary instructions (an approach still used in reduced instruction set computing (RISC) and very long instruction word (VLIW) architectures), but many traditional machines designed since the late 1960s have included special instructions for that purpose.

The call stack is usually implemented as a contiguous area of memory. It is an arbitrary design choice whether the bottom of the stack is the lowest or highest address within this area, so that the stack may grow forwards or backwards in memory; however, many architectures chose the latter.

Some designs, notably some Forth implementations, used two separate stacks, one mainly for control information (like return addresses and loop counters) and the other for data. The former was, or worked like, a call stack and was only indirectly accessible to the programmer through other language constructs while the latter was more directly accessible.

When stack-based procedure calls were first introduced, an important motivation was to save precious memory. With this scheme, the compiler does not have to reserve separate space in memory for the private data (parameters, return address, and local variables) of each procedure. At any moment, the stack contains only the private data of the calls that are currently active (namely, which have been called but haven't returned yet). Because of the ways in which programs were usually assembled from libraries, it was (and still is) not uncommon to find programs that include thousands of functions, of which only a handful are active at any given moment. For such programs, the call stack mechanism could save significant amounts of memory. Indeed, the call stack mechanism can be viewed as the earliest and simplest method for automatic memory management.

However, another advantage of the call stack method is that it allows recursive function calls, since each nested call to the same procedure gets a separate instance of its private data.

In a multi-threaded environment, there is generally more than one stack. An environment that fully supports coroutines or lazy evaluation may use data structures other than stacks to store their activation records.

Delayed stacking

One disadvantage of the call stack mechanism is the increased cost of a procedure call and its matching return. The extra cost includes incrementing and decrementing the stack pointer (and, in some architectures, checking for stack overflow), and accessing the local variables and parameters by frame-relative addresses, instead of absolute addresses. The cost may be realized in increased execution time, or increased processor complexity, or both.

This overhead is most obvious and objectionable in leaf procedures or leaf functions, which return without making any procedure calls themselves. To reduce that overhead, many modern compilers try to delay the use of a call stack until it is really needed. For example, the call of a procedure P may store the return address and parameters of the called procedure in certain processor registers, and transfer control to the procedure's body by a simple jump. If the procedure P returns without making any other call, the call stack is not used at all. If P needs to call another procedure Q, it will then use the call stack to save the contents of any registers (such as the return address) that will be needed after Q returns.

Examples

C and C++

In the C and C++ programming languages, subprograms are termed functions (further classified as member functions when associated with a class, or free functions when not). These languages use the special keyword void to indicate that a function does not return any value. Note that C/C++ functions can have side-effects, including modifying any variables whose addresses are passed as parameters. Examples:

void Function1() { /* some code */ }

The function does not return a value and has to be called as a stand-alone function, e.g., Function1();

int Function2() {
  return 5;
}

This function returns a result (the number 5), and the call can be part of an expression, e.g., x + Function2()

char Function3(int number) {
  char selection[] = {'S', 'M', 'T', 'W', 'T', 'F', 'S'};
  return selection[number];
}

This function converts a number between 0 and 6 into the initial letter of the corresponding day of the week, namely 0 to 'S', 1 to 'M', ..., 6 to 'S'. The result of calling it might be assigned to a variable, e.g., num_day = Function3(number);.

void Function4(int* pointer_to_var) {
  (*pointer_to_var)++;
}

This function does not return a value but modifies the variable whose address is passed as the parameter; it would be called with Function4(&variable_to_increment);.

BASIC dialects

Microsoft Small Basic

Example()                               ' Calls the subroutine

Sub Example                             ' Begins the subroutine
    TextWindow.WriteLine("This is an example of a subroutine in Microsoft Small Basic.")  ' What the subroutine does
EndSub                                  ' Ends the subroutine

In the example above, Example() calls the subroutine. To define the actual subroutine, the Sub keyword must be used, with the subroutine name following Sub. After content has followed, EndSub must be typed.

Visual Basic (classic)

In the Visual Basic (classic) language, subprograms are termed functions or subs (or methods when associated with a class). Visual Basic 6 uses various terms called types to define what is being passed as a parameter. By default, an unspecified variable is registered as a variant type and can be passed as ByRef (default) or ByVal. Also, when a function or sub is declared, it is given a public, private, or friend designation, which determines whether it can be accessed outside the module or project that it was declared in.

  • By value [ByVal] – a way of passing the value of an argument to a procedure by passing a copy of the value, instead of passing the address. As a result, the variable's actual value can't be changed by the procedure to which it is passed.
  • By reference [ByRef] – a way of passing the value of an argument to a procedure by passing an address of the variable, instead of passing a copy of its value. This allows the procedure to access the actual variable. As a result, the variable's actual value can be changed by the procedure to which it is passed. Unless otherwise specified, arguments are passed by reference.
  • Public (optional) – indicates that the function procedure is accessible to all other procedures in all modules. If used in a module that contains an Option Private, the procedure is not available outside the project.
  • Private (optional) – indicates that the function procedure is accessible only to other procedures in the module where it is declared.
  • Friend (optional) – used only in a class module. Indicates that the Function procedure is visible throughout the project, but not visible to a controller of an instance of an object.
Private Function Function1()
    ' Some Code Here
End Function

The function does not return a value and has to be called as a stand-alone function, e.g., Function1

Private Function Function2() as Integer
    Function2 = 5
End Function

This function returns a result (the number 5), and the call can be part of an expression, e.g., x + Function2()

Private Function Function3(ByVal intValue as Integer) as String
    Dim strArray(6) as String
    strArray = Array("M", "T", "W", "T", "F", "S", "S")
    Function3 = strArray(intValue)
End Function

This function converts a number between 0 and 6 into the initial letter of the corresponding day of the week, namely 0 to 'M', 1 to 'T', ..., 6 to 'S'. The result of calling it might be assigned to a variable, e.g., num_day = Function3(number).

Private Function Function4(ByRef intValue as Integer)
    intValue = intValue + 1
End Function

This function does not return a value but modifies the variable whose address is passed as the parameter; it would be called with "Function4(variable_to_increment)".

PL/I

In PL/I a called procedure may be passed a descriptor providing information about the argument, such as string lengths and array bounds. This allows the procedure to be more general and eliminates the need for the programmer to pass such information. By default PL/I passes arguments by reference. A (trivial) function to change the sign of each element of a two-dimensional array might look like:

change_sign: procedure(array);
  declare array(*,*) float;
  array = -array;
end change_sign;

This could be called with various arrays as follows:

/* first array bounds from -5 to +10 and 3 to 9 */
declare array1 (-5:10, 3:9)float;
/* second array bounds from 1 to 16 and 1 to 16 */
declare array2 (16,16) float;
call change_sign(array1);
call change_sign(array2);

Python

In Python, the keyword def is used to define a function. The statements that form the body of the function must either continue on the same line or start on the next line and be indented. The following example program prints "Hello world!" followed by "Wikipedia" on the next line.

def simple_function():
    print('Hello world!')
    print('Wikipedia')
simple_function()
# output will be:
Hello World!
Wikipedia
               

def func(name)
    print("welcome "+name)
print(func("martin"))
#output will be: welcome martin

Local variables, recursion and reentrancy

A subprogram may find it useful to make use of a certain amount of scratch space; that is, memory used during the execution of that subprogram to hold intermediate results. Variables stored in this scratch space are termed local variables, and the scratch space is termed an activation record. An activation record typically has a return address that tells it where to pass control back to when the subprogram finishes.

A subprogram may have any number and nature of call sites. If recursion is supported, a subprogram may even call itself, causing its execution to suspend while another nested execution of the same subprogram occurs. Recursion is a useful means to simplify some complex algorithms and break down complex problems. Recursive languages generally provide a new copy of local variables on each call. If the programmer desires the value of local variables to stay the same between calls, they can be declared static in some languages, or global values or common areas can be used. Here is an example of a recursive function in C/C++ to find Fibonacci numbers:

int Fib(int n) {
  if (n <= 1) {
    return n;
  }
  return Fib(n - 1) + Fib(n - 2);
}

Early languages like Fortran did not initially support recursion because variables were statically allocated, as well as the location for the return address. Early computer instruction sets made storing return addresses and variables on a stack difficult. Machines with index registers or general-purpose registers, e.g., CDC 6000 series, PDP-6, GE 635, System/360, UNIVAC 1100 series, could use one of those registers as a stack pointer.

Modern languages after ALGOL such as PL/I and C almost invariably use a stack, usually supported by most modern computer instruction sets to provide a fresh activation record for every execution of a subprogram. That way, the nested execution is free to modify its local variables without concern for the effect on other suspended executions in progress. As nested calls accumulate, a call stack structure is formed, consisting of one activation record for each suspended subprogram. In fact, this stack structure is virtually ubiquitous, and so activation records are commonly termed stack frames.

Some languages such as Pascal, PL/I, and Ada also support nested functions, which are functions callable only within the scope of an outer (parent) function. Inner functions have access to the local variables of the outer function that called them. This is accomplished by storing extra context information within the activation record, also termed a display.

If a subprogram can be executed properly even when another execution of the same subprogram is already in progress, that subprogram is said to be reentrant. A recursive subprogram must be reentrant. Reentrant subprograms are also useful in multi-threaded situations since multiple threads can call the same subprogram without fear of interfering with each other. In the IBM CICS transaction processing system, quasi-reentrant was a slightly less restrictive, but similar, requirement for application programs that were shared by many threads.

Overloading

In strongly typed languages, it is sometimes desirable to have a number of functions with the same name, but operating on different types of data, or with different parameter profiles. For example, a square root function might be defined to operate on reals, complex values or matrices. The algorithm to be used in each case is different, and the return result may be different. By writing three separate functions with the same name, the programmer has the convenience of not having to remember different names for each type of data. Further, if a subtype can be defined for the reals, to separate positive and negative reals, two functions can be written for the reals, one to return a real when the parameter is positive, and another to return a complex value when the parameter is negative.

In object-oriented programming, when a series of functions with the same name can accept different parameter profiles or parameters of different types, each of the functions is said to be overloaded.

Here is an example of function overloading in C++, demonstrating the implementation of two functions with the same name (Area) but different parameters:

#include <iostream>

double Area(double h, double w) { return h * w; } 
/* The first Area function is for finding the area of a rectangle, 
 * so it accepts two numbers as parameters, for the height and width. */

double Area(double r) { return r * r * 3.14; }
/* The second Area function is for finding the area of a circle,
 * so it only accepts one number as a parameter, for the radius. */

int main() {
  double rectangle_area = Area(3, 4); // This calls the first Area function, because two parameters are provided.
  double circle_area = Area(5);       // This calls the second Area function, because only one parameter is provided.

  std::cout << "Area of a rectangle is " << rectangle_area << std::endl;
  std::cout << "Area of a circle is " << circle_area << std::endl;
}

As another example, a function might construct an object that will accept directions, and trace its path to these points on screen. There are a plethora of parameters that could be passed in to the constructor (colour of the trace, starting x and y co-ordinates, trace speed). If the programmer wanted the constructor to be able to accept only the color parameter, then he could call another constructor that accepts only color, which in turn calls the constructor with all the parameters passing in a set of default values for all the other parameters (X and Y would generally be centered on screen or placed at the origin, and the speed would be set to another value of the coder's choosing).

PL/I has the GENERIC attribute to define a generic name for a set of entry references called with different types of arguments. Example:

DECLARE gen_name GENERIC(
                    name  WHEN(FIXED BINARY),
                    flame  WHEN(FLOAT),
                    pathname OTHERWISE 
                         );

Multiple argument definitions may be specified for each entry. A call to "gen_name" will result in a call to "name" when the argument is FIXED BINARY, "flame" when FLOAT", etc. If the argument matches none of the choices "pathname" will be called.

Closures

A closure is a subprogram together with the values of some of its variables captured from the environment in which it was created. Closures were a notable feature of the Lisp programming language, introduced by John McCarthy. Depending on the implementation, closures can serve as a mechanism for side-effects.

Conventions

A wide number of conventions for the coding of functions have been developed. Pertaining to their naming, many developers have adopted the approach that the name of a function should be a verb when it does a certain task, and adjective when it makes some inquiry, and a noun when it is used to substitute variables.

Some programmers suggest that a function should perform only one task, and if a function does perform more than one task, it should be split up into more functions. They argue that functions are key components in code maintenance, and their roles in the program must remain distinct.

Proponents of modular programming (modularizing code) advocate that each function should have minimal dependency on other pieces of code. For example, the use of global variables is generally deemed unwise by advocates for this perspective, because it adds tight coupling between the function and these global variables. If such coupling is not necessary, their advice is to refactor functions to accept passed parameters instead. However, increasing the number of parameters passed to functions can affect code readability.

Return codes

Besides its main or normal effect, a subroutine may need to inform the calling program about exceptional conditions that may have occurred during its execution. In some languages and programming standards, this is often done through a return code, an integer value placed by the subprogram in some standard location, which encodes the normal and exceptional conditions.

In the IBM System/360, where return code was expected from the subroutine, the return value was often designed to be a multiple of 4—so that it could be used as a direct branch table index into a branch table often located immediately after the call instruction to avoid extra conditional tests, further improving efficiency. In the System/360 assembly language, one would write, for example:

           BAL  14, SUBRTN01    go to a subroutine, storing return address in R14
           B    TABLE(15)      use returned value in reg 15 to index the branch table,
*                              branching to the appropriate branch instr.
TABLE      B    OK             return code =00   GOOD                  }
           B    BAD            return code =04   Invalid input         } Branch table
           B    ERROR          return code =08   Unexpected condition  }

Optimization of function calls

There is a significant runtime overhead in calling a function, including passing the arguments, branching to the subprogram, and branching back to the caller. The overhead often includes saving and restoring certain processor registers, allocating and reclaiming call frame storage, etc. In some languages, each function call also implies automatic testing of the function's return code or the handling of exceptions that it may raise. A significant source of overhead in object-oriented languages is the intensively used dynamic dispatch for method calls.

There are some seemingly obvious optimizations of procedure calls that cannot be applied if the procedures may have side effects. For example, in the expression (f(x)-1)/(f(x)+1), the function f must be called twice, because the two calls may return different results. Moreover, in the few languages which define the order of evaluation of the division operator's operands, the value of x must be fetched again before the second call, since the first call may have changed it. Determining whether a subprogram may have a side effect is very difficult (indeed, undecidable by virtue of Rice's theorem). So, while those optimizations are safe in purely functional programming languages, compilers of typical imperative programming usually have to assume the worst.

Inlining

A method used to eliminate this overhead is inline expansion or inlining of the subprogram's body at each call site (versus branching to the function and back). Not only does this avoid the call overhead, but it also allows the compiler to optimize the procedure's body more effectively by taking into account the context and arguments at that call. The inserted body can be optimized by the compiler. Inlining, however, will usually increase the code size, unless the program contains only one call to the function.

Algorithmic information theory

From Wikipedia, the free encyclopedia https://en.wikipedia.org/wiki/Algorithmic_information_theory ...