diff --git a/01_Fundamentals.ipynb b/01_Fundamentals.ipynb new file mode 100644 index 00000000..338b99ed --- /dev/null +++ b/01_Fundamentals.ipynb @@ -0,0 +1,742 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Modules/packages/libraries\n", + "\n", + "Definitions:\n", + "\n", + " * Modules:\n", + " A module is a file which contains python functions, global variables etc. It is nothing but .py file which has python executable code / statement.\n", + "\n", + " * Packages:\n", + " A package is namespace which contains multiple package/modules. It is a directory which contains a special file `__init__.py`\n", + " \n", + " * Libraries:\n", + " A library is a collection of various packages. There is no difference between package and python library conceptually.\n", + " \n", + "Modules/packages/libraries can be easily \"imported\" and made functional in your python code. A set of libriaries comes with every python installation. Others can be installed locally and then imported. Your own code sitting somewhere else in your local computer can be imported too.\n", + "\n", + "Further details (very important!) on packages and how to create them can be found online. We may find the need of creating our own during the course." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "###### all the \"stuff\" that is in the math library can be used\n", + "import math\n", + "print(math.pi)\n", + "\n", + "# you can give math a label for convenience\n", + "import math as m\n", + "print (m.pi)\n", + "\n", + "# alternatively you can import only a given \"thing\" from the library\n", + "from math import pi #you can add several libraries at once, just list them separated by a \", \"\n", + "print (pi)\n", + "\n", + "# or just get everything (very dangerous!!!)\n", + "from math import *\n", + "print (sqrt(7))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To know which modules are there for you to use just type:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print (help('modules') )\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`pip` is a special package. It is used from the command line to install properly (e.g. matching the version of the local packages) new packages. It can also be used from within python to check i.e. the set installed packages and their versions. N.B.: only the installed packages on top of the default ones will be listed " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pip\n", + "sorted([\"%s==%s\" % (i.key, i.version) for i in pip._internal.utils.misc.get_installed_distributions()])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Functions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def square(x):\n", + " \"\"\"Square of x.\"\"\"\n", + " return x*x\n", + "\n", + "def cube(x):\n", + " \"\"\"Cube of x.\"\"\"\n", + " return x*x*x\n", + "\n", + "# create a dictionary of functions\n", + "funcs = {\n", + " 'square': square,\n", + " 'cube': cube,\n", + "}\n", + "\n", + "x = 3\n", + "print(square(x))\n", + "print(cube(x))\n", + "\n", + "for func in sorted(funcs):\n", + " print (func, funcs[func](x))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Functions arguments" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What is passsed to a function is a copy of the input. Imagine we have a list *x =[1, 2, 3]*, i.e. a mutable object. If within the function the content of *x* is directly changed (e.g. *x[0] = 999*), then *x* changes outside the funciton as well. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def modify(x):\n", + " x[0] = 999\n", + " return x\n", + "\n", + "x = [1,2,3]\n", + "print (x)\n", + "print (modify(x))\n", + "print (x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, if *x* is reassigned within the function to a new object (e.g. another list), then the copy of the name *x* now points to the new object, but *x* outside the function is unhcanged." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def no_modify(x):\n", + " x = [4,5,6]\n", + " return x\n", + "\n", + "x = [1,2,3]\n", + "print (x)\n", + "print (no_modify(x))\n", + "print (x)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What if the function tries to modify the value of an immutable object?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Binding of default arguments occurs at function definition:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def f(x = []):\n", + " x.append(1)\n", + " return x\n", + "\n", + "print (f())\n", + "print (f())\n", + "print (f(x = [9,9,9]))\n", + "print (f())\n", + "print (f())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Try to aviod that!!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def f(x = None):\n", + " if x is None:\n", + " x = []\n", + " x.append(1)\n", + " return x\n", + "\n", + "print (f())\n", + "print (f())\n", + "print (f(x = [9,9,9]))\n", + "print (f())\n", + "print (f())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Higher order functions\n", + "\n", + "A function that uses another function as an input argument or returns a function is known as a higher-order function (HOF). The most familiar examples are `map` and `filter`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### map\n", + "\n", + "The map function applies a function to each member of a collection" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = list(map(square, range(5))) \n", + "print (x)\n", + "\n", + "# Note the difference w.r.t python 2. In python 3 map retuns an iterator so you can do stuff like:\n", + "for i in map(square,range(5)): print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### filter\n", + "\n", + "The filter function applies a predicate to each memmber of a collection, retaining only those members where the predicate is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def is_even(x):\n", + " return x%2 == 0\n", + "\n", + "print (list(filter(is_even, range(5))))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(map(square, filter(is_even, range(5))))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### reduce\n", + "\n", + "The reduce function reduces a collection using a binary operator to combine items two at a time. More often than not reduce can be substituted with a more efficient for loop. It is worth mentioning it for its key role in big-data applications together with map (the map-reduce paradigm). \n", + "N.B.: it no loger exist as built-in function in python 3, it is now part of the `functools` library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from functools import reduce\n", + "\n", + "def my_add(x, y):\n", + " return x + y\n", + "\n", + "# another implementation of the sum function\n", + "reduce(my_add, [1,2,3,4,5])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### zip\n", + "\n", + "zip is useful when you need to iterate over matched elements of multiple lists" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "xs = [1, 2, 3, 4]\n", + "ys = [10, 20, 30, 40]\n", + "zs = ['a', 'b', 'c', 'd', 'e']\n", + "\n", + "for x, y, z in zip(xs, ys, zs):\n", + " print (x, y, z)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Custom HOF" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def custom_sum(xs, transform):\n", + " \"\"\"Returns the sum of xs after a user specified transform.\"\"\"\n", + " return sum(map(transform, xs))\n", + "\n", + "xs = range(5)\n", + "print (custom_sum(xs, square))\n", + "print (custom_sum(xs, cube))\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Returning a function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def make_logger(target):\n", + " def logger(data):\n", + " with open(target, 'a') as f:\n", + " f.write(data + '\\n')\n", + " return logger\n", + "\n", + "foo_logger = make_logger('foo.txt') #foo.txt will be created if not there already\n", + "foo_logger('Hello')\n", + "foo_logger('World')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "! cat 'foo.txt'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Anonimous functions (lambda)\n", + "\n", + "When using functional style, there is often the need to create specific functions that perform a limited task as input to a HOF such as map or filter. In such cases, these functions are often written as anonymous or lambda functions. \n", + "The syntax is as follows:\n", + "\n", + "lambda *arguments* : *expression*\n", + "\n", + "\n", + "If you find it hard to understand what a lambda function is doing, it should probably be rewritten as a regular function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sum = lambda x,y: x+y\n", + "sum(3,4)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for i in map(lambda x: x*x, range(5)): print (i)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# what does this function do?\n", + "from functools import reduce\n", + "s1 = reduce(lambda x, y: x+y, map(lambda x: x**2, range(1,10)))\n", + "print(s1)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Recursive functions " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def fib1(n):\n", + " \"\"\"Fib with recursion.\"\"\"\n", + "\n", + " # base case\n", + " if n==0 or n==1:\n", + " return 1\n", + " # recurssive case\n", + " else:\n", + " return fib1(n-1) + fib1(n-2)\n", + "\n", + " \n", + "print ([fib1(i) for i in range(10)])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# In Python, a more efficient version that does not use recursion is\n", + "\n", + "def fib2(n):\n", + " \"\"\"Fib without recursion.\"\"\"\n", + " a, b = 0, 1\n", + " for i in range(1, n+1):\n", + " a, b = b, a+b\n", + " return b\n", + "\n", + "print ([fib2(i) for i in range(10)])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# check indeed the timing:\n", + "\n", + "%timeit fib1(20)\n", + "%timeit fib2(20)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Iterators\n", + "\n", + "Iterators represent streams of values. Because only one value is consumed at a time, they use very little memory. Use of iterators is very helpful for working with data sets too large to fit into RAM." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Iterators can be created from sequences with the built-in function iter()\n", + "\n", + "xs = [1,2,3]\n", + "x_iter = iter(xs)\n", + "\n", + "print (next(x_iter))\n", + "print (next(x_iter))\n", + "print (next(x_iter))\n", + "print (next(x_iter))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Most commonly, iterators are used (automatically) within a for loop\n", + "# which terminates when it encouters a StopIteration exception\n", + "\n", + "x_iter = iter(xs)\n", + "for x in x_iter:\n", + " print (x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## More on comprehensions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# A generator expression\n", + "\n", + "print ((x for x in range(10)))\n", + "\n", + "# A list comprehesnnion\n", + "\n", + "print ([x for x in range(10)])\n", + "\n", + "# A set comprehension\n", + "\n", + "print ({x for x in range(10)})\n", + "\n", + "# A dictionary comprehension\n", + "\n", + "print ({x: x for x in range(10)})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Useful Modules\n", + "\n", + "You may want to have a look at the content of the following modules for further usage of (HO) functions:\n", + " - [operator](https://docs.python.org/3/library/operator.html)\n", + " - [functools](https://docs.python.org/3/library/functools.html)\n", + " - [itertools](https://docs.python.org/3/library/itertools.html)\n", + " - [toolz](https://pypi.org/project/toolz/)\n", + " - [funcy](https://pypi.org/project/funcy/)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Decorators\n", + "\n", + "Decorators are a type of HOF that take a function and return a wrapped function that provides additional useful properties.\n", + "\n", + "Examples:\n", + "\n", + " - logging\n", + " - profiling\n", + " - Just-In-Time (JIT) compilation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def my_decorator(func):\n", + " def wrapper():\n", + " print(\"Something is happening before the function is called.\")\n", + " func()\n", + " print(\"Something is happening after the function is called.\")\n", + " return wrapper\n", + "\n", + "def say_whee():\n", + " print(\"Whee!\")\n", + "\n", + "say_whee = my_decorator(say_whee)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "say_whee()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Python allows you to use decorators in a simpler way with the @ symbol, sometimes called the “pie” syntax" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def my_decorator(func):\n", + " def wrapper():\n", + " print(\"Something is happening before the function is called.\")\n", + " func()\n", + " print(\"Something is happening after the function is called.\")\n", + " return wrapper\n", + "\n", + "@my_decorator\n", + "def say_whee():\n", + " print(\"Whee!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "say_whee()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Classes and Objects\n", + "\n", + "Old school object-oriented programming is possible and often used in python. Classes are defined similarly to standard object-oriented languages, with similar functionalities.\n", + "\n", + "The main python doc [page](https://docs.python.org/3.6/tutorial/classes.html) is worth reading through " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Pet:\n", + " # the \"constructor\"\n", + " def __init__(self, name, age): #inizialize the elements of the class\n", + " self.name=name\n", + " self.age=age\n", + " # class functions take the \"self\" parameter !!!\n", + " def set_name(self,name):\n", + " self.name=name\n", + " def convert_age(self,factor):\n", + " self.age*=factor\n", + "\n", + "buddy=Pet(\"buddy\",12)\n", + "print (buddy.name, buddy.age)\n", + "buddy.age=3\n", + "print (buddy.age)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ineritance is straightforward\n", + "class Dog(Pet):\n", + " # the following variables is \"global\", i.e. holds for all \"Dog\" objects\n", + " species = \"mammal\"\n", + " # functions can be redefined as usual\n", + " def convert_age(self):\n", + " self.age*=7\n", + " def set_species(self, species):\n", + " self.species = species\n", + " \n", + "puppy=Dog(\"tobia\",10)\n", + "print(puppy.name)\n", + "puppy.convert_age()\n", + "print(puppy.age)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/01ex_Fundamentals.ipynb b/01ex_Fundamentals.ipynb new file mode 100644 index 00000000..ebb0a167 --- /dev/null +++ b/01ex_Fundamentals.ipynb @@ -0,0 +1,166 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1\\. Write the following as a list comprehension" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 1\n", + "ans = []\n", + "for i in range(3):\n", + " for j in range(4):\n", + " ans.append((i, j))\n", + "print (ans)\n", + "\n", + "# 2\n", + "ans = map(lambda x: x*x, filter(lambda x: x%2 == 0, range(5)))\n", + "print (list(ans))\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2\\. Convert the following function into a pure function with no global variables or side effects" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = 5\n", + "def f(alist):\n", + " for i in range(x):\n", + " alist.append(i)\n", + " return alist\n", + "\n", + "alist = [1,2,3]\n", + "ans = f(alist)\n", + "print (ans)\n", + "print (alist) # alist has been changed!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3\\. Write a `decorator` hello that makes every wrapped function print “Hello!”, i.e. something like:\n", + "\n", + "```python\n", + "@hello\n", + "def square(x):\n", + " return x*x\n", + "```\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4\\. Write the factorial function so that it a) does and b) does not use recursion." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5\\. Use HOFs (zip in particular) to compute the weight of a circle, a disk and a sphere, assuming different radii and different densities:\n", + "\n", + "```python\n", + "densities = {\"Al\":[0.5,1,2],\"Fe\":[3,4,5],\"Pb\": [15,20,30]}\n", + "radii = [1,2,3]\n", + "```\n", + "\n", + "where the entries of the dictionary's values are the linear, superficial and volumetric densities of the materials respectively.\n", + "\n", + "In particular define a list of three lambda functions using a comprehension that computes the circumference, the area and the volume for a given radius.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "6\\. Edit the class defintion to add an instance attribute of is_hungry = True to the Dog class. Then add a method called eat() which changes the value of is_hungry to False when called. Figure out the best way to feed each dog and then output “My dogs are hungry.” if all are hungry or “My dogs are not hungry.” if all are not hungry. The final output should look like this:\n", + "\n", + "`I have 3 dogs. \n", + "Tom is 6. \n", + "Fletcher is 7. \n", + "Larry is 9. \n", + "And they're all mammals, of course. \n", + "My dogs are not hungry.\n", + "`\n", + "\n", + "```python\n", + "# Parent class\n", + "class Dog:\n", + "\n", + " # Class attribute\n", + " species = 'mammal'\n", + "\n", + " # Initializer / Instance attributes\n", + " def __init__(self, name, age):\n", + " self.name = name\n", + " self.age = age\n", + "\n", + " # instance method\n", + " def description(self):\n", + " return \"{} is {} years old\".format(self.name, self.age)\n", + "\n", + " # instance method\n", + " def speak(self, sound):\n", + " return \"{} says {}\".format(self.name, sound)\n", + "\n", + "# Child class (inherits from Dog class)\n", + "class RussellTerrier(Dog):\n", + " def run(self, speed):\n", + " return \"{} runs {}\".format(self.name, speed)\n", + "\n", + "# Child class (inherits from Dog class)\n", + "class Bulldog(Dog):\n", + " def run(self, speed):\n", + " return \"{} runs {}\".format(self.name, speed)\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/README_old.md b/README_old.md new file mode 100644 index 00000000..0535201a --- /dev/null +++ b/README_old.md @@ -0,0 +1,98 @@ +# LaboratoryOfComputationalPhysics + +Notebooks guiding students through the world of data analysis with python. + +This repo should be forked by each individual student. Exercises should be committed to the student's repo and notified to the professor by a pull request. +Such pull request should be made on this remote repo under the corresponding student branch (Dedicated branches will indeed be created in due time). + +## IPython notebooks instructions and tips +Notebooks are extremely powerful tools, you may find useful to discover some of their functionalities on this tutorial [page](https://nbviewer.jupyter.org/github/ipython/ipython/blob/3.x/examples/Notebook/Index.ipynb) or on this by checking this [list](https://www.dataquest.io/blog/jupyter-notebook-tips-tricks-shortcuts/) of tips + +## Git Instructions + +To start with, you need to have a github account. If you don't have one, go to [github](github.com) and follow instructions on how to create it. + +Suggestion: use a reasonable username that resembles your actual name. + +Once you have your github, as first thing fork this repository, i.e. go [there](https://github.com/PhysicsOfData/LaboratoryOfComputationalPhysics_Y4) and click on the top-right button *fork* + +### Setting up a local repository + +What follows needs to be done any time a new local repository is created. +In particular, if you are working in a location where such repo already exist, what follows doesn't need to be repeated. + * Clone your (forked) repository (i.e. create a local repository cloned from the remote one) + +`git clone https://github.com/YOUR_GIT_ACCOUNT/LaboratoryOfComputationalPhysics_Y4.git` + + where YOUR_GIT_ACCOUNT it your account on github. Now you can get to your local working folder: + + `cd LaboratoryOfComputationalPhysics_Y4/` + + * Configure your username and email: + +`git config --global user.name "YOUR_GIT_ACCOUNT"` + +`git config --global user.email "YOUR_EMAIL_ADDRESS"` + +(you must have understood what capital-letters-words stand for). Your git configuration is stored in `.gitconfig`, a file that you can alwasy edit by hand or via the `git config ..` commands. + +* Define PhysicsOfData's repo as the upstream repository (you may need to set the url too), check that actually succeeded and get (fetch) the updates that have been done on the remote repository: + +`git remote add upstream https://github.com/PhysicsOfData/LaboratoryOfComputationalPhysics_Y4.git` + +`git remote set-url origin https://YOUR_GIT_ACCOUNT@github.com/YOUR_GIT_ACCOUNT/LaboratoryOfComputationalPhysics_Y4.git` + +`git remote -v` + +`git fetch upstream` + + * The default branch is `master`, you should now create your development branch where to play and exercise with the code. Note that however you have a branch corresponding to you (name_surname) in the upstream repository (`upstream/name_surname`): that is the branch you should point the pull request to. In order to set up a proper development cycle, you must create a branch (in the example below called TRACKING_BRANCH_NAME) that *tracks* `upstream/name_surname`: + +`git branch -vv` + +`git checkout -b TRACKING_BRANCH_NAME upstream/name_surname` + +Note that the case you decide do make your development in a branch that does NOT track `upstream/name_surname`, you'll eventually need to merge your changes into the branch tracking `upstream/name_surname` which is the one you'll make the pull request for (see later). + +### Standard development cycle + + * Before starting to develop on the machine that you are using, in order to update the work with the last changes (suppose you have made some change from your home computer), type the follow command + +`git pull` + + * Before starting with the development you could check whether the orginal repository (PhysicsOfData's one) have been updated with respect to your forked version (that's likely to be the case prior to every lab class). If it had, then merge the chances into your master. + + `git fetch upstream` + + `git checkout master` + + `git merge upstream/master` + + The idea is that your master always reflects `upstream/master`, i.e. it keeps a local copy of the reference code as a starting point for your developments (i.e. solving the assigned problems). + Note that in order to update your repository on github, you need to push the local version (see later). + + * In the case a pull request of yours to PhysicsOfData has been recently approved, you also need to synch your development branch: + + `git checkout TRACKING_BRANCH_NAME` + + `git merge upstream/name_surname` + + * You may also need to get the updates from the master, i.e. need to merge the master: + + `git merge master` + + * Now do the real stuff, i.e. developing some code. Image you create a NEW_FILE. Add the file to your local repository and stages it for commit (To unstage a file, use 'git reset HEAD NEW_FILE)' + + `git add NEW_FILE` + + * Commits the (tracked) changes you made to the file and prepares them to be pushed to your remote repository on github + + `git commit -m "Add existing file"` + +(what follows after `-m` is a comment to later remind what was that commit about) + + * Now you want to propagate (push) your local changes to your remote repository on github (`origin`) + + `git push origin TRACKING_BRANCH_NAME` + + * Finally you may want to propagate your development also to the repo you originally forked from, i.e. PhysicsOfData's one (this is likely to happen anytime you'll be asked to deliver your homework!). For that you need to go for a "pull request", which is done from github itself. Be carefull to point your pull request to `PhysicsOfData/name_surname`